Hi,

Please find attached to this email an RFC patch implementing the basics
of the pg_dump --extension-script option. After much discussion around
the concept of an inline extension, we decided last year that a good
first step would be pg_dump support for an extension's script.

The approach I've been using here is to dump the script from the catalog
current dependencies, which mean that a sequence of CREATE EXTENSION
followed by a number of ALTER EXTENSION … UPDATE … will be consolidated
into a single CREATE EXTENSION command in the dump, much the same as
with CREATE TABLE then ALTER TABLE … ADD COLUMN and the like.

Currently the option behavior is the following, that looks sane to me,
and is open for discussion: the dump's schema always include the CREATE
EXTENSION commands you need. The extensions listed in the -X option
(that you can use more than once) will get dumped with their's current
member objects in a script, inline.

To try the attached patch, you could do as following:

    createdb foo
    psql -c "create extension hstore" -d foo
    pg_dump -X hstore -f /tmp/foo.sql foo

    createdb bar
    psql -1 -f /tmp/foo.sql -d bar

To be able to restore the dump, I've been adding some basic support to
the CREATE EXTENSION command so that it will find the data it needs from
the SQL command rather than the control file.

Note that the extension control file only contains information about how
to install an extension from a script file on disk. That's something we
don't need at all when installing the extension from a dump, using
either pg_restore or psql. We have some exceptions to that principle,
namely: requires (sets the search_path) and relocatable (found in the
catalogs, needs to survive dump/restore).

Given positive feedback on that way to attack the problem, the TODO list
includes:

 - document the new pg_dump --extension-script switch
 - add support for ALTER EXTENSION … WITH $$ <script here> $$;

The ALTER EXTENSION support is optional as far as pg_dump support goes,
it would be good to have it to make the User Interface complete.

Regards,
-- 
Dimitri Fontaine
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support

*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 75,80 **** typedef struct ExtensionControlFile
--- 75,81 ----
  	bool		superuser;		/* must be superuser to install? */
  	int			encoding;		/* encoding of the script file, or -1 */
  	List	   *requires;		/* names of prerequisite extensions */
+ 	char       *script;
  } ExtensionControlFile;
  
  /*
***************
*** 577,586 **** parse_extension_control_file(ExtensionControlFile *control,
  }
  
  /*
!  * Read the primary control file for the specified extension.
   */
  static ExtensionControlFile *
! read_extension_control_file(const char *extname)
  {
  	ExtensionControlFile *control;
  
--- 578,587 ----
  }
  
  /*
!  * Create an ExtensionControlFile with default values.
   */
  static ExtensionControlFile *
! default_extension_control_file(const char *extname)
  {
  	ExtensionControlFile *control;
  
***************
*** 593,598 **** read_extension_control_file(const char *extname)
--- 594,610 ----
  	control->superuser = true;
  	control->encoding = -1;
  
+ 	return control;
+ }
+ 
+ /*
+  * Read the primary control file for the specified extension.
+  */
+ static ExtensionControlFile *
+ read_extension_control_file(const char *extname)
+ {
+ 	ExtensionControlFile *control = default_extension_control_file(extname);
+ 
  	/*
  	 * Parse the primary control file.
  	 */
***************
*** 858,866 **** execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
  	CurrentExtensionObject = extensionOid;
  	PG_TRY();
  	{
! 		char	   *c_sql = read_extension_script_file(control, filename);
  		Datum		t_sql;
  
  		/* We use various functions that want to operate on text datums */
  		t_sql = CStringGetTextDatum(c_sql);
  
--- 870,883 ----
  	CurrentExtensionObject = extensionOid;
  	PG_TRY();
  	{
! 		char	   *c_sql;
  		Datum		t_sql;
  
+ 		if (control->script)
+ 			c_sql = control->script;
+ 		else
+ 			c_sql = read_extension_script_file(control, filename);
+ 
  		/* We use various functions that want to operate on text datums */
  		t_sql = CStringGetTextDatum(c_sql);
  
***************
*** 1178,1183 **** void
--- 1195,1203 ----
  CreateExtension(CreateExtensionStmt *stmt)
  {
  	DefElem    *d_schema = NULL;
+ 	DefElem    *d_script = NULL;
+ 	DefElem    *d_requires = NULL;
+ 	DefElem    *d_relocatable = NULL;
  	DefElem    *d_new_version = NULL;
  	DefElem    *d_old_version = NULL;
  	char	   *schemaName;
***************
*** 1229,1248 **** CreateExtension(CreateExtensionStmt *stmt)
  				 errmsg("nested CREATE EXTENSION is not supported")));
  
  	/*
! 	 * Read the primary control file.  Note we assume that it does not contain
! 	 * any non-ASCII data, so there is no need to worry about encoding at this
! 	 * point.
! 	 */
! 	pcontrol = read_extension_control_file(stmt->extname);
! 
! 	/*
! 	 * Read the statement option list
  	 */
  	foreach(lc, stmt->options)
  	{
  		DefElem    *defel = (DefElem *) lfirst(lc);
  
! 		if (strcmp(defel->defname, "schema") == 0)
  		{
  			if (d_schema)
  				ereport(ERROR,
--- 1249,1288 ----
  				 errmsg("nested CREATE EXTENSION is not supported")));
  
  	/*
! 	 * Read the statement option list.
! 	 *
! 	 * We need to read some of the options to know what to do next: if the
! 	 * "script" one is in use, we don't want to read any control file.
  	 */
  	foreach(lc, stmt->options)
  	{
  		DefElem    *defel = (DefElem *) lfirst(lc);
  
! 		if (strcmp(defel->defname, "script") == 0)
! 		{
! 			if (d_script)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options")));
! 			d_script = defel;
! 		}
! 		else if (strcmp(defel->defname, "relocatable") == 0)
! 		{
! 			if (d_relocatable)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options")));
! 			d_relocatable = defel;
! 		}
! 		else if (strcmp(defel->defname, "requires") == 0)
! 		{
! 			if (d_requires)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options")));
! 			d_requires = defel;
! 		}
! 		else if (strcmp(defel->defname, "schema") == 0)
  		{
  			if (d_schema)
  				ereport(ERROR,
***************
*** 1270,1275 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1310,1364 ----
  			elog(ERROR, "unrecognized option: %s", defel->defname);
  	}
  
+ 	if (d_script)
+ 	{
+ 		/*
+ 		 * We are given the extension's script in the SQL command, so create a
+ 		 * control file structure in memory, we won't have a control file on
+ 		 * disk to use.
+ 		 */
+ 		pcontrol = default_extension_control_file(stmt->extname);
+ 		pcontrol->script = strVal(d_script->arg);
+ 
+ 		if (d_relocatable)
+ 			pcontrol->relocatable = intVal(d_relocatable->arg) != 0;
+ 
+ 		if (d_requires)
+ 		{
+ 			/* Need a modifiable copy of string */
+ 			char	   *rawnames = pstrdup(strVal(d_requires->arg));
+ 
+ 			/* Parse string into list of identifiers */
+ 			if (!SplitIdentifierString(rawnames, ',', &pcontrol->requires))
+ 			{
+ 				/* syntax error in name list */
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						 errmsg("parameter \"requires\" must be a list of extension names")));
+ 			}
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * If the extension script is not in the command, then the user is not
+ 		 * the extension packager and we want to read about relocatable and
+ 		 * requires in the control file, not in the SQL command.
+ 		 */
+ 		if (d_relocatable || d_requires)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("parameter \"%s\" is only expected when using CREATE EXTENSION AS",
+ 							d_relocatable ? "relocatable" : "requires")));
+ 
+ 		/*
+ 		 * Read the primary control file. Note we assume that it does not
+ 		 * contain any non-ASCII data, so there is no need to worry about
+ 		 * encoding at this point.
+ 		 */
+ 		pcontrol = read_extension_control_file(stmt->extname);
+ 	}
+ 
  	/*
  	 * Determine the version to install
  	 */
***************
*** 1332,1340 **** CreateExtension(CreateExtensionStmt *stmt)
  	}
  
  	/*
! 	 * Fetch control parameters for installation target version
  	 */
! 	control = read_extension_aux_control_file(pcontrol, versionName);
  
  	/*
  	 * Determine the target schema to install the extension into
--- 1421,1433 ----
  	}
  
  	/*
! 	 * Fetch control parameters for installation target version. When the
! 	 * script is given in the command string, no auxiliary file is expected.
  	 */
! 	if (pcontrol->script == NULL)
! 		control = read_extension_aux_control_file(pcontrol, versionName);
! 	else
! 		control = pcontrol;
  
  	/*
  	 * Determine the target schema to install the extension into
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 3439,3444 **** DropTableSpaceStmt: DROP TABLESPACE name
--- 3439,3446 ----
   *             CREATE EXTENSION extension
   *             [ WITH ] [ SCHEMA schema ] [ VERSION version ] [ FROM oldversion ]
   *
+  *             CREATE EXTENSION extension ... AS $$ ... $$
+  *
   *****************************************************************************/
  
  CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
***************
*** 3457,3462 **** CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
--- 3459,3486 ----
  					n->options = $8;
  					$$ = (Node *) n;
  				}
+ 				| CREATE EXTENSION name opt_with create_extension_opt_list
+                   AS Sconst
+ 				{
+ 					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ 					n->extname = $3;
+ 					n->if_not_exists = false;
+ 					n->options = lappend($5,
+ 										 makeDefElem("script",
+ 													 (Node *)makeString($7)));
+ 					$$ = (Node *) n;
+ 				}
+ 				| CREATE EXTENSION IF_P NOT EXISTS name
+                   opt_with create_extension_opt_list AS Sconst
+ 				{
+ 					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ 					n->extname = $6;
+ 					n->if_not_exists = false;
+ 					n->options = lappend($8,
+ 										 makeDefElem("script",
+ 													 (Node *)makeString($10)));
+ 					$$ = (Node *) n;
+ 				}
  		;
  
  create_extension_opt_list:
***************
*** 3479,3484 **** create_extension_opt_item:
--- 3503,3535 ----
  				{
  					$$ = makeDefElem("old_version", (Node *)makeString($2));
  				}
+             | IDENT Sconst
+                 /*
+                  * We handle identifiers that aren't parser keywords with
+                  * the following special-case codes, to avoid bloating the
+                  * size of the main parser.
+                  */
+                 {
+ 					if (strcmp($1, "requires") == 0)
+ 						$$ = makeDefElem("requires", (Node *)makeString($2));
+ 					else
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_SYNTAX_ERROR),
+ 								 errmsg("unrecognized extension option \"%s\"", $1),
+ 									 parser_errposition(@1)));
+ 				}
+ 			| IDENT
+ 				{
+ 					if (strcmp($1, "relocatable") == 0)
+ 						$$ = makeDefElem("relocatable", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "norelocatable") == 0)
+ 						$$ = makeDefElem("relocatable", (Node *)makeInteger(FALSE));
+ 					else
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_SYNTAX_ERROR),
+ 								 errmsg("unrecognized extension option \"%s\"", $1),
+ 									 parser_errposition(@1)));
+ 				}
  		;
  
  /*****************************************************************************
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 119,124 **** static SimpleOidList table_exclude_oids = {NULL, NULL};
--- 119,127 ----
  static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
  static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
  
+ static SimpleStringList extension_include_patterns = {NULL, NULL};
+ static SimpleOidList extension_include_oids = {NULL, NULL};
+ 
  /* default, if no "inclusion" switches appear, is to dump everything */
  static bool include_everything = true;
  
***************
*** 150,155 **** static void expand_schema_name_patterns(Archive *fout,
--- 153,161 ----
  static void expand_table_name_patterns(Archive *fout,
  						   SimpleStringList *patterns,
  						   SimpleOidList *oids);
+ static void expand_extension_name_patterns(Archive *fout,
+ 										   SimpleStringList *patterns,
+ 										   SimpleOidList *oids);
  static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid, Oid objoid);
  static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
  static void guessConstraintInheritance(TableInfo *tblinfo, int numTables);
***************
*** 165,173 **** static void dumpSecLabel(Archive *fout, const char *target,
  static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
  			  SecLabelItem **items);
  static int	collectSecLabels(Archive *fout, SecLabelItem **items);
! static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
  static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
! static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
  static void dumpType(Archive *fout, TypeInfo *tyinfo);
  static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
  static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
--- 171,181 ----
  static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
  			  SecLabelItem **items);
  static int	collectSecLabels(Archive *fout, SecLabelItem **items);
! static void dumpDumpableObject(Archive *fout, DumpableObject *dobj,
! 							   DumpableObject **dobjs, int numObjs);
  static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
! static void dumpExtension(Archive *fout, ExtensionInfo *extinfo,
! 						  DumpableObject **dobjs, int numObjs);
  static void dumpType(Archive *fout, TypeInfo *tyinfo);
  static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
  static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
***************
*** 322,327 **** main(int argc, char **argv)
--- 330,336 ----
  		{"superuser", required_argument, NULL, 'S'},
  		{"table", required_argument, NULL, 't'},
  		{"exclude-table", required_argument, NULL, 'T'},
+ 		{"extension-script", required_argument, NULL, 'X'},
  		{"no-password", no_argument, NULL, 'w'},
  		{"password", no_argument, NULL, 'W'},
  		{"username", required_argument, NULL, 'U'},
***************
*** 388,394 **** main(int argc, char **argv)
  		}
  	}
  
! 	while ((c = getopt_long(argc, argv, "abcCE:f:F:h:in:N:oOp:RsS:t:T:U:vwWxZ:",
  							long_options, &optindex)) != -1)
  	{
  		switch (c)
--- 397,403 ----
  		}
  	}
  
! 	while ((c = getopt_long(argc, argv, "abcCE:f:F:h:in:N:oOp:RsS:t:T:U:vwWxX:Z:",
  							long_options, &optindex)) != -1)
  	{
  		switch (c)
***************
*** 471,476 **** main(int argc, char **argv)
--- 480,489 ----
  				simple_string_list_append(&table_exclude_patterns, optarg);
  				break;
  
+ 			case 'X':			/* include extension(s) script */
+ 				simple_string_list_append(&extension_include_patterns, optarg);
+ 				break;
+ 
  			case 'U':
  				username = pg_strdup(optarg);
  				break;
***************
*** 670,675 **** main(int argc, char **argv)
--- 683,692 ----
  	expand_table_name_patterns(fout, &tabledata_exclude_patterns,
  							   &tabledata_exclude_oids);
  
+ 	/* Expand extension selection patterns into OID lists */
+ 	expand_extension_name_patterns(fout, &extension_include_patterns,
+ 								   &extension_include_oids);
+ 
  	/* non-matching exclusion patterns aren't an error */
  
  	/*
***************
*** 746,752 **** main(int argc, char **argv)
  
  	/* Now the rearrangeable objects. */
  	for (i = 0; i < numObjs; i++)
! 		dumpDumpableObject(fout, dobjs[i]);
  
  	/*
  	 * Set up options info to ensure we dump what we want.
--- 763,769 ----
  
  	/* Now the rearrangeable objects. */
  	for (i = 0; i < numObjs; i++)
! 		dumpDumpableObject(fout, dobjs[i], dobjs, numObjs);
  
  	/*
  	 * Set up options info to ensure we dump what we want.
***************
*** 830,835 **** help(const char *progname)
--- 847,853 ----
  	printf(_("  -S, --superuser=NAME         superuser user name to use in plain-text format\n"));
  	printf(_("  -t, --table=TABLE            dump the named table(s) only\n"));
  	printf(_("  -T, --exclude-table=TABLE    do NOT dump the named table(s)\n"));
+ 	printf(_("  -X, --extension-script       dunp named extension(s) scripts\n"));
  	printf(_("  -x, --no-privileges          do not dump privileges (grant/revoke)\n"));
  	printf(_("  --binary-upgrade             for use by upgrade utilities only\n"));
  	printf(_("  --column-inserts             dump data as INSERT commands with column names\n"));
***************
*** 1062,1067 **** expand_table_name_patterns(Archive *fout,
--- 1080,1132 ----
  }
  
  /*
+  * Find the OIDs of all extensions matching the given list of patterns,
+  * and append them to the given ExtensionMembers list.
+  */
+ static void
+ expand_extension_name_patterns(Archive *fout,
+ 							   SimpleStringList *patterns,
+ 							   SimpleOidList *oids)
+ {
+ 	PQExpBuffer query;
+ 	PGresult   *res;
+ 	SimpleStringListCell *cell;
+ 	int			i;
+ 
+ 	if (patterns->head == NULL)
+ 		return;					/* nothing to do */
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/*
+ 	 * We use UNION ALL rather than UNION; this might sometimes result in
+ 	 * duplicate entries in the OID list, but we don't care.
+ 	 */
+ 
+ 	for (cell = patterns->head; cell; cell = cell->next)
+ 	{
+ 		if (cell != patterns->head)
+ 			appendPQExpBuffer(query, "UNION ALL\n");
+ 		appendPQExpBuffer(query,
+ 						  "SELECT e.oid"
+ 						  "\nFROM pg_catalog.pg_extension e\n");
+ 		processSQLNamePattern(GetConnection(fout), query,
+ 							  cell->val, false, false,
+ 							  NULL, "e.extname", NULL, NULL);
+ 	}
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	for (i = 0; i < PQntuples(res); i++)
+ 	{
+ 		simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
+ 	}
+ 
+ 	PQclear(res);
+ 	destroyPQExpBuffer(query);
+ }
+ 
+ /*
   * selectDumpableNamespace: policy-setting subroutine
   *		Mark a namespace as to be dumped or not
   */
***************
*** 1208,1213 **** selectDumpableExtension(ExtensionInfo *extinfo)
--- 1273,1286 ----
  		extinfo->dobj.dump = false;
  	else
  		extinfo->dobj.dump = include_everything;
+ 
+ 	/*
+ 	 * In any case, an extension can be included an inclusion switch
+ 	 */
+ 	if (extension_include_oids.head &&
+ 		simple_oid_list_member(&extension_include_oids,
+ 							   extinfo->dobj.catId.oid))
+ 		extinfo->dobj.dump = true;
  }
  
  /*
***************
*** 2737,2742 **** getExtensions(Archive *fout, int *numExtensions)
--- 2810,2816 ----
  	int			i_extversion;
  	int			i_extconfig;
  	int			i_extcondition;
+ 	int         i_extrequires;
  
  	/*
  	 * Before 9.1, there are no extensions.
***************
*** 2752,2761 **** getExtensions(Archive *fout, int *numExtensions)
  	/* Make sure we are in proper schema */
  	selectSourceSchema(fout, "pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT x.tableoid, x.oid, "
! 					  "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
! 					  "FROM pg_extension x "
! 					  "JOIN pg_namespace n ON n.oid = x.extnamespace");
  
  	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
  
--- 2826,2842 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema(fout, "pg_catalog");
  
! 	/* Get the extension and its requirements: extensions it depends on */
! 	appendPQExpBuffer(query,
! 					  "SELECT x.tableoid, x.oid, x.extname, n.nspname, "
! 					  "x.extrelocatable, x.extversion, x.extconfig, "
! 					  "x.extcondition, "
! 					  "array_to_string(array_agg(e.extname), ',') as extrequires "
! 					  "FROM pg_extension x JOIN pg_namespace n ON n.oid = x.extnamespace "
! 					  "LEFT JOIN pg_depend d on d.objid = x.oid "
! 					  "and d.refclassid = 'pg_extension'::regclass "
! 					  "LEFT JOIN pg_extension e ON e.oid = d.refobjid "
! 					  "group by 1, 2, 3, 4, 5, 6, 7, 8");
  
  	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
  
***************
*** 2771,2776 **** getExtensions(Archive *fout, int *numExtensions)
--- 2852,2858 ----
  	i_extversion = PQfnumber(res, "extversion");
  	i_extconfig = PQfnumber(res, "extconfig");
  	i_extcondition = PQfnumber(res, "extcondition");
+ 	i_extrequires = PQfnumber(res, "extrequires");
  
  	for (i = 0; i < ntups; i++)
  	{
***************
*** 2784,2789 **** getExtensions(Archive *fout, int *numExtensions)
--- 2866,2872 ----
  		extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
  		extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
  		extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
+ 		extinfo[i].extrequires = pg_strdup(PQgetvalue(res, i, i_extrequires));
  
  		/* Decide whether we want to dump it */
  		selectDumpableExtension(&(extinfo[i]));
***************
*** 7248,7254 **** collectComments(Archive *fout, CommentItem **items)
   * ArchiveEntries (TOC objects) for each object to be dumped.
   */
  static void
! dumpDumpableObject(Archive *fout, DumpableObject *dobj)
  {
  	switch (dobj->objType)
  	{
--- 7331,7338 ----
   * ArchiveEntries (TOC objects) for each object to be dumped.
   */
  static void
! dumpDumpableObject(Archive *fout, DumpableObject *dobj,
! 				   DumpableObject **dobjs, int numObjs)
  {
  	switch (dobj->objType)
  	{
***************
*** 7256,7262 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
  			dumpNamespace(fout, (NamespaceInfo *) dobj);
  			break;
  		case DO_EXTENSION:
! 			dumpExtension(fout, (ExtensionInfo *) dobj);
  			break;
  		case DO_TYPE:
  			dumpType(fout, (TypeInfo *) dobj);
--- 7340,7346 ----
  			dumpNamespace(fout, (NamespaceInfo *) dobj);
  			break;
  		case DO_EXTENSION:
! 			dumpExtension(fout, (ExtensionInfo *) dobj, dobjs, numObjs);
  			break;
  		case DO_TYPE:
  			dumpType(fout, (TypeInfo *) dobj);
***************
*** 7431,7444 **** dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
   *	  writes out to fout the queries to recreate an extension
   */
  static void
! dumpExtension(Archive *fout, ExtensionInfo *extinfo)
  {
  	PQExpBuffer q;
  	PQExpBuffer delq;
  	PQExpBuffer labelq;
  	char	   *qextname;
  
- 	/* Skip if not to be dumped */
  	if (!extinfo->dobj.dump || dataOnly)
  		return;
  
--- 7515,7528 ----
   *	  writes out to fout the queries to recreate an extension
   */
  static void
! dumpExtension(Archive *fout, ExtensionInfo *extinfo,
! 			  DumpableObject **dobjs, int numObjs)
  {
  	PQExpBuffer q;
  	PQExpBuffer delq;
  	PQExpBuffer labelq;
  	char	   *qextname;
  
  	if (!extinfo->dobj.dump || dataOnly)
  		return;
  
***************
*** 7450,7456 **** dumpExtension(Archive *fout, ExtensionInfo *extinfo)
  
  	appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
  
! 	if (!binary_upgrade)
  	{
  		/*
  		 * In a regular dump, we use IF NOT EXISTS so that there isn't a
--- 7534,7611 ----
  
  	appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
  
! 	/* dump this extension's content */
! 	if (extension_include_oids.head
! 		&& simple_oid_list_member(&extension_include_oids,
! 								  extinfo->dobj.catId.oid))
! 	{
! 		Archive       *eout;			/* the extension's script file */
! 		ArchiveHandle *EH;
! 		TocEntry      *te;
! 		int i;
! 
! 		/* See comment for !binary_upgrade case for IF NOT EXISTS. */
! 		appendPQExpBuffer(q,
! 						  "CREATE EXTENSION IF NOT EXISTS %s\n"
! 						  "  WITH SCHEMA %s\n"
! 						  "       VERSION '%s'\n"
! 						  "       %s\n"
! 						  "       REQUIRES '%s'\n"
! 						  "AS $%s$\n",
! 						  qextname,
! 						  fmtId(extinfo->namespace),
! 						  extinfo->extversion,
! 						  extinfo->relocatable ? "relocatable":"norelocatable",
! 						  extinfo->extrequires,
! 						  qextname);
! 
! 		/*
! 		 * Have another archive for this extension: this allows us to simply
! 		 * walk the extension's dependencies and use the existing pg_dump code
! 		 * to get the object create statement to be added in the script.
! 		 *
! 		 */
! 		eout = CreateArchive(NULL, archNull, 0, archModeAppend);
! 
! 		EH = (ArchiveHandle *) eout;
! 
! 		/* grab existing connection and remote version information */
! 		EH->connection = ((ArchiveHandle *)fout)->connection;
! 		eout->remoteVersion = fout->remoteVersion;
! 
! 		/* dump all objects for this extension, that have been sorted out in
! 		 * the right order following dependencies etc */
! 		for (i = 0; i < numObjs; i++)
! 		{
! 			DumpableObject *dobj = dobjs[i];
! 
! 			if (dobj->ext_member
! 				&& dobj->extension_oid == extinfo->dobj.catId.oid)
! 			{
! 				bool dump = dobj->dump;
! 
! 				/* force the DumpableObject here to be dumped */
! 				dobj->dump = true;
! 
! 				/* Dump the Object */
! 				dumpDumpableObject(eout, dobj, dobjs, numObjs);
! 
! 				/* now restore its old dump flag value */
! 				dobj->dump = dump;
! 			}
! 		}
! 
! 		/* restore the eout Archive into the local buffer */
! 		for (te = EH->toc->next; te != EH->toc; te = te->next)
! 		{
! 			if (strlen(te->defn) > 0)
! 				appendPQExpBuffer(q, "%s", te->defn);
! 		}
! 		CloseArchive(eout);
! 
! 		appendPQExpBuffer(q, "$%s$;\n", qextname);
! 	}
! 	else if (!binary_upgrade)
  	{
  		/*
  		 * In a regular dump, we use IF NOT EXISTS so that there isn't a
***************
*** 14101,14106 **** getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
--- 14256,14280 ----
  
  		dobj->ext_member = true;
  
+ 		/* track the extension Oid in the object for later processing */
+ 		if (extension_include_oids.head
+ 			&& simple_oid_list_member(&extension_include_oids, refobjId.oid))
+ 		{
+ 			dobj->extension_oid = refobjId.oid;
+ 		}
+ 
+ 		/* The Shell Type needs to be in the --extension-script */
+ 		if (dobj->objType == DO_TYPE)
+ 		{
+ 			TypeInfo   *typeInfo = (TypeInfo *) dobj;
+ 
+ 			if (typeInfo->shellType)
+ 			{
+ 				typeInfo->shellType->dobj.ext_member = true;
+ 				typeInfo->shellType->dobj.extension_oid = dobj->extension_oid;
+ 			}
+ 		}
+ 
  		/*
  		 * Normally, mark the member object as not to be dumped.  But in
  		 * binary upgrades, we still dump the members individually, since the
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 133,138 **** typedef struct _dumpableObject
--- 133,139 ----
  	struct _namespaceInfo *namespace;	/* containing namespace, or NULL */
  	bool		dump;			/* true if we want to dump this object */
  	bool		ext_member;		/* true if object is member of extension */
+ 	Oid 		extension_oid;	/* Oid of the extension woning this object */
  	DumpId	   *dependencies;	/* dumpIds of objects this one depends on */
  	int			nDeps;			/* number of valid dependencies */
  	int			allocDeps;		/* allocated size of dependencies[] */
***************
*** 151,156 **** typedef struct _extensionInfo
--- 152,158 ----
  	char	   *namespace;		/* schema containing extension's objects */
  	bool		relocatable;
  	char	   *extversion;
+ 	char	   *extrequires;
  	char	   *extconfig;		/* info about configuration tables */
  	char	   *extcondition;
  } ExtensionInfo;
-- 
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