Commit a0bf1a7f authored by Tom Lane's avatar Tom Lane

Fix pg_dump to dump serial columns as serials. Per pghackers discussion,

cause SERIAL column declaration not to imply UNIQUE, so that this can be
done without creating an extra index.
parent 6ebc90b0
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.98 2002/08/13 20:40:43 momjian Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.99 2002/08/19 19:33:33 tgl Exp $
--> -->
<chapter id="datatype"> <chapter id="datatype">
...@@ -665,14 +665,17 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> ( ...@@ -665,14 +665,17 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
<programlisting> <programlisting>
CREATE SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq; CREATE SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq;
CREATE TABLE <replaceable class="parameter">tablename</replaceable> ( CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
<replaceable class="parameter">colname</replaceable> integer DEFAULT nextval('<replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq') UNIQUE NOT NULL <replaceable class="parameter">colname</replaceable> integer DEFAULT nextval('<replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq') NOT NULL
); );
</programlisting> </programlisting>
Thus, we have created an integer column and arranged for its default Thus, we have created an integer column and arranged for its default
values to be assigned from a sequence generator. UNIQUE and NOT NULL values to be assigned from a sequence generator. A <literal>NOT NULL</>
constraints are applied to ensure that explicitly-inserted values constraint is applied to ensure that a NULL value cannot be explicitly
will never be duplicates, either. inserted, either. In most cases you would also want to attach a
<literal>UNIQUE</> or <literal>PRIMARY KEY</> constraint to prevent
duplicate values from being inserted by accident, but this is
not automatic.
</para> </para>
<para> <para>
...@@ -685,20 +688,23 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> ( ...@@ -685,20 +688,23 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
</para> </para>
<para> <para>
Implicit sequences supporting the <type>serial</type> types are The sequence created by a <type>serial</type> type is automatically
not automatically dropped when a table containing a serial type dropped when
is dropped. So, the following commands executed in order will likely fail: the owning column is dropped, and cannot be dropped otherwise.
(This was not true in <productname>PostgreSQL</productname> releases
<programlisting> before 7.3. Note that this automatic drop linkage will not occur for a
CREATE TABLE <replaceable class="parameter">tablename</replaceable> (<replaceable class="parameter">colname</replaceable> SERIAL); sequence created by reloading a dump from a pre-7.3 database; the dump
DROP TABLE <replaceable class="parameter">tablename</replaceable>; file does not contain the information needed to establish the dependency
CREATE TABLE <replaceable class="parameter">tablename</replaceable> (<replaceable class="parameter">colname</replaceable> SERIAL); link.)
</programlisting>
The sequence will remain in the database until explicitly dropped using
<command>DROP SEQUENCE</command>. (This annoyance will probably be
fixed in some future release.)
</para> </para>
<note><para>
Prior to <productname>PostgreSQL</productname> 7.3, <type>serial</type>
implied <literal>UNIQUE</literal>. This is no longer automatic. If
you wish a serial column to be <literal>UNIQUE</literal> or a
<literal>PRIMARY KEY</literal> it must now be specified, same as with
any other datatype.
</para></note>
</sect2> </sect2>
</sect1> </sect1>
......
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.147 2002/08/18 09:36:25 petere Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.148 2002/08/19 19:33:34 tgl Exp $
--> -->
<appendix id="release"> <appendix id="release">
...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without ...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
worries about funny characters. worries about funny characters.
--> -->
<literallayout><![CDATA[ <literallayout><![CDATA[
SERIAL no longer implies UNIQUE; specify explicitly if index is wanted
pg_dump -n and -N options have been removed. The new behavior is like -n but knows about key words. pg_dump -n and -N options have been removed. The new behavior is like -n but knows about key words.
CLUSTER is no longer hazardous to your schema CLUSTER is no longer hazardous to your schema
COPY accepts a list of columns to copy COPY accepts a list of columns to copy
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.241 2002/08/19 15:08:47 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.242 2002/08/19 19:33:34 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -876,11 +876,6 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, ...@@ -876,11 +876,6 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
constraint->keys = NIL; constraint->keys = NIL;
column->constraints = lappend(column->constraints, constraint); column->constraints = lappend(column->constraints, constraint);
constraint = makeNode(Constraint);
constraint->contype = CONSTR_UNIQUE;
constraint->name = NULL; /* assign later */
column->constraints = lappend(column->constraints, constraint);
constraint = makeNode(Constraint); constraint = makeNode(Constraint);
constraint->contype = CONSTR_NOTNULL; constraint->contype = CONSTR_NOTNULL;
column->constraints = lappend(column->constraints, constraint); column->constraints = lappend(column->constraints, constraint);
...@@ -1209,10 +1204,9 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -1209,10 +1204,9 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
/* /*
* Scan the index list and remove any redundant index specifications. * Scan the index list and remove any redundant index specifications.
* This can happen if, for instance, the user writes SERIAL PRIMARY * This can happen if, for instance, the user writes UNIQUE PRIMARY KEY.
* KEY or SERIAL UNIQUE. A strict reading of SQL92 would suggest * A strict reading of SQL92 would suggest raising an error instead,
* raising an error instead, but that strikes me as too * but that strikes me as too anal-retentive. - tgl 2001-02-14
* anal-retentive. - tgl 2001-02-14
* *
* XXX in ALTER TABLE case, it'd be nice to look for duplicate * XXX in ALTER TABLE case, it'd be nice to look for duplicate
* pre-existing indexes, too. * pre-existing indexes, too.
...@@ -1262,7 +1256,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -1262,7 +1256,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
/* /*
* Finally, select unique names for all not-previously-named indices, * Finally, select unique names for all not-previously-named indices,
* and display WARNING messages. * and display NOTICE messages.
* *
* XXX in ALTER TABLE case, we fail to consider name collisions against * XXX in ALTER TABLE case, we fail to consider name collisions against
* pre-existing indexes. * pre-existing indexes.
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2056,6 +2056,8 @@ getTables(int *numTables) ...@@ -2056,6 +2056,8 @@ getTables(int *numTables)
int i_relhasindex; int i_relhasindex;
int i_relhasrules; int i_relhasrules;
int i_relhasoids; int i_relhasoids;
int i_owning_tab;
int i_owning_col;
/* Make sure we are in proper schema */ /* Make sure we are in proper schema */
selectSourceSchema("pg_catalog"); selectSourceSchema("pg_catalog");
...@@ -2071,20 +2073,34 @@ getTables(int *numTables) ...@@ -2071,20 +2073,34 @@ getTables(int *numTables)
* *
* Note: in this phase we should collect only a minimal amount of * Note: in this phase we should collect only a minimal amount of
* information about each table, basically just enough to decide if * 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) if (g_fout->remoteVersion >= 70300)
{ {
/*
* Left join to pick up dependency info linking sequences to their
* serial column, if any
*/
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT pg_class.oid, relname, relacl, relkind, " "SELECT c.oid, relname, relacl, relkind, "
"relnamespace, " "relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, " "(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, " "relchecks, reltriggers, "
"relhasindex, relhasrules, relhasoids " "relhasindex, relhasrules, relhasoids, "
"from pg_class " "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') " "where relkind in ('%c', '%c', '%c') "
"order by oid", "order by c.oid",
RELKIND_SEQUENCE,
RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW); RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
} }
else if (g_fout->remoteVersion >= 70200) else if (g_fout->remoteVersion >= 70200)
...@@ -2095,7 +2111,9 @@ getTables(int *numTables) ...@@ -2095,7 +2111,9 @@ getTables(int *numTables)
"0::oid as relnamespace, " "0::oid as relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, " "(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, " "relchecks, reltriggers, "
"relhasindex, relhasrules, relhasoids " "relhasindex, relhasrules, relhasoids, "
"NULL::oid as owning_tab, "
"NULL::int4 as owning_col "
"from pg_class " "from pg_class "
"where relkind in ('%c', '%c', '%c') " "where relkind in ('%c', '%c', '%c') "
"order by oid", "order by oid",
...@@ -2109,7 +2127,10 @@ getTables(int *numTables) ...@@ -2109,7 +2127,10 @@ getTables(int *numTables)
"0::oid as relnamespace, " "0::oid as relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, " "(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, " "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 " "from pg_class "
"where relkind in ('%c', '%c', '%c') " "where relkind in ('%c', '%c', '%c') "
"order by oid", "order by oid",
...@@ -2131,7 +2152,10 @@ getTables(int *numTables) ...@@ -2131,7 +2152,10 @@ getTables(int *numTables)
"0::oid as relnamespace, " "0::oid as relnamespace, "
"(select usename from pg_user where relowner = usesysid) as usename, " "(select usename from pg_user where relowner = usesysid) as usename, "
"relchecks, reltriggers, " "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 " "from pg_class c "
"where relkind in ('%c', '%c') " "where relkind in ('%c', '%c') "
"order by oid", "order by oid",
...@@ -2175,6 +2199,8 @@ getTables(int *numTables) ...@@ -2175,6 +2199,8 @@ getTables(int *numTables)
i_relhasindex = PQfnumber(res, "relhasindex"); i_relhasindex = PQfnumber(res, "relhasindex");
i_relhasrules = PQfnumber(res, "relhasrules"); i_relhasrules = PQfnumber(res, "relhasrules");
i_relhasoids = PQfnumber(res, "relhasoids"); 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++) for (i = 0; i < ntups; i++)
{ {
...@@ -2190,13 +2216,28 @@ getTables(int *numTables) ...@@ -2190,13 +2216,28 @@ getTables(int *numTables)
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks)); tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers)); 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 */ /* 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; tblinfo[i].interesting = tblinfo[i].dump;
/* /*
...@@ -2314,7 +2355,8 @@ void ...@@ -2314,7 +2355,8 @@ void
getTableAttrs(TableInfo *tblinfo, int numTables) getTableAttrs(TableInfo *tblinfo, int numTables)
{ {
int i, int i,
j; j,
k;
PQExpBuffer q = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer();
int i_attname; int i_attname;
int i_atttypname; int i_atttypname;
...@@ -2329,23 +2371,25 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2329,23 +2371,25 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
for (i = 0; i < numTables; i++) for (i = 0; i < numTables; i++)
{ {
TableInfo *tbinfo = &tblinfo[i];
/* Don't bother to collect info for sequences */ /* Don't bother to collect info for sequences */
if (tblinfo[i].relkind == RELKIND_SEQUENCE) if (tbinfo->relkind == RELKIND_SEQUENCE)
continue; continue;
/* Don't bother to collect info for type relations */ /* Don't bother to collect info for type relations */
if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE) if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE)
continue; continue;
/* Don't bother with uninteresting tables, either */ /* Don't bother with uninteresting tables, either */
if (!tblinfo[i].interesting) if (!tbinfo->interesting)
continue; continue;
/* /*
* Make sure we are in proper schema for this table; this allows * Make sure we are in proper schema for this table; this allows
* correct retrieval of formatted type names and default exprs * 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 */ /* find all the user attributes and their types */
...@@ -2359,7 +2403,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2359,7 +2403,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
*/ */
if (g_verbose) if (g_verbose)
write_msg(NULL, "finding the columns and types for table %s\n", write_msg(NULL, "finding the columns and types for table %s\n",
tblinfo[i].relname); tbinfo->relname);
resetPQExpBuffer(q); resetPQExpBuffer(q);
...@@ -2372,7 +2416,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2372,7 +2416,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"where attrelid = '%s'::pg_catalog.oid " "where attrelid = '%s'::pg_catalog.oid "
"and attnum > 0::pg_catalog.int2 " "and attnum > 0::pg_catalog.int2 "
"order by attrelid, attnum", "order by attrelid, attnum",
tblinfo[i].oid); tbinfo->oid);
} }
else if (g_fout->remoteVersion >= 70100) else if (g_fout->remoteVersion >= 70100)
{ {
...@@ -2388,7 +2432,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2388,7 +2432,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"where attrelid = '%s'::oid " "where attrelid = '%s'::oid "
"and attnum > 0::int2 " "and attnum > 0::int2 "
"order by attrelid, attnum", "order by attrelid, attnum",
tblinfo[i].oid); tbinfo->oid);
} }
else else
{ {
...@@ -2400,7 +2444,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2400,7 +2444,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"where attrelid = '%s'::oid " "where attrelid = '%s'::oid "
"and attnum > 0::int2 " "and attnum > 0::int2 "
"order by attrelid, attnum", "order by attrelid, attnum",
tblinfo[i].oid); tbinfo->oid);
} }
res = PQexec(g_conn, q->data); res = PQexec(g_conn, q->data);
...@@ -2421,34 +2465,36 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2421,34 +2465,36 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_atthasdef = PQfnumber(res, "atthasdef"); i_atthasdef = PQfnumber(res, "atthasdef");
i_attisdropped = PQfnumber(res, "attisdropped"); i_attisdropped = PQfnumber(res, "attisdropped");
tblinfo[i].numatts = ntups; tbinfo->numatts = ntups;
tblinfo[i].attnames = (char **) malloc(ntups * sizeof(char *)); tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
tblinfo[i].atttypnames = (char **) malloc(ntups * sizeof(char *)); tbinfo->atttypnames = (char **) malloc(ntups * sizeof(char *));
tblinfo[i].atttypmod = (int *) malloc(ntups * sizeof(int)); tbinfo->atttypmod = (int *) malloc(ntups * sizeof(int));
tblinfo[i].attstattarget = (int *) malloc(ntups * sizeof(int)); tbinfo->attstattarget = (int *) malloc(ntups * sizeof(int));
tblinfo[i].attisdropped = (bool *) malloc(ntups * sizeof(bool)); tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
tblinfo[i].notnull = (bool *) malloc(ntups * sizeof(bool)); tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
tblinfo[i].adef_expr = (char **) malloc(ntups * sizeof(char *)); tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
tblinfo[i].inhAttrs = (bool *) malloc(ntups * sizeof(bool)); tbinfo->adef_expr = (char **) malloc(ntups * sizeof(char *));
tblinfo[i].inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tblinfo[i].inhNotNull = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
hasdefaults = false; hasdefaults = false;
for (j = 0; j < ntups; j++) for (j = 0; j < ntups; j++)
{ {
tblinfo[i].attnames[j] = strdup(PQgetvalue(res, j, i_attname)); tbinfo->attnames[j] = strdup(PQgetvalue(res, j, i_attname));
tblinfo[i].atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname)); tbinfo->atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
tblinfo[i].atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod)); tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
tblinfo[i].attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget)); tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
tblinfo[i].attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't'); tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
tblinfo[i].notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); tbinfo->attisserial[j] = false; /* fix below */
tblinfo[i].adef_expr[j] = NULL; /* 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') if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true; hasdefaults = true;
/* these flags will be set in flagInhAttrs() */ /* these flags will be set in flagInhAttrs() */
tblinfo[i].inhAttrs[j] = false; tbinfo->inhAttrs[j] = false;
tblinfo[i].inhAttrDef[j] = false; tbinfo->inhAttrDef[j] = false;
tblinfo[i].inhNotNull[j] = false; tbinfo->inhNotNull[j] = false;
} }
PQclear(res); PQclear(res);
...@@ -2459,7 +2505,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2459,7 +2505,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
if (g_verbose) if (g_verbose)
write_msg(NULL, "finding DEFAULT expressions for table %s\n", write_msg(NULL, "finding DEFAULT expressions for table %s\n",
tblinfo[i].relname); tbinfo->relname);
resetPQExpBuffer(q); resetPQExpBuffer(q);
if (g_fout->remoteVersion >= 70300) if (g_fout->remoteVersion >= 70300)
...@@ -2468,7 +2514,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2468,7 +2514,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc " "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
"FROM pg_catalog.pg_attrdef " "FROM pg_catalog.pg_attrdef "
"WHERE adrelid = '%s'::pg_catalog.oid", "WHERE adrelid = '%s'::pg_catalog.oid",
tblinfo[i].oid); tbinfo->oid);
} }
else if (g_fout->remoteVersion >= 70200) else if (g_fout->remoteVersion >= 70200)
{ {
...@@ -2476,14 +2522,14 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2476,14 +2522,14 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"pg_get_expr(adbin, adrelid) AS adsrc " "pg_get_expr(adbin, adrelid) AS adsrc "
"FROM pg_attrdef " "FROM pg_attrdef "
"WHERE adrelid = '%s'::oid", "WHERE adrelid = '%s'::oid",
tblinfo[i].oid); tbinfo->oid);
} }
else else
{ {
/* no pg_get_expr, so must rely on adsrc */ /* no pg_get_expr, so must rely on adsrc */
appendPQExpBuffer(q, "SELECT adnum, adsrc FROM pg_attrdef " appendPQExpBuffer(q, "SELECT adnum, adsrc FROM pg_attrdef "
"WHERE adrelid = '%s'::oid", "WHERE adrelid = '%s'::oid",
tblinfo[i].oid); tbinfo->oid);
} }
res = PQexec(g_conn, q->data); res = PQexec(g_conn, q->data);
if (!res || if (!res ||
...@@ -2502,13 +2548,51 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ...@@ -2502,13 +2548,51 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
if (adnum <= 0 || adnum > ntups) if (adnum <= 0 || adnum > ntups)
{ {
write_msg(NULL, "bogus adnum value %d for table %s\n", write_msg(NULL, "bogus adnum value %d for table %s\n",
adnum, tblinfo[i].relname); adnum, tbinfo->relname);
exit_nicely(); 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); 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); destroyPQExpBuffer(q);
...@@ -4884,11 +4968,26 @@ dumpACL(Archive *fout, const char *type, const char *name, ...@@ -4884,11 +4968,26 @@ dumpACL(Archive *fout, const char *type, const char *name,
static void static void
dumpTableACL(Archive *fout, TableInfo *tbinfo) dumpTableACL(Archive *fout, TableInfo *tbinfo)
{ {
char *tmp = strdup(fmtId(tbinfo->relname)); char *namecopy = strdup(fmtId(tbinfo->relname));
dumpACL(fout, "TABLE", tmp, 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->relnamespace->nspname, tbinfo->usename, tbinfo->relacl,
tbinfo->viewoid != NULL ? tbinfo->viewoid : tbinfo->oid); dumpoid);
free(tmp);
free(namecopy);
} }
...@@ -4902,7 +5001,10 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables, ...@@ -4902,7 +5001,10 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
{ {
int i; 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++) for (i = 0; i < numTables; i++)
{ {
TableInfo *tbinfo = &tblinfo[i]; TableInfo *tbinfo = &tblinfo[i];
...@@ -4910,7 +5012,7 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables, ...@@ -4910,7 +5012,7 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
if (tbinfo->relkind != RELKIND_SEQUENCE) if (tbinfo->relkind != RELKIND_SEQUENCE)
continue; continue;
if (tbinfo->dump) if (tbinfo->dump && tbinfo->owning_tab == NULL)
{ {
dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly); dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
if (!dataOnly && !aclsSkip) if (!dataOnly && !aclsSkip)
...@@ -4937,6 +5039,25 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables, ...@@ -4937,6 +5039,25 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
} }
} }
} }
/*
* 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);
}
}
} }
/* /*
...@@ -5089,24 +5210,48 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo) ...@@ -5089,24 +5210,48 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
appendPQExpBuffer(q, ","); appendPQExpBuffer(q, ",");
appendPQExpBuffer(q, "\n "); appendPQExpBuffer(q, "\n ");
/* Attr name & type */ /* Attribute name */
appendPQExpBuffer(q, "%s ", appendPQExpBuffer(q, "%s ",
fmtId(tbinfo->attnames[j])); fmtId(tbinfo->attnames[j]));
/* If no format_type, fake it */ /* Attribute type */
if (g_fout->remoteVersion >= 70100) 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 else
{
/* If no format_type, fake it */
appendPQExpBuffer(q, "%s", appendPQExpBuffer(q, "%s",
myFormatType(tbinfo->atttypnames[j], myFormatType(tbinfo->atttypnames[j],
tbinfo->atttypmod[j])); tbinfo->atttypmod[j]));
}
/* Default value */ /* Default value --- suppress if inherited or serial */
if (tbinfo->adef_expr[j] != NULL && !tbinfo->inhAttrDef[j]) if (tbinfo->adef_expr[j] != NULL &&
!tbinfo->inhAttrDef[j] &&
!tbinfo->attisserial[j])
appendPQExpBuffer(q, " DEFAULT %s", appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->adef_expr[j]); 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]) if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
appendPQExpBuffer(q, " NOT NULL"); appendPQExpBuffer(q, " NOT NULL");
...@@ -5708,15 +5853,17 @@ dumpOneSequence(Archive *fout, TableInfo *tbinfo, ...@@ -5708,15 +5853,17 @@ dumpOneSequence(Archive *fout, TableInfo *tbinfo,
called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0); called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
/* /*
* The logic we use for restoring sequences is as follows: - Add a * The logic we use for restoring sequences is as follows:
* basic CREATE SEQUENCE statement (use last_val for start if called *
* is false, else use min_val for start_val). * 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 * 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); resetPQExpBuffer(delqry);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_dump.h,v 1.96 2002/08/18 09:36:26 petere Exp $ * $Id: pg_dump.h,v 1.97 2002/08/19 19:33:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -110,6 +110,9 @@ typedef struct _tableInfo ...@@ -110,6 +110,9 @@ typedef struct _tableInfo
bool hasoids; /* does it have OIDs? */ bool hasoids; /* does it have OIDs? */
int ncheck; /* # of CHECK expressions */ int ncheck; /* # of CHECK expressions */
int ntrig; /* # of triggers */ int ntrig; /* # of triggers */
/* these two are set only if table is a SERIAL column's sequence: */
char *owning_tab; /* OID of table owning sequence */
int owning_col; /* attr # of column owning sequence */
bool interesting; /* true if need to collect more data */ bool interesting; /* true if need to collect more data */
bool dump; /* true if we want to dump it */ bool dump; /* true if we want to dump it */
...@@ -123,12 +126,13 @@ typedef struct _tableInfo ...@@ -123,12 +126,13 @@ typedef struct _tableInfo
char **atttypnames; /* attribute type names */ char **atttypnames; /* attribute type names */
int *atttypmod; /* type-specific type modifiers */ int *atttypmod; /* type-specific type modifiers */
int *attstattarget; /* attribute statistics targets */ int *attstattarget; /* attribute statistics targets */
bool *attisdropped; /* true if attr is dropped; don't dump it */
bool *attisserial; /* true if attr is serial or bigserial */
/* /*
* Note: we need to store per-attribute notnull and default stuff for * Note: we need to store per-attribute notnull and default stuff for
* all interesting tables so that we can tell which constraints were * all interesting tables so that we can tell which constraints were
* inherited. * inherited.
*/ */
bool *attisdropped; /* true if attr is dropped; don't dump it */
bool *notnull; /* Not null constraints on attributes */ bool *notnull; /* Not null constraints on attributes */
char **adef_expr; /* DEFAULT expressions */ char **adef_expr; /* DEFAULT expressions */
bool *inhAttrs; /* true if each attribute is inherited */ bool *inhAttrs; /* true if each attribute is inherited */
......
...@@ -6,7 +6,6 @@ CREATE TABLE x ( ...@@ -6,7 +6,6 @@ CREATE TABLE x (
e text e text
); );
NOTICE: CREATE TABLE will create implicit sequence 'x_a_seq' for SERIAL column 'x.a' NOTICE: CREATE TABLE will create implicit sequence 'x_a_seq' for SERIAL column 'x.a'
NOTICE: CREATE TABLE / UNIQUE will create implicit index 'x_a_key' for table 'x'
CREATE FUNCTION fn_x_before () RETURNS OPAQUE AS ' CREATE FUNCTION fn_x_before () RETURNS OPAQUE AS '
BEGIN BEGIN
NEW.e := ''before trigger fired''::text; NEW.e := ''before trigger fired''::text;
......
...@@ -137,7 +137,6 @@ INSERT INTO iportaltest (i, d, p) ...@@ -137,7 +137,6 @@ INSERT INTO iportaltest (i, d, p)
--- ---
CREATE TABLE serialTest (f1 text, f2 serial); CREATE TABLE serialTest (f1 text, f2 serial);
NOTICE: CREATE TABLE will create implicit sequence 'serialtest_f2_seq' for SERIAL column 'serialtest.f2' NOTICE: CREATE TABLE will create implicit sequence 'serialtest_f2_seq' for SERIAL column 'serialtest.f2'
NOTICE: CREATE TABLE / UNIQUE will create implicit index 'serialtest_f2_key' for table 'serialtest'
INSERT INTO serialTest VALUES ('foo'); INSERT INTO serialTest VALUES ('foo');
INSERT INTO serialTest VALUES ('bar'); INSERT INTO serialTest VALUES ('bar');
INSERT INTO serialTest VALUES ('force', 100); INSERT INTO serialTest VALUES ('force', 100);
......
...@@ -59,11 +59,10 @@ SELECT relname, relhasindex ...@@ -59,11 +59,10 @@ SELECT relname, relhasindex
pg_trigger | t pg_trigger | t
pg_type | t pg_type | t
road | t road | t
serialtest | t
shighway | t shighway | t
tenk1 | t tenk1 | t
tenk2 | t tenk2 | t
(53 rows) (52 rows)
-- --
-- another sanity check: every system catalog that has OIDs should have -- another sanity check: every system catalog that has OIDs should have
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment