*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.286 2002/08/18 21:05:32 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.287 2002/08/19 19:33:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int i_relhasindex;
int i_relhasrules;
int i_relhasoids;
+ int i_owning_tab;
+ int i_owning_col;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
*
* Note: in this phase we should collect only a minimal amount of
* information about each table, basically just enough to decide if
- * it is interesting.
+ * it is interesting. We must fetch all tables in this phase because
+ * otherwise we cannot correctly identify inherited columns, serial
+ * columns, etc.
*/
if (g_fout->remoteVersion >= 70300)
{
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * serial column, if any
+ */
appendPQExpBuffer(query,
- "SELECT pg_class.oid, relname, relacl, relkind, "
+ "SELECT c.oid, relname, relacl, relkind, "
"relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, "
- "relhasindex, relhasrules, relhasoids "
- "from pg_class "
+ "relhasindex, relhasrules, relhasoids, "
+ "d.refobjid as owning_tab, "
+ "d.refobjsubid as owning_col "
+ "from pg_class c "
+ "left join pg_depend d on "
+ "(c.relkind = '%c' and "
+ "d.classid = c.tableoid and d.objid = c.oid and "
+ "d.objsubid = 0 and "
+ "d.refclassid = c.tableoid and d.deptype = 'i') "
"where relkind in ('%c', '%c', '%c') "
- "order by oid",
+ "order by c.oid",
+ RELKIND_SEQUENCE,
RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
}
else if (g_fout->remoteVersion >= 70200)
"0::oid as relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, "
- "relhasindex, relhasrules, relhasoids "
+ "relhasindex, relhasrules, relhasoids, "
+ "NULL::oid as owning_tab, "
+ "NULL::int4 as owning_col "
"from pg_class "
"where relkind in ('%c', '%c', '%c') "
"order by oid",
"0::oid as relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, "
- "relhasindex, relhasrules, 't'::bool as relhasoids "
+ "relhasindex, relhasrules, "
+ "'t'::bool as relhasoids, "
+ "NULL::oid as owning_tab, "
+ "NULL::int4 as owning_col "
"from pg_class "
"where relkind in ('%c', '%c', '%c') "
"order by oid",
"0::oid as relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, "
- "relhasindex, relhasrules, 't'::bool as relhasoids "
+ "relhasindex, relhasrules, "
+ "'t'::bool as relhasoids, "
+ "NULL::oid as owning_tab, "
+ "NULL::int4 as owning_col "
"from pg_class c "
"where relkind in ('%c', '%c') "
"order by oid",
i_relhasindex = PQfnumber(res, "relhasindex");
i_relhasrules = PQfnumber(res, "relhasrules");
i_relhasoids = PQfnumber(res, "relhasoids");
+ i_owning_tab = PQfnumber(res, "owning_tab");
+ i_owning_col = PQfnumber(res, "owning_col");
for (i = 0; i < ntups; i++)
{
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers));
+ if (PQgetisnull(res, i, i_owning_tab))
+ {
+ tblinfo[i].owning_tab = NULL;
+ tblinfo[i].owning_col = 0;
+ }
+ else
+ {
+ tblinfo[i].owning_tab = strdup(PQgetvalue(res, i, i_owning_tab));
+ tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
+ }
/* other fields were zeroed above */
/*
- * Decide whether we want to dump this table.
+ * Decide whether we want to dump this table. Sequences owned
+ * by serial columns are never dumpable on their own; we will
+ * transpose their owning table's dump flag to them below.
*/
- selectDumpableTable(&tblinfo[i]);
+ if (tblinfo[i].owning_tab == NULL)
+ selectDumpableTable(&tblinfo[i]);
+ else
+ tblinfo[i].dump = false;
tblinfo[i].interesting = tblinfo[i].dump;
/*
getTableAttrs(TableInfo *tblinfo, int numTables)
{
int i,
- j;
+ j,
+ k;
PQExpBuffer q = createPQExpBuffer();
int i_attname;
int i_atttypname;
for (i = 0; i < numTables; i++)
{
+ TableInfo *tbinfo = &tblinfo[i];
+
/* Don't bother to collect info for sequences */
- if (tblinfo[i].relkind == RELKIND_SEQUENCE)
+ if (tbinfo->relkind == RELKIND_SEQUENCE)
continue;
/* Don't bother to collect info for type relations */
- if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+ if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE)
continue;
/* Don't bother with uninteresting tables, either */
- if (!tblinfo[i].interesting)
+ if (!tbinfo->interesting)
continue;
/*
* Make sure we are in proper schema for this table; this allows
* correct retrieval of formatted type names and default exprs
*/
- selectSourceSchema(tblinfo[i].relnamespace->nspname);
+ selectSourceSchema(tbinfo->relnamespace->nspname);
/* find all the user attributes and their types */
*/
if (g_verbose)
write_msg(NULL, "finding the columns and types for table %s\n",
- tblinfo[i].relname);
+ tbinfo->relname);
resetPQExpBuffer(q);
"where attrelid = '%s'::pg_catalog.oid "
"and attnum > 0::pg_catalog.int2 "
"order by attrelid, attnum",
- tblinfo[i].oid);
+ tbinfo->oid);
}
else if (g_fout->remoteVersion >= 70100)
{
"where attrelid = '%s'::oid "
"and attnum > 0::int2 "
"order by attrelid, attnum",
- tblinfo[i].oid);
+ tbinfo->oid);
}
else
{
"where attrelid = '%s'::oid "
"and attnum > 0::int2 "
"order by attrelid, attnum",
- tblinfo[i].oid);
+ tbinfo->oid);
}
res = PQexec(g_conn, q->data);
i_atthasdef = PQfnumber(res, "atthasdef");
i_attisdropped = PQfnumber(res, "attisdropped");
- tblinfo[i].numatts = ntups;
- tblinfo[i].attnames = (char **) malloc(ntups * sizeof(char *));
- tblinfo[i].atttypnames = (char **) malloc(ntups * sizeof(char *));
- tblinfo[i].atttypmod = (int *) malloc(ntups * sizeof(int));
- tblinfo[i].attstattarget = (int *) malloc(ntups * sizeof(int));
- tblinfo[i].attisdropped = (bool *) malloc(ntups * sizeof(bool));
- tblinfo[i].notnull = (bool *) malloc(ntups * sizeof(bool));
- tblinfo[i].adef_expr = (char **) malloc(ntups * sizeof(char *));
- tblinfo[i].inhAttrs = (bool *) malloc(ntups * sizeof(bool));
- tblinfo[i].inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
- tblinfo[i].inhNotNull = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->numatts = ntups;
+ tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
+ tbinfo->atttypnames = (char **) malloc(ntups * sizeof(char *));
+ tbinfo->atttypmod = (int *) malloc(ntups * sizeof(int));
+ tbinfo->attstattarget = (int *) malloc(ntups * sizeof(int));
+ tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->adef_expr = (char **) malloc(ntups * sizeof(char *));
+ tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
hasdefaults = false;
for (j = 0; j < ntups; j++)
{
- tblinfo[i].attnames[j] = strdup(PQgetvalue(res, j, i_attname));
- tblinfo[i].atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
- tblinfo[i].atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
- tblinfo[i].attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
- tblinfo[i].attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
- tblinfo[i].notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
- tblinfo[i].adef_expr[j] = NULL; /* fix below */
+ tbinfo->attnames[j] = strdup(PQgetvalue(res, j, i_attname));
+ tbinfo->atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
+ tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
+ tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
+ tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
+ tbinfo->attisserial[j] = false; /* fix below */
+ tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
+ tbinfo->adef_expr[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
/* these flags will be set in flagInhAttrs() */
- tblinfo[i].inhAttrs[j] = false;
- tblinfo[i].inhAttrDef[j] = false;
- tblinfo[i].inhNotNull[j] = false;
+ tbinfo->inhAttrs[j] = false;
+ tbinfo->inhAttrDef[j] = false;
+ tbinfo->inhNotNull[j] = false;
}
PQclear(res);
if (g_verbose)
write_msg(NULL, "finding DEFAULT expressions for table %s\n",
- tblinfo[i].relname);
+ tbinfo->relname);
resetPQExpBuffer(q);
if (g_fout->remoteVersion >= 70300)
"pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
"FROM pg_catalog.pg_attrdef "
"WHERE adrelid = '%s'::pg_catalog.oid",
- tblinfo[i].oid);
+ tbinfo->oid);
}
else if (g_fout->remoteVersion >= 70200)
{
"pg_get_expr(adbin, adrelid) AS adsrc "
"FROM pg_attrdef "
"WHERE adrelid = '%s'::oid",
- tblinfo[i].oid);
+ tbinfo->oid);
}
else
{
/* no pg_get_expr, so must rely on adsrc */
appendPQExpBuffer(q, "SELECT adnum, adsrc FROM pg_attrdef "
"WHERE adrelid = '%s'::oid",
- tblinfo[i].oid);
+ tbinfo->oid);
}
res = PQexec(g_conn, q->data);
if (!res ||
if (adnum <= 0 || adnum > ntups)
{
write_msg(NULL, "bogus adnum value %d for table %s\n",
- adnum, tblinfo[i].relname);
+ adnum, tbinfo->relname);
exit_nicely();
}
- tblinfo[i].adef_expr[adnum-1] = strdup(PQgetvalue(res, j, 1));
+ tbinfo->adef_expr[adnum-1] = strdup(PQgetvalue(res, j, 1));
}
PQclear(res);
}
+
+ /*
+ * Check to see if any columns are serial columns. Our first quick
+ * filter is that it must be integer or bigint with a default. If
+ * so, we scan to see if we found a sequence linked to this column.
+ * If we did, mark the column and sequence appropriately.
+ */
+ for (j = 0; j < ntups; j++)
+ {
+ /*
+ * Note assumption that format_type will show these types as
+ * exactly "integer" and "bigint" regardless of schema path.
+ * This is correct in 7.3 but needs to be watched.
+ */
+ if (strcmp(tbinfo->atttypnames[j], "integer") != 0 &&
+ strcmp(tbinfo->atttypnames[j], "bigint") != 0)
+ continue;
+ if (tbinfo->adef_expr[j] == NULL)
+ continue;
+ for (k = 0; k < numTables; k++)
+ {
+ TableInfo *seqinfo = &tblinfo[k];
+
+ if (seqinfo->owning_tab != NULL &&
+ strcmp(seqinfo->owning_tab, tbinfo->oid) == 0 &&
+ seqinfo->owning_col == j+1)
+ {
+ /*
+ * Found a match. Copy the table's interesting and
+ * dumpable flags to the sequence.
+ */
+ tbinfo->attisserial[j] = true;
+ seqinfo->interesting = tbinfo->interesting;
+ seqinfo->dump = tbinfo->dump;
+ break;
+ }
+ }
+ }
}
destroyPQExpBuffer(q);
static void
dumpTableACL(Archive *fout, TableInfo *tbinfo)
{
- char *tmp = strdup(fmtId(tbinfo->relname));
- dumpACL(fout, "TABLE", tmp, tbinfo->relname,
+ char *namecopy = strdup(fmtId(tbinfo->relname));
+ char *dumpoid;
+
+ /*
+ * Choose OID to use for sorting ACL into position. For a view, sort
+ * by the view OID; for a serial sequence, sort by the owning table's
+ * OID; otherwise use the table's own OID.
+ */
+ if (tbinfo->viewoid != NULL)
+ dumpoid = tbinfo->viewoid;
+ else if (tbinfo->owning_tab != NULL)
+ dumpoid = tbinfo->owning_tab;
+ else
+ dumpoid = tbinfo->oid;
+
+ dumpACL(fout, "TABLE", namecopy, tbinfo->relname,
tbinfo->relnamespace->nspname, tbinfo->usename, tbinfo->relacl,
- tbinfo->viewoid != NULL ? tbinfo->viewoid : tbinfo->oid);
- free(tmp);
+ dumpoid);
+
+ free(namecopy);
}
{
int i;
- /* Dump sequences first, in case they are referenced in table defn's */
+ /*
+ * Dump non-serial sequences first, in case they are referenced in
+ * table defn's
+ */
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &tblinfo[i];
if (tbinfo->relkind != RELKIND_SEQUENCE)
continue;
- if (tbinfo->dump)
+ if (tbinfo->dump && tbinfo->owning_tab == NULL)
{
dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
if (!dataOnly && !aclsSkip)
}
}
}
+
+ /*
+ * Dump serial sequences last (we will not emit any CREATE commands,
+ * but we do have to think about ACLs and setval operations).
+ */
+ for (i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+
+ if (tbinfo->relkind != RELKIND_SEQUENCE)
+ continue;
+
+ if (tbinfo->dump && tbinfo->owning_tab != NULL)
+ {
+ dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
+ if (!dataOnly && !aclsSkip)
+ dumpTableACL(fout, tbinfo);
+ }
+ }
}
/*
appendPQExpBuffer(q, ",");
appendPQExpBuffer(q, "\n ");
- /* Attr name & type */
+ /* Attribute name */
appendPQExpBuffer(q, "%s ",
fmtId(tbinfo->attnames[j]));
- /* If no format_type, fake it */
+ /* Attribute type */
if (g_fout->remoteVersion >= 70100)
- appendPQExpBuffer(q, "%s", tbinfo->atttypnames[j]);
+ {
+ char *typname = tbinfo->atttypnames[j];
+
+ if (tbinfo->attisserial[j])
+ {
+ if (strcmp(typname, "integer") == 0)
+ typname = "serial";
+ else if (strcmp(typname, "bigint") == 0)
+ typname = "bigserial";
+ }
+ appendPQExpBuffer(q, "%s", typname);
+ }
else
+ {
+ /* If no format_type, fake it */
appendPQExpBuffer(q, "%s",
myFormatType(tbinfo->atttypnames[j],
tbinfo->atttypmod[j]));
+ }
- /* Default value */
- if (tbinfo->adef_expr[j] != NULL && !tbinfo->inhAttrDef[j])
+ /* Default value --- suppress if inherited or serial */
+ if (tbinfo->adef_expr[j] != NULL &&
+ !tbinfo->inhAttrDef[j] &&
+ !tbinfo->attisserial[j])
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->adef_expr[j]);
- /* Not Null constraint */
+ /*
+ * Not Null constraint --- suppress if inherited
+ *
+ * Note: we could suppress this for serial columns since
+ * SERIAL implies NOT NULL. We choose not to for forward
+ * compatibility, since there has been some talk of making
+ * SERIAL not imply NOT NULL, in which case the explicit
+ * specification would be needed.
+ */
if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
appendPQExpBuffer(q, " NOT NULL");
called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
/*
- * The logic we use for restoring sequences is as follows: - Add a
- * basic CREATE SEQUENCE statement (use last_val for start if called
- * is false, else use min_val for start_val).
+ * The logic we use for restoring sequences is as follows:
+ *
+ * Add a basic CREATE SEQUENCE statement (use last_val for start if
+ * called is false, else use min_val for start_val). Skip this if the
+ * sequence came from a SERIAL column.
*
* Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
- * data
+ * data. We do this for serial sequences too.
*/
- if (!dataOnly)
+ if (!dataOnly && tbinfo->owning_tab == NULL)
{
resetPQExpBuffer(delqry);