mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-04 08:47:21 +02:00
pg_dump: Fix dumping of inherited generated columns
Generation expressions of generated columns are always inherited, so there is no need to set them separately in child tables, and there is no syntax to do so either. The code previously used the code paths for the handling of default values, for which different rules apply; in particular it might want to set a default value explicitly for an inherited column. This resulted in unrestorable dumps. For generated columns, just skip them in inherited tables. Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://www.postgresql.org/message-id/flat/15830.1575468847%40sss.pgh.pa.us
This commit is contained in:
parent
0fe8b1f7d4
commit
1d3ce0223c
@ -431,12 +431,22 @@ flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||||||
/* flagInhAttrs -
|
/* flagInhAttrs -
|
||||||
* for each dumpable table in tblinfo, flag its inherited attributes
|
* for each dumpable table in tblinfo, flag its inherited attributes
|
||||||
*
|
*
|
||||||
* What we need to do here is detect child columns that inherit NOT NULL
|
* What we need to do here is:
|
||||||
* bits from their parents (so that we needn't specify that again for the
|
*
|
||||||
* child) and child columns that have DEFAULT NULL when their parents had
|
* - Detect child columns that inherit NOT NULL bits from their parents, so
|
||||||
* some non-null default. In the latter case, we make up a dummy AttrDefInfo
|
* that we needn't specify that again for the child.
|
||||||
* object so that we'll correctly emit the necessary DEFAULT NULL clause;
|
*
|
||||||
* otherwise the backend will apply an inherited default to the column.
|
* - Detect child columns that have DEFAULT NULL when their parents had some
|
||||||
|
* non-null default. In this case, we make up a dummy AttrDefInfo object so
|
||||||
|
* that we'll correctly emit the necessary DEFAULT NULL clause; otherwise
|
||||||
|
* the backend will apply an inherited default to the column.
|
||||||
|
*
|
||||||
|
* - Detect child columns that have a generation expression when their parents
|
||||||
|
* also have one. Generation expressions are always inherited, so there is
|
||||||
|
* no need to set them again in child tables, and there is no syntax for it
|
||||||
|
* either. (Exception: In binary upgrade mode we dump them because
|
||||||
|
* inherited tables are recreated standalone first and then reattached to
|
||||||
|
* the parent.)
|
||||||
*
|
*
|
||||||
* modifies tblinfo
|
* modifies tblinfo
|
||||||
*/
|
*/
|
||||||
@ -474,6 +484,7 @@ flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
|
|||||||
{
|
{
|
||||||
bool foundNotNull; /* Attr was NOT NULL in a parent */
|
bool foundNotNull; /* Attr was NOT NULL in a parent */
|
||||||
bool foundDefault; /* Found a default in a parent */
|
bool foundDefault; /* Found a default in a parent */
|
||||||
|
bool foundGenerated; /* Found a generated in a parent */
|
||||||
|
|
||||||
/* no point in examining dropped columns */
|
/* no point in examining dropped columns */
|
||||||
if (tbinfo->attisdropped[j])
|
if (tbinfo->attisdropped[j])
|
||||||
@ -481,6 +492,7 @@ flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
|
|||||||
|
|
||||||
foundNotNull = false;
|
foundNotNull = false;
|
||||||
foundDefault = false;
|
foundDefault = false;
|
||||||
|
foundGenerated = false;
|
||||||
for (k = 0; k < numParents; k++)
|
for (k = 0; k < numParents; k++)
|
||||||
{
|
{
|
||||||
TableInfo *parent = parents[k];
|
TableInfo *parent = parents[k];
|
||||||
@ -492,7 +504,8 @@ flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
|
|||||||
if (inhAttrInd >= 0)
|
if (inhAttrInd >= 0)
|
||||||
{
|
{
|
||||||
foundNotNull |= parent->notnull[inhAttrInd];
|
foundNotNull |= parent->notnull[inhAttrInd];
|
||||||
foundDefault |= (parent->attrdefs[inhAttrInd] != NULL);
|
foundDefault |= (parent->attrdefs[inhAttrInd] != NULL && !parent->attgenerated[inhAttrInd]);
|
||||||
|
foundGenerated |= parent->attgenerated[inhAttrInd];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,6 +547,10 @@ flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
|
|||||||
|
|
||||||
tbinfo->attrdefs[j] = attrDef;
|
tbinfo->attrdefs[j] = attrDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove generation expression from child */
|
||||||
|
if (foundGenerated && !dopt->binary_upgrade)
|
||||||
|
tbinfo->attrdefs[j] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8736,13 +8736,37 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
|
|||||||
attrdefs[j].dobj.dump = tbinfo->dobj.dump;
|
attrdefs[j].dobj.dump = tbinfo->dobj.dump;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defaults on a VIEW must always be dumped as separate ALTER
|
* Figure out whether the default/generation expression should
|
||||||
* TABLE commands. Defaults on regular tables are dumped as
|
* be dumped as part of the main CREATE TABLE (or similar)
|
||||||
* part of the CREATE TABLE if possible, which it won't be if
|
* command or as a separate ALTER TABLE (or similar) command.
|
||||||
* the column is not going to be emitted explicitly.
|
* The preference is to put it into the CREATE command, but in
|
||||||
|
* some cases that's not possible.
|
||||||
*/
|
*/
|
||||||
if (tbinfo->relkind == RELKIND_VIEW)
|
if (tbinfo->attgenerated[adnum - 1])
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Column generation expressions cannot be dumped
|
||||||
|
* separately, because there is no syntax for it. The
|
||||||
|
* !shouldPrintColumn case below will be tempted to set
|
||||||
|
* them to separate if they are attached to an inherited
|
||||||
|
* column without a local definition, but that would be
|
||||||
|
* wrong and unnecessary, because generation expressions
|
||||||
|
* are always inherited, so there is no need to set them
|
||||||
|
* again in child tables, and there is no syntax for it
|
||||||
|
* either. By setting separate to false here we prevent
|
||||||
|
* the "default" from being processed as its own dumpable
|
||||||
|
* object, and flagInhAttrs() will remove it from the
|
||||||
|
* table when it detects that it belongs to an inherited
|
||||||
|
* column.
|
||||||
|
*/
|
||||||
|
attrdefs[j].separate = false;
|
||||||
|
}
|
||||||
|
else if (tbinfo->relkind == RELKIND_VIEW)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Defaults on a VIEW must always be dumped as separate
|
||||||
|
* ALTER TABLE commands.
|
||||||
|
*/
|
||||||
attrdefs[j].separate = true;
|
attrdefs[j].separate = true;
|
||||||
}
|
}
|
||||||
else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
|
else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
|
||||||
@ -8753,7 +8777,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
attrdefs[j].separate = false;
|
attrdefs[j].separate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attrdefs[j].separate)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Mark the default as needing to appear before the table,
|
* Mark the default as needing to appear before the table,
|
||||||
* so that any dependencies it has must be emitted before
|
* so that any dependencies it has must be emitted before
|
||||||
|
@ -2487,6 +2487,52 @@ my %tests = (
|
|||||||
unlike => { exclude_dump_test_schema => 1, },
|
unlike => { exclude_dump_test_schema => 1, },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'CREATE TABLE test_table_generated_child1 (without local columns)' => {
|
||||||
|
create_order => 4,
|
||||||
|
create_sql => 'CREATE TABLE dump_test.test_table_generated_child1 ()
|
||||||
|
INHERITS (dump_test.test_table_generated);',
|
||||||
|
regexp => qr/^
|
||||||
|
\QCREATE TABLE dump_test.test_table_generated_child1 (\E\n
|
||||||
|
\)\n
|
||||||
|
\QINHERITS (dump_test.test_table_generated);\E\n
|
||||||
|
/xms,
|
||||||
|
like =>
|
||||||
|
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
|
||||||
|
unlike => {
|
||||||
|
binary_upgrade => 1,
|
||||||
|
exclude_dump_test_schema => 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'ALTER TABLE test_table_generated_child1' => {
|
||||||
|
regexp =>
|
||||||
|
qr/^\QALTER TABLE ONLY dump_test.test_table_generated_child1 ALTER COLUMN col2 \E/m,
|
||||||
|
|
||||||
|
# should not get emitted
|
||||||
|
like => {},
|
||||||
|
},
|
||||||
|
|
||||||
|
'CREATE TABLE test_table_generated_child2 (with local columns)' => {
|
||||||
|
create_order => 4,
|
||||||
|
create_sql => 'CREATE TABLE dump_test.test_table_generated_child2 (
|
||||||
|
col1 int,
|
||||||
|
col2 int
|
||||||
|
) INHERITS (dump_test.test_table_generated);',
|
||||||
|
regexp => qr/^
|
||||||
|
\QCREATE TABLE dump_test.test_table_generated_child2 (\E\n
|
||||||
|
\s+\Qcol1 integer,\E\n
|
||||||
|
\s+\Qcol2 integer\E\n
|
||||||
|
\)\n
|
||||||
|
\QINHERITS (dump_test.test_table_generated);\E\n
|
||||||
|
/xms,
|
||||||
|
like =>
|
||||||
|
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
|
||||||
|
unlike => {
|
||||||
|
binary_upgrade => 1,
|
||||||
|
exclude_dump_test_schema => 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
'CREATE TABLE table_with_stats' => {
|
'CREATE TABLE table_with_stats' => {
|
||||||
create_order => 98,
|
create_order => 98,
|
||||||
create_sql => 'CREATE TABLE dump_test.table_index_stats (
|
create_sql => 'CREATE TABLE dump_test.table_index_stats (
|
||||||
|
Loading…
Reference in New Issue
Block a user