Re: [GENERAL] pg_migrator not setting values of sequences?

Поиск
Список
Период
Сортировка
От Bruce Momjian
Тема Re: [GENERAL] pg_migrator not setting values of sequences?
Дата
Msg-id 200907201901.n6KJ1aZ20865@momjian.us
обсуждение исходный текст
Ответ на Re: [GENERAL] pg_migrator not setting values of sequences?  (Bruce Momjian <bruce@momjian.us>)
Список pgsql-hackers
Bruce Momjian wrote:
> Alvaro Herrera wrote:
> > Bruce Momjian wrote:
> >
> > > Something is certainly wrong.  Did we change sequence table format from
> > > 8.3 to 8.4?
> >
> > 8.3 does not have start_value.
>
> Looking at an invalidly-migrated sequence's columns:
>
>     regression=> \d serialtest_f2_foo
>           Sequence "public.serialtest_f2_foo"
>         Column     |  Type   |        Value
>     ---------------+---------+---------------------
>      sequence_name | name    | serialtest_f2_foo
>      last_value    | bigint  | 3
>      start_value   | bigint  | 1
>      increment_by  | bigint  | 9223372036854775807
>      max_value     | bigint  | 1
>      min_value     | bigint  | 1
>      cache_value   | bigint  | 0
>      log_cnt       | bigint  | 25387551686912
>      is_cycled     | boolean | f
>      is_called     | boolean |
>
> Should pg_migrator just pull the misaligned values and do an ALTER
> SEQUENCE/seval() to fix it, or create a script to do that?

I have applied the attached patch to pg_migrator that will properly
handle migrating sequences;  it should apply cleanly to pg_migrator
8.4.1 alpha 1.

What I found during research is that pg_dump --schema-only already
creates the sequence:

    CREATE SEQUENCE check_seq
        START WITH 1
        INCREMENT BY 1
        NO MAXVALUE
        NO MINVALUE
        CACHE 1;

What it does not do is to call setval() to set the sequence value and
'is_called'.  What I did was to _not_ migrate the sequence file, but
rather create a script from the old cluster that uses setval() to set
the sequence values.  This can be safely run by pg_migrator
unconditionally because we are not migrating the sequence files, even in
link mode.

This solves the sequence migration problem, with no changes to pg_dump.

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
? tools
? log
? src/pg_migrator
Index: src/info.c
===================================================================
RCS file: /cvsroot/pg-migrator/pg_migrator/src/info.c,v
retrieving revision 1.18
diff -c -r1.18 info.c
*** src/info.c    14 Jul 2009 02:34:59 -0000    1.18
--- src/info.c    20 Jul 2009 18:55:48 -0000
***************
*** 322,353 ****
      int            i_oid = -1;
      int            i_relfilenode = -1;
      int            i_reltoastrelid = -1;

!     res = executeQueryOrDie(ctx, conn,
!                             "SELECT DISTINCT c.oid, n.nspname, c.relname, "
!                             "    c.relfilenode, c.reltoastrelid, "
!                             "    t.spclocation,n.nspname "
!                             "FROM (pg_catalog.pg_class c JOIN "
!                             "        pg_catalog.pg_namespace n "
!                             "    ON c.relnamespace = n.oid) "
!                             "   LEFT OUTER JOIN pg_catalog.pg_tablespace t "
!                             "    ON c.reltablespace = t.oid "
!                             "WHERE relnamespace NOT IN "
!                             "    ( "
!                             "        SELECT oid "
!                             "        FROM pg_catalog.pg_namespace "
!                             "        WHERE nspname IN "
!                             "            ('pg_catalog', 'information_schema') "
!                             "    ) "
!                             "    AND c.oid >= "
!                             STRINGIFY(FirstNormalObjectId) " "
!                             "    AND "
!                             "    (relkind = 'r' OR relkind = 't' OR "
!                             "     relkind = 'i' OR relkind = 'S') "
!                             "GROUP BY  c.oid, n.nspname, c.relname, c.relfilenode,"
!                             "            c.reltoastrelid, t.spclocation, "
!                             "            n.nspname "
!                             "ORDER BY n.nspname, c.relname;");

      ntups = PQntuples(res);

--- 322,360 ----
      int            i_oid = -1;
      int            i_relfilenode = -1;
      int            i_reltoastrelid = -1;
+     char        query[QUERY_ALLOC];

!     snprintf(query, sizeof(query),
!                 "SELECT DISTINCT c.oid, n.nspname, c.relname, "
!                 "    c.relfilenode, c.reltoastrelid, "
!                 "    t.spclocation,n.nspname "
!                 "FROM (pg_catalog.pg_class c JOIN "
!                 "        pg_catalog.pg_namespace n "
!                 "    ON c.relnamespace = n.oid) "
!                 "   LEFT OUTER JOIN pg_catalog.pg_tablespace t "
!                 "    ON c.reltablespace = t.oid "
!                 "WHERE relnamespace NOT IN "
!                 "    ( "
!                 "        SELECT oid "
!                 "        FROM pg_catalog.pg_namespace "
!                 "        WHERE nspname IN "
!                 "            ('pg_catalog', 'information_schema') "
!                 "    ) "
!                 "    AND c.oid >= "
!                 STRINGIFY(FirstNormalObjectId) " "
!                 "    AND "
!                 "    (relkind = 'r' OR relkind = 't' OR "
!                 "     relkind = 'i'%s)"
!                 "GROUP BY  c.oid, n.nspname, c.relname, c.relfilenode,"
!                 "            c.reltoastrelid, t.spclocation, "
!                 "            n.nspname "
!                 "ORDER BY n.nspname, c.relname;",
!                 /* see the comment at the top of v8_3_adjust_sequences() */
!                 (GET_MAJOR_VERSION(ctx->old.pg_version) == 803 &&
!                  GET_MAJOR_VERSION(ctx->new.pg_version) > 803) ?
!                     "" : " OR relkind = 'S'");
!
!     res = executeQueryOrDie(ctx, conn, query);

      ntups = PQntuples(res);

Index: src/pg_migrator.c
===================================================================
RCS file: /cvsroot/pg-migrator/pg_migrator/src/pg_migrator.c,v
retrieving revision 1.56
diff -c -r1.56 pg_migrator.c
*** src/pg_migrator.c    3 Jul 2009 16:46:49 -0000    1.56
--- src/pg_migrator.c    20 Jul 2009 18:55:48 -0000
***************
*** 37,43 ****
  main(int argc, char **argv)
  {
      migratorContext ctx;
!
      memset(&ctx, 0, sizeof(ctx));

      parseCommandLine(&ctx, argc, argv);
--- 37,44 ----
  main(int argc, char **argv)
  {
      migratorContext ctx;
!     char *sequence_script_file_name = NULL;
!
      memset(&ctx, 0, sizeof(ctx));

      parseCommandLine(&ctx, argc, argv);
***************
*** 78,83 ****
--- 79,92 ----
              v8_3_invalidate_hash_gin_indexes(&ctx, true, CLUSTER_OLD);
              v8_3_invalidate_bpchar_pattern_ops_indexes(&ctx, true, CLUSTER_OLD);
          }
+         else
+             /*
+              *    While we have the old server running, create the script
+              *    to properly restore its sequence values but we report this
+              *    at the end.
+              */
+             sequence_script_file_name =
+                 v8_3_create_sequence_script(&ctx, CLUSTER_OLD);
      }

      /* Looks okay so far.  Prepare the pg_dump output */
***************
*** 245,250 ****
--- 254,273 ----
          GET_MAJOR_VERSION(ctx.new.pg_version) > 803)
      {
          start_postmaster(&ctx, CLUSTER_NEW, true);
+         /* restore proper sequence values using file created from old server */
+         if (strlen(sequence_script_file_name) > 0)
+         {
+             prep_status(&ctx, "Adjusting sequences");
+             exec_prog(&ctx, true,
+                       SYSTEMQUOTE "\"%s/%s\" --set ON_ERROR_STOP=on --port %d "
+                       "-f \"%s\" --dbname template1 >> \"%s\"" SYSTEMQUOTE,
+                       ctx.new.bindir, ctx.new_psql_exe, ctx.new.port,
+                       sequence_script_file_name, ctx.logfile);
+             unlink(sequence_script_file_name);
+             check_ok(&ctx);
+         }
+         pg_free(sequence_script_file_name);
+
          v8_3_rebuild_tsvector_tables(&ctx, false, CLUSTER_NEW);
          v8_3_invalidate_hash_gin_indexes(&ctx, false, CLUSTER_NEW);
          v8_3_invalidate_bpchar_pattern_ops_indexes(&ctx, false, CLUSTER_NEW);
Index: src/pg_migrator.h
===================================================================
RCS file: /cvsroot/pg-migrator/pg_migrator/src/pg_migrator.h,v
retrieving revision 1.63
diff -c -r1.63 pg_migrator.h
*** src/pg_migrator.h    18 Jul 2009 00:14:01 -0000    1.63
--- src/pg_migrator.h    20 Jul 2009 18:55:48 -0000
***************
*** 26,31 ****
--- 26,32 ----

  #define MAX_STRING        1024
  #define LINE_ALLOC        4096
+ #define QUERY_ALLOC        8192

  #define MIGRATOR_API_VERSION    1

***************
*** 390,392 ****
--- 391,396 ----
                              bool check_mode, Cluster whichCluster);
  void        v8_3_invalidate_bpchar_pattern_ops_indexes(migratorContext *ctx,
                              bool check_mode, Cluster whichCluster);
+ char         *v8_3_create_sequence_script(migratorContext *ctx,
+                             Cluster whichCluster);
+
Index: src/version.c
===================================================================
RCS file: /cvsroot/pg-migrator/pg_migrator/src/version.c,v
retrieving revision 1.22
diff -c -r1.22 version.c
*** src/version.c    2 Jul 2009 23:22:53 -0000    1.22
--- src/version.c    20 Jul 2009 18:55:48 -0000
***************
*** 417,423 ****
                      "| when executed by psql by the database super-user, will rebuild\n"
                      "| all tables with tsvector columns.\n\n",
                      output_path);
-
      }
      else
          check_ok(ctx);
--- 417,422 ----
***************
*** 528,534 ****
                      "| when executed by psql by the database super-user, will recreate\n"
                      "| all invalid indexes; until then, none of these indexes will be used.\n\n",
                      output_path);
-
      }
      else
          check_ok(ctx);
--- 527,532 ----
***************
*** 657,659 ****
--- 655,756 ----
      else
          check_ok(ctx);
  }
+
+
+ /*
+  * v8_3_create_sequence_script()
+  *
+  *    8.4 added the column "start_value" to all sequences.  For this reason,
+  *    we don't transfer sequence files but instead use the CREATE SEQUENCE
+  *    command from the schema dump, and use setval() to restore the sequence
+  *    value and 'is_called' from the old database.  This is safe to run
+  *    by pg_migrator because sequence files are not transfered from the old
+  *    server, even in link mode.
+  */
+ char *
+ v8_3_create_sequence_script(migratorContext *ctx, Cluster whichCluster)
+ {
+     ClusterInfo    *active_cluster = (whichCluster == CLUSTER_OLD) ?
+                     &ctx->old : &ctx->new;
+     int            dbnum;
+     FILE        *script = NULL;
+     bool        found = false;
+     char        *output_path = pg_malloc(ctx, MAXPGPATH);
+
+     snprintf(output_path, MAXPGPATH, "%s/adjust_sequences.txt", ctx->home_dir);
+
+     prep_status(ctx, "Creating script to adjust sequences");
+
+     for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
+     {
+         PGresult   *res;
+          bool        db_used = false;
+         int            ntups;
+         int            rowno;
+         int            i_nspname, i_relname;
+         DbInfo       *active_db = &active_cluster->dbarr.dbs[dbnum];
+         PGconn       *conn = connectToServer(ctx, active_db->db_name, whichCluster);
+
+         /* Find any sequences */
+         res = executeQueryOrDie(ctx, conn,
+                                 "SELECT n.nspname, c.relname "
+                                 "FROM    pg_catalog.pg_class c, "
+                                 "        pg_catalog.pg_namespace n "
+                                 "WHERE    c.relkind = 'S' AND "
+                                 "        c.relnamespace = n.oid AND "
+                                 "        n.nspname != 'pg_catalog' AND "
+                                 "        n.nspname != 'information_schema'");
+
+         ntups = PQntuples(res);
+         i_nspname = PQfnumber(res, "nspname");
+         i_relname = PQfnumber(res, "relname");
+         for (rowno = 0; rowno < ntups; rowno++)
+         {
+             PGresult   *seq_res;
+             int            i_last_value, i_is_called;
+             const char *nspname = PQgetvalue(res, rowno, i_nspname);
+             const char *relname = PQgetvalue(res, rowno, i_relname);
+
+             found = true;
+
+             if (script == NULL && (script = fopen(output_path, "w")) == NULL)
+                     pg_log(ctx, PG_FATAL, "Could not create necessary file:  %s\n", output_path);
+             if (!db_used)
+             {
+                 fprintf(script, "\\connect %s\n\n",
+                         quote_identifier(ctx, active_db->db_name));
+                 db_used = true;
+             }
+
+             /* Find the desired sequence */
+             seq_res = executeQueryOrDie(ctx, conn,
+                                     "SELECT s.last_value, s.is_called "
+                                     "FROM    %s.%s s",
+                                     quote_identifier(ctx, nspname),
+                                     quote_identifier(ctx, relname));
+
+             assert(PQntuples(seq_res) == 1);
+             i_last_value = PQfnumber(seq_res, "last_value");
+             i_is_called = PQfnumber(seq_res, "is_called");
+
+             fprintf(script, "SELECT setval('%s.%s', %s, '%s');\n",
+                     quote_identifier(ctx, nspname), quote_identifier(ctx, relname),
+                     PQgetvalue(seq_res, 0, i_last_value), PQgetvalue(seq_res, 0, i_is_called));
+             PQclear(seq_res);
+         }
+         if (db_used)
+             fprintf(script, "\n");
+
+         PQclear(res);
+
+         PQfinish(conn);
+     }
+     if (found)
+         fclose(script);
+     else    /* mark script as unused */
+         output_path[0] = '\0';
+
+     check_ok(ctx);
+
+     return output_path;
+ }

В списке pgsql-hackers по дате отправления:

Предыдущее
От: David Fetter
Дата:
Сообщение: Re: WIP: Deferrable unique constraints
Следующее
От: Joshua Brindle
Дата:
Сообщение: Re: [PATCH] SE-PgSQL/tiny rev.2193