Re: [9.1] 2 bugs with extensions - Mailing list pgsql-hackers

From Tom Lane
Subject Re: [9.1] 2 bugs with extensions
Date
Msg-id 630.1351183792@sss.pgh.pa.us
Whole thread Raw
In response to Re: [9.1] 2 bugs with extensions  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: [9.1] 2 bugs with extensions  (Dimitri Fontaine <dimitri@2ndQuadrant.fr>)
List pgsql-hackers
I wrote:
> The fact that SEQUENCE SET is considered pre-data has bitten us several
> times already, eg
> http://archives.postgresql.org/pgsql-bugs/2012-05/msg00084.php

> I think it may be time to bite the bullet and change that (including
> breaking dumpSequence() into two separate functions).  I'm a little bit
> worried about the compatibility implications of back-patching such a
> change, though.  Is it likely that anybody out there is depending on the
> fact that, eg, pg_dump --section=pre-data currently includes SEQUENCE SET
> items?  Personally I think it's more likely that that'd be seen as a
> bug, but ...

Specifically, I'm thinking this, which looks rather bulky but most of
the diff is from reindenting the guts of dumpSequence().

            regards, tom lane

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4223b415362f4673097b6950c1c1f8b8349ca7d7..82330cbd915d7d23f7976253f5135beeec1abcf9 100644
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpTable(Archive *fout, Tab
*** 192,197 ****
--- 192,198 ----
  static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
  static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
  static void dumpSequence(Archive *fout, TableInfo *tbinfo);
+ static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo);
  static void dumpIndex(Archive *fout, IndxInfo *indxinfo);
  static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo);
  static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo);
*************** makeTableDataInfo(TableInfo *tbinfo, boo
*** 1640,1648 ****
      /* Skip VIEWs (no data to dump) */
      if (tbinfo->relkind == RELKIND_VIEW)
          return;
-     /* Skip SEQUENCEs (handled elsewhere) */
-     if (tbinfo->relkind == RELKIND_SEQUENCE)
-         return;
      /* Skip FOREIGN TABLEs (no data to dump) */
      if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
          return;
--- 1641,1646 ----
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 7318,7324 ****
              dumpCast(fout, (CastInfo *) dobj);
              break;
          case DO_TABLE_DATA:
!             dumpTableData(fout, (TableDataInfo *) dobj);
              break;
          case DO_DUMMY_TYPE:
              /* table rowtypes and array types are never dumped separately */
--- 7316,7325 ----
              dumpCast(fout, (CastInfo *) dobj);
              break;
          case DO_TABLE_DATA:
!             if (((TableDataInfo *) dobj)->tdtable->relkind == RELKIND_SEQUENCE)
!                 dumpSequenceData(fout, (TableDataInfo *) dobj);
!             else
!                 dumpTableData(fout, (TableDataInfo *) dobj);
              break;
          case DO_DUMMY_TYPE:
              /* table rowtypes and array types are never dumped separately */
*************** collectSecLabels(Archive *fout, SecLabel
*** 12226,12238 ****
  static void
  dumpTable(Archive *fout, TableInfo *tbinfo)
  {
!     if (tbinfo->dobj.dump)
      {
          char       *namecopy;

          if (tbinfo->relkind == RELKIND_SEQUENCE)
              dumpSequence(fout, tbinfo);
!         else if (!dataOnly)
              dumpTableSchema(fout, tbinfo);

          /* Handle the ACL here */
--- 12227,12239 ----
  static void
  dumpTable(Archive *fout, TableInfo *tbinfo)
  {
!     if (tbinfo->dobj.dump && !dataOnly)
      {
          char       *namecopy;

          if (tbinfo->relkind == RELKIND_SEQUENCE)
              dumpSequence(fout, tbinfo);
!         else
              dumpTableSchema(fout, tbinfo);

          /* Handle the ACL here */
*************** findLastBuiltinOid_V70(Archive *fout)
*** 13347,13366 ****
      return last_oid;
  }

  static void
  dumpSequence(Archive *fout, TableInfo *tbinfo)
  {
      PGresult   *res;
      char       *startv,
-                *last,
                 *incby,
                 *maxv = NULL,
                 *minv = NULL,
                 *cache;
      char        bufm[100],
                  bufx[100];
!     bool        cycled,
!                 called;
      PQExpBuffer query = createPQExpBuffer();
      PQExpBuffer delqry = createPQExpBuffer();
      PQExpBuffer labelq = createPQExpBuffer();
--- 13348,13369 ----
      return last_oid;
  }

+ /*
+  * dumpSequence
+  *      write the declaration (not data) of one user-defined sequence
+  */
  static void
  dumpSequence(Archive *fout, TableInfo *tbinfo)
  {
      PGresult   *res;
      char       *startv,
                 *incby,
                 *maxv = NULL,
                 *minv = NULL,
                 *cache;
      char        bufm[100],
                  bufx[100];
!     bool        cycled;
      PQExpBuffer query = createPQExpBuffer();
      PQExpBuffer delqry = createPQExpBuffer();
      PQExpBuffer labelq = createPQExpBuffer();
*************** dumpSequence(Archive *fout, TableInfo *t
*** 13375,13381 ****
      {
          appendPQExpBuffer(query,
                            "SELECT sequence_name, "
!                           "start_value, last_value, increment_by, "
                     "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
                     "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
                            "     ELSE max_value "
--- 13378,13384 ----
      {
          appendPQExpBuffer(query,
                            "SELECT sequence_name, "
!                           "start_value, increment_by, "
                     "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
                     "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
                            "     ELSE max_value "
*************** dumpSequence(Archive *fout, TableInfo *t
*** 13384,13390 ****
                     "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
                            "     ELSE min_value "
                            "END AS min_value, "
!                           "cache_value, is_cycled, is_called from %s",
                            bufx, bufm,
                            fmtId(tbinfo->dobj.name));
      }
--- 13387,13393 ----
                     "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
                            "     ELSE min_value "
                            "END AS min_value, "
!                           "cache_value, is_cycled FROM %s",
                            bufx, bufm,
                            fmtId(tbinfo->dobj.name));
      }
*************** dumpSequence(Archive *fout, TableInfo *t
*** 13392,13398 ****
      {
          appendPQExpBuffer(query,
                            "SELECT sequence_name, "
!                           "0 AS start_value, last_value, increment_by, "
                     "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
                     "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
                            "     ELSE max_value "
--- 13395,13401 ----
      {
          appendPQExpBuffer(query,
                            "SELECT sequence_name, "
!                           "0 AS start_value, increment_by, "
                     "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
                     "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
                            "     ELSE max_value "
*************** dumpSequence(Archive *fout, TableInfo *t
*** 13401,13407 ****
                     "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
                            "     ELSE min_value "
                            "END AS min_value, "
!                           "cache_value, is_cycled, is_called from %s",
                            bufx, bufm,
                            fmtId(tbinfo->dobj.name));
      }
--- 13404,13410 ----
                     "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
                            "     ELSE min_value "
                            "END AS min_value, "
!                           "cache_value, is_cycled FROM %s",
                            bufx, bufm,
                            fmtId(tbinfo->dobj.name));
      }
*************** dumpSequence(Archive *fout, TableInfo *t
*** 13428,13592 ****
  #endif

      startv = PQgetvalue(res, 0, 1);
!     last = PQgetvalue(res, 0, 2);
!     incby = PQgetvalue(res, 0, 3);
      if (!PQgetisnull(res, 0, 4))
!         maxv = PQgetvalue(res, 0, 4);
!     if (!PQgetisnull(res, 0, 5))
!         minv = PQgetvalue(res, 0, 5);
!     cache = PQgetvalue(res, 0, 6);
!     cycled = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
!     called = (strcmp(PQgetvalue(res, 0, 8), "t") == 0);

      /*
!      * The logic we use for restoring sequences is as follows:
!      *
!      * Add a CREATE SEQUENCE statement as part of a "schema" dump (use
!      * last_val for start if called is false, else use min_val for start_val).
!      * Also, if the sequence is owned by a column, add an ALTER SEQUENCE OWNED
!      * BY command for it.
!      *
!      * Add a 'SETVAL(seq, last_val, iscalled)' as part of a "data" dump.
       */
!     if (!dataOnly)
!     {
!         /*
!          * DROP must be fully qualified in case same name appears in
!          * pg_catalog
!          */
!         appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
!                           fmtId(tbinfo->dobj.namespace->dobj.name));
!         appendPQExpBuffer(delqry, "%s;\n",
!                           fmtId(tbinfo->dobj.name));

!         resetPQExpBuffer(query);

!         if (binary_upgrade)
!         {
!             binary_upgrade_set_pg_class_oids(fout, query,
!                                              tbinfo->dobj.catId.oid, false);
!             binary_upgrade_set_type_oids_by_rel_oid(fout, query,
!                                                     tbinfo->dobj.catId.oid);
!         }

!         appendPQExpBuffer(query,
!                           "CREATE SEQUENCE %s\n",
!                           fmtId(tbinfo->dobj.name));

!         if (fout->remoteVersion >= 80400)
!             appendPQExpBuffer(query, "    START WITH %s\n", startv);
!         else
!         {
!             /*
!              * Versions before 8.4 did not remember the true start value.  If
!              * is_called is false then the sequence has never been incremented
!              * so we can use last_val.    Otherwise punt and let it default.
!              */
!             if (!called)
!                 appendPQExpBuffer(query, "    START WITH %s\n", last);
!         }

!         appendPQExpBuffer(query, "    INCREMENT BY %s\n", incby);

!         if (minv)
!             appendPQExpBuffer(query, "    MINVALUE %s\n", minv);
!         else
!             appendPQExpBuffer(query, "    NO MINVALUE\n");

!         if (maxv)
!             appendPQExpBuffer(query, "    MAXVALUE %s\n", maxv);
!         else
!             appendPQExpBuffer(query, "    NO MAXVALUE\n");

!         appendPQExpBuffer(query,
!                           "    CACHE %s%s",
!                           cache, (cycled ? "\n    CYCLE" : ""));

!         appendPQExpBuffer(query, ";\n");

!         appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));

!         /* binary_upgrade:    no need to clear TOAST table oid */

!         if (binary_upgrade)
!             binary_upgrade_extension_member(query, &tbinfo->dobj,
!                                             labelq->data);

!         ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
!                      tbinfo->dobj.name,
!                      tbinfo->dobj.namespace->dobj.name,
!                      NULL,
!                      tbinfo->rolname,
!                      false, "SEQUENCE", SECTION_PRE_DATA,
!                      query->data, delqry->data, NULL,
!                      NULL, 0,
!                      NULL, NULL);

!         /*
!          * If the sequence is owned by a table column, emit the ALTER for it
!          * as a separate TOC entry immediately following the sequence's own
!          * entry.  It's OK to do this rather than using full sorting logic,
!          * because the dependency that tells us it's owned will have forced
!          * the table to be created first.  We can't just include the ALTER in
!          * the TOC entry because it will fail if we haven't reassigned the
!          * sequence owner to match the table's owner.
!          *
!          * We need not schema-qualify the table reference because both
!          * sequence and table must be in the same schema.
!          */
!         if (OidIsValid(tbinfo->owning_tab))
!         {
!             TableInfo  *owning_tab = findTableByOid(tbinfo->owning_tab);

!             if (owning_tab && owning_tab->dobj.dump)
!             {
!                 resetPQExpBuffer(query);
!                 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
!                                   fmtId(tbinfo->dobj.name));
!                 appendPQExpBuffer(query, " OWNED BY %s",
!                                   fmtId(owning_tab->dobj.name));
!                 appendPQExpBuffer(query, ".%s;\n",
                          fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));

!                 ArchiveEntry(fout, nilCatalogId, createDumpId(),
!                              tbinfo->dobj.name,
!                              tbinfo->dobj.namespace->dobj.name,
!                              NULL,
!                              tbinfo->rolname,
!                              false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
!                              query->data, "", NULL,
!                              &(tbinfo->dobj.dumpId), 1,
!                              NULL, NULL);
!             }
          }
-
-         /* Dump Sequence Comments and Security Labels */
-         dumpComment(fout, labelq->data,
-                     tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-                     tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
-         dumpSecLabel(fout, labelq->data,
-                      tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-                      tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
      }

!     if (!schemaOnly)
!     {
!         resetPQExpBuffer(query);
!         appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
!         appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
!         appendPQExpBuffer(query, ", %s, %s);\n",
!                           last, (called ? "true" : "false"));
!
!         ArchiveEntry(fout, nilCatalogId, createDumpId(),
!                      tbinfo->dobj.name,
!                      tbinfo->dobj.namespace->dobj.name,
!                      NULL,
!                      tbinfo->rolname,
!                      false, "SEQUENCE SET", SECTION_PRE_DATA,
!                      query->data, "", NULL,
!                      &(tbinfo->dobj.dumpId), 1,
!                      NULL, NULL);
!     }

      PQclear(res);

--- 13431,13550 ----
  #endif

      startv = PQgetvalue(res, 0, 1);
!     incby = PQgetvalue(res, 0, 2);
!     if (!PQgetisnull(res, 0, 3))
!         maxv = PQgetvalue(res, 0, 3);
      if (!PQgetisnull(res, 0, 4))
!         minv = PQgetvalue(res, 0, 4);
!     cache = PQgetvalue(res, 0, 5);
!     cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);

      /*
!      * DROP must be fully qualified in case same name appears in pg_catalog
       */
!     appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
!                       fmtId(tbinfo->dobj.namespace->dobj.name));
!     appendPQExpBuffer(delqry, "%s;\n",
!                       fmtId(tbinfo->dobj.name));

!     resetPQExpBuffer(query);

!     if (binary_upgrade)
!     {
!         binary_upgrade_set_pg_class_oids(fout, query,
!                                          tbinfo->dobj.catId.oid, false);
!         binary_upgrade_set_type_oids_by_rel_oid(fout, query,
!                                                 tbinfo->dobj.catId.oid);
!     }

!     appendPQExpBuffer(query,
!                       "CREATE SEQUENCE %s\n",
!                       fmtId(tbinfo->dobj.name));

!     if (fout->remoteVersion >= 80400)
!         appendPQExpBuffer(query, "    START WITH %s\n", startv);

!     appendPQExpBuffer(query, "    INCREMENT BY %s\n", incby);

!     if (minv)
!         appendPQExpBuffer(query, "    MINVALUE %s\n", minv);
!     else
!         appendPQExpBuffer(query, "    NO MINVALUE\n");

!     if (maxv)
!         appendPQExpBuffer(query, "    MAXVALUE %s\n", maxv);
!     else
!         appendPQExpBuffer(query, "    NO MAXVALUE\n");

!     appendPQExpBuffer(query,
!                       "    CACHE %s%s",
!                       cache, (cycled ? "\n    CYCLE" : ""));

!     appendPQExpBuffer(query, ";\n");

!     appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));

!     /* binary_upgrade:    no need to clear TOAST table oid */

!     if (binary_upgrade)
!         binary_upgrade_extension_member(query, &tbinfo->dobj,
!                                         labelq->data);

!     ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
!                  tbinfo->dobj.name,
!                  tbinfo->dobj.namespace->dobj.name,
!                  NULL,
!                  tbinfo->rolname,
!                  false, "SEQUENCE", SECTION_PRE_DATA,
!                  query->data, delqry->data, NULL,
!                  NULL, 0,
!                  NULL, NULL);

!     /*
!      * If the sequence is owned by a table column, emit the ALTER for it as a
!      * separate TOC entry immediately following the sequence's own entry.
!      * It's OK to do this rather than using full sorting logic, because the
!      * dependency that tells us it's owned will have forced the table to be
!      * created first.  We can't just include the ALTER in the TOC entry
!      * because it will fail if we haven't reassigned the sequence owner to
!      * match the table's owner.
!      *
!      * We need not schema-qualify the table reference because both sequence
!      * and table must be in the same schema.
!      */
!     if (OidIsValid(tbinfo->owning_tab))
!     {
!         TableInfo  *owning_tab = findTableByOid(tbinfo->owning_tab);

!         if (owning_tab && owning_tab->dobj.dump)
!         {
!             resetPQExpBuffer(query);
!             appendPQExpBuffer(query, "ALTER SEQUENCE %s",
!                               fmtId(tbinfo->dobj.name));
!             appendPQExpBuffer(query, " OWNED BY %s",
!                               fmtId(owning_tab->dobj.name));
!             appendPQExpBuffer(query, ".%s;\n",
                          fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));

!             ArchiveEntry(fout, nilCatalogId, createDumpId(),
!                          tbinfo->dobj.name,
!                          tbinfo->dobj.namespace->dobj.name,
!                          NULL,
!                          tbinfo->rolname,
!                          false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
!                          query->data, "", NULL,
!                          &(tbinfo->dobj.dumpId), 1,
!                          NULL, NULL);
          }
      }

!     /* Dump Sequence Comments and Security Labels */
!     dumpComment(fout, labelq->data,
!                 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
!                 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
!     dumpSecLabel(fout, labelq->data,
!                  tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
!                  tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);

      PQclear(res);

*************** dumpSequence(Archive *fout, TableInfo *t
*** 13595,13600 ****
--- 13553,13613 ----
      destroyPQExpBuffer(labelq);
  }

+ /*
+  * dumpSequenceData
+  *      write the data of one user-defined sequence
+  */
+ static void
+ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
+ {
+     TableInfo  *tbinfo = tdinfo->tdtable;
+     PGresult   *res;
+     char       *last;
+     bool        called;
+     PQExpBuffer query = createPQExpBuffer();
+
+     /* Make sure we are in proper schema */
+     selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
+
+     appendPQExpBuffer(query,
+                       "SELECT last_value, is_called FROM %s",
+                       fmtId(tbinfo->dobj.name));
+
+     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+     if (PQntuples(res) != 1)
+     {
+         write_msg(NULL, ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)\n",
+                                  "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
+                                  PQntuples(res)),
+                   tbinfo->dobj.name, PQntuples(res));
+         exit_nicely(1);
+     }
+
+     last = PQgetvalue(res, 0, 0);
+     called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+     resetPQExpBuffer(query);
+     appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
+     appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+     appendPQExpBuffer(query, ", %s, %s);\n",
+                       last, (called ? "true" : "false"));
+
+     ArchiveEntry(fout, nilCatalogId, createDumpId(),
+                  tbinfo->dobj.name,
+                  tbinfo->dobj.namespace->dobj.name,
+                  NULL,
+                  tbinfo->rolname,
+                  false, "SEQUENCE SET", SECTION_DATA,
+                  query->data, "", NULL,
+                  &(tbinfo->dobj.dumpId), 1,
+                  NULL, NULL);
+
+     PQclear(res);
+
+     destroyPQExpBuffer(query);
+ }
+
  static void
  dumpTrigger(Archive *fout, TriggerInfo *tginfo)
  {

pgsql-hackers by date:

Previous
From: "Kevin Grittner"
Date:
Subject: Re: ToDo: KNN Search should to support DISTINCT clasuse?
Next
From: Tom Lane
Date:
Subject: Re: ToDo: KNN Search should to support DISTINCT clasuse?