As of PostgreSQL 9.4: Different to the documentation of CREATE VIEW, the documentation of CREATE MATERIALIZED VIEW does NOT mention the REPLACE keyword. There seems to be no shortcut aside from dropping all dependent objects and rebuilding each one.
When you do so, I can only recommend two small things:
- Use DROP MATERIALIZED VIEW blabla CASCADE to get a list of all dependent objects
- Do the drop and recreation of all dependent object in one transaction.
To create a copy as close as possible you must use INCLUDING ALL
with CREATE TABLE .. (LIKE ..)
since there can be any number of columns with defaults that you obviously want to copy.
You just want serial
columns to get their own, independent sequence, which makes a lot of sense and probably should be the default behavior. For now, this should do the job:
Function to copy any given table
Copies any given table (must exist) with new given name and independent serial
columns (if any).
Data is not included, it's trivial to copy that, too.
CREATE OR REPLACE FUNCTION f_copy_tbl(_tbl regclass, _newtbl text)
RETURNS void AS
$func$
DECLARE
_sql text;
BEGIN
-- Copy table
EXECUTE format('CREATE TABLE %I (LIKE %s INCLUDING ALL);', _newtbl, _tbl);
-- Fix serial columns, if any
SELECT INTO _sql
string_agg('CREATE SEQUENCE ' || seq, E';\n') || E';\n'
|| string_agg(format('ALTER SEQUENCE %s OWNED BY %I.%I'
, seq, _newtbl, a.attname), E';\n') || E';\n'
|| 'ALTER TABLE ' || quote_ident(_newtbl) || E'\n '
|| string_agg(format($$ALTER %I SET DEFAULT nextval('%s'::regclass)$$
, a.attname, seq), E'\n, ')
FROM pg_attribute a
JOIN pg_attrdef ad ON ad.adrelid = a.attrelid
AND ad.adnum = a.attnum
, quote_ident(_newtbl || '_' || a.attname || '_seq') AS seq -- new seq name
WHERE a.attrelid = _tbl
AND a.attnum > 0
AND NOT a.attisdropped
AND a.atttypid = ANY ('{int,int8,int2}'::regtype[])
AND ad.adsrc = 'nextval('''
|| (pg_get_serial_sequence (a.attrelid::regclass::text, a.attname))::regclass
|| '''::regclass)'
;
IF _sql IS NOT NULL THEN
EXECUTE _sql;
END IF;
END
$func$ LANGUAGE plpgsql VOLATILE;
Call:
SELECT f_copy_tbl('tbl', 'tbl1');
Produces and executes SQL code of the form:
CREATE TABLE tbl1 (LIKE tbl INCLUDING ALL);
-- only if there are serial columns:
CREATE SEQUENCE tbl1_tbl_id_seq; -- one line per serial type ..
CREATE SEQUENCE "tbl1_Odd_COL_seq"; -- .. two in this example
ALTER SEQUENCE tbl1_tbl_id_seq OWNED BY tbl1.tbl_id;
ALTER SEQUENCE "tbl1_Odd_COL_seq" OWNED BY tbl1."Odd_COL";
ALTER TABLE tbl1
ALTER tbl_id SET DEFAULT nextval('tbl1_tbl_id_seq'::regclass)
, ALTER "Odd_COL" SET DEFAULT nextval('"tbl1_Odd_COL_seq"'::regclass);
Only serial
columns get their own sequence. Other column default are copied unchanged - including nextval()
from a sequence that is not owned by the column or differs in any way from a serial
.
The function is safe against SQL injection and should work with arbitrary table and column names.
SQL Fiddle.
It would be a useful option for the LIKE
feature to create separate sequences for serial
columns. You may want to post a friendly feature request to pgsql-general.
Best Answer
By default, the error message tells you more. Like:
General solution
Include the object mentioned in the
Detail
line in your script.Also, to do it safely, wrap all of it in a transaction, and write-lock the old table before working on the new to avoid losing work done during the transition:
There may be more depending objects. Same procedure.
In the example, to get the view definition:
See:
And you may want to rename constraints and indices to fit the renamed table - or hold back on those and create with correct names after renaming the new table. (Again, be sure to get the definition before dropping the old table!)
Related:
If there are many depending objects and the table is not too big, a good alternative might be to prepare the new table (possibly as temporary table for better performance), then
TRUNCATE
the original andINSERT
from the prepared new table. This way, depending objects like views, FK constraints, rules, function using the table type etc. can just stay in place. See:Or you might be able to update the old table in place? This related answer shows code for all three variants:
Specific solution
Your particular error message:
... indicates that you are (ab-)using a sequence to generate default values for multiple tables. Or at least it's owned by the wrong column. The solution depends on how you want to deal with the situation.
Or you created the new table copying the complete structure including default values depending on the same sequence.
Typically, you want a dedicated sequence. So you should probably make the new column own the
SEQUENCE
:DROP TABLE
only cascades to the sequence if it's owned by one of the columns in the table to be dropped.Related:
You may be interested in
IDENTITY
columns which avoid this kind of complication withserial
columns. See: