Hi,

Thanks for your review!

Ali Dar <ali.munir....@gmail.com> writes:
> 1) I don't know if community is really looking for this functionality, I
> followed the thread a bit and appraently there is a disagreement over it. I
> will not comments on that. Let the big guys decide if they really want this
> feature.

Let's open the extension feature to users who are not at ease with
building packages for their distribution of choice or our users who are
not granted root privileges on their database servers.

After all you don't even need to be a superuser to CREATE an EXTENSION.

> 2) I think comments are very limited, I would be really great if you can
> add thorough comments of whatever new that you have added. Couple of
> examples are given below:

I've added some comments in the attached new revision of the patch, and
fixed the errors you've been listing.

> ii) What is the need of "require" option?
> It would be great if you can go through all the comments and try to add the
> missing comments and improve the old ones.

When CREATE EXTENSION is given the extension's script in the SQL command
itself directly, the control files property are not to be found in a
separate file. Most of those properties relate to the packaging and the
reading of the file so we don't need them by construction, but it's not
the case with two of them.

The parameter "require" is used in the create extension code to be able
to set the search_path correctly before running the extension's script,
so that dependencies are found automatically.

The parameter "relocatable" ends up in the pg_extension catalogs for
being able to generate an ERROR if the user attempts to ALTER EXTENSION
… SET SCHEMA, so we need a way to have it from the SQL command now (so
that pg_dump emits a non lossy command).

Here's the comment I've been adding to gram.y to explain that. Note that
I wouldn't expect to find such a comment at this place, but didn't see
another place where to detail it…

        /*
         * This option is needed only when the script is given
         * inline. We then don't have any control file but still
         * need to set the search_path according to the dependency
         * list when running the extension's script.
         */
        if (strcmp($1, "requires") == 0)

> 3) There are spelling mistakes in the existing comments, for example:

Fixed, thanks.

> 4) 'if_no_exits' flag of new grammar rule is set to false, even though the
> rule states that the extension is created as 'IF NOT EXISTS'.

Oh, a copy/paste bug, the worse kind. Fixed, thanks.

> 5) Why 'relocatable' option is added to the new grammar rule of create
> extension? In a regular 'create extension' statement, when 'schema' option
> is specified, it means that the extension is relocatable. So why is option
> needed? If there is a specific reason why 'scheme' option wasn't used, then
> please explain.

When schema option is specificied, it does NOT mean that the extension
is relocatable. The relocatable option is choosen by the extension's
author to cope with some dependencies and other problems, and is
important for reading the script (@extschema@ substitutions). Also it's
then essential to disallow ALTER EXTENSION … SET SCHEMA … even if that
very command could work, so we keep that parameter in the catalogs.

It's one of the only two control parameters that still needs to be
passed on the SQL command now.

> 6) I am unable to figure out why new 'require' option is needed(comments
> would have helped)? Is it because when we will dump the extension with -X
> flag, it will first dump that referenced extension?

That's not how it works, no. We can of course review that, but I don't
currently see any reason to force dumping required extensions scripts.

The 'require' processing didn't change at all in this patch. You are
just allowed to give the list in the SQL command rather than the control
file, that's all.

> I created the following extension(hstore was already created):
> create extension testex with version '1' requires 'hstore' as
> $testex$ create type testtype as(a char) $testex$;
>
> I then dumped the extension using -X option, only 'testex' extension was
> dumped and not 'hstore'.

I suppose that at restore time you will have to have "hstore" on disk
with its .so file, but maybe won't have "testex" readily deployed on the
new server. So my understanding is that what's happening is what needs
to happen. What do you think?

> 7) You have removed an old PG comments in pg_dump.c line:7526 above the
> following check:

Oops. Added back. Thanks.

> 8) In extension.c the following check is added:
> /*
>  * 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")));
>
> Is the above code really reachable? If the user has not specified a
> script(old create extension syntax is used), then 'd_relocatable' and
> 'd_requires' will never be true, because the old 'create extension' syntax
> doesn't allow it.

The grammar itself is not protecting against sloppy mixing of options,
and I don't think it should in our case. When shipping an extension as
an OS package with a control file, I suppose that the "packager options"
are not meant to be easily overriden by the DBA.

We could open that defensive coding, though, if the need is justified.

> 9) For every extension being dumped, and then for every object inside that
> extension. The code traverses all the fetched items of the database to
> compare if that item belong to that specific extension. Because of that
> 'DumpableObject **dobjs' is traversed fully, every time a single extension
> is dumped. This seems to be big big performance hit for a huge database.
> And considering if many many extensions are dumped in a huge database, dump
> might become very slow. Can you think of any other scheme? Maybe you should
> follow PG's design, and fetch the extension objects in dumpExtension() and
> dump them.

It seems to me that my implementation in the patch follows PG design
here. Please consider that the new traversal only happens once per
extension listed in the --extension-script accumulative option.

In a previous (local) implementation of this patch I kept a list of
objects per extension that was maintained in getExtensionMembership(),
but it stroke me as useless and I finally prefered tracking the
extension's OID in the DumpableObject structure directly. That makes for
much simpler code.

I could rewrite the tracking the other way round if that's deemed
necessary, it's not done in the attached, though.

> 10) pg_dumpall is not handled at all. User can't use -X switch if he wants
> to dump all the databases with extensions.

True, I didn't have a look at pg_dumpall. Extensions are per-database in
our catalogs… do we want to have the --extension-script option available
to pg_dumpall?!

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;			/* extension's script, when given inline */
  } 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
***************
*** 3443,3448 **** DropTableSpaceStmt: DROP TABLESPACE name
--- 3443,3450 ----
   *             CREATE EXTENSION extension
   *             [ WITH ] [ SCHEMA schema ] [ VERSION version ] [ FROM oldversion ]
   *
+  *             CREATE EXTENSION extension ... AS $$ ... $$
+  *
   *****************************************************************************/
  
  CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
***************
*** 3461,3466 **** CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
--- 3463,3490 ----
  					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 = true;
+ 					n->options = lappend($8,
+ 										 makeDefElem("script",
+ 													 (Node *)makeString($10)));
+ 					$$ = (Node *) n;
+ 				}
  		;
  
  create_extension_opt_list:
***************
*** 3483,3488 **** create_extension_opt_item:
--- 3507,3545 ----
  				{
  					$$ = 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.
+                  */
+                 {
+ 					/*
+ 					 * This option is needed only when the script is given
+ 					 * inline. We then don't have any control file but still
+ 					 * need to set the search_path according to the dependency
+ 					 * list when running the extension's script.
+ 					 */
+ 					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       dump 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,7437 **** 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;
--- 7515,7522 ----
   *	  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;
***************
*** 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
--- 7535,7612 ----
  
  	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[],
--- 14257,14281 ----
  
  		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 owning 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