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  <br...@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;
+ }
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to