*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 2664,2669 ****
--- 2664,2725 ----
+
+ pg_extension
+
+
+ pg_extension
+
+
+
+ The catalog pg_extension stores information
+ about the installed extension. See for
+ details about extensions.
+
+
+
+ pg_extension> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ extname
+ name
+
+ Extension name
+
+
+
+ extversion
+ text
+
+ Version string of the extension, as provided by its author.
+
+
+
+ custom_class
+ text
+
+ Extenion's entries that are dynamically added
+ to custom_variable_classes.
+
+
+
+
+
+
+
+
+
pg_foreign_data_wrapper
***************
*** 5926,5931 ****
--- 5982,5992 ----
+ pg_extension
+ available and installed extensions
+
+
+ pg_groupgroups of database users
***************
*** 6135,6140 ****
--- 6196,6271 ----
+
+ pg_extensions
+
+
+ pg_extensions
+
+
+
+ The pg_extensions view lists the extensions that
+ are currently available and installed.
+
+
+
+ pg_extensions> Columns
+
+
+
+
+ Name
+ Type
+ Description
+
+
+
+
+
+ name
+ text
+ The name of the extensions
+
+
+
+ version
+ text
+ The version string of the extension
+
+
+
+ custom_variable_classes
+ text
+ The coma-separated list of custom variable classes provided by
+ this extension
+
+
+
+ comment
+ text
+ The comment field provided in the control
+ file, or NULL if none were provided.
+
+
+
+ installed
+ boolean
+
+ true if the extension is already
+ installed; false otherwise
+
+
+
+
+
+
+
+
+ The pg_extensions view is read only.
+
+
+
+
pg_group
*** a/doc/src/sgml/extend.sgml
--- b/doc/src/sgml/extend.sgml
***************
*** 336,339 ****
--- 336,402 ----
+
+ Putting it all together: create extension
+
+
+ extension
+
+
+
+ The way to put together a coherent set of custom SQL> objects
+ is to create an extension. There are two sides of the
+ extension, the Operating System side contains several files and
+ the SQL> one uses them.
+
+
+
+ The CREATE EXTENSION command
+ relies on a couple
+ of files to be placed in SHAREDIR/contrib.
+
+
+
+
+
+ The extension.control
+ file should contain the information necessary to register the
+ extension. The file format must follow the per-line key =
+ 'value' syntax, with comments supported on any line after
+ a # character.
+
+
+ This control file allows for registering the extension and should
+ provide values for the following keys: name and
+ version.
+
+
+ The control file can also contain
+ a custom_variable_classes key followed by any
+ number of custom settings,
+ named class.varname.
+ The custom_variable_classes value takes effect
+ at CREATE EXTENSION time, and is persistent. The
+ custom settings are set automatically too, but are not persistent.
+
+
+
+
+ Once registered, the extension is loaded by means of executing the
+ commands from
+ the extension.sql
+ file.
+
+
+
+
+
+ The admin
+ function pg_extension_flag_dump
+ can be used to revert the default pg_dump policy
+ about objects that belong to an extension and force the flagged objects
+ to be part of the backups.
+
+
+
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 13897,13902 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 13897,13909 ----
recordReturn information about a file
+
+
+ pg_execute_from_file(filename> text>)
+
+ void
+ Executes the SQL> commands contained in a file
+
***************
*** 13935,13940 **** SELECT (pg_stat_file('filename')).modification;
--- 13942,14038 ----
+
+ pg_execute_from_file
+
+
+ pg_execute_from_file> makes the server
+ execute SQL> commands to be found in a file. This function is
+ reserved to superusers.
+
+
+
+ The functions shown in are
+ facilities for extension authors. For details about extensions, see
+ .
+
+
+
+ Extension Related Functions
+
+
+ NameReturn TypeDescription
+
+
+
+
+
+
+ pg_extension_flag_dump(objid> oid>)
+
+ bool
+ Flag an extension's objectid as worthy
+ of pg_dump.
+
+
+
+ pg_extension_with_user_data()
+
+ bool
+ Return true only when the extension's
+ script should care for user data.
+
+
+
+
+
+
+
+ pg_extension_flag_dump
+
+
+ pg_extension_flag_dump> changes the dependency type between
+ an extension's and one of its object, given
+ by oid. The default behavior of an extension is to
+ skip objects in pg_dump, but in some cases that's not
+ what you want to achieve, as an extension author. If your extension
+ provides user data (in a table, for example), then flag this table so
+ that it's part of the backups, like so:
+
+ SELECT pg_extension_flag_dump('schema.tablename'::regclass);
+
+
+
+ This function can only be called when loading a SQL> script
+ using the command CREATE
+ EXTENSION. See
+ and .
+
+
+
+ pg_extension_with_user_data
+
+
+ pg_extension_with_user_data> allows extension's author to
+ prepare installation scripts that will work well for initial
+ installation and when restoring from a pg_dump
+ backup, which issues CREATE EXTENSION foo WITH NO
+ DATA;. See for
+ details. One way to use it is as following:
+
+ DO $$
+ BEGIN
+ IF pg_extension_with_user_data() THEN
+ create schema foo;
+ create table foo.bar(id serial primary key);
+ perform pg_extension_flag_dump('foo.bar_id_seq'::regclass);
+ perform pg_extension_flag_dump('foo.bar::regclass);
+ END IF;
+ END;
+ $$;
+
+
+
The functions shown in manage
advisory locks. For details about proper use of these functions, see
***************
*** 13957,13962 **** SELECT (pg_stat_file('filename')).modification;
--- 14055,14061 ----
voidObtain exclusive advisory lock
+
pg_advisory_lock(key1> int>, key2> int>)
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 50,55 **** Complete list of usable sgml source files in this directory.
--- 50,56 ----
+
***************
*** 85,90 **** Complete list of usable sgml source files in this directory.
--- 86,92 ----
+
*** /dev/null
--- b/doc/src/sgml/ref/create_extension.sgml
***************
*** 0 ****
--- 1,109 ----
+
+
+
+
+ CREATE EXTENSION
+ 7
+ SQL - Language Statements
+
+
+
+ CREATE EXTENSION
+ define and install a new extension
+
+
+
+ CREATE EXTENSION
+
+
+
+
+ CREATE EXTENSION extension_name [ WITH [ NO ] DATA ]
+
+
+
+
+ Description
+
+
+ CREATE EXTENSION registers a new extension. The
+ extension name must be distinct from the name of any existing extension
+ in the database.
+
+
+
+ An extension allows superusers to load a script in the database. This
+ script will typically create new SQL> objects such as
+ functions, data types, operators and index support methods.
+
+
+
+
+
+ Parameters
+
+
+
+ extension_name
+
+
+ The name of an extension to be
+ installed. PostgreSQL will register the
+ extension using details from the
+ file SHAREDIR/contrib/extension.control
+ file then load
+ the SHAREDIR/contrib/extension.sql
+ script.
+
+
+
+
+
+ WITH NO DATA
+
+
+ This option is used by . As it's possible
+ to flag extension's objects so that they get dumped
+ (see ), this option allow
+ extension authors to prepare installation scripts that will avoid
+ creating user data when restoring.
+
+
+
+
+
+
+
+ Examples
+
+
+ Install the hstore extension:
+
+ CREATE EXTENSION hstore;
+
+
+
+
+
+ Compatibility
+
+
+ CREATE EXTENSION is a PostgreSQL>
+ extension.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/drop_extension.sgml
***************
*** 0 ****
--- 1,115 ----
+
+
+
+
+ DROP EXTENSION
+ 7
+ SQL - Language Statements
+
+
+
+ DROP EXTENSION
+ remove a tablespace
+
+
+
+ DROP EXTENSION
+
+
+
+
+ DROP EXTENSION [ IF EXISTS ] extension_name [ CASCADE | RESTRICT ]
+
+
+
+
+ Description
+
+
+ DROP EXTENSION removes an extension from the
+ system. This will also DROP all and
+ any SQL> objects installed by the extension.
+
+
+
+ An extension can only be dropped by a superuser.
+
+
+
+
+ Parameters
+
+
+
+
+ IF EXISTS
+
+
+ Do not throw an error if the extension does not exist. A notice is issued
+ in this case.
+
+
+
+
+
+ extension_name
+
+
+ The name of an installed extension.
+
+
+
+
+
+ CASCADE
+
+
+ Automatically drop objects that depend on the index.
+
+
+
+
+
+ RESTRICT
+
+
+ Refuse to drop the index if any objects depend on it. This is
+ the default.
+
+
+
+
+
+
+
+ Examples
+
+
+ To remove the extension hstore from the system:
+
+ DROP EXTENSION hstore;
+
+
+
+
+
+ Compatibility
+
+
+ DROP EXTENSION is a PostgreSQL>
+ extension.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 78,83 ****
--- 78,84 ----
&createConversion;
&createDatabase;
&createDomain;
+ &createExtension;
&createForeignDataWrapper;
&createFunction;
&createGroup;
***************
*** 113,118 ****
--- 114,120 ----
&dropConversion;
&dropDatabase;
&dropDomain;
+ &dropExtension;
&dropForeignDataWrapper;
&dropFunction;
&dropGroup;
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 55,60 ****
--- 55,61 ----
#include "utils/guc.h"
#include "utils/ps_status.h"
#include "utils/relmapper.h"
+ #include "utils/cfparser.h"
#include "pg_trace.h"
***************
*** 5018,5117 **** str_time(pg_time_t tnow)
}
/*
- * Parse one line from recovery.conf. 'cmdline' is the raw line from the
- * file. If the line is parsed successfully, returns true, false indicates
- * syntax error. On success, *key_p and *value_p are set to the parameter
- * name and value on the line, respectively. If the line is an empty line,
- * consisting entirely of whitespace and comments, function returns true
- * and *keyp_p and *value_p are set to NULL.
- *
- * The pointers returned in *key_p and *value_p point to an internal buffer
- * that is valid only until the next call of parseRecoveryCommandFile().
- */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- char *ptr;
- char *bufp;
- char *key;
- char *value;
- static char *buf = NULL;
-
- *key_p = *value_p = NULL;
-
- /*
- * Allocate the buffer on first use. It's used to hold both the parameter
- * name and value.
- */
- if (buf == NULL)
- buf = malloc(MAXPGPATH + 1);
- bufp = buf;
-
- /* Skip any whitespace at the beginning of line */
- for (ptr = cmdline; *ptr; ptr++)
- {
- if (!isspace((unsigned char) *ptr))
- break;
- }
- /* Ignore empty lines */
- if (*ptr == '\0' || *ptr == '#')
- return true;
-
- /* Read the parameter name */
- key = bufp;
- while (*ptr && !isspace((unsigned char) *ptr) &&
- *ptr != '=' && *ptr != '\'')
- *(bufp++) = *(ptr++);
- *(bufp++) = '\0';
-
- /* Skip to the beginning quote of the parameter value */
- ptr = strchr(ptr, '\'');
- if (!ptr)
- return false;
- ptr++;
-
- /* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- value = bufp;
- for (;;)
- {
- if (*ptr == '\'')
- {
- ptr++;
- if (*ptr == '\'')
- *(bufp++) = '\'';
- else
- {
- /* end of parameter */
- *bufp = '\0';
- break;
- }
- }
- else if (*ptr == '\0')
- return false; /* unterminated quoted string */
- else
- *(bufp++) = *ptr;
-
- ptr++;
- }
- *(bufp++) = '\0';
-
- /* Check that there's no garbage after the value */
- while (*ptr)
- {
- if (*ptr == '#')
- break;
- if (!isspace((unsigned char) *ptr))
- return false;
- ptr++;
- }
-
- /* Success! */
- *key_p = key;
- *value_p = value;
- return true;
- }
-
- /*
* See if there is a recovery command file (recovery.conf), and if so
* read in parameters for archive recovery and XLOG streaming.
*
--- 5019,5024 ----
***************
*** 5147,5153 **** readRecoveryCommandFile(void)
char *tok1;
char *tok2;
! if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
{
syntaxError = true;
break;
--- 5054,5060 ----
char *tok1;
char *tok2;
! if (!cfParseOneLine(cmdline, &tok1, &tok2))
{
syntaxError = true;
break;
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 39,44 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
--- 39,45 ----
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_default_acl.h pg_seclabel.h \
+ pg_extension.h \
toasting.h indexing.h \
)
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 34,39 ****
--- 34,40 ----
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
***************
*** 55,60 ****
--- 56,62 ----
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+ #include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
***************
*** 152,158 **** static const Oid object_classes[MAX_OCLASS] = {
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
! DefaultAclRelationId /* OCLASS_DEFACL */
};
--- 154,161 ----
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
! DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId /* OCLASS_EXTENSION */
};
***************
*** 549,558 **** findDependentObjects(const ObjectAddress *object,
/* no problem */
break;
case DEPENDENCY_INTERNAL:
-
/*
* This object is part of the internal implementation of
! * another object. We have three cases:
*
* 1. At the outermost recursion level, disallow the DROP. (We
* just ereport here, rather than proceeding, since no other
--- 552,560 ----
/* no problem */
break;
case DEPENDENCY_INTERNAL:
/*
* This object is part of the internal implementation of
! * another object. We have four cases:
*
* 1. At the outermost recursion level, disallow the DROP. (We
* just ereport here, rather than proceeding, since no other
***************
*** 596,602 **** findDependentObjects(const ObjectAddress *object,
break;
/*
! * 3. When recursing from anyplace else, transform this
* deletion request into a delete of the other object.
*
* First, release caller's lock on this object and get
--- 598,611 ----
break;
/*
! * 3. When droping an extension, it's ok to drop its
! * dependencies first, even if they're internal.
! */
! if (otherObject.classId == ExtensionRelationId)
! break;
!
! /*
! * 4. When recursing from anyplace else, transform this
* deletion request into a delete of the other object.
*
* First, release caller's lock on this object and get
***************
*** 1148,1153 **** doDeletion(const ObjectAddress *object)
--- 1157,1166 ----
RemoveUserMappingById(object->objectId);
break;
+ case OCLASS_EXTENSION:
+ RemoveExtensionById(object->objectId);
+ break;
+
case OCLASS_DEFACL:
RemoveDefaultACLById(object->objectId);
break;
***************
*** 2082,2087 **** getObjectClass(const ObjectAddress *object)
--- 2095,2104 ----
case DefaultAclRelationId:
Assert(object->objectSubId == 0);
return OCLASS_DEFACL;
+
+ case ExtensionRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_EXTENSION;
}
/* shouldn't get here */
***************
*** 2563,2568 **** getObjectDescription(const ObjectAddress *object)
--- 2580,2597 ----
break;
}
+ case OCLASS_EXTENSION:
+ {
+ char *extension;
+
+ extension = get_extension_name(object->objectId);
+ if (!extension)
+ elog(ERROR, "cache lookup failed for extension %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("extension %s"), extension);
+ break;
+ }
+
case OCLASS_TBLSPACE:
{
char *tblspace;
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 49,54 ****
--- 49,55 ----
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
#include "catalog/storage.h"
+ #include "commands/extension.h"
#include "commands/tablecmds.h"
#include "commands/typecmds.h"
#include "miscadmin.h"
***************
*** 1139,1145 **** heap_create_with_catalog(const char *relname,
/*
* Make a dependency link to force the relation to be deleted if its
* namespace is. Also make a dependency link to its owner, as well as
! * dependencies for any roles mentioned in the default ACL.
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
--- 1140,1147 ----
/*
* Make a dependency link to force the relation to be deleted if its
* namespace is. Also make a dependency link to its owner, as well as
! * dependencies for any roles mentioned in the default ACL. Record a
! * dependency to the extension we're part of if any.
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
***************
*** 1186,1191 **** heap_create_with_catalog(const char *relname,
--- 1188,1198 ----
0, NULL,
nnewmembers, newmembers);
}
+
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 28,33 ****
--- 28,34 ----
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+ #include "catalog/pg_extension.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
***************
*** 47,52 ****
--- 48,54 ----
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
+ #include "commands/extension.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "nodes/makefuncs.h"
***************
*** 128,133 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 130,136 ----
address = get_object_address_relobject(objtype, objname, &relation);
break;
case OBJECT_DATABASE:
+ case OBJECT_EXTENSION:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
case OBJECT_SCHEMA:
***************
*** 266,271 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 269,277 ----
case OBJECT_DATABASE:
msg = gettext_noop("database name cannot be qualified");
break;
+ case OBJECT_EXTENSION:
+ msg = gettext_noop("extension name cannot be qualified");
+ break;
case OBJECT_TABLESPACE:
msg = gettext_noop("tablespace name cannot be qualified");
break;
***************
*** 298,303 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 304,314 ----
address.objectId = get_database_oid(name, false);
address.objectSubId = 0;
break;
+ case OBJECT_EXTENSION:
+ address.classId = ExtensionRelationId;
+ address.objectId = get_extension_oid(name, false);
+ address.objectSubId = 0;
+ break;
case OBJECT_TABLESPACE:
address.classId = TableSpaceRelationId;
address.objectId = get_tablespace_oid(name, false);
***************
*** 635,640 **** object_exists(ObjectAddress address)
--- 646,654 ----
case TSConfigRelationId:
cache = TSCONFIGOID;
break;
+ case ExtensionRelationId:
+ indexoid = ExtensionOidIndexId;
+ break;
default:
elog(ERROR, "unrecognized classid: %u", address.classId);
}
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
***************
*** 32,37 ****
--- 32,38 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
***************
*** 293,298 **** AggregateCreate(const char *aggName,
--- 294,305 ----
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* Depends on currently installed extension, if any */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/catalog/pg_conversion.c
--- b/src/backend/catalog/pg_conversion.c
***************
*** 30,35 ****
--- 30,36 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
/*
* ConversionCreate
***************
*** 131,136 **** ConversionCreate(const char *conname, Oid connamespace,
--- 132,143 ----
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner);
+ /* Depends on currently installed extension, if any */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_depend.c
--- b/src/backend/catalog/pg_depend.c
***************
*** 265,270 **** changeDependencyFor(Oid classId, Oid objectId,
--- 265,325 ----
}
/*
+ * Change the deptype of the given dependency.
+ *
+ * Returns the number of lines that we changed.
+ */
+ long
+ changeDependencyTypeFor(Oid objid,
+ Oid refclassId, Oid refobjectId, int refobjsubid,
+ DependencyType expected, DependencyType newtype)
+ {
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+ long count = 0;
+
+ depRel = heap_open(DependRelationId, RowExclusiveLock);
+
+ /* Now search for dependency records */
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(refclassId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(refobjectId));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_refobjsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(refobjsubid));
+ scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ SnapshotNow, 3, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->objid == objid && depform->deptype == expected)
+ {
+ count++;
+
+ /* make a modifiable copy */
+ tup = heap_copytuple(tup);
+ depform = (Form_pg_depend) GETSTRUCT(tup);
+ depform->deptype = newtype;
+ simple_heap_update(depRel, &tup->t_self, tup);
+ }
+ }
+ systable_endscan(scan);
+ heap_close(depRel, RowExclusiveLock);
+ return count;
+ }
+
+
+ /*
* isObjectPinned()
*
* Test if an object is required for basic database functionality.
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 21,26 ****
--- 21,27 ----
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
/* ----------------
***************
*** 75,79 **** NamespaceCreate(const char *nspName, Oid ownerId)
--- 76,92 ----
/* Record dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+ /* Record dependency on extension, if we're in a CREATE EXTENSION */
+ if (create_extension != NULL )
+ {
+ ObjectAddress myself;
+
+ myself.classId = NamespaceRelationId;
+ myself.objectId = nspoid;
+ myself.objectSubId = 0;
+
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
return nspoid;
}
*** a/src/backend/catalog/pg_operator.c
--- b/src/backend/catalog/pg_operator.c
***************
*** 33,38 ****
--- 33,39 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
static Oid OperatorGet(const char *operatorName,
***************
*** 846,849 **** makeOperatorDependencies(HeapTuple tuple)
--- 847,856 ----
/* Dependency on owner */
recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
oper->oprowner);
+
+ /* Dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 35,40 ****
--- 35,41 ----
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
***************
*** 614,619 **** ProcedureCreate(const char *procedureName,
--- 615,626 ----
nnewmembers, newmembers);
}
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 31,36 ****
--- 31,38 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
+
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
***************
*** 625,630 **** GenerateTypeDependencies(Oid typeNamespace,
--- 627,638 ----
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 150,155 **** CREATE VIEW pg_locks AS
--- 150,158 ----
CREATE VIEW pg_cursors AS
SELECT * FROM pg_cursor() AS C;
+ CREATE VIEW pg_extensions AS
+ SELECT * FROM pg_extensions() AS E;
+
CREATE VIEW pg_prepared_xacts AS
SELECT P.transaction, P.gid, P.prepared,
U.rolname AS owner, D.datname AS database
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 14,20 **** include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
constraint.o conversioncmds.o copy.o \
! dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
--- 14,21 ----
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
constraint.o conversioncmds.o copy.o \
! dbcommands.o define.o discard.o \
! extension.o explain.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
*** a/src/backend/commands/comment.c
--- b/src/backend/commands/comment.c
***************
*** 142,147 **** CommentObject(CommentStmt *stmt)
--- 142,153 ----
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to comment on procedural language")));
break;
+ case OBJECT_EXTENSION:
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on extension")));
+ break;
case OBJECT_OPCLASS:
if (!pg_opclass_ownercheck(address.objectId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
*** /dev/null
--- b/src/backend/commands/extension.c
***************
*** 0 ****
--- 1,915 ----
+ /*-------------------------------------------------------------------------
+ *
+ * extension.c
+ * Commands to manipulate extensions
+ *
+ * Extensions in PostgreSQL allow user defined behavior plugged in at
+ * runtime. They will typically create SQL objects that you want to avoid
+ * dumping, issuing a single CREATE EXTENSION foo; command instead.
+ *
+ * All we need internally to manage an extension is an OID so that the
+ * dependent objects can get associated with it. An extension is created by
+ * populating the pg_extension catalog from a "control" file, containing
+ * some parameters.
+ *
+ * The extension control file format is the most simple name = value, we
+ * don't need anything more there. The SQL file to execute commands from is
+ * hardcoded to `pg_config --sharedir`/.install.sql.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/extension.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include
+ #include
+ #include
+ #include
+
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/sysattr.h"
+ #include "access/xact.h"
+ #include "catalog/dependency.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
+ #include "catalog/pg_type.h"
+ #include "commands/comment.h"
+ #include "commands/extension.h"
+ #include "commands/portalcmds.h"
+ #include "funcapi.h"
+ #include "nodes/parsenodes.h"
+ #include "miscadmin.h"
+ #include "storage/fd.h"
+ #include "utils/builtins.h"
+ #include "utils/cfparser.h"
+ #include "utils/fmgroids.h"
+ #include "utils/guc.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+
+ /*
+ * See commands/extension.h for details.
+ */
+ ObjectAddress *create_extension = NULL;
+ bool create_extension_with_user_data = true;
+
+ char *
+ get_extension_control_basepath(void)
+ {
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ get_share_path(my_exec_path, sharepath);
+ result = palloc(MAXPGPATH);
+ snprintf(result, MAXPGPATH, "%s/contrib", sharepath);
+
+ return result;
+ }
+
+ char *
+ get_extension_install_filename(const char *extname)
+ {
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ get_share_path(my_exec_path, sharepath);
+ result = palloc(MAXPGPATH);
+ snprintf(result, MAXPGPATH, "%s/contrib/%s.sql", sharepath, extname);
+
+ return result;
+ }
+
+ char *
+ get_extension_control_filename(const char *extname)
+ {
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ get_share_path(my_exec_path, sharepath);
+ result = palloc(MAXPGPATH);
+ snprintf(result, MAXPGPATH, "%s/contrib/%s.control", sharepath, extname);
+
+ return result;
+ }
+
+ /*
+ * Given a relative pathname such as "name.control", return the full path to
+ * the control file.
+ */
+ char *
+ get_extension_control_fullname(const char *filename)
+ {
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ get_share_path(my_exec_path, sharepath);
+ result = palloc(MAXPGPATH);
+ snprintf(result, MAXPGPATH, "%s/contrib/%s", sharepath, filename);
+
+ return result;
+ }
+
+ /*
+ * The control file is supposed to be very short, half a dozen lines, and
+ * reading it is only allowed to superuser, so we don't even try to minimize
+ * memory allocation risks here.
+ *
+ * Also note that only happens in CREATE EXTENSION and pg_extensions().
+ *
+ * To support listing available extensions, the parameter init_gucs can be
+ * set to false to inhibit any GUC processing.
+ */
+ ExtensionControlFile *
+ read_extension_control_file(const char *filename, bool init_gucs)
+ {
+ int64 fsize = -1;
+ struct stat fst;
+ FILE *file;
+ char line[MAXPGPATH];
+ bool syntaxError = false;
+ ExtensionControlFile *control =
+ (ExtensionControlFile *)palloc(sizeof(ExtensionControlFile));
+
+ /* default values, all fields are NULL til parsed */
+ control->name = control->version = control->custom_class = control->comment = NULL;
+
+ /*
+ * Stat the file to get its size, then read its content into memory so
+ * that we can "parse" it.
+ */
+ if (stat(filename, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+
+ fsize = Int64GetDatum((int64) fst.st_size);
+
+ if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ filename)));
+
+ if (ferror(file))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+
+ /*
+ * Prepare our ExtensionControlFile structure with default values, then
+ * "parse" the control file.
+ *
+ * The parsing uses directly the code for parsing Recovery Commands as
+ * we're abusing the exact same format here.
+ */
+ control->name = NULL;
+ control->version = NULL;
+ control->custom_class = NULL;
+
+ while (fgets(line, sizeof(line), file) != NULL)
+ {
+ char *tok1;
+ char *tok2;
+ char *dot;
+
+ if (!cfParseOneLine(line, &tok1, &tok2))
+ {
+ syntaxError = true;
+ break;
+ }
+ if (tok1 == NULL)
+ continue;
+
+ dot = strchr(tok1, GUC_QUALIFIER_SEPARATOR);
+
+ if (strcmp(tok1, "name") == 0)
+ {
+ control->name = pstrdup(tok2);
+ }
+ else if (strcmp(tok1, "version") == 0)
+ {
+ control->version = pstrdup(tok2);
+ }
+ else if (strcmp(tok1, "comment") == 0)
+ {
+ control->comment = pstrdup(tok2);
+ }
+ else if (strcmp(tok1, "custom_variable_classes") == 0)
+ {
+ if (control->name == NULL)
+ ereport(ERROR,
+ (errmsg("Please setup the extension name first, then "
+ "its custom_variable_classes.")));
+
+ control->custom_class = (char *)
+ assign_custom_variable_classes(tok2, false, PGC_S_EXTENSION);
+
+ /*
+ * We're in an interactive command, CREATE EXTENSION.
+ * The file parsing code only gets run at this time.
+ *
+ * Don't report to many details so as not to make it appear like
+ * the user could avoid the error message by tweaking the
+ * control file.
+ */
+ if (init_gucs)
+ {
+ if (!add_extension_custom_variable_classes(control->name, tok2, true))
+ ereport(ERROR,
+ (errmsg("Extension '%s' could not set "
+ "custom_variable_classes.", control->name)));
+ }
+ }
+ else if (dot != NULL &&
+ is_custom_class(tok1, dot - tok1, control->custom_class))
+ {
+ if (init_gucs)
+ {
+ add_placeholder_variable(tok1, LOG, true);
+ SetConfigOption(tok1, tok2, PGC_SUSET, PGC_S_EXTENSION);
+ elog(NOTICE, "Extension '%s' SET %s TO '%s': please edit '%s'.",
+ control->name, tok1, tok2,
+ GetConfigOption("config_file", true));
+ }
+ }
+ else
+ ereport(ERROR,
+ (errmsg("Unsupported parameter '%s' in file: %s",
+ tok1, filename),
+ errhint("Be sure to have 'custom_variable_classes' set "
+ "in a line before any custom variable.")));
+ }
+
+ if( control->name == NULL )
+ ereport(ERROR,
+ (errmsg("Missing a name parameter in file: %s", filename)));
+
+ if (syntaxError)
+ ereport(ERROR,
+ (errmsg("syntax error in extension control file: %s", line),
+ errhint("Lines should have the format parameter = 'value'.")));
+
+ FreeFile(file);
+ return control;
+ }
+
+ /*
+ * Add extension specific custom class to global custom_variable_classes
+ *
+ * That can get called either via the interactive CREATE EXTENSION command,
+ * or at backend startup time to set the custom_variable_classes.
+ *
+ * Please remind that the extension specific code need to set the
+ * custom_variable_classes for all installed extensions at backend startup
+ * time, but *NOT* the custom variables themselves. We offer this facility
+ * for extensions authors to register their custom class easily and in a
+ * decentralized way (no need to edit postgresql.conf), but the settings
+ * themselves will need to be set as usual by the users.
+ *
+ * Avoid useless verbosity when that's happening in the postinit.
+ */
+ bool
+ add_extension_custom_variable_classes(const char *extname,
+ const char *ext_custom_classes,
+ bool verbose)
+ {
+ char *newval;
+ int g_cvc_len = 0; /* length of global custom_variable_classes */
+ int e_cvc_len = 0; /* length of extension's custom_variable_classes */
+
+ if (ext_custom_classes == NULL)
+ /* we did it, right? */
+ return true;
+
+ /*
+ * Concat the extension's custom variable classes to the current global
+ * one, then act as if it were done from guc.c itself
+ *
+ * See src/backend/misc/README for why we use malloc() here.
+ */
+ e_cvc_len = strlen(ext_custom_classes);
+
+ if (custom_variable_classes != NULL)
+ {
+ g_cvc_len = strlen(custom_variable_classes);
+ newval = (char *)malloc((g_cvc_len + e_cvc_len + 2) * sizeof(char));
+ sprintf(newval, "%s,%s", custom_variable_classes, ext_custom_classes);
+ }
+ else
+ newval = (char *)ext_custom_classes;
+
+ /* FIXME: add PGC_EXTENSION so that we don't abuse PGC_SIGHUP here? */
+ SetConfigOption("custom_variable_classes",
+ newval, PGC_SIGHUP, PGC_S_EXTENSION);
+
+ if (verbose)
+ elog(NOTICE,
+ "Extension '%s' SET custom_variable_classes TO '%s'",
+ extname, custom_variable_classes);
+
+ return true;
+ }
+
+ /*
+ * Get all known extensions (for the current database, because we just
+ * seqscan the pg_extension catalog here) and init custom_variable_classes
+ * from there.
+ */
+ void
+ ExtensionSetCVC(void)
+ {
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ bool isnull;
+ Datum classes;
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ /* memset(values, 0, sizeof(values)); */
+ /* MemSet(nulls, false, sizeof(nulls)); */
+
+ scandesc = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+ while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ {
+ if (HeapTupleIsValid(tuple))
+ {
+ classes = heap_getattr(tuple,
+ Anum_pg_extension_custom_class,
+ rel->rd_att,
+ &isnull);
+
+ if (!isnull)
+ add_extension_custom_variable_classes(
+ NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname),
+ TextDatumGetCString(classes),
+ false /* be quiet */);
+ }
+ }
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+ return;
+ }
+
+
+ /*
+ * Create an extension
+ *
+ * Only superusers can create an extension.
+ */
+ void
+ CreateExtension(CreateExtensionStmt *stmt)
+ {
+ ExtensionControlFile *control;
+ Relation rel;
+ Datum values[Natts_pg_extension];
+ bool nulls[Natts_pg_extension];
+ HeapTuple tuple;
+ Oid extensionoid;
+ text *filename;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create extension \"%s\"",
+ stmt->extname),
+ errhint("Must be superuser to create an extension.")));
+
+ if( InvalidOid != get_extension_oid(stmt->extname, true) )
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("extension \"%s\" already exists", stmt->extname)));
+ }
+
+ control = read_extension_control_file(
+ get_extension_control_filename(stmt->extname), true);
+
+ /*
+ * Insert tuple into pg_extension.
+ *
+ * We use ExclusiveLock here so that there's only CREATE EXTENSION
+ * possible at any given time. It's necessary because of the way the
+ * global variable create_extension is handled, and it seems like a
+ * restriction that's easy to live with.
+ */
+ rel = heap_open(ExtensionRelationId, ExclusiveLock);
+
+ memset(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_extension_extname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(control->name));
+
+ if( control->version == NULL )
+ nulls[Anum_pg_extension_extversion - 1] = true;
+ else
+ values[Anum_pg_extension_extversion - 1] =
+ CStringGetTextDatum(control->version);
+
+ if( control->custom_class == NULL )
+ nulls[Anum_pg_extension_custom_class - 1] = true;
+ else
+ values[Anum_pg_extension_custom_class - 1] =
+ CStringGetTextDatum(control->custom_class);
+
+ tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ extensionoid = simple_heap_insert(rel, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+ heap_freetuple(tuple);
+ heap_close(rel, ExclusiveLock);
+
+ /*
+ * Comment on extension
+ */
+ if (control->comment != NULL)
+ CreateComments(extensionoid, ExtensionRelationId, 0, control->comment);
+
+ /*
+ * Set the global create_extension so that any object created in the
+ * script has a dependency recorded towards the extension here.
+ */
+ create_extension = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ create_extension->classId = ExtensionRelationId;
+ create_extension->objectId = extensionoid;
+ create_extension->objectSubId = 0;
+
+ /*
+ * Also set create_extension_with_user_data so that the function
+ * pg_extension_with_user_data returns the right value.
+ */
+ create_extension_with_user_data = stmt->user_data;
+
+ elog(NOTICE,
+ "Installing extension '%s' from '%s', %s user data",
+ stmt->extname,
+ get_extension_install_filename(stmt->extname),
+ create_extension_with_user_data ? "with" : "whithout");
+
+ filename = cstring_to_text(get_extension_install_filename(stmt->extname));
+ DirectFunctionCall1(pg_execute_from_file, PointerGetDatum(filename));
+
+ /* reset the current_extension dependency tracker */
+ pfree(create_extension);
+ create_extension = NULL;
+
+ return;
+ }
+
+ /*
+ * Drop an extension
+ */
+ void
+ DropExtension(DropExtensionStmt *stmt)
+ {
+ char *extname = stmt->extname;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+ Oid extensionoid;
+ ObjectAddress object;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to drop extension \"%s\"",
+ stmt->extname),
+ errhint("Must be superuser to drop an extension.")));
+
+ /*
+ * Find the target tuple
+ */
+ rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_extension_extname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(extname));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ if (!stmt->missing_ok)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("extension \"%s\" does not exist", extname)));
+ }
+ else
+ {
+ ereport(NOTICE,
+ (errmsg("extension \"%s\" does not exist, skipping",
+ extname)));
+ /* XXX I assume I need one or both of these next two calls */
+ heap_endscan(scandesc);
+ heap_close(rel, RowExclusiveLock);
+ }
+ return;
+ }
+ extensionoid = HeapTupleGetOid(tuple);
+ heap_endscan(scandesc);
+ heap_close(rel, RowExclusiveLock);
+
+ /*
+ * Do the deletion
+ */
+ object.classId = ExtensionRelationId;
+ object.objectId = extensionoid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+
+ return;
+ }
+
+ /*
+ * get_extension_oid - given an extension name, look up the OID
+ *
+ * If missing_ok is false, throw an error if extension name not found. If
+ * true, just return InvalidOid.
+ */
+ Oid
+ get_extension_oid(const char *extname, bool missing_ok)
+ {
+ Oid result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_extension_extname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(extname));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = HeapTupleGetOid(tuple);
+ else
+ result = InvalidOid;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ if (!OidIsValid(result) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("extension \"%s\" does not exist",
+ extname)));
+
+ return result;
+ }
+
+ /*
+ * get_extension_name - given an extension OID, look up the name
+ *
+ * Returns a palloc'd string, or NULL if no such tablespace.
+ */
+ char *
+ get_extension_name(Oid ext_oid)
+ {
+ char *result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ /*
+ * Search pg_extension. We use a heapscan here even though there is an
+ * index on oid, on the theory that pg_extension will usually have just a
+ * few entries and so an indexed lookup is a waste of effort.
+ */
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(ext_oid));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
+ else
+ result = NULL;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return result;
+ }
+
+ /*
+ * Drop extension by OID. This is called to clean up dependencies.
+ */
+ char *
+ RemoveExtensionById(Oid extId)
+ {
+ char *result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ /*
+ * Search pg_extension. We use a heapscan here even though there is an
+ * index on oid, on the theory that pg_extension will usually have just a
+ * few entries and so an indexed lookup is a waste of effort.
+ */
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(extId));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
+ else
+ result = NULL;
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return result;
+ }
+
+ /*
+ * Public (SQL callable) function to let the extension install script know
+ * if it should care for user data or not. The typical usage is from pg_dump
+ * that will issue
+ * CREATE EXTENSION foo WITH NO DATA;
+ *
+ * That allows any object flagged as worthy of dump to be restored by the
+ * script produced by pg_dump rather than installed with default content
+ * from the extension's script.
+ */
+ Datum
+ pg_extension_with_user_data(PG_FUNCTION_ARGS)
+ {
+ if (create_extension == NULL)
+ ereport(ERROR,
+ (errmsg("This function can only be used from CREATE EXTENSION.")));
+
+ PG_RETURN_BOOL(create_extension_with_user_data);
+ }
+
+ /*
+ * Public (SQL callable) function for managing user object that you want to
+ * dump from an extension.
+ *
+ * pg_dump queries are filtering out objects that have an internal ('i')
+ * dependency with the extension's oid, so we provide a function to change
+ * the dependency fron 'i' to 'n'. As a result, pg_dump will consider such
+ * objects.
+ *
+ * update pg_depend
+ * set deptype = 'n'
+ * where refclassid = 'pg_extension'::regclass
+ * and refobjid = $ext_oid and oibjid = $1
+ * and deptype = 'i'
+ */
+ Datum
+ pg_extension_flag_dump(PG_FUNCTION_ARGS)
+ {
+ Oid objid = PG_GETARG_OID(0);
+ long count;
+
+ /*
+ * CREATE EXTENSION is superuser only and we check we're in that code
+ * path, so we don't add another explicit check for superuser here.
+ */
+ if (create_extension == NULL)
+ ereport(ERROR,
+ (errmsg("This function can only be used from CREATE EXTENSION.")));
+
+ count = changeDependencyTypeFor(objid,
+ ExtensionRelationId, create_extension->objectId, 0,
+ DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL);
+
+ if (count == 0)
+ ereport(ERROR,
+ (errmsg("Can't find pg_depend entry for oid %u "
+ "referring to current extension.", objid)));
+ else if (count > 1)
+ ereport(ERROR,
+ (errmsg("Current extension has more than one "
+ "dependency with object id %u.", objid)));
+
+ PG_RETURN_BOOL(true);
+ }
+
+ /*
+ * read (seqscan) the pg_extension catalog and fill in the exts array (that
+ * we allocate --- the caller has to free it). returns the number of items
+ * added into the array.
+ */
+ static int
+ extension_list_installed(ExtensionControlFile **exts, bool name_only)
+ {
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ bool visnull, cisnull;
+ Datum d_version, d_classes;
+ int num = 0, size = 10;
+
+ *exts = (ExtensionControlFile *)palloc(size*sizeof(ExtensionControlFile));
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+ scandesc = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+ while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ {
+ if (HeapTupleIsValid(tuple))
+ {
+ if( num == size )
+ {
+ size += 10;
+ *exts = (ExtensionControlFile *)
+ repalloc(exts, size*sizeof(ExtensionControlFile));
+ }
+
+ (*exts)[num].name =
+ pstrdup(NameStr(((Form_pg_extension)
+ GETSTRUCT(tuple))->extname));
+
+ if (!name_only)
+ {
+ d_version = heap_getattr(tuple,
+ Anum_pg_extension_extversion,
+ rel->rd_att,
+ &visnull);
+ d_classes = heap_getattr(tuple,
+ Anum_pg_extension_custom_class,
+ rel->rd_att,
+ &cisnull);
+
+ if (visnull)
+ (*exts)[num].version = NULL;
+ else
+ (*exts)[num].version = pstrdup(TextDatumGetCString(d_version));
+
+ if (cisnull)
+ (*exts)[num].custom_class = NULL;
+ else
+ (*exts)[num].custom_class = pstrdup(TextDatumGetCString(d_classes));
+
+ (*exts)[num].comment =
+ GetComment(HeapTupleGetOid(tuple), tuple->t_tableOid, 0);
+ }
+ num++;
+ }
+ }
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return num;
+ }
+
+ /*
+ * This function lists all extensions, the one currently already installed
+ * but also the one available for installation. For those, we parse the
+ * control file and return the information that would be available in the
+ * pg_extension catalog.
+ *
+ * A system view pg_extensions provides the user interface to this SRF
+ */
+ Datum
+ pg_extensions(PG_FUNCTION_ARGS)
+ {
+ FuncCallContext *funcctx;
+ struct dirent *de;
+ extension_fctx *fctx;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to get extensions listings"))));
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ MemoryContext oldcontext;
+ TupleDesc tupdesc;
+ ExtensionControlFile *installed;
+ int num_ext_installed;
+
+ funcctx = SRF_FIRSTCALL_INIT();
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ tupdesc = CreateTemplateTupleDesc(5, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
+ NAMEOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "version",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "custom_variable_classes",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "comment",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 5, "installed",
+ BOOLOID, -1, 0);
+
+ funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+
+ /* Get an array of installed extensions, but only their names */
+ num_ext_installed = extension_list_installed(&installed, true);
+
+ fctx = palloc(sizeof(extension_fctx));
+ fctx->dir.location = get_extension_control_basepath();
+ fctx->dir.dirdesc = AllocateDir(fctx->dir.location);
+ fctx->num_installed = num_ext_installed;
+ fctx->installed = installed;
+
+ if (!fctx->dir.dirdesc)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m",
+ fctx->dir.location)));
+
+ funcctx->user_fctx = fctx;
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ fctx = (extension_fctx *) funcctx->user_fctx;
+
+ while ((de = ReadDir(fctx->dir.dirdesc, fctx->dir.location)) != NULL)
+ {
+ ExtensionControlFile *control;
+ Datum values[5];
+ bool nulls[5];
+ bool is_installed = false;
+ int i = 0;
+ HeapTuple tuple;
+
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0 ||
+ strrchr(de->d_name, '.') == NULL ||
+ strcmp(strrchr(de->d_name, '.'), ".control") != 0)
+ continue;
+
+ control = read_extension_control_file(
+ get_extension_control_fullname(de->d_name), false);
+
+ memset(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+
+ values[0] = DirectFunctionCall1(namein, CStringGetDatum(control->name));
+
+ if( control->version == NULL )
+ nulls[1] = true;
+ else
+ values[1] = CStringGetTextDatum(control->version);
+
+ if( control->custom_class == NULL )
+ nulls[2] = true;
+ else
+ values[2] = CStringGetTextDatum(control->custom_class);
+
+ if( control->comment == NULL )
+ nulls[3] = true;
+ else
+ values[3] = CStringGetTextDatum(control->comment);
+
+ /*
+ * We add a boolean "installed" to the pg_extension struct. As we
+ * expect installations to typically have very few extensions
+ * installed, we simple rescan the same array each time.
+ */
+ while( !is_installed && i < fctx->num_installed )
+ is_installed = 0 == strcmp(fctx->installed[i++].name,
+ control->name);
+
+ values[4] = BoolGetDatum(is_installed);
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+ SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+ }
+ FreeDir(fctx->dir.dirdesc);
+ SRF_RETURN_DONE(funcctx);
+ }
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
***************
*** 32,37 ****
--- 32,38 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
/*
***************
*** 411,416 **** CreateForeignDataWrapper(CreateFdwStmt *stmt)
--- 412,423 ----
referenced.objectId = fdwvalidator;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
***************
*** 696,701 **** CreateForeignServer(CreateForeignServerStmt *stmt)
--- 703,714 ----
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, NoLock);
}
***************
*** 967,972 **** CreateUserMapping(CreateUserMappingStmt *stmt)
--- 980,991 ----
/* Record the mapped user dependency */
recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, NoLock);
}
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 61,67 ****
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
!
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
Oid newOwnerId);
--- 61,67 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
! #include "commands/extension.h"
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
Oid newOwnerId);
***************
*** 1744,1749 **** CreateCast(CreateCastStmt *stmt)
--- 1744,1754 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_freetuple(tuple);
heap_close(relation, RowExclusiveLock);
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 42,47 ****
--- 42,48 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
/*
***************
*** 306,311 **** CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
--- 307,318 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, RowExclusiveLock);
return opfamilyoid;
***************
*** 693,698 **** DefineOpClass(CreateOpClassStmt *stmt)
--- 700,711 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, RowExclusiveLock);
}
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
***************
*** 36,41 ****
--- 36,42 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
typedef struct
***************
*** 425,430 **** create_proc_lang(const char *languageName, bool replace,
--- 426,437 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, RowExclusiveLock);
}
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 46,51 ****
--- 46,52 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
***************
*** 154,159 **** makeParserDependencies(HeapTuple tuple)
--- 155,166 ----
referenced.objectId = prs->prsheadline;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 423,428 **** makeDictionaryDependencies(HeapTuple tuple)
--- 430,441 ----
referenced.objectId = dict->dicttemplate;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on extension, if in create extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 965,970 **** makeTSTemplateDependencies(HeapTuple tuple)
--- 978,989 ----
referenced.objectId = tmpl->tmplinit;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* dependency on extension, if in create extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 1289,1294 **** makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
--- 1308,1319 ----
/* Record 'em (this includes duplicate elimination) */
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+ /* dependencies on extension being created, if any */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
free_object_addresses(addrs);
}
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3115,3120 **** _copyDiscardStmt(DiscardStmt *from)
--- 3115,3143 ----
return newnode;
}
+ static CreateExtensionStmt *
+ _copyCreateExtensionStmt(CreateExtensionStmt *from)
+ {
+ CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
+
+ COPY_STRING_FIELD(extname);
+ COPY_SCALAR_FIELD(user_data);
+
+ return newnode;
+ }
+
+ static DropExtensionStmt *
+ _copyDropExtensionStmt(DropExtensionStmt *from)
+ {
+ DropExtensionStmt *newnode = makeNode(DropExtensionStmt);
+
+ COPY_STRING_FIELD(extname);
+ COPY_SCALAR_FIELD(missing_ok);
+ COPY_SCALAR_FIELD(behavior);
+
+ return newnode;
+ }
+
static CreateTableSpaceStmt *
_copyCreateTableSpaceStmt(CreateTableSpaceStmt *from)
{
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1590,1595 **** _equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b)
--- 1590,1614 ----
}
static bool
+ _equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
+ {
+ COMPARE_STRING_FIELD(extname);
+ COMPARE_SCALAR_FIELD(user_data);
+
+ return true;
+ }
+
+ static bool
+ _equalDropExtensionStmt(DropExtensionStmt *a, DropExtensionStmt *b)
+ {
+ COMPARE_STRING_FIELD(extname);
+ COMPARE_SCALAR_FIELD(missing_ok);
+ COMPARE_SCALAR_FIELD(behavior);
+
+ return true;
+ }
+
+ static bool
_equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a,
AlterTableSpaceOptionsStmt *b)
{
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 190,196 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
! CreateDomainStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
--- 190,196 ----
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
! CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
***************
*** 198,204 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
--- 198,204 ----
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt DropExtensionStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
***************
*** 484,490 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
! EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
FREEZE FROM FULL FUNCTION FUNCTIONS
--- 484,490 ----
DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
! EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
FREEZE FROM FULL FUNCTION FUNCTIONS
***************
*** 680,685 **** stmt :
--- 680,686 ----
| CreateCastStmt
| CreateConversionStmt
| CreateDomainStmt
+ | CreateExtensionStmt
| CreateFdwStmt
| CreateForeignServerStmt
| CreateFunctionStmt
***************
*** 705,710 **** stmt :
--- 706,712 ----
| DoStmt
| DropAssertStmt
| DropCastStmt
+ | DropExtensionStmt
| DropFdwStmt
| DropForeignServerStmt
| DropGroupStmt
***************
*** 3081,3086 **** opt_procedural:
--- 3083,3129 ----
/*****************************************************************************
*
* QUERY:
+ * CREATE EXTENSION extension [ WITH [ NO ] DATA ]
+ *
+ *****************************************************************************/
+
+ CreateExtensionStmt: CREATE EXTENSION name opt_with_data
+ {
+ CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ n->extname = $3;
+ n->user_data = $4;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * DROP EXTENSION [ RESTRICT | CASCADE ]
+ *
+ ****************************************************************************/
+
+ DropExtensionStmt: DROP EXTENSION name opt_drop_behavior
+ {
+ DropExtensionStmt *n = makeNode(DropExtensionStmt);
+ n->extname = $3;
+ n->missing_ok = false;
+ n->behavior = $4;
+ $$ = (Node *) n;
+ }
+ | DROP EXTENSION IF_P EXISTS name opt_drop_behavior
+ {
+ DropExtensionStmt *n = makeNode(DropExtensionStmt);
+ n->extname = $5;
+ n->missing_ok = true;
+ n->behavior = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY:
* CREATE TABLESPACE tablespace LOCATION '/path/to/tablespace/'
*
*****************************************************************************/
***************
*** 4185,4191 **** opt_restart_seqs:
*
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
! * CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
* TEXT SEARCH TEMPLATE |
* TEXT SEARCH CONFIGURATION ] |
--- 4228,4234 ----
*
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
! * CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
* TEXT SEARCH TEMPLATE |
* TEXT SEARCH CONFIGURATION ] |
***************
*** 4364,4369 **** comment_type:
--- 4407,4413 ----
| VIEW { $$ = OBJECT_VIEW; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
+ | EXTENSION { $$ = OBJECT_EXTENSION; }
| ROLE { $$ = OBJECT_ROLE; }
;
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 32,37 ****
--- 32,38 ----
#include "commands/defrem.h"
#include "commands/discard.h"
#include "commands/explain.h"
+ #include "commands/extension.h"
#include "commands/lockcmds.h"
#include "commands/portalcmds.h"
#include "commands/prepare.h"
***************
*** 175,180 **** check_xact_readonly(Node *parsetree)
--- 176,182 ----
case T_CreateConversionStmt:
case T_CreatedbStmt:
case T_CreateDomainStmt:
+ case T_CreateExtensionStmt:
case T_CreateFunctionStmt:
case T_CreateRoleStmt:
case T_IndexStmt:
***************
*** 194,199 **** check_xact_readonly(Node *parsetree)
--- 196,202 ----
case T_DropCastStmt:
case T_DropStmt:
case T_DropdbStmt:
+ case T_DropExtensionStmt:
case T_DropTableSpaceStmt:
case T_RemoveFuncStmt:
case T_DropRoleStmt:
***************
*** 557,562 **** standard_ProcessUtility(Node *parsetree,
--- 560,573 ----
}
break;
+ case T_CreateExtensionStmt:
+ CreateExtension((CreateExtensionStmt *) parsetree);
+ break;
+
+ case T_DropExtensionStmt:
+ DropExtension((DropExtensionStmt *) parsetree);
+ break;
+
case T_CreateTableSpaceStmt:
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
***************
*** 1498,1503 **** CreateCommandTag(Node *parsetree)
--- 1509,1522 ----
tag = "CREATE TABLE";
break;
+ case T_CreateExtensionStmt:
+ tag = "CREATE EXTENSION";
+ break;
+
+ case T_DropExtensionStmt:
+ tag = "DROP EXTENSION";
+ break;
+
case T_CreateTableSpaceStmt:
tag = "CREATE TABLESPACE";
break;
***************
*** 2292,2297 **** GetCommandLogLevel(Node *parsetree)
--- 2311,2324 ----
lev = LOGSTMT_DDL;
break;
+ case T_CreateExtensionStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_DropExtensionStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreateTableSpaceStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
* Copyright (c) 2004-2010, PostgreSQL Global Development Group
*
* Author: Andreas Pflug
+ * Dimitri Fontaine
*
* IDENTIFICATION
* src/backend/utils/adt/genfile.c
***************
*** 21,41 ****
#include
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "postmaster/syslogger.h"
#include "storage/fd.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
- typedef struct
- {
- char *location;
- DIR *dirdesc;
- } directory_fctx;
-
/*
* Convert a "text" filename argument to C string, and check it's allowable.
--- 22,38 ----
#include
#include "catalog/pg_type.h"
+ #include "executor/spi.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "postmaster/syslogger.h"
#include "storage/fd.h"
#include "utils/builtins.h"
+ #include "utils/genfile.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
/*
* Convert a "text" filename argument to C string, and check it's allowable.
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 261,331 ----
SRF_RETURN_DONE(funcctx);
}
+
+ /*
+ * Read a file then execute the SQL commands it contains.
+ */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ text *filename_t = PG_GETARG_TEXT_P(0);
+ char *filename;
+ FILE *file;
+ int64 fsize = -1, nbytes;
+ struct stat fst;
+ char *query_string = NULL;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to get file information"))));
+
+ /*
+ * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ * uses that too. Don't double check the PATH. Also note that
+ * extension's install files are not in $PGDATA but `pg_config
+ * --sharedir`.
+ */
+ filename = text_to_cstring(filename_t);
+
+ if (stat(filename, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+
+ fsize = Int64GetDatum((int64) fst.st_size);
+
+ if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ filename)));
+
+ if (ferror(file))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+
+ query_string = (char *)palloc((fsize+1)*sizeof(char));
+ memset(query_string, 0, fsize+1);
+ nbytes = fread(query_string, 1, (size_t) fsize, file);
+ pg_verifymbstr(query_string, nbytes, false);
+ FreeFile(file);
+
+ /*
+ * We abuse some internal knowledge from spi.h here. As we don't know
+ * which queries are going to get executed, we don't know what to expect
+ * as an OK return code from SPI_execute(). We assume that
+ * SPI_OK_CONNECT, SPI_OK_FINISH and SPI_OK_FETCH are quite improbable,
+ * though, and the errors are negatives. So a valid return code is
+ * considered to be SPI_OK_UTILITY or anything from there.
+ */
+ SPI_connect();
+ if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("File '%s' contains invalid query", filename)));
+ SPI_finish();
+
+ PG_RETURN_VOID();
+ }
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 337,342 **** CheckMyDatabase(const char *name, bool am_superuser)
--- 337,345 ----
SetConfigOption("client_encoding", GetDatabaseEncodingName(),
PGC_BACKEND, PGC_S_DEFAULT);
+ /* Add extension dependent entries to custom_variable_classes */
+ ExtensionSetCVC();
+
/* assign locale variables */
collate = NameStr(dbform->datcollate);
ctype = NameStr(dbform->datctype);
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
! rbtree.o
# This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h.
--- 15,21 ----
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
! rbtree.o cfparser.o
# This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h.
*** /dev/null
--- b/src/backend/utils/misc/cfparser.c
***************
*** 0 ****
--- 1,113 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cfparser.c
+ * Function for parsing RecoveryCommandFile lines
+ *
+ * This very simple file format (varible = value) is now also used in the
+ * extension control file format
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/cfparser.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #include "postgres.h"
+ #include "utils/cfparser.h"
+
+ /*
+ * Parse one line from recovery.conf. 'cmdline' is the raw line from the
+ * file. If the line is parsed successfully, returns true, false indicates
+ * syntax error. On success, *key_p and *value_p are set to the parameter
+ * name and value on the line, respectively. If the line is an empty line,
+ * consisting entirely of whitespace and comments, function returns true
+ * and *keyp_p and *value_p are set to NULL.
+ *
+ * The pointers returned in *key_p and *value_p point to an internal buffer
+ * that is valid only until the next call of parseRecoveryCommandFile().
+ */
+ bool
+ cfParseOneLine(char *cmdline, char **key_p, char **value_p)
+ {
+ char *ptr;
+ char *bufp;
+ char *key;
+ char *value;
+ static char *buf = NULL;
+
+ *key_p = *value_p = NULL;
+
+ /*
+ * Allocate the buffer on first use. It's used to hold both the parameter
+ * name and value.
+ */
+ if (buf == NULL)
+ buf = malloc(MAXPGPATH + 1);
+ bufp = buf;
+
+ /* Skip any whitespace at the beginning of line */
+ for (ptr = cmdline; *ptr; ptr++)
+ {
+ if (!isspace((unsigned char) *ptr))
+ break;
+ }
+ /* Ignore empty lines */
+ if (*ptr == '\0' || *ptr == '#')
+ return true;
+
+ /* Read the parameter name */
+ key = bufp;
+ while (*ptr && !isspace((unsigned char) *ptr) &&
+ *ptr != '=' && *ptr != '\'')
+ *(bufp++) = *(ptr++);
+ *(bufp++) = '\0';
+
+ /* Skip to the beginning quote of the parameter value */
+ ptr = strchr(ptr, '\'');
+ if (!ptr)
+ return false;
+ ptr++;
+
+ /* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
+ value = bufp;
+ for (;;)
+ {
+ if (*ptr == '\'')
+ {
+ ptr++;
+ if (*ptr == '\'')
+ *(bufp++) = '\'';
+ else
+ {
+ /* end of parameter */
+ *bufp = '\0';
+ break;
+ }
+ }
+ else if (*ptr == '\0')
+ return false; /* unterminated quoted string */
+ else
+ *(bufp++) = *ptr;
+
+ ptr++;
+ }
+ *(bufp++) = '\0';
+
+ /* Check that there's no garbage after the value */
+ while (*ptr)
+ {
+ if (*ptr == '#')
+ break;
+ if (!isspace((unsigned char) *ptr))
+ return false;
+ ptr++;
+ }
+
+ /* Success! */
+ *key_p = key;
+ *value_p = value;
+ return true;
+ }
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 206,221 **** ProcessConfigFile(GucContext context)
* We have to consider three cases for custom variables:
*
* 1. The class name is not valid according to the (new) setting
! * of custom_variable_classes. If so, reject. We don't care
! * which side is at fault.
*/
if (!is_custom_class(item->name, sep - item->name, cvc))
{
! ereport(elevel,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("unrecognized configuration parameter \"%s\"",
! item->name)));
! goto cleanup_list;
}
/*
* 2. There is no GUC entry. If we called set_config_option then
--- 206,228 ----
* We have to consider three cases for custom variables:
*
* 1. The class name is not valid according to the (new) setting
! * of custom_variable_classes. If so, we would reject, but to
! * support custom_variable_classes being PGC_SUSET, we maintain
! * a list of not-yet-valid items.
! *
! * This list will be processed at assign_custom_variable_classes
! * time: each time cvc changes, we have candidates to
! * consider. Such a time is for example ExtensionSetCVC() from
! * backend post init, where we read additional cvc in
! * pg_extension catalog.
*/
if (!is_custom_class(item->name, sep - item->name, cvc))
{
! if ( find_invalid_option(item->name, item->value, true, elevel) == NULL)
! goto cleanup_list;
!
! /* don't set_config_option() as it's an invalid item */
! continue;
}
/*
* 2. There is no GUC entry. If we called set_config_option then
***************
*** 314,319 **** ProcessConfigFile(GucContext context)
--- 321,330 ----
{
char *pre_value = NULL;
+ /* this list contains invalid placeholders, skip them here */
+ if (find_invalid_option(item->name, NULL, false, elevel) != NULL)
+ continue;
+
/* In SIGHUP cases in the postmaster, report changes */
if (context == PGC_SIGHUP && !IsUnderPostmaster)
{
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 162,169 **** static bool assign_session_replication_role(int newval, bool doit,
GucSource source);
static const char *show_num_temp_buffers(void);
static bool assign_phony_autocommit(bool newval, bool doit, GucSource source);
- static const char *assign_custom_variable_classes(const char *newval, bool doit,
- GucSource source);
static bool assign_debug_assertions(bool newval, bool doit, GucSource source);
static bool assign_bonjour(bool newval, bool doit, GucSource source);
static bool assign_ssl(bool newval, bool doit, GucSource source);
--- 162,167 ----
***************
*** 427,433 **** static char *log_timezone_string;
static char *timezone_abbreviations_string;
static char *XactIsoLevel_string;
static char *data_directory;
- static char *custom_variable_classes;
static int max_function_args;
static int max_index_keys;
static int max_identifier_length;
--- 425,430 ----
***************
*** 442,447 **** static int effective_io_concurrency;
--- 439,446 ----
char *role_string;
char *session_authorization_string;
+ /* should be static, but commands/extension.c needs to get at these */
+ char *custom_variable_classes;
/*
* Displayable names for context types (enum GucContext)
***************
*** 468,473 **** const char *const GucSource_Names[] =
--- 467,473 ----
/* PGC_S_DEFAULT */ "default",
/* PGC_S_ENV_VAR */ "environment variable",
/* PGC_S_FILE */ "configuration file",
+ /* PGC_S_EXTENSION */ "extension",
/* PGC_S_ARGV */ "command line",
/* PGC_S_DATABASE */ "database",
/* PGC_S_USER */ "user",
***************
*** 2602,2608 **** static struct config_string ConfigureNamesString[] =
},
{
! {"custom_variable_classes", PGC_SIGHUP, CUSTOM_OPTIONS,
gettext_noop("Sets the list of known custom variable classes."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
--- 2602,2608 ----
},
{
! {"custom_variable_classes", PGC_SUSET, CUSTOM_OPTIONS,
gettext_noop("Sets the list of known custom variable classes."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
***************
*** 2916,2927 **** static const char *const map_old_guc_names[] = {
--- 2916,2930 ----
* Actual lookup of variables is done through this single, sorted array.
*/
static struct config_generic **guc_variables;
+ static struct config_generic **guc_invalid_ph;
/* Current number of variables contained in the vector */
static int num_guc_variables;
+ static int num_guc_invalid_ph = 0;
/* Vector capacity */
static int size_guc_variables;
+ static int size_guc_invalid_ph = 0;
static bool guc_dirty; /* TRUE if need to do commit/abort work */
***************
*** 3191,3211 **** build_guc_variables(void)
size_guc_variables = size_vars;
qsort((void *) guc_variables, num_guc_variables,
sizeof(struct config_generic *), guc_var_compare);
}
/*
* Add a new GUC variable to the list of known variables. The
* list is expanded if needed.
*/
static bool
! add_guc_variable(struct config_generic * var, int elevel)
{
! if (num_guc_variables + 1 >= size_guc_variables)
{
/*
* Increase the vector by 25%
*/
! int size_vars = size_guc_variables + size_guc_variables / 4;
struct config_generic **guc_vars;
if (size_vars == 0)
--- 3194,3230 ----
size_guc_variables = size_vars;
qsort((void *) guc_variables, num_guc_variables,
sizeof(struct config_generic *), guc_var_compare);
+
+ /*
+ * Also build guc_invalid_ph, but leave it empty of course
+ */
+ num_guc_invalid_ph = 0;
+ size_guc_invalid_ph = 10;
+ guc_invalid_ph = (struct config_generic **)
+ guc_malloc(FATAL, size_guc_invalid_ph * sizeof(struct config_generic *));
}
/*
* Add a new GUC variable to the list of known variables. The
* list is expanded if needed.
+ *
+ * When valid is true, we're dealing with guc_variables, when it's not we're
+ * dealing with currently invalid placeholders, maintained in guc_invalid_ph.
*/
static bool
! add_guc_variable(struct config_generic * var, int elevel, bool valid)
{
! int num = valid ? num_guc_variables : num_guc_invalid_ph;
! int size = valid ? size_guc_variables : size_guc_invalid_ph;
!
! struct config_generic **gvars = valid ? guc_variables : guc_invalid_ph;
!
! if (num + 1 >= size)
{
/*
* Increase the vector by 25%
*/
! int size_vars = size + size / 4;
struct config_generic **guc_vars;
if (size_vars == 0)
***************
*** 3217,3243 **** add_guc_variable(struct config_generic * var, int elevel)
else
{
guc_vars = (struct config_generic **)
! guc_realloc(elevel, guc_variables, size_vars * sizeof(struct config_generic *));
}
if (guc_vars == NULL)
return false; /* out of memory */
! guc_variables = guc_vars;
! size_guc_variables = size_vars;
}
! guc_variables[num_guc_variables++] = var;
! qsort((void *) guc_variables, num_guc_variables,
sizeof(struct config_generic *), guc_var_compare);
return true;
}
/*
! * Create and add a placeholder variable. It's presumed to belong
! * to a valid custom variable class at this point.
*/
! static struct config_generic *
! add_placeholder_variable(const char *name, int elevel)
{
size_t sz = sizeof(struct config_string) + sizeof(char *);
struct config_string *var;
--- 3236,3274 ----
else
{
guc_vars = (struct config_generic **)
! guc_realloc(elevel, gvars, size_vars * sizeof(struct config_generic *));
}
if (guc_vars == NULL)
return false; /* out of memory */
! gvars = guc_vars;
! if (valid)
! size_guc_variables = size_vars;
! else
! size_guc_invalid_ph = size_vars;
}
! gvars[num++] = var;
! if (valid)
! num_guc_variables = num;
! else
! num_guc_invalid_ph = num;
!
! qsort((void *) gvars, num,
sizeof(struct config_generic *), guc_var_compare);
+
return true;
}
/*
! * Create and add a placeholder variable. It's presumed to belong to a valid
! * custom variable class at this point, unless valid is false. In that case
! * we add it to the guc_invalid_ph variable.
! *
! * We use that from src/backend/commands/extension.c too.
*/
! struct config_generic *
! add_placeholder_variable(const char *name, int elevel, bool valid)
{
size_t sz = sizeof(struct config_string) + sizeof(char *);
struct config_string *var;
***************
*** 3269,3275 **** add_placeholder_variable(const char *name, int elevel)
*/
var->variable = (char **) (var + 1);
! if (!add_guc_variable((struct config_generic *) var, elevel))
{
free((void *) gen->name);
free(var);
--- 3300,3306 ----
*/
var->variable = (char **) (var + 1);
! if (!add_guc_variable((struct config_generic *) var, elevel, valid))
{
free((void *) gen->name);
free(var);
***************
*** 3285,3291 **** add_placeholder_variable(const char *name, int elevel)
* formatted the way that assign_custom_variable_classes does it, ie,
* no whitespace. NULL is valid for custom_var_classes.
*/
! static bool
is_custom_class(const char *name, int dotPos, const char *custom_var_classes)
{
bool result = false;
--- 3316,3322 ----
* formatted the way that assign_custom_variable_classes does it, ie,
* no whitespace. NULL is valid for custom_var_classes.
*/
! bool
is_custom_class(const char *name, int dotPos, const char *custom_var_classes)
{
bool result = false;
***************
*** 3362,3374 **** find_option(const char *name, bool create_placeholders, int elevel)
if (dot != NULL &&
is_custom_class(name, dot - name, custom_variable_classes))
! return add_placeholder_variable(name, elevel);
}
/* Unknown name */
return NULL;
}
/*
* comparator for qsorting and bsearching guc_variables array
--- 3393,3454 ----
if (dot != NULL &&
is_custom_class(name, dot - name, custom_variable_classes))
! return add_placeholder_variable(name, elevel, true);
}
/* Unknown name */
return NULL;
}
+ /*
+ * Same as find_option, but uses guc_invalid_ph rather than guc_variables:
+ * that allows to support custom_variable_classes as PGC_SIGHUP, as changes
+ * to it will be able to trigger a traversal of known invalid place holders.
+ *
+ * We copy find_option() so as to minimize changes in existing call sites
+ * that don't care at all with that subtelty.
+ */
+ static struct config_generic *
+ find_invalid_option(const char *name, const char *value,
+ bool create_placeholders, int elevel)
+ {
+ const char **key = &name;
+ struct config_generic **res;
+ char *dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
+
+ Assert(name);
+
+ /*
+ * We're only dealing with invalid placeholders here.
+ */
+ if (dot == NULL)
+ return NULL;
+
+ /*
+ * By equating const char ** with struct config_generic *, we are assuming
+ * the name field is first in config_generic.
+ */
+ res = (struct config_generic **) bsearch((void *) &key,
+ (void *) guc_invalid_ph,
+ num_guc_invalid_ph,
+ sizeof(struct config_generic *),
+ guc_var_compare);
+ if (res)
+ return *res;
+
+ if (create_placeholders)
+ {
+ char *newval = guc_strdup(elevel, value);
+ struct config_generic *gconf =
+ add_placeholder_variable(name, elevel, false);
+ struct config_string *conf = (struct config_string *) gconf;
+
+ conf->boot_val = newval;
+ InitializeOneGUCOption(gconf);
+ return gconf;
+ }
+ return NULL;
+ }
/*
* comparator for qsorting and bsearching guc_variables array
***************
*** 5899,5905 **** define_custom_variable(struct config_generic * variable)
* make sure it's initialized to its default value.
*/
InitializeOneGUCOption(variable);
! add_guc_variable(variable, ERROR);
return;
}
--- 5979,5985 ----
* make sure it's initialized to its default value.
*/
InitializeOneGUCOption(variable);
! add_guc_variable(variable, ERROR, true);
return;
}
***************
*** 7701,7707 **** assign_phony_autocommit(bool newval, bool doit, GucSource source)
return true;
}
! static const char *
assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
{
/*
--- 7781,7790 ----
return true;
}
! /*
! * The function is exported so that extension.c is able to abuse it
! */
! const char *
assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
{
/*
***************
*** 7755,7760 **** assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
--- 7838,7878 ----
/* GUC wants the result malloc'd */
newval = guc_strdup(LOG, buf.data);
+ /*
+ * Now that the value changed, rescan the invalid GUCs we recorded while
+ * reading the configuration file, chances are that some of them are now
+ * perfectly fine. Assuming the list of invalid custom variables should
+ * be rather short, we don't bother cleaning it when a element of it
+ * becomes valid.
+ *
+ * Note that this happen at each backend postinit, scanning pg_extension
+ * custom classes to add them dynamically to what's in the main file.
+ */
+ if (doit)
+ {
+ int i;
+ for (i = 0; i < num_guc_invalid_ph; i++)
+ {
+ struct config_generic *igconf = guc_invalid_ph[i];
+ struct config_string *iconf = (struct config_string *) igconf;
+ const char *dot = strchr(igconf->name, GUC_QUALIFIER_SEPARATOR);
+
+ if (is_custom_class(igconf->name, dot - igconf->name, newval))
+ {
+ /*
+ * Make a valid placeholder out of the invalid we have.
+ */
+ struct config_generic *gconf =
+ add_placeholder_variable(igconf->name, LOG, true);
+ struct config_string *conf = (struct config_string *) gconf;
+ char *newval = guc_strdup(LOG, iconf->boot_val);
+
+ conf->boot_val = newval;
+ InitializeOneGUCOption(gconf);
+ }
+ }
+ }
+
pfree(buf.data);
return newval;
}
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
***************
*** 78,83 **** static int strInArray(const char *pattern, char **arr, int arr_size);
--- 78,84 ----
TableInfo *
getSchemaData(int *numTablesPtr)
{
+ ExtensionInfo *extinfo;
NamespaceInfo *nsinfo;
AggInfo *agginfo;
InhInfo *inhinfo;
***************
*** 94,99 **** getSchemaData(int *numTablesPtr)
--- 95,101 ----
FdwInfo *fdwinfo;
ForeignServerInfo *srvinfo;
DefaultACLInfo *daclinfo;
+ int numExtensions;
int numNamespaces;
int numAggregates;
int numInherits;
***************
*** 112,117 **** getSchemaData(int *numTablesPtr)
--- 114,123 ----
int numDefaultACLs;
if (g_verbose)
+ write_msg(NULL, "reading extensions\n");
+ extinfo = getExtensions(&numExtensions);
+
+ if (g_verbose)
write_msg(NULL, "reading schemas\n");
nsinfo = getNamespaces(&numNamespaces);
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 157,162 **** static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
--- 157,163 ----
SecLabelItem **items);
static int collectSecLabels(Archive *fout, SecLabelItem **items);
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
+ static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
static void dumpType(Archive *fout, TypeInfo *tyinfo);
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
***************
*** 2376,2381 **** binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, Oid pg_class_oid,
--- 2377,2456 ----
}
/*
+ * getExtensions:
+ * read all extensions in the system catalogs and return them in the
+ * ExtensionInfo* structure
+ *
+ * numExtensions is set to the number of extensions read in
+ */
+ ExtensionInfo *
+ getExtensions(int *numExtensions)
+ {
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query;
+ ExtensionInfo *extinfo = NULL;
+ int i_tableoid;
+ int i_oid;
+ int i_extname;
+ int i_extversion;
+ int i_extcclass;
+
+ /*
+ * Before 9.1, there are no extensions.
+ */
+ if (g_fout->remoteVersion < 90100)
+ {
+ *numExtensions = 0;
+ return extinfo;
+ }
+
+ query = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ /*
+ * we fetch all namespaces including system ones, so that every object we
+ * read in can be linked to a containing namespace.
+ */
+ appendPQExpBuffer(query,
+ "SELECT tableoid, oid, extname, extversion, custom_class "
+ "FROM pg_extension");
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ extinfo = (ExtensionInfo *) malloc(ntups * sizeof(ExtensionInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_extname = PQfnumber(res, "extname");
+ i_extversion = PQfnumber(res, "extversion");
+ i_extcclass = PQfnumber(res, "custom_class");
+
+ for (i = 0; i < ntups; i++)
+ {
+ extinfo[i].dobj.objType = DO_EXTENSION;
+ extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&extinfo[i].dobj);
+ extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname));
+ extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion));
+ extinfo[i].custom_class = strdup(PQgetvalue(res, i, i_extcclass));
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ *numExtensions = ntups;
+ return extinfo;
+ }
+
+ /*
* getNamespaces:
* read all namespaces in the system catalogs and return them in the
* NamespaceInfo* structure
***************
*** 2439,2448 **** getNamespaces(int *numNamespaces)
* we fetch all namespaces including system ones, so that every object we
* read in can be linked to a containing namespace.
*/
! appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
! "(%s nspowner) AS rolname, "
! "nspacl FROM pg_namespace",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 2514,2570 ----
* we fetch all namespaces including system ones, so that every object we
* read in can be linked to a containing namespace.
*/
! if (g_fout->remoteVersion >= 90100)
! {
! /*
! * So we want the namespaces, but we want to filter out any
! * namespace created by an extension's script. That's unless the
! * user went over his head and created objects into the extension's
! * schema: we now want the schema not to be filtered out to avoid:
! *
! * pg_dump: schema with OID 77869 does not exist
! */
! appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
! "(%s nspowner) AS rolname, "
! "n.nspacl "
! " FROM pg_namespace n "
! " WHERE n.nspname != 'information_schema' "
! " AND CASE WHEN (SELECT count(*) "
! " FROM pg_depend "
! " WHERE refobjid = n.oid and deptype != 'p') > 0 "
! " THEN EXISTS( "
! "WITH RECURSIVE depends AS ( "
! " select n.oid as nsp, objid, refobjid, array[refobjid] as deps "
! " from pg_depend "
! " where refobjid = n.oid and deptype != 'p' "
! " UNION ALL "
! " select p.nsp, p.objid, d.refobjid, deps || d.refobjid "
! " from pg_depend d JOIN depends p ON d.objid = p.objid "
! " where d.deptype != 'p' and not d.refobjid = any(deps) "
! " and not (d.refclassid = 'pg_extension'::regclass and d.deptype = 'n') "
! ") "
! " SELECT nsp, objid, array_agg(distinct refobjid) "
! " FROM depends "
! "GROUP BY nsp, objid "
! " HAVING NOT array_agg(distinct refobjid) && array(select oid from pg_extension) "
! ") "
! " ELSE true "
! " END "
! "UNION ALL "
! "SELECT n.tableoid, n.oid, n.nspname, "
! "(%s nspowner) AS rolname, "
! "nspacl FROM pg_namespace n "
! "WHERE n.nspname = 'information_schema'",
! username_subquery,
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
! "(%s nspowner) AS rolname, "
! "nspacl FROM pg_namespace",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 2506,2512 **** findNamespace(Oid nsoid, Oid objoid)
if (nsoid == nsinfo->dobj.catId.oid)
return nsinfo;
}
! write_msg(NULL, "schema with OID %u does not exist\n", nsoid);
exit_nicely();
}
else
--- 2628,2635 ----
if (nsoid == nsinfo->dobj.catId.oid)
return nsinfo;
}
! write_msg(NULL, "schema with OID %u does not exist, "
! "but is needed for object %u\n", nsoid, objoid);
exit_nicely();
}
else
***************
*** 2559,2565 **** getTypes(int *numTypes)
* we include even the built-in types because those may be used as array
* elements by user-defined types
*
! * we filter out the built-in types when we dump out the types
*
* same approach for undefined (shell) types and array types
*
--- 2682,2689 ----
* we include even the built-in types because those may be used as array
* elements by user-defined types
*
! * we filter out the built-in types when we dump out the types, and from
! * 9.1 we also filter out types that depend on an extension
*
* same approach for undefined (shell) types and array types
*
***************
*** 2574,2580 **** getTypes(int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, "
--- 2698,2723 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
! "t.typnamespace, "
! "(%s typowner) AS rolname, "
! "t.typinput::oid AS typinput, "
! "t.typoutput::oid AS typoutput, t.typelem, t.typrelid, "
! "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
! "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
! "t.typtype, t.typisdefined, "
! "t.typname[0] = '_' AND t.typelem != 0 AND "
! "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
! "FROM pg_type t "
! "LEFT JOIN pg_depend d ON d.objid = t.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, "
***************
*** 2806,2812 **** getOperators(int *numOprs)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
"oprnamespace, "
--- 2949,2968 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.oprname, "
! "o.oprnamespace, "
! "(%s oprowner) AS rolname, "
! "o.oprcode::oid AS oprcode "
! "FROM pg_operator o "
! "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
"oprnamespace, "
***************
*** 2985,2991 **** getOpclasses(int *numOpclasses)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
"opcnamespace, "
--- 3141,3159 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.opcname, "
! "o.opcnamespace, "
! "(%s opcowner) AS rolname "
! "FROM pg_opclass o "
! "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
"opcnamespace, "
***************
*** 3091,3101 **** getOpfamilies(int *numOpfamilies)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
! "opfnamespace, "
! "(%s opfowner) AS rolname "
! "FROM pg_opfamily",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 3259,3284 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.opfname, "
! "o.opfnamespace, "
! "(%s opfowner) AS rolname "
! "FROM pg_opfamily o "
! "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
! "opfnamespace, "
! "(%s opfowner) AS rolname "
! "FROM pg_opfamily",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 3169,3175 **** getAggregates(int *numAggs)
/* find all user-defined aggregates */
! if (g_fout->remoteVersion >= 80200)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
"pronamespace AS aggnamespace, "
--- 3352,3374 ----
/* find all user-defined aggregates */
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT a.tableoid, a.oid, a.proname AS aggname, "
! "a.pronamespace AS aggnamespace, "
! "a.pronargs, a.proargtypes, "
! "(%s proowner) AS rolname, "
! "a.proacl AS aggacl "
! "FROM pg_proc a "
! "LEFT JOIN pg_depend d ON d.objid = a.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL AND proisagg "
! "AND pronamespace != "
! "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 80200)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
"pronamespace AS aggnamespace, "
***************
*** 3315,3321 **** getFuncs(int *numFuncs)
/* find all user-defined funcs */
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
--- 3514,3537 ----
/* find all user-defined funcs */
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query,
! "SELECT f.tableoid, f.oid, f.proname, f.prolang, "
! "f.pronargs, f.proargtypes, f.prorettype, f.proacl, "
! "f.pronamespace, "
! "(%s proowner) AS rolname "
! "FROM pg_proc f "
! "LEFT JOIN pg_depend d ON d.objid = f.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL AND NOT proisagg "
! "AND pronamespace != "
! "(SELECT oid FROM pg_namespace "
! "WHERE nspname = 'pg_catalog')",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
***************
*** 3477,3483 **** getTables(int *numTables)
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
! if (g_fout->remoteVersion >= 90000)
{
/*
* Left join to pick up dependency info linking sequences to their
--- 3693,3736 ----
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
! if (g_fout->remoteVersion >= 90100)
! {
! /*
! * Left join to pick up dependency info linking sequences to their
! * owning column, if any (note this dependency is AUTO as of 8.2)
! */
! appendPQExpBuffer(query,
! "SELECT c.tableoid, c.oid, c.relname, "
! "c.relacl, c.relkind, c.relnamespace, "
! "(%s c.relowner) AS rolname, "
! "c.relchecks, c.relhastriggers, "
! "c.relhasindex, c.relhasrules, c.relhasoids, "
! "c.relfrozenxid, "
! "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
! "d.refobjid AS owning_tab, "
! "d.refobjsubid AS owning_col, "
! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
! "array_to_string(c.reloptions, ', ') AS reloptions, "
! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
! "FROM pg_class c "
! "LEFT JOIN pg_depend d ON "
! "(c.relkind = '%c' AND "
! "d.classid = c.tableoid AND d.objid = c.oid AND "
! "d.objsubid = 0 AND "
! "d.refclassid = c.tableoid AND d.deptype = 'a') "
! "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
! "LEFT JOIN pg_depend dx ON dx.objid = c.oid and dx.deptype = 'i' "
! "and dx.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON dx.refobjid = x.oid "
! "WHERE x.oid IS NULL "
! "AND c.relkind in ('%c', '%c', '%c', '%c') "
! "ORDER BY c.oid",
! username_subquery,
! RELKIND_SEQUENCE,
! RELKIND_RELATION, RELKIND_SEQUENCE,
! RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
! }
! else if (g_fout->remoteVersion >= 90000)
{
/*
* Left join to pick up dependency info linking sequences to their
***************
*** 4838,4844 **** getProcLangs(int *numProcLangs)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90000)
{
/* pg_language has a laninline column */
appendPQExpBuffer(query, "SELECT tableoid, oid, "
--- 5091,5112 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! /* pg_language has a laninline column */
! appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, "
! "l.lanname, l.lanpltrusted, l.lanplcallfoid, "
! "l.laninline, l.lanvalidator, l.lanacl, "
! "(%s lanowner) AS lanowner "
! "FROM pg_language l "
! "LEFT JOIN pg_depend d ON d.objid = l.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL and lanispl "
! "ORDER BY oid",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 90000)
{
/* pg_language has a laninline column */
appendPQExpBuffer(query, "SELECT tableoid, oid, "
***************
*** 4992,4998 **** getCasts(int *numCasts)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
--- 5260,5278 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, "
! "c.castsource, c.casttarget, c.castfunc, c.castcontext, "
! "c.castmethod "
! "FROM pg_cast c "
! "LEFT JOIN pg_depend d ON d.objid = c.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL "
! "ORDER BY 3,4");
! }
! else if (g_fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
***************
*** 5586,5595 **** getTSParsers(int *numTSParsers)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, "
! "prsstart::oid, prstoken::oid, "
! "prsend::oid, prsheadline::oid, prslextype::oid "
! "FROM pg_ts_parser");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 5866,5889 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, p.prsname, p.prsnamespace, "
! "p.prsstart::oid, p.prstoken::oid, "
! "p.prsend::oid, p.prsheadline::oid, p.prslextype::oid "
! "FROM pg_ts_parser p "
! "LEFT JOIN pg_depend d ON d.objid = p.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL");
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, "
! "prsstart::oid, prstoken::oid, "
! "prsend::oid, prsheadline::oid, prslextype::oid "
! "FROM pg_ts_parser");
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5668,5678 **** getTSDictionaries(int *numTSDicts)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
! "dictnamespace, (%s dictowner) AS rolname, "
! "dicttemplate, dictinitoption "
! "FROM pg_ts_dict",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 5962,5987 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT tsd.tableoid, tsd.oid, tsd.dictname, "
! "tsd.dictnamespace, (%s dictowner) AS rolname, "
! "tsd.dicttemplate, tsd.dictinitoption "
! "FROM pg_ts_dict tsd "
! "LEFT JOIN pg_depend d ON d.objid = tsd.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
! "dictnamespace, (%s dictowner) AS rolname, "
! "dicttemplate, dictinitoption "
! "FROM pg_ts_dict",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5749,5757 **** getTSTemplates(int *numTSTemplates)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, "
! "tmplnamespace, tmplinit::oid, tmpllexize::oid "
! "FROM pg_ts_template");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6058,6079 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.tmplname, "
! "t.tmplnamespace, t.tmplinit::oid, t.tmpllexize::oid "
! "FROM pg_ts_template t "
! "LEFT JOIN pg_depend d ON d.objid = t.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL");
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, "
! "tmplnamespace, tmplinit::oid, tmpllexize::oid "
! "FROM pg_ts_template");
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5823,5832 **** getTSConfigurations(int *numTSConfigs)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
! "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
! "FROM pg_ts_config",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6145,6168 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.cfgname, "
! "c.cfgnamespace, (%s cfgowner) AS rolname, c.cfgparser "
! "FROM pg_ts_config c "
! "LEFT JOIN pg_depend d ON d.objid = c.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
! "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
! "FROM pg_ts_config",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5899,5911 **** getForeignDataWrappers(int *numForeignDataWrappers)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
! "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
! "FROM pg_foreign_data_wrapper",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6235,6264 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT fdw.tableoid, fdw.oid, fdw.fdwname, "
! "(%s fdwowner) AS rolname, fdw.fdwvalidator::pg_catalog.regproc, fdwacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fdw.fdwoptions)), ', ') AS fdwoptions "
! "FROM pg_foreign_data_wrapper fdw "
! "LEFT JOIN pg_depend d ON d.objid = fdw.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
! "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
! "FROM pg_foreign_data_wrapper",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5983,5996 **** getForeignServers(int *numForeignServers)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
! "(%s srvowner) AS rolname, "
! "srvfdw, srvtype, srvversion, srvacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(srvoptions)), ', ') AS srvoptions "
! "FROM pg_foreign_server",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6336,6367 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT fs.tableoid, fs.oid, fs.srvname, "
! "(%s srvowner) AS rolname, "
! "fs.srvfdw, fs.srvtype, fs.srvversion, fs.srvacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fs.srvoptions)), ', ') AS srvoptions "
! "FROM pg_foreign_server fs "
! "LEFT JOIN pg_depend d ON d.objid = fs.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
! "(%s srvowner) AS rolname, "
! "srvfdw, srvtype, srvversion, srvacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(srvoptions)), ', ') AS srvoptions "
! "FROM pg_foreign_server",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 6467,6472 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6838,6846 ----
{
switch (dobj->objType)
{
+ case DO_EXTENSION:
+ dumpExtension(fout, (ExtensionInfo *) dobj);
+ break;
case DO_NAMESPACE:
dumpNamespace(fout, (NamespaceInfo *) dobj);
break;
***************
*** 6563,6568 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6937,6988 ----
}
/*
+ * dumpExtension
+ * writes out to fout the queries to recreate an extension
+ */
+ static void
+ dumpExtension(Archive *fout, ExtensionInfo *extinfo)
+ {
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ char *qextname;
+
+ /* Skip if not to be dumped */
+ if (!extinfo->dobj.dump || dataOnly)
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+
+ qextname = strdup(fmtId(extinfo->dobj.name));
+
+ appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
+
+ appendPQExpBuffer(q, "CREATE EXTENSION %s WITH NO DATA;\n", qextname);
+
+ ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
+ extinfo->dobj.name,
+ NULL, NULL,
+ "",
+ false, "EXTENSION", SECTION_PRE_DATA,
+ q->data, delq->data, NULL,
+ extinfo->dobj.dependencies, extinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Extension Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "EXTENSION %s", qextname);
+ dumpComment(fout, q->data,
+ NULL, "",
+ extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+
+ free(qextname);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ }
+
+ /*
* dumpNamespace
* writes out to fout the queries to recreate a user-defined namespace
*/
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 88,93 **** typedef struct SimpleStringList
--- 88,94 ----
typedef enum
{
/* When modifying this enum, update priority tables in pg_dump_sort.c! */
+ DO_EXTENSION,
DO_NAMESPACE,
DO_TYPE,
DO_SHELL_TYPE,
***************
*** 132,137 **** typedef struct _dumpableObject
--- 133,146 ----
int allocDeps; /* allocated size of dependencies[] */
} DumpableObject;
+ typedef struct _extensionInfo
+ {
+ DumpableObject dobj;
+ char *extname;
+ char *extversion;
+ char *custom_class;
+ } ExtensionInfo;
+
typedef struct _namespaceInfo
{
DumpableObject dobj;
***************
*** 511,516 **** extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs);
--- 520,526 ----
/*
* version specific routines
*/
+ extern ExtensionInfo *getExtensions(int *numExtensions);
extern NamespaceInfo *getNamespaces(int *numNamespaces);
extern TypeInfo *getTypes(int *numTypes);
extern FuncInfo *getFuncs(int *numFuncs);
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 28,33 **** static const char *modulename = gettext_noop("sorter");
--- 28,34 ----
*/
static const int oldObjectTypePriority[] =
{
+ 1, /* DO_EXTENSION */
1, /* DO_NAMESPACE */
2, /* DO_TYPE */
2, /* DO_SHELL_TYPE */
***************
*** 65,70 **** static const int oldObjectTypePriority[] =
--- 66,72 ----
*/
static const int newObjectTypePriority[] =
{
+ 1, /* DO_EXTENSION */
1, /* DO_NAMESPACE */
3, /* DO_TYPE */
3, /* DO_SHELL_TYPE */
***************
*** 1018,1023 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1020,1030 ----
{
switch (obj->objType)
{
+ case DO_EXTENSION:
+ snprintf(buf, bufsize,
+ "EXTENSION %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
case DO_NAMESPACE:
snprintf(buf, bufsize,
"SCHEMA %s (ID %d OID %u)",
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 488,493 **** exec_command(const char *cmd,
--- 488,496 ----
break;
}
break;
+ case 'x': /* Extensions */
+ success = listExtensions(pattern, show_verbose);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
}
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2735,2740 **** listSchemas(const char *pattern, bool verbose)
--- 2735,2787 ----
return true;
}
+ /*
+ * \dx
+ *
+ * Describes extensions
+ */
+ bool
+ listExtensions(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT x.name AS \"%s\", x.version AS \"%s\", "
+ "x.custom_variable_classes AS \"%s\", "
+ "x.comment as \"%s\" "
+ " FROM pg_catalog.pg_extensions x ",
+ gettext_noop("Name"),
+ gettext_noop("Version"),
+ gettext_noop("Custom Variable Classes"),
+ gettext_noop("Description"));
+
+ if (!verbose)
+ appendPQExpBuffer(&buf, "WHERE installed ");
+
+ processSQLNamePattern(pset.db, &buf, pattern, !verbose, false,
+ NULL, "x.extname", NULL,
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of extensions");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
/*
* \dFp
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
***************
*** 54,59 **** extern bool listTSDictionaries(const char *pattern, bool verbose);
--- 54,62 ----
/* \dFt */
extern bool listTSTemplates(const char *pattern, bool verbose);
+ /* \dx */
+ bool listExtensions(const char *pattern, bool verbose);
+
/* \l */
extern bool listAllDbs(bool verbose);
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 219,224 **** slashUsage(unsigned short int pager)
--- 219,225 ----
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[+] [PATTERN] list roles (users)\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dx [PATTERN] list extensions\n"));
fprintf(output, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 138,143 **** typedef enum ObjectClass
--- 138,144 ----
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
OCLASS_USER_MAPPING, /* pg_user_mapping */
OCLASS_DEFACL, /* pg_default_acl */
+ OCLASS_EXTENSION, /* pg_extension */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
***************
*** 197,202 **** extern long changeDependencyFor(Oid classId, Oid objectId,
--- 198,207 ----
Oid refClassId, Oid oldRefObjectId,
Oid newRefObjectId);
+ extern long changeDependencyTypeFor(Oid objid,
+ Oid refclassId, Oid refobjectId, int refobjsubid,
+ DependencyType expected, DependencyType newtype);
+
extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
extern void markSequenceUnowned(Oid seqId);
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 284,289 **** DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol
--- 284,295 ----
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
#define SecLabelObjectIndexId 3597
+ DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3695, on pg_extension using btree(oid oid_ops));
+ #define ExtensionOidIndexId 3695
+
+ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3697, on pg_extension using btree(extname name_ops));
+ #define ExtensionNameIndexId 3697
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
*** /dev/null
--- b/src/include/catalog/pg_extension.h
***************
*** 0 ****
--- 1,61 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_extension.h
+ * definition of the system "extension" relation (pg_extension)
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_extension.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_EXTENSION_H
+ #define PG_EXTENSION_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_extension definition. cpp turns this into
+ * typedef struct FormData_pg_extension
+ * ----------------
+ */
+ #define ExtensionRelationId 3696
+
+ CATALOG(pg_extension,3696)
+ {
+ NameData extname; /* name of the extension */
+ text extversion; /* version "name" of the extension */
+ text custom_class; /* custom class used by the extension */
+ } FormData_pg_extension;
+
+ /* ----------------
+ * Form_pg_extension corresponds to a pointer to a tuple with
+ * the format of pg_extension relation.
+ * ----------------
+ */
+ typedef FormData_pg_extension *Form_pg_extension;
+
+ /* ----------------
+ * compiler constants for pg_extension
+ * ----------------
+ */
+
+ #define Natts_pg_extension 3
+ #define Anum_pg_extension_extname 1
+ #define Anum_pg_extension_extversion 2
+ #define Anum_pg_extension_custom_class 3
+
+ /* ----------------
+ * initial contents of pg_extension
+ * ----------------
+ */
+
+ /* btree
+ DATA(insert ("PostgreSQL" PG_VERSION _null_));
+ */
+ #endif /* PG_EXTENSION_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
DATA(insert OID = 2622 ( pg_rotate_logfile PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
DESCR("rotate log file");
! DATA(insert OID = 2623 ( pg_stat_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
DESCR("sleep for the specified time in seconds");
DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
DESCR("convert boolean to text");
--- 3386,3401 ----
DATA(insert OID = 2622 ( pg_rotate_logfile PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
DESCR("rotate log file");
! DATA(insert OID = 2623 ( pg_stat_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
DESCR("convert boolean to text");
***************
*** 4850,4855 **** DESCR("fetch the last row value");
--- 4852,4864 ----
DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ ));
DESCR("fetch the Nth row value");
+ /* Extensions */
+ DATA(insert OID = 3995 ( pg_extensions PGNSP PGUID 12 1 100 0 f f f t t s 0 0 2249 "" "{19,25,25,25,16}" "{o,o,o,o,o}" "{name,version,custom_variable_classes,comment,installed}" _null_ pg_extensions _null_ _null_ _null_ ));
+ DESCR("flag an extension's object to be part of pg_dump");
+ DATA(insert OID = 3996 ( pg_extension_flag_dump PGNSP PGUID 12 1 0 0 f f f t f v 1 0 16 "26" _null_ _null_ _null_ _null_ pg_extension_flag_dump _null_ _null_ _null_ ));
+ DESCR("flag an extension's object to be part of pg_dump");
+ DATA(insert OID = 3997 ( pg_extension_with_user_data PGNSP PGUID 12 1 0 0 f f f t f s 0 0 16 "" _null_ _null_ _null_ _null_ pg_extension_with_user_data _null_ _null_ _null_ ));
+ DESCR("return true if the extension install script should create user data");
/*
* Symbolic values for provolatile column: these indicate whether the result
*** a/src/include/catalog/toasting.h
--- b/src/include/catalog/toasting.h
***************
*** 43,48 **** extern void BootstrapToastTable(char *relName,
--- 43,49 ----
DECLARE_TOAST(pg_attrdef, 2830, 2831);
DECLARE_TOAST(pg_constraint, 2832, 2833);
DECLARE_TOAST(pg_description, 2834, 2835);
+ DECLARE_TOAST(pg_extension, 3698, 3699);
DECLARE_TOAST(pg_proc, 2836, 2837);
DECLARE_TOAST(pg_rewrite, 2838, 2839);
DECLARE_TOAST(pg_seclabel, 3598, 3599);
*** /dev/null
--- b/src/include/commands/extension.h
***************
*** 0 ****
--- 1,68 ----
+ /*-------------------------------------------------------------------------
+ *
+ * extension.h
+ * Extension management commands (create/drop extension).
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/extension.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef EXTENSION_H
+ #define EXTENSION_H
+
+ #include "catalog/objectaddress.h"
+ #include "utils/genfile.h"
+
+
+ /*
+ * create_extension is only set when running a CREATE EXTENSION command, it
+ * allows to register the (INTERNAL) dependencies between the pg_extension
+ * row and the SQL objects created by its installation script.
+ *
+ * For that to work out, all CREATE commands have been modified so that they
+ * will inquire about create_extension and call recordDependencyOn() when
+ * it's set.
+ */
+ extern ObjectAddress *create_extension;
+ bool create_extension_with_user_data;
+
+ typedef struct ExtensionControlFile
+ {
+ char *name; /* name of the extension */
+ char *version; /* version "name" of the extension */
+ char *custom_class; /* custom class used by the extension */
+ char *comment; /* the control file can also contain a comment */
+ } ExtensionControlFile;
+
+ typedef struct extension_fctx
+ {
+ directory_fctx dir;
+ int num_installed;
+ ExtensionControlFile *installed;
+ } extension_fctx;
+
+ char *get_extension_control_basepath(void);
+ char *get_extension_install_filename(const char *extname);
+ char *get_extension_control_filename(const char *extname);
+ char *get_extension_control_fullname(const char *filename);
+ char * extension_parse_control_line(const char *content, ExtensionControlFile *control);
+ bool add_extension_custom_variable_classes(const char *extname,
+ const char *ext_custom_classes,
+ bool verbose);
+ ExtensionControlFile *read_extension_control_file(const char *filename,
+ bool init_gucs);
+
+ extern void ExtensionSetCVC(void);
+ extern void CreateExtension(CreateExtensionStmt *stmt);
+ extern void DropExtension(DropExtensionStmt *stmt);
+
+ Oid get_extension_oid(const char *extname, bool missing_ok);
+ char * get_extension_name(Oid ext_oid);
+ char * RemoveExtensionById(Oid extId);
+
+
+ #endif /* EXTENSION_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 351,356 **** typedef enum NodeTag
--- 351,358 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
+ T_CreateExtensionStmt,
+ T_DropExtensionStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1056,1061 **** typedef enum ObjectType
--- 1056,1062 ----
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DOMAIN,
+ OBJECT_EXTENSION,
OBJECT_FDW,
OBJECT_FOREIGN_SERVER,
OBJECT_FUNCTION,
***************
*** 1482,1487 **** typedef struct Constraint
--- 1483,1508 ----
} Constraint;
/* ----------------------
+ * Create/Drop Extension Statements
+ * ----------------------
+ */
+
+ typedef struct CreateExtensionStmt
+ {
+ NodeTag type;
+ char *extname;
+ bool user_data;
+ } CreateExtensionStmt;
+
+ typedef struct DropExtensionStmt
+ {
+ NodeTag type;
+ char *extname;
+ bool missing_ok; /* skip error if missing? */
+ DropBehavior behavior; /* drop behavior - cascade/restrict */
+ } DropExtensionStmt;
+
+ /* ----------------------
* Create/Drop Table Space Statements
* ----------------------
*/
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 150,155 **** PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD)
--- 150,156 ----
PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD)
PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD)
+ PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD)
PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD)
PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD)
PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD)
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
extern Datum pg_stat_file(PG_FUNCTION_ARGS);
extern Datum pg_read_file(PG_FUNCTION_ARGS);
extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
/* misc.c */
extern Datum current_database(PG_FUNCTION_ARGS);
***************
*** 1054,1057 **** extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
--- 1055,1063 ----
/* utils/mmgr/portalmem.c */
extern Datum pg_cursor(PG_FUNCTION_ARGS);
+ /* commands/extension.c */
+ extern Datum pg_extensions(PG_FUNCTION_ARGS);
+ extern Datum pg_extension_with_user_data(PG_FUNCTION_ARGS);
+ extern Datum pg_extension_flag_dump(PG_FUNCTION_ARGS);
+
#endif /* BUILTINS_H */
*** /dev/null
--- b/src/include/utils/cfparser.h
***************
*** 0 ****
--- 1,18 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cfparser.h
+ * Function for parsing RecoveryCommandFile lines
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/cfparser.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef CFPARSER_H
+ #define CFPARSER_H
+
+ bool cfParseOneLine(char *cmdline, char **key_p, char **value_p);
+
+ #endif /* CFPARSER_H */
*** /dev/null
--- b/src/include/utils/genfile.h
***************
*** 0 ****
--- 1,22 ----
+ /*--------------------------------------------------------------------
+ * genfile.h
+ *
+ * External declarations pertaining to backend/utils/misc/genfile.c
+ *
+ * Copyright (c) 2000-2010, PostgreSQL Global Development Group
+ *
+ * src/include/utils/genfile.h
+ *--------------------------------------------------------------------
+ */
+ #ifndef GENFILE_H
+ #define GENFILE_H
+
+ #include
+
+ typedef struct
+ {
+ char *location;
+ DIR *dirdesc;
+ } directory_fctx;
+
+ #endif /* GENFILE_H */
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 85,90 **** typedef enum
--- 85,91 ----
PGC_S_DEFAULT, /* wired-in default */
PGC_S_ENV_VAR, /* postmaster environment variable */
PGC_S_FILE, /* postgresql.conf */
+ PGC_S_EXTENSION, /* extension control file */
PGC_S_ARGV, /* postmaster command line */
PGC_S_DATABASE, /* per-database setting */
PGC_S_USER, /* per-user setting */
***************
*** 315,318 **** extern const char *assign_search_path(const char *newval,
--- 316,329 ----
extern bool assign_xlog_sync_method(int newval,
bool doit, GucSource source);
+ /*
+ * The following function is in guc.c but also used from extension.c
+ */
+ char *custom_variable_classes;
+ extern const char *assign_custom_variable_classes(const char *newval, bool doit,
+ GucSource source);
+ bool is_custom_class(const char *name,
+ int dotPos, const char *custom_var_classes);
+ struct config_generic *add_placeholder_variable(const char *name, int elevel, bool valid);
+
#endif /* GUC_H */
*** a/src/makefiles/pgxs.mk
--- b/src/makefiles/pgxs.mk
***************
*** 93,102 **** include $(top_srcdir)/src/Makefile.shlib
all: all-lib
endif # MODULE_big
! install: all installdirs
! ifneq (,$(DATA)$(DATA_built))
! @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
done
--- 93,126 ----
all: all-lib
endif # MODULE_big
+ # create extension support
+ ifndef CONTROL
+ ifndef EXTENSION
+ ifdef DATA_built
+ EXTENSION = $(basename $(notdir $(firstword $(DATA_built))))
+ else ifdef DATA
+ EXTENSION = $(basename $(notdir $(firstword $(DATA))))
+ endif # DATA
+ endif # EXTENSION
+ ifndef EXTVERSION
+ EXTVERSION = $(MAJORVERSION)
+ endif
+ ifdef EXTENSION
+ CONTROL = $(EXTENSION).control
+ endif
+ endif
! $(CONTROL):
! # create .control to keep track that we created the control file
! test ! -f $@ && touch .control
! test -f .control && (echo "name = '$(EXTENSION)'"; echo "version = '$(EXTVERSION)'") > $@
! ifdef EXTCOMMENT
! test -f .control && echo "comment = '$(EXTCOMMENT)'" >> $@
! endif
!
! install: all installdirs $(CONTROL)
! ifneq (,$(DATA)$(DATA_built)$(CONTROL))
! @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) $(CONTROL); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
done
***************
*** 221,226 **** ifeq ($(PORTNAME), win)
--- 245,251 ----
rm -f regress.def
endif
endif # REGRESS
+ if [ -f .control ]; then rm -f .control $(CONTROL); fi
ifdef MODULE_big
clean: clean-lib