diff options
Diffstat (limited to 'src/bin/pg_dump/pg_dump.c')
-rw-r--r-- | src/bin/pg_dump/pg_dump.c | 305 |
1 files changed, 275 insertions, 30 deletions
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index b7b822da624..a8c141b689d 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -343,6 +343,10 @@ static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, c static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo); static void buildMatViewRefreshDependencies(Archive *fout); static void getTableDataFKConstraints(void); +static void determineNotNullFlags(Archive *fout, PGresult *res, int r, + TableInfo *tbinfo, int j, + int i_notnull_name, int i_notnull_noinherit, + int i_notnull_islocal); static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg); static char *format_function_signature(Archive *fout, @@ -8751,7 +8755,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) int i_attlen; int i_attalign; int i_attislocal; - int i_attnotnull; + int i_notnull_name; + int i_notnull_noinherit; + int i_notnull_islocal; int i_attoptions; int i_attcollation; int i_attcompression; @@ -8761,13 +8767,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) /* * We want to perform just one query against pg_attribute, and then just - * one against pg_attrdef (for DEFAULTs) and one against pg_constraint - * (for CHECK constraints). However, we mustn't try to select every row - * of those catalogs and then sort it out on the client side, because some - * of the server-side functions we need would be unsafe to apply to tables - * we don't have lock on. Hence, we build an array of the OIDs of tables - * we care about (and now have lock on!), and use a WHERE clause to - * constrain which rows are selected. + * one against pg_attrdef (for DEFAULTs) and two against pg_constraint + * (for CHECK constraints and for NOT NULL constraints). However, we + * mustn't try to select every row of those catalogs and then sort it out + * on the client side, because some of the server-side functions we need + * would be unsafe to apply to tables we don't have lock on. Hence, we + * build an array of the OIDs of tables we care about (and now have lock + * on!), and use a WHERE clause to constrain which rows are selected. */ appendPQExpBufferChar(tbloids, '{'); appendPQExpBufferChar(checkoids, '{'); @@ -8814,7 +8820,6 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) "a.attstattarget,\n" "a.attstorage,\n" "t.typstorage,\n" - "a.attnotnull,\n" "a.atthasdef,\n" "a.attisdropped,\n" "a.attlen,\n" @@ -8831,6 +8836,30 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) "ORDER BY option_name" "), E',\n ') AS attfdwoptions,\n"); + /* + * Find out any NOT NULL markings for each column. In 18 and up we read + * pg_constraint to obtain the constraint name. notnull_noinherit is set + * according to the NO INHERIT property. For versions prior to 18, we + * store an empty string as the name when a constraint is marked as + * attnotnull (this cues dumpTableSchema to print the NOT NULL clause + * without a name); also, such cases are never NO INHERIT. + * + * We track in notnull_islocal whether the constraint was defined directly + * in this table or via an ancestor, for binary upgrade. flagInhAttrs + * might modify this later for servers older than 18; it's also in charge + * of determining the correct inhcount. + */ + if (fout->remoteVersion >= 180000) + appendPQExpBufferStr(q, + "co.conname AS notnull_name,\n" + "co.connoinherit AS notnull_noinherit,\n" + "co.conislocal AS notnull_islocal,\n"); + else + appendPQExpBufferStr(q, + "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n" + "false AS notnull_noinherit,\n" + "a.attislocal AS notnull_islocal,\n"); + if (fout->remoteVersion >= 140000) appendPQExpBufferStr(q, "a.attcompression AS attcompression,\n"); @@ -8865,11 +8894,25 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n" "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) " "LEFT JOIN pg_catalog.pg_type t " - "ON (a.atttypid = t.oid)\n" - "WHERE a.attnum > 0::pg_catalog.int2\n" - "ORDER BY a.attrelid, a.attnum", + "ON (a.atttypid = t.oid)\n", tbloids->data); + /* + * In versions 18 and up, we need pg_constraint for explicit NOT NULL + * entries. Also, we need to know if the NOT NULL for each column is + * backing a primary key. + */ + if (fout->remoteVersion >= 180000) + appendPQExpBufferStr(q, + " LEFT JOIN pg_catalog.pg_constraint co ON " + "(a.attrelid = co.conrelid\n" + " AND co.contype = 'n' AND " + "co.conkey = array[a.attnum])\n"); + + appendPQExpBufferStr(q, + "WHERE a.attnum > 0::pg_catalog.int2\n" + "ORDER BY a.attrelid, a.attnum"); + res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK); ntups = PQntuples(res); @@ -8887,7 +8930,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) i_attlen = PQfnumber(res, "attlen"); i_attalign = PQfnumber(res, "attalign"); i_attislocal = PQfnumber(res, "attislocal"); - i_attnotnull = PQfnumber(res, "attnotnull"); + i_notnull_name = PQfnumber(res, "notnull_name"); + i_notnull_noinherit = PQfnumber(res, "notnull_noinherit"); + i_notnull_islocal = PQfnumber(res, "notnull_islocal"); i_attoptions = PQfnumber(res, "attoptions"); i_attcollation = PQfnumber(res, "attcollation"); i_attcompression = PQfnumber(res, "attcompression"); @@ -8952,8 +8997,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char)); tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *)); tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *)); - tbinfo->notnull = (bool *) pg_malloc(numatts * sizeof(bool)); - tbinfo->inhNotNull = (bool *) pg_malloc(numatts * sizeof(bool)); + tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *)); + tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool)); + tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool)); tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *)); hasdefaults = false; @@ -8977,7 +9023,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen)); tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign)); tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't'); - tbinfo->notnull[j] = (PQgetvalue(res, r, i_attnotnull)[0] == 't'); + + /* Handle not-null constraint name and flags */ + determineNotNullFlags(fout, res, r, + tbinfo, j, + i_notnull_name, i_notnull_noinherit, + i_notnull_islocal); + tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions)); tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation)); tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression)); @@ -8986,8 +9038,6 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->attrdefs[j] = NULL; /* fix below */ if (PQgetvalue(res, r, i_atthasdef)[0] == 't') hasdefaults = true; - /* these flags will be set in flagInhAttrs() */ - tbinfo->inhNotNull[j] = false; } if (hasdefaults) @@ -9269,6 +9319,110 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) } /* + * Based on the getTableAttrs query's row corresponding to one column, set + * the name and flags to handle a not-null constraint for that column in + * the tbinfo struct. + * + * Result row 'r' is for tbinfo's attribute 'j'. + * + * There are three possibilities: + * 1) the column has no not-null constraints. In that case, ->notnull_constrs + * (the constraint name) remains NULL. + * 2) The column has a constraint with no name (this is the case when + * constraints come from pre-18 servers). In this case, ->notnull_constrs + * is set to the empty string; dumpTableSchema will print just "NOT NULL". + * 3) The column has a constraint with a known name; in that case + * notnull_constrs carries that name and dumpTableSchema will print + * "CONSTRAINT the_name NOT NULL". However, if the name is the default + * (table_column_not_null), there's no need to print that name in the dump, + * so notnull_constrs is set to the empty string and it behaves as the case + * above. + * + * In a child table that inherits from a parent already containing NOT NULL + * constraints and the columns in the child don't have their own NOT NULL + * declarations, we suppress printing constraints in the child: the + * constraints are acquired at the point where the child is attached to the + * parent. This is tracked in ->notnull_inh (which is set in flagInhAttrs for + * servers pre-18). + * + * Any of these constraints might have the NO INHERIT bit. If so we set + * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema. + * + * In case 3 above, the name comparison is a bit of a hack; it actually fails + * to do the right thing in all but the trivial case. However, the downside + * of getting it wrong is simply that the name is printed rather than + * suppressed, so it's not a big deal. + */ +static void +determineNotNullFlags(Archive *fout, PGresult *res, int r, + TableInfo *tbinfo, int j, + int i_notnull_name, int i_notnull_noinherit, + int i_notnull_islocal) +{ + DumpOptions *dopt = fout->dopt; + + /* + * notnull_noinh is straight from the query result. notnull_islocal also, + * though flagInhAttrs may change that one later in versions < 18. + */ + tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't'; + tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't'; + + /* + * Determine a constraint name to use. If the column is not marked not- + * null, we set NULL which cues ... to do nothing. An empty string says + * to print an unnamed NOT NULL, and anything else is a constraint name to + * use. + */ + if (fout->remoteVersion < 180000) + { + /* + * < 18 doesn't have not-null names, so an unnamed constraint is + * sufficient. + */ + if (PQgetisnull(res, r, i_notnull_name)) + tbinfo->notnull_constrs[j] = NULL; + else + tbinfo->notnull_constrs[j] = ""; + } + else + { + if (PQgetisnull(res, r, i_notnull_name)) + tbinfo->notnull_constrs[j] = NULL; + else + { + /* + * In binary upgrade of inheritance child tables, must have a + * constraint name that we can UPDATE later. + */ + if (dopt->binary_upgrade && + !tbinfo->ispartition && + !tbinfo->notnull_islocal) + { + tbinfo->notnull_constrs[j] = + pstrdup(PQgetvalue(res, r, i_notnull_name)); + } + else + { + char *default_name; + + /* XXX should match ChooseConstraintName better */ + default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name, + tbinfo->attnames[j]); + if (strcmp(default_name, + PQgetvalue(res, r, i_notnull_name)) == 0) + tbinfo->notnull_constrs[j] = ""; + else + { + tbinfo->notnull_constrs[j] = + pstrdup(PQgetvalue(res, r, i_notnull_name)); + } + } + } + } +} + +/* * Test whether a column should be printed as part of table's CREATE TABLE. * Column number is zero-based. * @@ -15970,13 +16124,14 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) !tbinfo->attrdefs[j]->separate); /* - * Not Null constraint --- suppress if inherited, except - * if partition, or in binary-upgrade case where that - * won't work. + * Not Null constraint --- print it if it is locally + * defined, or if binary upgrade. (In the latter case, we + * reset conislocal below.) */ - print_notnull = (tbinfo->notnull[j] && - (!tbinfo->inhNotNull[j] || - tbinfo->ispartition || dopt->binary_upgrade)); + print_notnull = (tbinfo->notnull_constrs[j] != NULL && + (tbinfo->notnull_islocal[j] || + dopt->binary_upgrade || + tbinfo->ispartition)); /* * Skip column if fully defined by reloftype, except in @@ -16032,9 +16187,22 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->attrdefs[j]->adef_expr); } + print_notnull = (tbinfo->notnull_constrs[j] != NULL && + (tbinfo->notnull_islocal[j] || + dopt->binary_upgrade || + tbinfo->ispartition)); if (print_notnull) - appendPQExpBufferStr(q, " NOT NULL"); + { + if (tbinfo->notnull_constrs[j][0] == '\0') + appendPQExpBufferStr(q, " NOT NULL"); + else + appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL", + fmtId(tbinfo->notnull_constrs[j])); + + if (tbinfo->notnull_noinh[j]) + appendPQExpBufferStr(q, " NO INHERIT"); + } /* Add collation if not default for the type */ if (OidIsValid(tbinfo->attcollation[j])) @@ -16212,6 +16380,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->relkind == RELKIND_PARTITIONED_TABLE)) { bool firstitem; + bool firstitem_extra; /* * Drop any dropped columns. Merge the pg_attribute manipulations @@ -16290,6 +16459,71 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) appendPQExpBufferStr(q, ");\n"); /* + * Fix up not-null constraints that come from inheritance. As + * above, do the pg_constraint manipulations in a single SQL + * command. (Actually, two in special cases, if we're doing an + * upgrade from < 18). + */ + firstitem = true; + firstitem_extra = true; + resetPQExpBuffer(extra); + for (j = 0; j < tbinfo->numatts; j++) + { + /* + * If a not-null constraint comes from inheritance, reset + * conislocal. The inhcount is fixed by ALTER TABLE INHERIT, + * below. Special hack: in versions < 18, columns with no + * local definition need their constraint to be matched by + * column number in conkeys instead of by contraint name, + * because the latter is not available. (We distinguish the + * case because the constraint name is the empty string.) + */ + if (tbinfo->notnull_constrs[j] != NULL && + !tbinfo->notnull_islocal[j]) + { + if (tbinfo->notnull_constrs[j][0] != '\0') + { + if (firstitem) + { + appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n" + "SET conislocal = false\n" + "WHERE contype = 'n' AND conrelid = "); + appendStringLiteralAH(q, qualrelname, fout); + appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n" + "conname IN ("); + firstitem = false; + } + else + appendPQExpBufferStr(q, ", "); + appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout); + } + else + { + if (firstitem_extra) + { + appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n" + "SET conislocal = false\n" + "WHERE contype = 'n' AND conrelid = "); + appendStringLiteralAH(extra, qualrelname, fout); + appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n" + "conkey IN ("); + firstitem_extra = false; + } + else + appendPQExpBufferStr(extra, ", "); + appendPQExpBuffer(extra, "'{%d}'", j + 1); + } + } + } + if (!firstitem) + appendPQExpBufferStr(q, ");\n"); + if (!firstitem_extra) + appendPQExpBufferStr(extra, ");\n"); + + if (extra->len > 0) + appendBinaryPQExpBuffer(q, extra->data, extra->len); + + /* * Add inherited CHECK constraints, if any. * * For partitions, they were already dumped, and conislocal @@ -16428,11 +16662,22 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) * we have to mark it separately. */ if (!shouldPrintColumn(dopt, tbinfo, j) && - tbinfo->notnull[j] && !tbinfo->inhNotNull[j]) - appendPQExpBuffer(q, - "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET NOT NULL;\n", - foreign, qualrelname, - fmtId(tbinfo->attnames[j])); + tbinfo->notnull_constrs[j] != NULL && + (tbinfo->notnull_islocal[j] && !tbinfo->ispartition && !dopt->binary_upgrade)) + { + /* No constraint name desired? */ + if (tbinfo->notnull_constrs[j][0] == '\0') + appendPQExpBuffer(q, + "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET NOT NULL;\n", + foreign, qualrelname, + fmtId(tbinfo->attnames[j])); + else + appendPQExpBuffer(q, + "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s NOT NULL %s;\n", + foreign, qualrelname, + tbinfo->notnull_constrs[j], + fmtId(tbinfo->attnames[j])); + } /* * Dump per-column statistics information. We only issue an ALTER |