*
  *
  * 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);