*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 2767,2772 ****
--- 2767,2827 ----
+
+ 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
+
+
+
+ relocatable
+ bool
+
+ An extension is relocatable> when it's possible to
+ alter the schema in which all its objects are installed.
+
+
+
+ extversion
+ text
+
+ Version string of the extension, as provided by its author.
+
+
+
+
+
+
+
+
pg_foreign_data_wrapper
***************
*** 6083,6088 ****
--- 6138,6148 ----
+ pg_available_extensions
+ available and installed extensions
+
+
+ pg_groupgroups of database users
***************
*** 6292,6297 ****
--- 6352,6428 ----
+
+ pg_available_extensions
+
+
+ pg_available_extensions
+
+
+
+ The pg_available_extensions view lists the
+ extensions that are currently available and installed.
+
+
+
+ pg_available_extensions> Columns
+
+
+
+
+ Name
+ Type
+ Description
+
+
+
+
+
+ name
+ text
+ The name of the extensions
+
+
+
+ version
+ text
+ The version string of the extension
+
+
+
+ relocatable
+ bool
+
+ An extension is relocatable> when it's possible to
+ alter the schema in which all its objects are installed.
+
+
+
+ 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_available_extensions view is read only.
+
+
+
+
pg_group
*** a/doc/src/sgml/extend.sgml
--- b/doc/src/sgml/extend.sgml
***************
*** 336,339 ****
--- 336,545 ----
+
+ Putting it all together: create extension
+
+
+ extension
+
+
+
+ The way to put together a coherent set of custom SQL> objects
+ is to create an extension>, which is the name of a
+ packaged script>. To wrap an extension you need
+ the SQL> script that defines its objects (functions,
+ operators, types, etc) and a control> file that is used to
+ register it. The script itself might also require than a shared
+ library> is installed on your system, too.
+
+
+ The goal of using extensions is so that pg_dump> knows
+ not to dump all the object definitions into your backups, but rather
+ issue a single command.
+
+
+
+ Extension files
+
+
+ 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 keys described below.
+
+
+
+
+ Once registered, the extension is loaded by means of executing the
+ commands from
+ the extension.sql
+ file.
+
+
+
+
+
+ control
+
+
+
+ The extension control file can contain the following
+ keys:
+
+
+
+ comment
+
+
+ The comment (free form) about the provided extension, that's shown
+ to users in the extension listings. The comment is added by means of
+ the command .
+
+
+
+
+ version
+
+
+ The version string (free form) of the provided extension.
+
+
+
+
+ script
+
+
+ The filename of the SQL> script to load when creating the
+ extension. Defaults to the same name sans extension as the control
+ filename, with the .sql extension.
+
+
+
+
+ encoding
+
+
+ The client_encoding to set while loading the
+ script file.
+
+
+
+
+ relocatable
+
+
+ An extension is relocatable> when it installs all its
+ object into the first schema of the search_path>. As an
+ extension's script author, you have to make sure you're not schema
+ qualifying any object that you create. When an extension
+ is relocatable> then users will be able to
+ use .
+
+
+ An extension that needs to know where some of its objects are
+ installed is not relocatable>. As the user won't be able
+ to change the schema where the extensions gets installed, he's given
+ the possibility to specify the schema at installation time. The
+ extension installation script is then required to use the
+ @extschema@> placeholer as the schema to work with. That
+ will typically mean the script's first line is:
+
+ SET search_path TO @extschema@;
+
+
+
+
+
+
+
+
+ Extension functions
+
+
+ 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 complete example
+
+
+ Let's walk trough a complete example of an SQL> only
+ extension, a two-value composite type that can store any type of value
+ in its slots, which are named "k" and "v".
+
+
+ It all begins with the script pair.sql>, here it is:
+
+ CREATE TYPE pair AS ( k text, v text );
+
+ CREATE OR REPLACE FUNCTION pair(anyelement, text)
+ RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+
+ CREATE OR REPLACE FUNCTION pair(text, anyelement)
+ RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+
+ CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
+ RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+
+ CREATE OR REPLACE FUNCTION pair(text, text)
+ RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';
+
+ CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair);
+ CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair);
+ CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair);
+ CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
+
+
+
+ You then need to provide a pair.control> file with the
+ following content:
+
+ # pair extension
+ comment = 'A key/value pair data type'
+ version = '0.1.2'
+ relocatable = true
+
+
+
+ Finaly, you need to provide a Makefile> as such:
+
+ DATA = pair.sql
+ EXTENSION = pair
+
+ PG_CONFIG ?= pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+
+
+
+ Using the command make install> will then produce
+ the pair.control> file for you, then install the files in
+ the right place for the PostgreSQL> cluster found
+ by pg_config>.
+
+
+ It's then possible for you to use
+ the command.
+
+
+
+
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 14524,14529 **** SELECT (pg_stat_file('filename')).modification;
--- 14524,14628 ----
+ 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_objects(name> name>, OUT class> regclass>, OUT classid> oid>, OUT objid> oid>, OUT objdesc> text>)
+ setof record
+ List the objects that are part of an extension.
+
+
+
+
+
+
+
+ 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 USER
+ 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;
+ $$;
+
+
+
+
+ pg_extension_objects
+
+
+ pg_extension_objects> list all objects created by the given
+ extension, with their human friendly description. You can use this
+ function to discover what's in an extension, or as an extension
+ author to discover what oid has been assigned to
+ some object of your extension, in your upgrade script e.g.
+
+
+
The functions shown in manage
advisory locks. For details about proper use of these functions, see
.
***************
*** 14545,14550 **** SELECT (pg_stat_file('filename')).modification;
--- 14644,14650 ----
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
***************
*** 11,16 **** Complete list of usable sgml source files in this directory.
--- 11,17 ----
+
***************
*** 50,55 **** Complete list of usable sgml source files in this directory.
--- 51,57 ----
+
***************
*** 86,91 **** Complete list of usable sgml source files in this directory.
--- 88,94 ----
+
*** /dev/null
--- b/doc/src/sgml/ref/alter_extension.sgml
***************
*** 0 ****
--- 1,90 ----
+
+
+
+
+ ALTER EXTENSION
+ 7
+ SQL - Language Statements
+
+
+
+ ALTER EXTENSION
+
+ change the definition of an extension
+
+
+
+
+ ALTER EXTENSION
+
+
+
+
+ ALTER EXTENSION name SET SCHEMA new_schema
+
+
+
+
+ Description
+
+
+ ALTER EXTENSION changes the definition of an existing extension.
+ There are only one subforms:
+
+
+
+ SET SCHEMA
+
+
+ This form moves the extension objects into another schema. The
+ extension has to be relocatable> for this command to
+ succeed. See for details.
+
+
+
+
+
+
+
+
+ Parameters
+
+
+
+ new_schema
+
+
+ The new schema for the type.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ To change the schema of the extension hstore
+ to utils:
+
+ ALTER EXTENSION hstore SET SCHEMA utils;
+
+
+
+
+
+ See Also
+
+
+
+
+ Extension Related Functions
+ Additional Supplied Modules
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/create_extension.sgml
***************
*** 0 ****
--- 1,136 ----
+
+
+
+
+ CREATE EXTENSION
+ 7
+ SQL - Language Statements
+
+
+
+ CREATE EXTENSION
+ define and install a new extension
+
+
+
+ CREATE EXTENSION
+
+
+
+
+ CREATE EXTENSION extension_name
+ [ [ WITH ] [ [ NO ] USER DATA ]
+ [ SCHEMA [=] schema ]
+
+
+
+
+ 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.
+
+
+
+
+
+ NO USER DATA
+
+
+ The goal of bundling an SQL> script as an extension is
+ for to issue a single command per
+ extension rather than a command per object installed by means of
+ running the script. That allows to maintain the script separately
+ from the database.
+
+
+ This option is systematically 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. It does so by having the
+ function pg_extension_with_user_data>
+ returns False> when called from the extension's script,
+ which is then supposed to avoid> creating the objects
+ where users are to edit the data.
+
+
+
+
+ schema
+
+
+ Name of the schema where to install the extension
+ objects, given that the extension's script supports the option. To
+ support the option, the script must use the
+ placeholder @extschema@ rather than hardcode some
+ schema.
+
+
+
+
+
+
+
+ Examples
+
+
+ Install the hstore extension:
+
+ CREATE EXTENSION hstore;
+
+
+
+
+
+ Compatibility
+
+
+ CREATE EXTENSION is a PostgreSQL>
+ extension.
+
+
+
+
+ See Also
+
+
+
+
+ Extension Related Functions
+ Additional Supplied Modules
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/drop_extension.sgml
***************
*** 0 ****
--- 1,117 ----
+
+
+
+
+ DROP EXTENSION
+ 7
+ SQL - Language Statements
+
+
+
+ DROP EXTENSION
+ remove an extension
+
+
+
+ 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 extension.
+
+
+
+
+
+ RESTRICT
+
+
+ Refuse to drop the extension 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
+
+
+
+
+ Additional Supplied Modules
+
+
+
+
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 1358,1363 **** testdb=>
--- 1358,1377 ----
+ \dx[+] [ pattern ]
+
+
+ Lists installed
+ extensions. If pattern
+ is specified, only those extensions whose names match the pattern
+ are listed. If the form \dx+ is used, available
+ extensions are added to the list of installed extension.
+
+
+
+
+
+ \edit> (or \e>) filename> line_number>
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 39,44 ****
--- 39,45 ----
&alterDatabase;
&alterDefaultPrivileges;
&alterDomain;
+ &alterExtension;
&alterForeignDataWrapper;
&alterForeignTable;
&alterFunction;
***************
*** 78,83 ****
--- 79,85 ----
&createConversion;
&createDatabase;
&createDomain;
+ &createExtension;
&createForeignDataWrapper;
&createForeignTable;
&createFunction;
***************
*** 114,119 ****
--- 116,122 ----
&dropConversion;
&dropDatabase;
&dropDomain;
+ &dropExtension;
&dropForeignDataWrapper;
&dropForeignTable;
&dropFunction;
*** a/doc/src/sgml/xfunc.sgml
--- b/doc/src/sgml/xfunc.sgml
***************
*** 2425,2430 **** concat_text(PG_FUNCTION_ARGS)
--- 2425,2431 ----
MODULES = isbn_issn
DATA_built = isbn_issn.sql
DOCS = README.isbn_issn
+ EXTENSION = isbn_issn
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
***************
*** 2504,2509 **** include $(PGXS)
--- 2505,2525 ----
+ EXTENSION
+
+
+ extension control files to install
+ into prefix/share/$MODULEDIR,
+ derived from
+ the extension.control
+ file to add in your sources. EXTENSION can also
+ be a list, you then have to provide a .control
+ file per extension in this list.
+
+
+
+
+ DATA_TSEARCH
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 40,45 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
--- 40,46 ----
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.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_foreign_table.h"
***************
*** 56,61 ****
--- 57,63 ----
#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"
***************
*** 77,111 ****
#include "utils/tqual.h"
- /*
- * Deletion processing requires additional state for each ObjectAddress that
- * it's planning to delete. For simplicity and code-sharing we make the
- * ObjectAddresses code support arrays with or without this extra state.
- */
- typedef struct
- {
- int flags; /* bitmask, see bit definitions below */
- ObjectAddress dependee; /* object whose deletion forced this one */
- } ObjectAddressExtra;
-
- /* ObjectAddressExtra flag bits */
- #define DEPFLAG_ORIGINAL 0x0001 /* an original deletion target */
- #define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */
- #define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */
- #define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */
-
-
- /* expansible list of ObjectAddresses */
- struct ObjectAddresses
- {
- ObjectAddress *refs; /* => palloc'd array */
- ObjectAddressExtra *extras; /* => palloc'd array, or NULL if not used */
- int numrefs; /* current number of references */
- int maxrefs; /* current size of palloc'd array(s) */
- };
-
- /* typedef ObjectAddresses appears in dependency.h */
-
/* threaded list of ObjectAddresses, for recursion detection */
typedef struct ObjectAddressStack
{
--- 79,84 ----
***************
*** 154,159 **** static const Oid object_classes[MAX_OCLASS] = {
--- 127,133 ----
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
ForeignTableRelationId, /* OCLASS_FOREIGN_TABLE */
+ ExtensionRelationId, /* OCLASS_EXTENSION */
DefaultAclRelationId /* OCLASS_DEFACL */
};
***************
*** 551,560 **** 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
--- 525,533 ----
/* 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
***************
*** 598,604 **** 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
--- 571,584 ----
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
***************
*** 1150,1155 **** doDeletion(const ObjectAddress *object)
--- 1130,1139 ----
RemoveUserMappingById(object->objectId);
break;
+ case OCLASS_EXTENSION:
+ RemoveExtensionById(object->objectId);
+ break;
+
case OCLASS_DEFACL:
RemoveDefaultACLById(object->objectId);
break;
***************
*** 2080,2085 **** getObjectClass(const ObjectAddress *object)
--- 2064,2073 ----
case DefaultAclRelationId:
return OCLASS_DEFACL;
+
+ case ExtensionRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_EXTENSION;
}
/* shouldn't get here */
***************
*** 2561,2566 **** getObjectDescription(const ObjectAddress *object)
--- 2549,2566 ----
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;
***************
*** 2835,2837 **** pg_describe_object(PG_FUNCTION_ARGS)
--- 2835,2873 ----
description = getObjectDescription(&address);
PG_RETURN_TEXT_P(cstring_to_text(description));
}
+
+ /*
+ * Base function to list an extension's objects, used in the
+ * pg_extension_objects SRF.
+ *
+ * Caller is responsible for freeing the resulting list with
+ * free_object_addresses.
+ */
+ ObjectAddresses *
+ listDependentObjects(ObjectAddress *object)
+ {
+ Relation depRel;
+ ObjectAddresses *targetObjects;
+
+ /*
+ * We save some cycles by opening pg_depend just once and passing the
+ * Relation pointer down to all the recursive deletion steps.
+ */
+ depRel = heap_open(DependRelationId, RowExclusiveLock);
+
+ /*
+ * Construct a list of dependent objects (ie, the given object plus
+ * everything directly or indirectly dependent on it).
+ */
+ targetObjects = new_object_addresses();
+
+ findDependentObjects(object,
+ DEPFLAG_ORIGINAL,
+ NULL, /* empty stack */
+ targetObjects,
+ NULL, /* no pendingObjects */
+ depRel);
+
+ heap_close(depRel, RowExclusiveLock);
+ return targetObjects;
+ }
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 51,56 ****
--- 51,57 ----
#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"
***************
*** 920,925 **** heap_create_with_catalog(const char *relname,
--- 921,927 ----
Oid old_type_oid;
Oid new_type_oid;
Oid new_array_oid = InvalidOid;
+ ObjectAddress myself;
pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock);
***************
*** 1160,1175 **** heap_create_with_catalog(const char *relname,
* Also, skip this in bootstrap mode, since we don't make dependencies
* while bootstrapping.
*/
if (relkind != RELKIND_COMPOSITE_TYPE &&
relkind != RELKIND_TOASTVALUE &&
!IsBootstrapProcessingMode())
{
! ObjectAddress myself,
! referenced;
- myself.classId = RelationRelationId;
- myself.objectId = relid;
- myself.objectSubId = 0;
referenced.classId = NamespaceRelationId;
referenced.objectId = relnamespace;
referenced.objectSubId = 0;
--- 1162,1177 ----
* Also, skip this in bootstrap mode, since we don't make dependencies
* while bootstrapping.
*/
+ myself.classId = RelationRelationId;
+ myself.objectId = relid;
+ myself.objectSubId = 0;
+
if (relkind != RELKIND_COMPOSITE_TYPE &&
relkind != RELKIND_TOASTVALUE &&
!IsBootstrapProcessingMode())
{
! ObjectAddress referenced;
referenced.classId = NamespaceRelationId;
referenced.objectId = relnamespace;
referenced.objectSubId = 0;
***************
*** 1202,1207 **** heap_create_with_catalog(const char *relname,
--- 1204,1220 ----
InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
/*
+ * Record a dependency to the extension we're part of if any. We could
+ * bypass that step but it simplifies pg_dump queries a lot to have the
+ * direct dependency recorded here, as embedded recursive SQL ain't that
+ * easy to maintain: WHERE NOT EXISTS(WITH RECURSIVE ...).
+ */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
+ /*
* Store any supplied constraints and defaults.
*
* NB: this may do a CommandCounterIncrement and rebuild the relcache
*** 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"
***************
*** 46,51 ****
--- 47,53 ----
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+ #include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
***************
*** 129,134 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 131,137 ----
address = get_object_address_relobject(objtype, objname, &relation);
break;
case OBJECT_DATABASE:
+ case OBJECT_EXTENSION:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
case OBJECT_SCHEMA:
***************
*** 267,272 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 270,278 ----
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;
***************
*** 299,304 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 305,315 ----
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);
***************
*** 643,648 **** object_exists(ObjectAddress address)
--- 654,662 ----
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
***************
*** 23,28 ****
--- 23,29 ----
#include "catalog/pg_proc.h"
#include "catalog/pg_proc_fn.h"
#include "catalog/pg_type.h"
+ #include "commands/extension.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
***************
*** 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)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/catalog/pg_conversion.c
--- b/src/backend/catalog/pg_conversion.c
***************
*** 23,28 ****
--- 23,29 ----
#include "catalog/pg_conversion_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
+ #include "commands/extension.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/acl.h"
***************
*** 132,137 **** ConversionCreate(const char *conname, Oid connamespace,
--- 133,144 ----
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner);
+ /* Depends on currently installed extension, if any */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new conversion */
InvokeObjectAccessHook(OAT_POST_CREATE,
ConversionRelationId, HeapTupleGetOid(tup), 0);
*** a/src/backend/catalog/pg_depend.c
--- b/src/backend/catalog/pg_depend.c
***************
*** 20,25 ****
--- 20,27 ----
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
+ #include "catalog/pg_namespace.h"
#include "miscadmin.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
***************
*** 265,270 **** changeDependencyFor(Oid classId, Oid objectId,
--- 267,334 ----
}
/*
+ * 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);
+ CatalogUpdateIndexes(depRel, tup);
+
+ heap_freetuple(tup);
+ }
+ }
+
+ systable_endscan(scan);
+ heap_close(depRel, RowExclusiveLock);
+
+ return count;
+ }
+
+
+ /*
* isObjectPinned()
*
* Test if an object is required for basic database functionality.
***************
*** 588,590 **** get_index_constraint(Oid indexId)
--- 652,706 ----
return constraintId;
}
+
+ /*
+ * get_extension_namespace
+ * Given the OID of an extension, return the OID of the schema it
+ * depends on, or InvalidOid when not found
+ */
+ Oid
+ get_extension_namespace(Oid extensionId)
+ {
+ Oid nspId = InvalidOid;
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ /* Search the dependency table for the index */
+ depRel = heap_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(ExtensionRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(extensionId));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(0));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ SnapshotNow, 3, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (deprec->refclassid == NamespaceRelationId &&
+ deprec->refobjsubid == 0 &&
+ deprec->deptype == DEPENDENCY_NORMAL)
+ {
+ nspId = deprec->refobjid;
+ break;
+ }
+ }
+
+ systable_endscan(scan);
+ heap_close(depRel, AccessShareLock);
+
+ return nspId;
+ }
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 19,24 ****
--- 19,25 ----
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
+ #include "commands/extension.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 76,81 **** NamespaceCreate(const char *nspName, Oid ownerId)
--- 77,94 ----
/* Record dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+ /* Record dependency on extension, if we're in a CREATE EXTENSION */
+ if (create_extension)
+ {
+ ObjectAddress myself;
+
+ myself.classId = NamespaceRelationId;
+ myself.objectId = nspoid;
+ myself.objectSubId = 0;
+
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new schema */
InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
*** a/src/backend/catalog/pg_operator.c
--- b/src/backend/catalog/pg_operator.c
***************
*** 27,32 ****
--- 27,33 ----
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+ #include "commands/extension.h"
#include "miscadmin.h"
#include "parser/parse_oper.h"
#include "utils/acl.h"
***************
*** 855,858 **** makeOperatorDependencies(HeapTuple tuple)
--- 856,865 ----
/* Dependency on owner */
recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
oper->oprowner);
+
+ /* Dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
}
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 24,29 ****
--- 24,30 ----
#include "catalog/pg_proc.h"
#include "catalog/pg_proc_fn.h"
#include "catalog/pg_type.h"
+ #include "commands/extension.h"
#include "executor/functions.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
***************
*** 615,620 **** ProcedureCreate(const char *procedureName,
--- 616,627 ----
nnewmembers, newmembers);
}
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
heap_freetuple(tup);
/* Post creation hook for new function */
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 23,28 ****
--- 23,29 ----
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
+ #include "commands/extension.h"
#include "commands/typecmds.h"
#include "miscadmin.h"
#include "parser/scansup.h"
***************
*** 33,38 ****
--- 34,40 ----
#include "utils/rel.h"
#include "utils/syscache.h"
+
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
/* ----------------------------------------------------------------
***************
*** 632,637 **** GenerateTypeDependencies(Oid typeNamespace,
--- 634,645 ----
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 153,158 **** CREATE VIEW pg_locks AS
--- 153,161 ----
CREATE VIEW pg_cursors AS
SELECT * FROM pg_cursor() AS C;
+ CREATE VIEW pg_available_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/alter.c
--- b/src/backend/commands/alter.c
***************
*** 23,28 ****
--- 23,29 ----
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+ #include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/tablecmds.h"
***************
*** 188,193 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 189,198 ----
AlterConversionNamespace(stmt->object, stmt->newschema);
break;
+ case OBJECT_EXTENSION:
+ AlterExtensionNamespace(stmt->object, stmt->newschema);
+ break;
+
case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false,
stmt->newschema);
***************
*** 252,259 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
*
* Relation must already by open, it's the responsibility of the caller to
* close it.
*/
! void
AlterObjectNamespace(Relation rel, int cacheId,
Oid classId, Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
--- 257,268 ----
*
* Relation must already by open, it's the responsibility of the caller to
* close it.
+ *
+ * This function returns the old namespace OID so that at ALTER EXTENSION
+ * SET SCHEMA time we are able to ERROR out when we see more than one schema
+ * being used.
*/
! Oid
AlterObjectNamespace(Relation rel, int cacheId,
Oid classId, Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
***************
*** 336,343 **** AlterObjectNamespace(Relation rel, int cacheId,
--- 345,457 ----
/* update dependencies to point to the new schema */
changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid);
+
+ return oldNspOid;
}
+ /*
+ * Do the SET SCHEMA depending on the object class.
+ *
+ * We only consider objects that have a namespace and that can exist
+ * without depending on another object (like a table) which will
+ * have its dependencies follow the SET SCHEMA operation.
+ *
+ * This function returns the old namespace OID so that at ALTER EXTENSION
+ * SET SCHEMA time we are able to ERROR out when we see more than one schema
+ * being used.
+ */
+ Oid
+ AlterObjectNamespace_internal(ObjectAddress *thisobj, Oid nspOid)
+ {
+ Oid oldNspOid = InvalidOid;
+
+ switch (getObjectClass(thisobj))
+ {
+ case OCLASS_CLASS:
+ {
+ Relation classRel;
+ Relation rel = relation_open(thisobj->objectId, RowExclusiveLock);
+
+ switch (rel->rd_rel->relkind)
+ {
+ case RELKIND_COMPOSITE_TYPE:
+ /*
+ * just skip the pg_class entry, we have a pg_type
+ * entry too
+ */
+ break;
+
+ default:
+ classRel = heap_open(RelationRelationId, RowExclusiveLock);
+ oldNspOid =
+ AlterRelationNamespaceInternal(
+ classRel,
+ RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ nspOid,
+ true);
+ heap_close(classRel, RowExclusiveLock);
+ break;
+ }
+ relation_close(rel, RowExclusiveLock);
+ break;
+ }
+
+ case OCLASS_PROC:
+ oldNspOid = AlterFunctionNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_TYPE:
+ {
+ /* don't allow direct alteration of array types, skip */
+ Oid elemOid = get_element_type(thisobj->objectId);
+ if (OidIsValid(elemOid)
+ && get_array_type(elemOid) == thisobj->objectId)
+ break;
+
+ oldNspOid = AlterTypeNamespace_oid(thisobj->objectId, nspOid);
+ break;
+ }
+
+ case OCLASS_CONVERSION:
+ oldNspOid = AlterConversionNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_OPERATOR:
+ oldNspOid = AlterOperatorNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_OPCLASS:
+ oldNspOid = AlterOpClassNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_OPFAMILY:
+ oldNspOid = AlterOpFamilyNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_TSPARSER:
+ oldNspOid = AlterTSParserNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_TSDICT:
+ oldNspOid =
+ AlterTSDictionaryNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_TSTEMPLATE:
+ oldNspOid = AlterTSTemplateNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ case OCLASS_TSCONFIG:
+ oldNspOid =
+ AlterTSConfigurationNamespace_oid(thisobj->objectId, nspOid);
+ break;
+
+ default:
+ break;
+ }
+ return oldNspOid;
+ }
/*
* Executes an ALTER OBJECT / OWNER TO statement. Based on the object
*** a/src/backend/commands/comment.c
--- b/src/backend/commands/comment.c
***************
*** 143,148 **** CommentObject(CommentStmt *stmt)
--- 143,154 ----
(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,
*** a/src/backend/commands/conversioncmds.c
--- b/src/backend/commands/conversioncmds.c
***************
*** 32,38 ****
#include "utils/rel.h"
#include "utils/syscache.h"
! static void AlterConversionOwner_internal(Relation rel, Oid conversionOid,
Oid newOwnerId);
/*
--- 32,38 ----
#include "utils/rel.h"
#include "utils/syscache.h"
! static void AlterConversionOwner_internal(Relation rel, Oid convOid,
Oid newOwnerId);
/*
***************
*** 354,356 **** AlterConversionNamespace(List *name, const char *newschema)
--- 354,379 ----
heap_close(rel, NoLock);
}
+
+ /*
+ * Change conversion schema, by oid
+ */
+ Oid
+ AlterConversionNamespace_oid(Oid convOid, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(ConversionRelationId, RowExclusiveLock);
+
+ oldNspOid = AlterObjectNamespace(rel, CONVOID,
+ ConversionRelationId, convOid, newNspOid,
+ Anum_pg_conversion_conname,
+ Anum_pg_conversion_connamespace,
+ Anum_pg_conversion_conowner,
+ ACL_KIND_CONVERSION,
+ false);
+
+ heap_close(rel, NoLock);
+ return oldNspOid;
+ }
*** /dev/null
--- b/src/backend/commands/extension.c
***************
*** 0 ****
--- 1,1304 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 "catalog/dependency.h"
+ #include "catalog/indexing.h"
+ #include "catalog/namespace.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_type.h"
+ #include "commands/alter.h"
+ #include "commands/comment.h"
+ #include "commands/extension.h"
+ #include "executor/spi.h"
+ #include "funcapi.h"
+ #include "mb/pg_wchar.h"
+ #include "miscadmin.h"
+ #include "storage/fd.h"
+ #include "utils/array.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/genfile.h"
+ #include "utils/guc.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+
+
+ typedef struct extension_fctx
+ {
+ directory_fctx dir;
+ ExtensionList *installed;
+ } extension_fctx;
+
+ /*
+ * See commands/extension.h for details.
+ */
+ ObjectAddress CreateExtensionAddress;
+ bool create_extension = false;
+ bool create_extension_with_user_data = true;
+
+ /*
+ * Utility functions to handle extension related paths
+ */
+ static bool
+ filename_extension_control_p(const char *filename)
+ {
+ return ! ( strcmp(filename, ".") == 0
+ || strcmp(filename, "..") == 0
+ || strrchr(filename, '.') == NULL
+ || strcmp(strrchr(filename, '.'), ".control") != 0 );
+ }
+
+ /*
+ * Given the 'name.control' filename, return the extension's name:
+ *
+ * `basename $filename .control`
+ */
+ static char *
+ get_extension_name_from_control_filename(const char *filename)
+ {
+ if (filename_extension_control_p(filename))
+ {
+ char *extname;
+ char *slash = strrchr(filename, '/');
+ char *dot = strrchr(filename, '.');
+
+ if (slash == NULL)
+ /* relative filename... */
+ slash = (char *)filename;
+ else
+ /* the file ends with .control and has a / before, that's safe */
+ slash++;
+
+ extname = palloc(MAXPGPATH);
+ memset(extname, '\0', MAXPGPATH);
+ strncpy(extname, slash, dot - slash);
+ return extname;
+ }
+ else
+ return NULL;
+ }
+
+ static 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;
+ }
+
+ static 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.sql", return the full path to
+ * the script file. When given an absolute name, just return it.
+ */
+ static char *
+ get_extension_absolute_path(const char *filename)
+ {
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ if (is_absolute_path(filename))
+ return (char *)filename;
+
+ 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().
+ */
+ static ExtensionControlFile *
+ parse_extension_control_file(const char *extname,
+ const char *filename)
+ {
+ FILE *file;
+ bool saw_relocatable = false;
+ ExtensionControlFile *control =
+ (ExtensionControlFile *)palloc(sizeof(ExtensionControlFile));
+ ConfigVariable *item,
+ *head = NULL,
+ *tail = NULL;
+
+ elog(DEBUG1, "parse_extension_control_file(%s, '%s')",
+ extname, filename);
+
+ /*
+ * default values, all string fields are NULL til parsed, except for the
+ * name, which ain't parsed
+ */
+ control->name = pstrdup(extname);
+ control->script = control->version = control->comment = NULL;
+ control->encoding = -1;
+
+ /*
+ * Now parse the file content, by line
+ */
+ if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ filename)));
+
+ ParseConfigFp(file, filename, 0, ERROR, &head, &tail);
+
+ for (item = head; item; item = item->next)
+ {
+ if (strcmp(item->name, "script") == 0)
+ {
+ control->script = pstrdup(item->value);
+ }
+ else if (strcmp(item->name, "version") == 0)
+ {
+ control->version = pstrdup(item->value);
+ }
+ else if (strcmp(item->name, "comment") == 0)
+ {
+ control->comment = pstrdup(item->value);
+ }
+ else if (strcmp(item->name, "relocatable") == 0)
+ {
+ saw_relocatable = true;
+ if (!parse_bool(item->value, &control->relocatable))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" requires a Boolean value",
+ item->name)));
+ return false;
+ }
+ }
+ else if (strcmp(item->name, "encoding") == 0)
+ {
+ control->encoding = pg_valid_server_encoding(item->value);
+ if (control->encoding < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("%s is not a valid encoding name in '%s'",
+ item->value, filename)));
+ }
+ else
+ ereport(ERROR,
+ (errmsg("Unsupported parameter '%s' in file: %s",
+ item->name, filename)));
+ }
+
+ if (control->name == NULL)
+ ereport(ERROR,
+ (errmsg("Missing a name parameter in file: %s", filename)));
+
+ /*
+ * Handle default values
+ */
+ if (control->script == NULL)
+ {
+ /*
+ * script defaults to ${extension-name}.sql
+ */
+ char *script;
+
+ script = palloc(MAXPGPATH);
+ memset(script, '\0', MAXPGPATH);
+ sprintf(script, "%s.sql", control->name);
+ control->script = script;;
+ }
+
+ if (!saw_relocatable)
+ ereport(ERROR,
+ (errmsg("syntax error in extension control file: %s", filename),
+ errhint("The parameter 'relocatable' is mandatory.")));
+
+ FreeFile(file);
+ return control;
+ }
+
+ /*
+ * We're given an extension name, parse the {extname}.control file.
+ */
+ static ExtensionControlFile *
+ read_extension_control_file(const char *extname)
+ {
+ char *filename = get_extension_control_filename(extname);
+
+ if (access(filename, R_OK) == 0)
+ {
+ return parse_extension_control_file(extname, filename);
+ }
+
+ ereport(ERROR,
+ (errmsg("could not read extension control file '%s' "
+ "for extension %s", filename, extname)));
+
+ /* make compiler happy */
+ return NULL;
+ }
+
+ /*
+ * Read the SQL script into a string, and care for its encoding
+ */
+ static char *
+ read_extension_script_file(const ExtensionControlFile *control)
+ {
+ char *filename = get_extension_absolute_path(control->script);
+ Datum script = CStringGetTextDatum(filename);
+ Datum raw = DirectFunctionCall1(pg_read_binary_file_all, script);
+ int src_encoding;
+ int dest_encoding = GetDatabaseEncoding();
+ bytea *content;
+ const char *src_str;
+ char *dest_str;
+ int len;
+
+ /* use database encoding if not given */
+ if (control->encoding == -1)
+ src_encoding = dest_encoding;
+ else
+ src_encoding = control->encoding;
+
+ if (src_encoding < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid source encoding name \"%s\"",
+ pg_encoding_to_char(control->encoding))));
+
+ content = (bytea *)DatumGetPointer(raw);
+
+ /* make sure that source string is valid */
+ len = VARSIZE_ANY_EXHDR(content);
+ src_str = VARDATA_ANY(content);
+ pg_verify_mbstr_len(src_encoding, src_str, len, false);
+
+ /* convert the encoding to the database encoding */
+ dest_str = (char *) pg_do_encoding_conversion(
+ (unsigned char *) src_str, len, src_encoding, dest_encoding);
+
+ if (dest_str != src_str)
+ return dest_str;
+ else
+ return text_to_cstring((text *)content);
+ }
+
+ /*
+ * Execute given SQL string, and returns true only when success.
+ */
+ static bool
+ execute_sql_string(const char *sql)
+ {
+ /*
+ * 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.
+ */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
+
+ if (SPI_execute(sql, false, 0) < SPI_OK_UTILITY)
+ return false;
+
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
+
+ return true;
+ }
+
+ /*
+ * Execute given script to install or upgrade an extension
+ *
+ * Caller must have prepared CreateExtensionAddress.
+ */
+ static void
+ execute_extension_script(ExtensionControlFile *control,
+ const char *schema, bool user_data)
+ {
+ char *filename = get_extension_absolute_path(control->script);
+ char *old_cmsgs = NULL, *old_lmsgs = NULL; /* silence compiler */
+ bool reset_cmsgs = false, reset_lmsgs = false;
+
+ /*
+ * Set create_extension and create_extension_with_user_data so that the
+ * function pg_extension_with_user_data() returns the right value.
+ */
+ create_extension = true;
+ create_extension_with_user_data = user_data;
+
+ elog(DEBUG1,
+ "Installing extension '%s' from '%s', in schema %s, %s user data",
+ control->name,
+ filename,
+ schema,
+ create_extension_with_user_data ? "with" : "without");
+
+ /*
+ * Force the min message settings here, because SPI will dump the
+ * query_string into the messages and in this case, that's a full
+ * extension script. At the time of this writing, contrib/isn/isn.sql.in
+ * is already 3197 lines and will issue lots of CREATE TYPE shell
+ * notices.
+ *
+ * We want to avoid dumping the full script for each of those.
+ */
+ reset_cmsgs = client_min_messages < WARNING;
+ reset_lmsgs = log_min_messages < WARNING;
+
+ if (reset_cmsgs)
+ {
+ old_cmsgs = pstrdup((char *)GetConfigOption("client_min_messages", false));
+ SetConfigOption("client_min_messages", "warning", PGC_SUSET, PGC_S_SESSION);
+ }
+ if (reset_lmsgs)
+ {
+ old_lmsgs = pstrdup((char *)GetConfigOption("log_min_messages", false));
+ SetConfigOption("log_min_messages", "warning", PGC_SUSET, PGC_S_SESSION);
+ }
+
+ /*
+ * On failure, ensure that create_extension does not remain set.
+ */
+ PG_TRY();
+ {
+ char *sql = read_extension_script_file(control);
+
+ elog(DEBUG1, "Execute script \"%s\"", filename);
+
+ /*
+ * An extension that is relocatable does not need any replacement,
+ * we will ALTER EXTENSION SET SCHEMA just after installing it. See
+ * below for details.
+ *
+ * An extension that is not relocatable need to offer a mechanism
+ * for the users to be able to choose where to intall it, that's
+ * using the placeholder @extschema@.
+ */
+ if (control->relocatable)
+ {
+ List *search_path = fetch_search_path(false);
+ Oid first_schema, target_schema;
+
+ if (!execute_sql_string(sql))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("could not execute sql file: '%s'", filename)));
+
+ /*
+ * CREATE EXTENSION foo WITH SCHEMA bar;
+ *
+ * The extension is relocatable, so we run its install script
+ * and it has to install all objects into the same schema. Then
+ * we relocate all the objects to the target SCHEMA bar,
+ * ERRORing out if some objects are not in the same schema than
+ * others, that's doing:
+ *
+ * ALTER EXTENSION foo SET SCHEMA bar;
+ *
+ * We skip this step if the target schema happens to be the
+ * first schema of the current search_path.
+ */
+ first_schema = linitial_oid(search_path);
+ target_schema = LookupCreationNamespace(schema);
+ list_free(search_path);
+
+ if (first_schema != target_schema)
+ AlterExtensionNamespace_oid(CreateExtensionAddress.objectId,
+ target_schema);
+ }
+ else
+ {
+ /*
+ * Prepare the replacements we use in execute_sql_file(). Even if
+ * the schema has not been specified by the user, we still have to
+ * set @extschema@ to 'public' so that extension scripts can rely on
+ * this placeholder.
+ */
+ sql = text_to_cstring(
+ DatumGetTextPP(
+ DirectFunctionCall3(replace_text,
+ CStringGetTextDatum(sql),
+ CStringGetTextDatum("@extschema@"),
+ CStringGetTextDatum(schema))));
+
+ if (!execute_sql_string(sql))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("could not execute sql file: '%s'", filename)));
+ }
+ }
+ PG_CATCH();
+ {
+ create_extension = false;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ if (reset_cmsgs)
+ SetConfigOption("client_min_messages",
+ old_cmsgs, PGC_SUSET, PGC_S_SESSION);
+ if (reset_lmsgs)
+ SetConfigOption("log_min_messages",
+ old_lmsgs, PGC_SUSET, PGC_S_SESSION);
+
+ /* reset the current_extension dependency tracker */
+ create_extension = false;
+ }
+
+ /*
+ * CREATE EXTENSION
+ *
+ * Only superusers can create an extension.
+ */
+ void
+ CreateExtension(CreateExtensionStmt *stmt)
+ {
+
+ ListCell *option;
+ DefElem *d_user_data = NULL;
+ DefElem *d_schema = NULL;
+ bool user_data = true;
+ char *schema = NULL;
+
+ ExtensionControlFile *control;
+
+ Relation rel;
+ Datum values[Natts_pg_extension];
+ bool nulls[Natts_pg_extension];
+ HeapTuple tuple;
+ Oid extensionoid;
+
+ /* 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.")));
+
+ /*
+ * This check is done once here, early, before we bother parsing the
+ * control file, then redone once we have the ExclusiveLock on the
+ * pg_extension catalog to forbid having two backends concurrently
+ * creating the same extension.
+ */
+ if( InvalidOid != get_extension_oid(stmt->extname, true) )
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("extension \"%s\" already exists", stmt->extname)));
+ }
+
+ /*
+ * The control file does not contain any text given to the backend
+ * operations, so setting the client_encoding here would have no
+ * effect. Also, the control file might contain a specific encoding to
+ * apply to reading the script, so we have to read it before to know
+ * which encoding to choose.
+ */
+ control = read_extension_control_file(stmt->extname);
+
+ /*
+ * Read the statement option list
+ * [ [ WITH ] [ [ NO ] USER DATA ] ]
+ */
+ foreach(option, stmt->options)
+ {
+ DefElem *defel = (DefElem *) lfirst(option);
+
+ if (strcmp(defel->defname, "user_data") == 0)
+ {
+ if (d_user_data)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_user_data = defel;
+ }
+ else if (strcmp(defel->defname, "schema") == 0)
+ {
+ if (d_schema)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_schema = defel;
+ }
+ }
+
+ if (d_user_data && d_user_data->arg)
+ user_data = intVal(d_user_data->arg) == TRUE;
+
+ if (d_schema && d_schema->arg)
+ schema = strVal(d_schema->arg);
+ else
+ /*
+ * Default to using schema public in replacements when no schema has
+ * been given.
+ */
+ schema = pstrdup("public");
+
+ if (control->encoding != -1)
+ control->encoding = PG_UTF8;
+
+ elog(DEBUG1, "CreateExtensionStmt: %s user data, schema = '%s', encoding = '%s'",
+ user_data ? "with" : "without",
+ schema,
+ pg_encoding_to_char(control->encoding));
+
+ /*
+ * Insert tuple into pg_extension.
+ *
+ * We use ExclusiveLock here so that there's only CREATE EXTENSION
+ * possible at any given time. Maybe we could relax that, but it does
+ * not seem like a huge contention point.
+ */
+ rel = heap_open(ExtensionRelationId, ExclusiveLock);
+
+ /*
+ * This check is done once here, early, before we bother parsing the
+ * control file, then redone once we have the ExclusiveLock on the
+ * pg_extension catalog to forbid having two backends concurrently
+ * creating the same extension.
+ */
+ if( InvalidOid != get_extension_oid(stmt->extname, true) )
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("extension \"%s\" already exists", stmt->extname)));
+ }
+
+ memset(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_extension_extname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(control->name));
+
+ values[Anum_pg_extension_relocatable - 1] =
+ BoolGetDatum(control->relocatable);
+
+ if( control->version == NULL )
+ nulls[Anum_pg_extension_extversion - 1] = true;
+ else
+ values[Anum_pg_extension_extversion - 1] =
+ CStringGetTextDatum(control->version);
+
+ 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 globals create_extension and extension so that any object
+ * created in the script has a dependency recorded towards the extension
+ * here.
+ */
+ CreateExtensionAddress.classId = ExtensionRelationId;
+ CreateExtensionAddress.objectId = extensionoid;
+ CreateExtensionAddress.objectSubId = 0;
+
+ /*
+ * the extension depends on the schema we used, record that
+ */
+ {
+ ObjectAddress nsp;
+
+ nsp.classId = NamespaceRelationId;
+ nsp.objectId = get_namespace_oid(schema, false);
+ nsp.objectSubId = 0;
+
+ recordDependencyOn(&CreateExtensionAddress, &nsp, DEPENDENCY_NORMAL);
+ }
+
+ execute_extension_script(control, schema, user_data);
+ 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 extension.
+ */
+ 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;
+ }
+
+ /*
+ * extension_relocatable_p
+ *
+ * Returns true when the extension is marked 'relocatable'
+ */
+ bool
+ extension_relocatable_p(Oid ext_oid)
+ {
+ bool relocatable;
+ 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))
+ relocatable = ((Form_pg_extension) GETSTRUCT(tuple))->relocatable;
+ else
+ relocatable = false;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return relocatable;
+ }
+
+ /*
+ * Drop extension by OID. This is called to clean up dependencies.
+ */
+ void
+ RemoveExtensionById(Oid extId)
+ {
+ 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))
+ simple_heap_delete(rel, &tuple->t_self);
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+ }
+
+ /*
+ * 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)
+ ereport(ERROR,
+ (errmsg("This function can only be called from SQL "
+ "script executed by 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)
+ ereport(ERROR,
+ (errmsg("this function can only be used from CREATE EXTENSION")));
+
+ count = changeDependencyTypeFor(objid,
+ ExtensionRelationId,
+ CreateExtensionAddress.objectId, 0,
+ DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL);
+
+ if (count == 0)
+ ereport(ERROR,
+ (errmsg("could not find a 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 return it as a list.
+ * Note that properties other than the name of the extensions are only
+ * filled if the caller specifically asks for them.
+ *
+ * Caller is responsible for freeing the resulting struct.
+ */
+ static ExtensionList *
+ extension_list_installed(bool name_only)
+ {
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ bool visnull;
+ Datum d_version;
+ ExtensionList *elist;
+
+ elist = (ExtensionList *) palloc(sizeof(ExtensionList));
+
+ elist->numrefs = 0;
+ elist->maxrefs = 8;
+ elist->exts = (ExtensionControlFile *)
+ palloc(elist->maxrefs * sizeof(ExtensionControlFile));
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+ scandesc = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+ while (HeapTupleIsValid(tuple = heap_getnext(scandesc, ForwardScanDirection)))
+ {
+ Form_pg_extension e = (Form_pg_extension) GETSTRUCT(tuple);
+
+ if (elist->numrefs >= elist->maxrefs)
+ {
+ elist->maxrefs *= 2;
+ elist->exts = (ExtensionControlFile *)
+ repalloc(elist->exts,
+ elist->maxrefs * sizeof(ExtensionControlFile));
+ }
+
+ elist->exts[elist->numrefs].name = pstrdup(NameStr(e->extname));
+
+ if (!name_only)
+ {
+ elist->exts[elist->numrefs].relocatable = e->relocatable;
+
+ d_version = heap_getattr(tuple,
+ Anum_pg_extension_extversion,
+ rel->rd_att,
+ &visnull);
+
+ if (visnull)
+ elist->exts[elist->numrefs].version = NULL;
+ else
+ elist->exts[elist->numrefs].version =
+ pstrdup(TextDatumGetCString(d_version));
+
+ elist->exts[elist->numrefs].comment =
+ GetComment(HeapTupleGetOid(tuple), tuple->t_tableOid, 0);
+ }
+ elist->numrefs++;
+ }
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return elist;
+ }
+
+ /*
+ * 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;
+ ExtensionList *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, "relocatable",
+ BOOLOID, -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 */
+ installed = extension_list_installed(true);
+
+ fctx = palloc(sizeof(extension_fctx));
+ fctx->dir.location = get_extension_control_basepath();
+ fctx->dir.dirdesc = AllocateDir(fctx->dir.location);
+ 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 (!filename_extension_control_p(de->d_name))
+ continue;
+
+ control = parse_extension_control_file(
+ get_extension_name_from_control_filename(de->d_name),
+ get_extension_absolute_path(de->d_name));
+
+ memset(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+
+ values[0] = DirectFunctionCall1(namein, CStringGetDatum(control->name));
+ values[2] = BoolGetDatum(control->relocatable);
+
+ if( control->version == NULL )
+ nulls[1] = true;
+ else
+ values[1] = CStringGetTextDatum(control->version);
+
+ 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->installed->numrefs )
+ is_installed = 0 == strcmp(fctx->installed->exts[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);
+ }
+
+ /*
+ * List all objects that pertain to a given extension.
+ *
+ * SELECT class, classid, objid, objdesc
+ * FROM pg_extension_objects('hstore');
+ */
+ Datum
+ pg_extension_objects(PG_FUNCTION_ARGS)
+ {
+ char *extname = NameStr(*PG_GETARG_NAME(0));
+ FuncCallContext *funcctx;
+ extension_objects_fctx *fctx;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ MemoryContext oldcontext;
+ TupleDesc tupdesc;
+ ObjectAddress *object;
+
+ funcctx = SRF_FIRSTCALL_INIT();
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ tupdesc = CreateTemplateTupleDesc(4, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "class",
+ REGCLASSOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "classid",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "objid",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "objdesc",
+ TEXTOID, -1, 0);
+
+ funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+
+ fctx = (extension_objects_fctx *)palloc(sizeof(extension_objects_fctx));
+
+ object = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ object->classId = ExtensionRelationId;
+ object->objectId = get_extension_oid(extname, false);
+ object->objectSubId = 0;
+
+ fctx->targetObjects = listDependentObjects(object);
+ fctx->i = fctx->targetObjects->numrefs - 1;
+
+ funcctx->user_fctx = fctx;
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ fctx = (extension_objects_fctx *) funcctx->user_fctx;
+
+ while (fctx->i >= 0)
+ {
+ ObjectAddress *thisobj = fctx->targetObjects->refs + fctx->i--;
+ char *regclass = (char *)palloc(NAMEDATALEN*sizeof(char));
+ Datum values[4];
+ bool nulls[4];
+ HeapTuple tuple;
+
+ sprintf(regclass, "%u", thisobj->classId);
+ memset(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+
+ values[0] = DirectFunctionCall1(regclassin, CStringGetDatum(regclass));
+ values[1] = ObjectIdGetDatum(thisobj->classId);
+ values[2] = ObjectIdGetDatum(thisobj->objectId);
+ values[3] = CStringGetTextDatum(getObjectDescription(thisobj));
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+ SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+ }
+ free_object_addresses(fctx->targetObjects);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /*
+ * Execute ALTER EXTENSION SET SCHEMA
+ */
+ void
+ AlterExtensionNamespace(List *name, const char *newschema)
+ {
+ Oid extensionOid, nspOid;
+
+ Assert(list_length(name) == 1);
+ extensionOid = get_extension_oid(strVal(linitial(name)), false);
+ nspOid = LookupCreationNamespace(newschema);
+
+ AlterExtensionNamespace_oid(extensionOid, nspOid);
+ }
+
+ void
+ AlterExtensionNamespace_oid(Oid extensionOid, Oid nspOid)
+ {
+ Oid oldNspOid = InvalidOid;
+ ObjectAddress *object;
+ ObjectAddresses *targetObjects;
+ int i;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to ALTER EXTENSION"))));
+
+ if (!extension_relocatable_p(extensionOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ (errmsg("this extension does not support SET SCHEMA"))));
+
+ object = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ object->classId = ExtensionRelationId;
+ object->objectId = extensionOid;
+ object->objectSubId = 0;
+
+ targetObjects = listDependentObjects(object);
+
+ for (i = 0; i < targetObjects->numrefs; i++)
+ {
+ Oid thisobj_oldNspOid;
+ ObjectAddress *thisobj = targetObjects->refs + i;
+
+ elog(DEBUG1, "SET SCHEMA on %u: %s",
+ thisobj->objectId, getObjectDescription(thisobj));
+
+ thisobj_oldNspOid = AlterObjectNamespace_internal(thisobj, nspOid);
+
+ /*
+ * When called from CREATE EXTENSION, we have to track the first's
+ * object Oid here
+ */
+ if (thisobj_oldNspOid != InvalidOid && oldNspOid == InvalidOid)
+ oldNspOid = thisobj_oldNspOid;
+
+ /* we get InvalidOid for no-namespace objects */
+ if (thisobj_oldNspOid != InvalidOid && thisobj_oldNspOid != oldNspOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ (errmsg("this extension does not support SET SCHEMA"),
+ errdetail("%s is not in the extension's schema [%i %i]",
+ getObjectDescription(thisobj),
+ oldNspOid,
+ thisobj_oldNspOid))));
+ }
+
+ /* update dependencies to point to the new schema */
+ changeDependencyFor(ExtensionRelationId, extensionOid,
+ NamespaceRelationId, oldNspOid, nspOid);
+
+ free_object_addresses(targetObjects);
+ }
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
***************
*** 35,40 ****
--- 35,41 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
/*
***************
*** 414,419 **** CreateForeignDataWrapper(CreateFdwStmt *stmt)
--- 415,426 ----
referenced.objectId = fdwvalidator;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
}
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
***************
*** 703,708 **** CreateForeignServer(CreateForeignServerStmt *stmt)
--- 710,721 ----
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new foreign server */
InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
***************
*** 977,982 **** CreateUserMapping(CreateUserMappingStmt *stmt)
--- 990,1001 ----
/* Record the mapped user dependency */
recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new user mapping */
InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 62,68 ****
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
!
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
Oid newOwnerId);
--- 62,68 ----
#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);
***************
*** 1762,1767 **** CreateCast(CreateCastStmt *stmt)
--- 1762,1772 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new cast */
InvokeObjectAccessHook(OAT_POST_CREATE,
CastRelationId, myself.objectId, 0);
***************
*** 1875,1887 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
const char *newschema)
{
Oid procOid;
- Oid oldNspOid;
Oid nspOid;
- HeapTuple tup;
- Relation procRel;
- Form_pg_proc proc;
-
- procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
/* get function OID */
if (isagg)
--- 1880,1886 ----
***************
*** 1894,1899 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
--- 1893,1913 ----
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(name));
+ nspOid = LookupCreationNamespace(newschema);
+
+ AlterFunctionNamespace_oid(procOid, nspOid);
+ }
+
+ Oid
+ AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
+ {
+ Oid oldNspOid;
+ HeapTuple tup;
+ Relation procRel;
+ Form_pg_proc proc;
+
+ procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
+
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for function %u", procOid);
***************
*** 1901,1909 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
oldNspOid = proc->pronamespace;
- /* get schema OID and check its permissions */
- nspOid = LookupCreationNamespace(newschema);
-
/* common checks on switching namespaces */
CheckSetNamespace(oldNspOid, nspOid, ProcedureRelationId, procOid);
--- 1915,1920 ----
***************
*** 1916,1922 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function \"%s\" already exists in schema \"%s\"",
NameStr(proc->proname),
! newschema)));
/* OK, modify the pg_proc row */
--- 1927,1933 ----
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function \"%s\" already exists in schema \"%s\"",
NameStr(proc->proname),
! get_namespace_name(nspOid))));
/* OK, modify the pg_proc row */
***************
*** 1930,1940 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
if (changeDependencyFor(ProcedureRelationId, procOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "failed to change schema dependency for function \"%s\"",
! NameListToString(name));
heap_freetuple(tup);
heap_close(procRel, RowExclusiveLock);
}
--- 1941,1953 ----
if (changeDependencyFor(ProcedureRelationId, procOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "failed to change schema dependency for function \"%s\"",
! NameStr(proc->proname));
heap_freetuple(tup);
heap_close(procRel, RowExclusiveLock);
+
+ return oldNspOid;
}
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 44,49 ****
--- 44,50 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
/*
***************
*** 309,314 **** CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
--- 310,321 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new operator family */
InvokeObjectAccessHook(OAT_POST_CREATE,
OperatorFamilyRelationId, opfamilyoid, 0);
***************
*** 709,714 **** DefineOpClass(CreateOpClassStmt *stmt)
--- 716,727 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new operator class */
InvokeObjectAccessHook(OAT_POST_CREATE,
OperatorClassRelationId, opclassoid, 0);
***************
*** 2021,2026 **** AlterOpClassNamespace(List *name, char *access_method, const char *newschema)
--- 2034,2066 ----
heap_close(rel, NoLock);
}
+ Oid
+ AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ HeapTuple tup;
+ Relation rel;
+
+ rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
+
+ tup = SearchSysCacheCopy1(CLAOID, ObjectIdGetDatum(opclassOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, CLAOID, OperatorClassRelationId,
+ HeapTupleGetOid(tup), newNspOid,
+ Anum_pg_opfamily_opfname,
+ Anum_pg_opfamily_opfnamespace,
+ Anum_pg_opfamily_opfowner,
+ ACL_KIND_OPCLASS,
+ false);
+
+ heap_freetuple(tup);
+ heap_close(rel, NoLock);
+ return oldNspOid;
+ }
+
/*
* Change opfamily owner by name
*/
***************
*** 2209,2211 **** AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema)
--- 2249,2278 ----
heap_close(rel, NoLock);
}
+
+ Oid
+ AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ HeapTuple tup;
+ Relation rel;
+
+ rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+
+ tup = SearchSysCacheCopy1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, OPFAMILYOID, OperatorFamilyRelationId,
+ opfamilyOid, newNspOid,
+ Anum_pg_opfamily_opfname,
+ Anum_pg_opfamily_opfnamespace,
+ Anum_pg_opfamily_opfowner,
+ ACL_KIND_OPFAMILY,
+ false);
+
+ heap_freetuple(tup);
+ heap_close(rel, NoLock);
+ return oldNspOid;
+ }
*** a/src/backend/commands/operatorcmds.c
--- b/src/backend/commands/operatorcmds.c
***************
*** 486,488 **** AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
--- 486,509 ----
heap_close(rel, NoLock);
}
+
+ Oid
+ AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(OperatorRelationId, RowExclusiveLock);
+
+ oldNspOid = AlterObjectNamespace(rel, OPEROID, OperatorRelationId, operOid,
+ newNspOid,
+ Anum_pg_operator_oprname,
+ Anum_pg_operator_oprnamespace,
+ Anum_pg_operator_oprowner,
+ ACL_KIND_OPER,
+ false);
+
+ heap_close(rel, NoLock);
+
+ return oldNspOid;
+ }
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
***************
*** 37,42 ****
--- 37,43 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
typedef struct
***************
*** 426,431 **** create_proc_lang(const char *languageName, bool replace,
--- 427,438 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
/* Post creation hook for new procedural language */
InvokeObjectAccessHook(OAT_POST_CREATE,
LanguageRelationId, myself.objectId, 0);
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 8414,8420 **** AlterTableNamespace(RangeVar *relation, const char *newschema,
* entry, and the pg_depend entry if any. Caller must already have
* opened and write-locked pg_class.
*/
! void
AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
Oid oldNspOid, Oid newNspOid,
bool hasDependEntry)
--- 8414,8420 ----
* entry, and the pg_depend entry if any. Caller must already have
* opened and write-locked pg_class.
*/
! Oid
AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
Oid oldNspOid, Oid newNspOid,
bool hasDependEntry)
***************
*** 8452,8457 **** AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
--- 8452,8459 ----
NameStr(classForm->relname));
heap_freetuple(classTup);
+
+ return oldNspOid;
}
/*
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 33,38 ****
--- 33,39 ----
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
+ #include "commands/extension.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
***************
*** 156,161 **** makeParserDependencies(HeapTuple tuple)
--- 157,168 ----
referenced.objectId = prs->prsheadline;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* dependency on extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 422,427 **** AlterTSParserNamespace(List *name, const char *newschema)
--- 429,453 ----
heap_close(rel, NoLock);
}
+ Oid
+ AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSParserRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSPARSEROID,
+ TSParserRelationId, prsId, newNspOid,
+ Anum_pg_ts_parser_prsname,
+ Anum_pg_ts_parser_prsnamespace,
+ -1, -1, true);
+
+ heap_close(rel, NoLock);
+ return oldNspOid;
+ }
+
/* ---------------------- TS Dictionary commands -----------------------*/
/*
***************
*** 452,457 **** makeDictionaryDependencies(HeapTuple tuple)
--- 478,489 ----
referenced.objectId = dict->dicttemplate;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on extension, if in create extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 678,683 **** AlterTSDictionaryNamespace(List *name, const char *newschema)
--- 710,736 ----
heap_close(rel, NoLock);
}
+ Oid
+ AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSDICTOID,
+ TSDictionaryRelationId, dictId, newNspOid,
+ Anum_pg_ts_dict_dictname,
+ Anum_pg_ts_dict_dictnamespace,
+ Anum_pg_ts_dict_dictowner,
+ ACL_KIND_TSDICTIONARY,
+ false);
+
+ heap_close(rel, NoLock);
+ return oldNspOid;
+ }
+
/*
* DROP TEXT SEARCH DICTIONARY
*/
***************
*** 1024,1029 **** makeTSTemplateDependencies(HeapTuple tuple)
--- 1077,1088 ----
referenced.objectId = tmpl->tmplinit;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* dependency on extension, if in create extension */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 1186,1191 **** AlterTSTemplateNamespace(List *name, const char *newschema)
--- 1245,1269 ----
heap_close(rel, NoLock);
}
+ Oid
+ AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSTEMPLATEOID, TSTemplateRelationId,
+ tmplId, newNspOid,
+ Anum_pg_ts_template_tmplname,
+ Anum_pg_ts_template_tmplnamespace,
+ -1, -1, true);
+
+ heap_close(rel, NoLock);
+ return oldNspOid;
+ }
+
/*
* DROP TEXT SEARCH TEMPLATE
*/
***************
*** 1376,1381 **** makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
--- 1454,1465 ----
/* Record 'em (this includes duplicate elimination) */
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+ /* dependencies on extension being created, if any */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
free_object_addresses(addrs);
}
***************
*** 1613,1618 **** AlterTSConfigurationNamespace(List *name, const char *newschema)
--- 1697,1723 ----
heap_close(rel, NoLock);
}
+ Oid
+ AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
+ {
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSConfigRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSCONFIGOID,
+ TSConfigRelationId, cfgId, newNspOid,
+ Anum_pg_ts_config_cfgname,
+ Anum_pg_ts_config_cfgnamespace,
+ Anum_pg_ts_config_cfgowner,
+ ACL_KIND_TSCONFIGURATION,
+ false);
+
+ heap_close(rel, NoLock);
+ return oldNspOid;
+ }
+
/*
* DROP TEXT SEARCH CONFIGURATION
*/
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 2765,2784 **** AlterTypeNamespace(List *names, const char *newschema)
TypeName *typename;
Oid typeOid;
Oid nspOid;
- Oid elemOid;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
typeOid = typenameTypeId(NULL, typename);
/* check permissions on type */
if (!pg_type_ownercheck(typeOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
format_type_be(typeOid));
- /* get schema OID and check its permissions */
- nspOid = LookupCreationNamespace(newschema);
-
/* don't allow direct alteration of array types */
elemOid = get_element_type(typeOid);
if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
--- 2765,2791 ----
TypeName *typename;
Oid typeOid;
Oid nspOid;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
typeOid = typenameTypeId(NULL, typename);
+ /* get schema OID and check its permissions */
+ nspOid = LookupCreationNamespace(newschema);
+
+ AlterTypeNamespace_oid(typeOid, nspOid);
+ }
+
+ Oid
+ AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
+ {
+ Oid elemOid;
+
/* check permissions on type */
if (!pg_type_ownercheck(typeOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
format_type_be(typeOid));
/* don't allow direct alteration of array types */
elemOid = get_element_type(typeOid);
if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
***************
*** 2790,2796 **** AlterTypeNamespace(List *names, const char *newschema)
format_type_be(elemOid))));
/* and do the work */
! AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
}
/*
--- 2797,2803 ----
format_type_be(elemOid))));
/* and do the work */
! return AlterTypeNamespaceInternal(typeOid, nspOid, false, false);
}
/*
***************
*** 2806,2812 **** AlterTypeNamespace(List *names, const char *newschema)
* a table type. ALTER TABLE has to be used to move a table to a new
* namespace.
*/
! void
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType)
--- 2813,2819 ----
* a table type. ALTER TABLE has to be used to move a table to a new
* namespace.
*/
! Oid
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType)
***************
*** 2913,2916 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
--- 2920,2925 ----
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
+
+ return oldNspOid;
}
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3162,3167 **** _copyDiscardStmt(DiscardStmt *from)
--- 3162,3190 ----
return newnode;
}
+ static CreateExtensionStmt *
+ _copyCreateExtensionStmt(CreateExtensionStmt *from)
+ {
+ CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
+
+ COPY_STRING_FIELD(extname);
+ COPY_NODE_FIELD(options);
+
+ 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)
{
***************
*** 4193,4198 **** copyObject(void *from)
--- 4216,4227 ----
case T_DropTableSpaceStmt:
retval = _copyDropTableSpaceStmt(from);
break;
+ case T_CreateExtensionStmt:
+ retval = _copyCreateExtensionStmt(from);
+ break;
+ case T_DropExtensionStmt:
+ retval = _copyDropExtensionStmt(from);
+ break;
case T_AlterTableSpaceOptionsStmt:
retval = _copyAlterTableSpaceOptionsStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1614,1619 **** _equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b)
--- 1614,1638 ----
}
static bool
+ _equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
+ {
+ COMPARE_STRING_FIELD(extname);
+ COMPARE_NODE_FIELD(options);
+
+ 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)
{
***************
*** 2797,2802 **** equal(void *a, void *b)
--- 2816,2827 ----
case T_DiscardStmt:
retval = _equalDiscardStmt(a, b);
break;
+ case T_CreateExtensionStmt:
+ retval = _equalCreateExtensionStmt(a, b);
+ break;
+ case T_DropExtensionStmt:
+ retval = _equalDropExtensionStmt(a, b);
+ break;
case T_CreateTableSpaceStmt:
retval = _equalCreateTableSpaceStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 191,197 **** 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 CreateForeignTableStmt
--- 191,197 ----
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
! CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
***************
*** 200,206 **** 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
--- 200,206 ----
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
***************
*** 227,235 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
%type opt_drop_behavior
%type createdb_opt_list alterdb_opt_list copy_opt_list
! transaction_mode_list
%type createdb_opt_item alterdb_opt_item copy_opt_item
! transaction_mode_item
%type opt_lock lock_type cast_context
%type vacuum_option_list vacuum_option_elem
--- 227,235 ----
%type opt_drop_behavior
%type createdb_opt_list alterdb_opt_list copy_opt_list
! transaction_mode_list create_extension_opt_list
%type createdb_opt_item alterdb_opt_item copy_opt_item
! transaction_mode_item create_extension_opt_item
%type opt_lock lock_type cast_context
%type vacuum_option_list vacuum_option_elem
***************
*** 489,495 **** 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
--- 489,495 ----
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
***************
*** 689,694 **** stmt :
--- 689,695 ----
| CreateCastStmt
| CreateConversionStmt
| CreateDomainStmt
+ | CreateExtensionStmt
| CreateFdwStmt
| CreateForeignServerStmt
| CreateForeignTableStmt
***************
*** 715,720 **** stmt :
--- 716,722 ----
| DoStmt
| DropAssertStmt
| DropCastStmt
+ | DropExtensionStmt
| DropFdwStmt
| DropForeignServerStmt
| DropGroupStmt
***************
*** 3110,3115 **** opt_procedural:
--- 3112,3183 ----
/*****************************************************************************
*
* QUERY:
+ * CREATE EXTENSION extension
+ * [ [ WITH ] [ [ NO ] USER DATA ]
+ * [ SCHEMA [=] schema ] ]
+ *
+ *****************************************************************************/
+
+ CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
+ {
+ CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ n->extname = $3;
+ n->options = $5;
+ $$ = (Node *) n;
+ }
+ ;
+
+ create_extension_opt_list:
+ create_extension_opt_list create_extension_opt_item
+ {
+ $$ =lappend($1, $2);
+ }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
+ create_extension_opt_item:
+ USER DATA_P
+ {
+ $$ = makeDefElem("user_data", (Node *)makeInteger(TRUE));
+ }
+ | NO USER DATA_P
+ {
+ $$ = makeDefElem("user_data", (Node *)makeInteger(FALSE));
+ }
+ | SCHEMA opt_equal name
+ {
+ $$ = makeDefElem("schema", (Node *)makeString($3));
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * 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/'
*
*****************************************************************************/
***************
*** 4337,4343 **** 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 |
* FOREIGN TABLE ] |
--- 4405,4411 ----
*
* 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 |
* FOREIGN TABLE ] |
***************
*** 4516,4521 **** comment_type:
--- 4584,4590 ----
| VIEW { $$ = OBJECT_VIEW; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
+ | EXTENSION { $$ = OBJECT_EXTENSION; }
| ROLE { $$ = OBJECT_ROLE; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
;
***************
*** 6202,6207 **** AlterObjectSchemaStmt:
--- 6271,6284 ----
n->newschema = $6;
$$ = (Node *)n;
}
+ | ALTER EXTENSION any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_EXTENSION;
+ n->object = $3;
+ n->newschema = $6;
+ $$ = (Node *)n;
+ }
| ALTER FUNCTION function_with_argtypes SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 22,27 ****
--- 22,28 ----
#include "catalog/objectaccess.h"
#include "catalog/pg_rewrite.h"
#include "catalog/storage.h"
+ #include "commands/extension.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_utilcmd.h"
***************
*** 168,173 **** InsertRule(char *rulname,
--- 169,180 ----
recordDependencyOnExpr(&myself, (Node *) action, NIL,
DEPENDENCY_NORMAL);
+ /* Depends on currently installed extension, if any */
+ if (create_extension)
+ {
+ recordDependencyOn(&myself, &CreateExtensionAddress, DEPENDENCY_INTERNAL);
+ }
+
if (event_qual != NULL)
{
/* Find query containing OLD/NEW rtable entries */
*** 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:
***************
*** 195,200 **** check_xact_readonly(Node *parsetree)
--- 197,203 ----
case T_DropCastStmt:
case T_DropStmt:
case T_DropdbStmt:
+ case T_DropExtensionStmt:
case T_DropTableSpaceStmt:
case T_RemoveFuncStmt:
case T_DropRoleStmt:
***************
*** 576,581 **** standard_ProcessUtility(Node *parsetree,
--- 579,592 ----
}
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);
***************
*** 1528,1533 **** CreateCommandTag(Node *parsetree)
--- 1539,1552 ----
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;
***************
*** 1737,1742 **** CreateCommandTag(Node *parsetree)
--- 1756,1764 ----
case OBJECT_DOMAIN:
tag = "ALTER DOMAIN";
break;
+ case OBJECT_EXTENSION:
+ tag = "ALTER EXTENSION";
+ break;
case OBJECT_OPERATOR:
tag = "ALTER OPERATOR";
break;
***************
*** 2366,2371 **** GetCommandLogLevel(Node *parsetree)
--- 2388,2401 ----
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
***************
*** 21,42 ****
#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.
*
--- 21,37 ----
#include
#include "catalog/pg_type.h"
+ #include "commands/extension.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.
*
***************
*** 51,56 **** convert_and_check_filename(text *arg)
--- 46,73 ----
filename = text_to_cstring(arg);
canonicalize_path(filename); /* filename can change length here */
+ /*
+ * When reading the SQL script for executing CREATE EXTENSION, we accept
+ * path in sharepath rather than in $PGDATA.
+ */
+ if (create_extension)
+ {
+ char sharepath[MAXPGPATH];
+ get_share_path(my_exec_path, sharepath);
+
+ if (path_is_prefix_of_path(sharepath, filename))
+ return filename;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("script path not allowed"),
+ errdetail("Extension's script are expected in \"%s\".",
+ sharepath))));
+ return NULL;
+ }
+ }
+
/* Disallow ".." in the path */
if (path_contains_parent_reference(filename))
ereport(ERROR,
*** a/src/backend/utils/adt/varlena.c
--- b/src/backend/utils/adt/varlena.c
***************
*** 24,29 ****
--- 24,30 ----
#include "miscadmin.h"
#include "parser/scansup.h"
#include "regex/regex.h"
+ #include "utils/array.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/lsyscache.h"
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 29,34 ****
--- 29,35 ----
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_tablespace.h"
+ #include "commands/extension.h"
#include "libpq/auth.h"
#include "libpq/libpq-be.h"
#include "mb/pg_wchar.h"
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 128,134 **** extern char *temp_tablespaces;
extern bool synchronize_seqscans;
extern bool fullPageWrites;
extern int ssl_renegotiation_limit;
- extern char *SSLCipherSuites;
#ifdef TRACE_SORT
extern bool trace_sort;
--- 128,133 ----
***************
*** 140,145 **** extern bool trace_syncscan;
--- 139,148 ----
extern bool optimize_bounded_sort;
#endif
+ #ifdef USE_SSL
+ extern char *SSLCipherSuites;
+ #endif
+
static void set_config_sourcefile(const char *name, char *sourcefile,
int sourceline);
***************
*** 148,161 **** static const char *assign_log_destination(const char *value,
#ifdef HAVE_SYSLOG
static int syslog_facility = LOG_LOCAL0;
- #else
- static int syslog_facility = 0;
- #endif
static bool assign_syslog_facility(int newval,
bool doit, GucSource source);
static const char *assign_syslog_ident(const char *ident,
bool doit, GucSource source);
static bool assign_session_replication_role(int newval, bool doit,
GucSource source);
--- 151,162 ----
#ifdef HAVE_SYSLOG
static int syslog_facility = LOG_LOCAL0;
static bool assign_syslog_facility(int newval,
bool doit, GucSource source);
static const char *assign_syslog_ident(const char *ident,
bool doit, GucSource source);
+ #endif
static bool assign_session_replication_role(int newval, bool doit,
GucSource source);
***************
*** 279,286 **** static const struct config_enum_entry session_replication_role_options[] = {
{NULL, 0, false}
};
- static const struct config_enum_entry syslog_facility_options[] = {
#ifdef HAVE_SYSLOG
{"local0", LOG_LOCAL0, false},
{"local1", LOG_LOCAL1, false},
{"local2", LOG_LOCAL2, false},
--- 280,287 ----
{NULL, 0, false}
};
#ifdef HAVE_SYSLOG
+ static const struct config_enum_entry syslog_facility_options[] = {
{"local0", LOG_LOCAL0, false},
{"local1", LOG_LOCAL1, false},
{"local2", LOG_LOCAL2, false},
***************
*** 289,299 **** static const struct config_enum_entry syslog_facility_options[] = {
{"local5", LOG_LOCAL5, false},
{"local6", LOG_LOCAL6, false},
{"local7", LOG_LOCAL7, false},
- #else
- {"none", 0, false},
- #endif
{NULL, 0}
};
static const struct config_enum_entry track_function_options[] = {
{"none", TRACK_FUNC_OFF, false},
--- 290,298 ----
{"local5", LOG_LOCAL5, false},
{"local6", LOG_LOCAL6, false},
{"local7", LOG_LOCAL7, false},
{NULL, 0}
};
+ #endif
static const struct config_enum_entry track_function_options[] = {
{"none", TRACK_FUNC_OFF, false},
***************
*** 411,417 **** int tcp_keepalives_count;
--- 410,418 ----
*/
static char *log_destination_string;
+ #ifdef HAVE_SYSLOG
static char *syslog_ident_str;
+ #endif
static bool phony_autocommit;
static bool session_auth_is_superuser;
static double phony_random_seed;
***************
*** 2530,2535 **** static struct config_string ConfigureNamesString[] =
--- 2531,2537 ----
"postgresql-%Y-%m-%d_%H%M%S.log", NULL, NULL
},
+ #ifdef HAVE_SYSLOG
{
{"syslog_ident", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Sets the program name used to identify PostgreSQL "
***************
*** 2539,2544 **** static struct config_string ConfigureNamesString[] =
--- 2541,2547 ----
&syslog_ident_str,
"postgres", assign_syslog_ident, NULL
},
+ #endif
{
{"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
***************
*** 2677,2682 **** static struct config_string ConfigureNamesString[] =
--- 2680,2686 ----
"pg_catalog.simple", assignTSCurrentConfig, NULL
},
+ #ifdef USE_SSL
{
{"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
***************
*** 2684,2696 **** static struct config_string ConfigureNamesString[] =
GUC_SUPERUSER_ONLY
},
&SSLCipherSuites,
! #ifdef USE_SSL
! "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH",
! #else
! "none",
! #endif
! NULL, NULL
},
{
{"application_name", PGC_USERSET, LOGGING_WHAT,
--- 2688,2696 ----
GUC_SUPERUSER_ONLY
},
&SSLCipherSuites,
! "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH", NULL, NULL
},
+ #endif /* USE_SSL */
{
{"application_name", PGC_USERSET, LOGGING_WHAT,
***************
*** 2807,2825 **** static struct config_enum ConfigureNamesEnum[] =
LOGSTMT_NONE, log_statement_options, NULL, NULL
},
{
{"syslog_facility", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Sets the syslog \"facility\" to be used when syslog enabled."),
NULL
},
&syslog_facility,
! #ifdef HAVE_SYSLOG
! LOG_LOCAL0,
! #else
! 0,
! #endif
! syslog_facility_options, assign_syslog_facility, NULL
},
{
{"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT,
--- 2807,2822 ----
LOGSTMT_NONE, log_statement_options, NULL, NULL
},
+ #ifdef HAVE_SYSLOG
{
{"syslog_facility", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Sets the syslog \"facility\" to be used when syslog enabled."),
NULL
},
&syslog_facility,
! LOG_LOCAL0, syslog_facility_options, assign_syslog_facility, NULL
},
+ #endif
{
{"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT,
***************
*** 7640,7654 **** assign_log_destination(const char *value, bool doit, GucSource source)
return value;
}
static bool
assign_syslog_facility(int newval, bool doit, GucSource source)
{
- #ifdef HAVE_SYSLOG
if (doit)
set_syslog_parameters(syslog_ident_str ? syslog_ident_str : "postgres",
newval);
- #endif
- /* Without syslog support, just ignore it */
return true;
}
--- 7637,7650 ----
return value;
}
+ #ifdef HAVE_SYSLOG
+
static bool
assign_syslog_facility(int newval, bool doit, GucSource source)
{
if (doit)
set_syslog_parameters(syslog_ident_str ? syslog_ident_str : "postgres",
newval);
return true;
}
***************
*** 7656,7669 **** assign_syslog_facility(int newval, bool doit, GucSource source)
static const char *
assign_syslog_ident(const char *ident, bool doit, GucSource source)
{
- #ifdef HAVE_SYSLOG
if (doit)
set_syslog_parameters(ident, syslog_facility);
- #endif
- /* Without syslog support, it will always be set to "none", so ignore */
return ident;
}
static bool
--- 7652,7663 ----
static const char *
assign_syslog_ident(const char *ident, bool doit, GucSource source)
{
if (doit)
set_syslog_parameters(ident, syslog_facility);
return ident;
}
+ #endif /* HAVE_SYSLOG */
static bool
*** 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
***************
*** 158,163 **** static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
--- 158,164 ----
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);
***************
*** 2389,2394 **** binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, Oid pg_class_oid,
--- 2390,2476 ----
}
/*
+ * 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_relocatable;
+ int i_extversion;
+ int i_nspname;
+
+ /*
+ * 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 x.tableoid, x.oid, "
+ "x.extname, x.relocatable, x.extversion, n.nspname "
+ "FROM pg_extension x "
+ "JOIN pg_depend d ON d.objid = x.oid "
+ "and d.refclassid = 'pg_catalog.pg_namespace'::regclass "
+ "JOIN pg_namespace n ON d.refobjid = n.oid");
+
+ 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_relocatable = PQfnumber(res, "relocatable");
+ i_extversion = PQfnumber(res, "extversion");
+ i_nspname = PQfnumber(res, "nspname");
+
+ 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].relocatable = strdup(PQgetvalue(res, i, i_relocatable));
+ extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion));
+ extinfo[i].namespace = strdup(PQgetvalue(res, i, i_nspname));
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ *numExtensions = ntups;
+ return extinfo;
+ }
+
+ /*
* getNamespaces:
* read all namespaces in the system catalogs and return them in the
* NamespaceInfo* structure
***************
*** 2452,2461 **** 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);
--- 2534,2590 ----
* 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);
***************
*** 2519,2525 **** 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
--- 2648,2655 ----
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
***************
*** 2572,2578 **** 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
*
--- 2702,2709 ----
* 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
*
***************
*** 2587,2593 **** 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, "
--- 2718,2743 ----
/* 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, "
***************
*** 2819,2825 **** 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, "
--- 2969,2988 ----
/* 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, "
***************
*** 2998,3004 **** 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, "
--- 3161,3179 ----
/* 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, "
***************
*** 3104,3114 **** 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);
--- 3279,3304 ----
/* 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);
***************
*** 3182,3188 **** getAggregates(int *numAggs)
/* find all user-defined aggregates */
! if (g_fout->remoteVersion >= 80200)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
"pronamespace AS aggnamespace, "
--- 3372,3394 ----
/* 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, "
***************
*** 3328,3334 **** getFuncs(int *numFuncs)
/* find all user-defined funcs */
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
--- 3534,3557 ----
/* 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, "
***************
*** 3516,3523 **** getTables(int *numTables)
"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) "
! "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
"ORDER BY c.oid",
username_subquery,
RELKIND_SEQUENCE,
--- 3739,3750 ----
"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,
***************
*** 4527,4533 **** getRules(int *numRules)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT "
"tableoid, oid, rulename, "
--- 4754,4773 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT "
! "r.tableoid, r.oid, r.rulename, "
! "r.ev_class AS ruletable, r.ev_type, r.is_instead, "
! "r.ev_enabled "
! "FROM pg_rewrite r "
! "LEFT JOIN pg_depend d ON d.objid = r.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 oid");
! }
! else if (g_fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT "
"tableoid, oid, rulename, "
***************
*** 4888,4894 **** 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, "
--- 5128,5149 ----
/* 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, "
***************
*** 5042,5048 **** 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, "
--- 5297,5315 ----
/* 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, "
***************
*** 5636,5645 **** 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);
--- 5903,5926 ----
/* 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);
***************
*** 5718,5728 **** 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);
--- 5999,6024 ----
/* 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);
***************
*** 5799,5807 **** 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);
--- 6095,6116 ----
/* 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);
***************
*** 5873,5882 **** 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);
--- 6182,6205 ----
/* 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);
***************
*** 5949,5961 **** 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);
--- 6272,6301 ----
/* 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);
***************
*** 6033,6046 **** 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);
--- 6373,6404 ----
/* 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);
***************
*** 6517,6522 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6875,6883 ----
{
switch (dobj->objType)
{
+ case DO_EXTENSION:
+ dumpExtension(fout, (ExtensionInfo *) dobj);
+ break;
case DO_NAMESPACE:
dumpNamespace(fout, (NamespaceInfo *) dobj);
break;
***************
*** 6613,6618 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6974,7026 ----
}
/*
+ * 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 SCHEMA %s NO USER DATA ;\n",
+ qextname, strdup(extinfo->namespace));
+
+ 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,147 ----
int allocDeps; /* allocated size of dependencies[] */
} DumpableObject;
+ typedef struct _extensionInfo
+ {
+ DumpableObject dobj;
+ char *extname;
+ char *relocatable;
+ char *extversion;
+ char *namespace;
+ } ExtensionInfo;
+
typedef struct _namespaceInfo
{
DumpableObject dobj;
***************
*** 512,517 **** extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs);
--- 522,528 ----
/*
* 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
***************
*** 492,497 **** exec_command(const char *cmd,
--- 492,507 ----
break;
}
break;
+ case 'x': /* Installed Extensions */
+ if (pattern)
+ success = describeExtension(pattern, show_verbose);
+ else
+ success = listExtensions(show_verbose);
+ break;
+ break;
+ case 'X': /* Available Extensions */
+ success = listAvailableExtensions(pattern);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
}
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 38,43 **** static bool describeOneTSConfig(const char *oid, const char *nspname,
--- 38,45 ----
const char *cfgname,
const char *pnspname, const char *prsname);
static void printACLColumn(PQExpBuffer buf, const char *colname);
+ static bool describeOneExtension(const char *extname,
+ const char *oid, bool verbose);
/*----------------
***************
*** 2822,2827 **** listSchemas(const char *pattern, bool verbose, bool showSystem)
--- 2824,3028 ----
return true;
}
+ /*
+ * \dx
+ *
+ * Describes installed extensions
+ */
+ bool
+ listExtensions(bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\", e.extname AS \"%s\", "
+ "e.extversion AS \"%s\", c.description as \"%s\" "
+ "FROM pg_catalog.pg_extension e "
+ "LEFT JOIN pg_catalog.pg_depend d ""ON d.objid = e.oid "
+ "AND d.refclassid = 'pg_catalog.pg_namespace'::regclass "
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.refobjid "
+ "JOIN pg_catalog.pg_description c ON c.objoid = e.oid "
+ "AND c.classoid = 'pg_catalog.pg_extension'::regclass ",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Version"),
+ gettext_noop("Description"));
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ 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;
+ }
+
+ /*
+ * \dx pattern
+ */
+ bool
+ describeExtension(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ int i;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT e.oid,\n"
+ " e.extname\n"
+ "FROM pg_catalog.pg_extension e\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "e.extname", NULL,
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ if (PQntuples(res) == 0)
+ {
+ if (!pset.quiet)
+ fprintf(stderr, _("Did not find any extension named \"%s\".\n"),
+ pattern);
+ PQclear(res);
+ return false;
+ }
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ const char *oid;
+ const char *extname;
+
+ oid = PQgetvalue(res, i, 0);
+ extname = PQgetvalue(res, i, 1);
+
+ if (!describeOneExtension(extname, oid, verbose))
+ {
+ PQclear(res);
+ return false;
+ }
+ if (cancel_pressed)
+ {
+ PQclear(res);
+ return false;
+ }
+ }
+
+ PQclear(res);
+ return true;
+ }
+
+ bool
+ describeOneExtension(const char *extname, const char *oid, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ char title[1024];
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ if (verbose)
+ {
+ printfPQExpBuffer(&buf,
+ "SELECT x.objid as \"%s\", x.classid as \"%s\", "
+ "x.class as \"%s\", x.objdesc as \"%s\" "
+ " FROM pg_extension_objects('%s') x",
+ gettext_noop("Object OID"),
+ gettext_noop("Object ClassOID"),
+ gettext_noop("Object Class"),
+ gettext_noop("Object Description"),
+ extname);
+ }
+ else
+ {
+ printfPQExpBuffer(&buf,
+ "SELECT x.class as \"%s\", x.objdesc as \"%s\" "
+ " FROM pg_extension_objects('%s') x",
+ gettext_noop("Object Class"),
+ gettext_noop("Object Description"),
+ extname);
+ }
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ sprintf(title, _("Objects in extension \"%s\""), extname);
+ myopt.title = title;
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
+ /*
+ * \dX
+ *
+ * Describes available and installed extensions
+ */
+ bool
+ listAvailableExtensions(const char *pattern)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname as \"%s\", x.name AS \"%s\", "
+ "x.version AS \"%s\", x.comment as \"%s\" "
+ "FROM pg_catalog.pg_available_extensions x "
+ "LEFT JOIN pg_catalog.pg_extension e ON e.extname = x.name "
+ "LEFT JOIN pg_catalog.pg_depend d ON d.objid = e.oid "
+ "AND d.refclassid = 'pg_catalog.pg_namespace'::regclass "
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.refobjid ",
+ gettext_noop("Schema"),
+ gettext_noop("Name"),
+ gettext_noop("Version"),
+ gettext_noop("Description"));
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "x.extname", NULL,
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 2;");
+
+ 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,68 ----
/* \dFt */
extern bool listTSTemplates(const char *pattern, bool verbose);
+ /* \dx */
+ bool listExtensions(bool verbose);
+
+ /* \dx PATTERN */
+ bool describeExtension(const char *pattern, bool verbose);
+
+ /* \dX */
+ bool listAvailableExtensions(const char *pattern);
+
/* \l */
extern bool listAllDbs(bool verbose);
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 221,226 **** slashUsage(unsigned short int pager)
--- 221,229 ----
fprintf(output, _(" \\du[+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
+ fprintf(output, _(" \\dx list installed extensions\n"));
+ fprintf(output, _(" \\dx[+] [PATTERN] list extension's objects\n"));
+ fprintf(output, _(" \\dX [PATTERN] list installed and available 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
***************
*** 101,108 **** typedef enum SharedDependencyType
SHARED_DEPENDENCY_INVALID = 0
} SharedDependencyType;
! /* expansible list of ObjectAddresses (private in dependency.c) */
! typedef struct ObjectAddresses ObjectAddresses;
/*
* This enum covers all system catalogs whose OIDs can appear in
--- 101,131 ----
SHARED_DEPENDENCY_INVALID = 0
} SharedDependencyType;
! /*
! * Deletion processing requires additional state for each ObjectAddress that
! * it's planning to delete. For simplicity and code-sharing we make the
! * ObjectAddresses code support arrays with or without this extra state.
! */
! typedef struct
! {
! int flags; /* bitmask, see bit definitions below */
! ObjectAddress dependee; /* object whose deletion forced this one */
! } ObjectAddressExtra;
!
! /* ObjectAddressExtra flag bits */
! #define DEPFLAG_ORIGINAL 0x0001 /* an original deletion target */
! #define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */
! #define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */
! #define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */
!
! /* expansible list of ObjectAddresses (used in dependency.c and extension.c) */
! typedef struct
! {
! ObjectAddress *refs; /* => palloc'd array */
! ObjectAddressExtra *extras; /* => palloc'd array, or NULL if not used */
! int numrefs; /* current number of references */
! int maxrefs; /* current size of palloc'd array(s) */
! } ObjectAddresses;
/*
* This enum covers all system catalogs whose OIDs can appear in
***************
*** 139,144 **** typedef enum ObjectClass
--- 162,168 ----
OCLASS_USER_MAPPING, /* pg_user_mapping */
OCLASS_FOREIGN_TABLE, /* pg_foreign_table */
OCLASS_DEFACL, /* pg_default_acl */
+ OCLASS_EXTENSION, /* pg_extension */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
***************
*** 168,173 **** extern ObjectClass getObjectClass(const ObjectAddress *object);
--- 192,199 ----
extern char *getObjectDescription(const ObjectAddress *object);
extern char *getObjectDescriptionOids(Oid classid, Oid objid);
+ extern char *getObjectDescriptionOids(Oid classid, Oid objid);
+
extern ObjectAddresses *new_object_addresses(void);
extern void add_exact_object_address(const ObjectAddress *object,
***************
*** 182,187 **** extern void record_object_address_dependencies(const ObjectAddress *depender,
--- 208,215 ----
extern void free_object_addresses(ObjectAddresses *addrs);
+ extern ObjectAddresses *listDependentObjects(ObjectAddress *object);
+
/* in pg_depend.c */
extern void recordDependencyOn(const ObjectAddress *depender,
***************
*** 199,204 **** extern long changeDependencyFor(Oid classId, Oid objectId,
--- 227,236 ----
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);
***************
*** 209,214 **** extern Oid get_constraint_index(Oid constraintId);
--- 241,249 ----
extern Oid get_index_constraint(Oid indexId);
+ extern Oid get_extension_namespace(Oid extensionId);
+
+
/* in pg_shdepend.c */
extern void recordSharedDependencyOn(ObjectAddress *depender,
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 289,294 **** DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol
--- 289,300 ----
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, 3995, on pg_extension using btree(oid oid_ops));
+ #define ExtensionOidIndexId 3995
+
+ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3997, on pg_extension using btree(extname name_ops));
+ #define ExtensionNameIndexId 3997
+
/* 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 3996
+
+ CATALOG(pg_extension,3996)
+ {
+ NameData extname; /* name of the extension */
+ bool relocatable; /* when true we can ALTER EXTENSION SET SCHEMA */
+ text extversion; /* version "name" of 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_relocatable 2
+ #define Anum_pg_extension_extversion 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
***************
*** 3419,3427 **** 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 = 3826 ( pg_read_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_read_file_all _null_ _null_ _null_ ));
DESCR("read text from a file");
--- 3419,3427 ----
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 = 3826 ( pg_read_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_read_file_all _null_ _null_ _null_ ));
DESCR("read text from a file");
***************
*** 3431,3437 **** DATA(insert OID = 3828 ( pg_read_binary_file PGNSP PGUID 12 1 0 0 f f f t f v 1
DESCR("read bytea 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_ ));
--- 3431,3437 ----
DESCR("read bytea 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_ ));
***************
*** 4893,4898 **** DESCR("fetch the last row value");
--- 4893,4907 ----
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 = 3991 ( pg_extensions PGNSP PGUID 12 1 100 0 f f f t t s 0 0 2249 "" "{19,25,16,25,16}" "{o,o,o,o,o}" "{name,version,relocatable,comment,installed}" _null_ pg_extensions _null_ _null_ _null_ ));
+ DESCR("list available and installed extensions");
+ DATA(insert OID = 3992 ( pg_extension_objects PGNSP PGUID 12 1 100 0 f f f t t s 1 0 2249 "19" "{19,2205,26,26,25}" "{i,o,o,o,o}" "{name,class,classid,objid,objdesc}" _null_ pg_extension_objects _null_ _null_ _null_ ));
+ DESCR("list a given extension objects");
+ DATA(insert OID = 3993 ( 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 = 3994 ( 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, 3998, 3999);
DECLARE_TOAST(pg_proc, 2836, 2837);
DECLARE_TOAST(pg_rewrite, 2838, 2839);
DECLARE_TOAST(pg_seclabel, 3598, 3599);
*** a/src/include/commands/alter.h
--- b/src/include/commands/alter.h
***************
*** 14,30 ****
#ifndef ALTER_H
#define ALTER_H
#include "nodes/parsenodes.h"
#include "utils/acl.h"
#include "utils/relcache.h"
extern void ExecRenameStmt(RenameStmt *stmt);
extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
! extern void AlterObjectNamespace(Relation rel, int cacheId,
Oid classId, Oid objid, Oid nspId,
int Anum_name, int Anum_namespace, int Anum_owner,
AclObjectKind acl_kind,
bool superuser_only);
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
#endif /* ALTER_H */
--- 14,32 ----
#ifndef ALTER_H
#define ALTER_H
+ #include "catalog/objectaddress.h"
#include "nodes/parsenodes.h"
#include "utils/acl.h"
#include "utils/relcache.h"
extern void ExecRenameStmt(RenameStmt *stmt);
extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
! extern Oid AlterObjectNamespace(Relation rel, int cacheId,
Oid classId, Oid objid, Oid nspId,
int Anum_name, int Anum_namespace, int Anum_owner,
AclObjectKind acl_kind,
bool superuser_only);
+ extern Oid AlterObjectNamespace_internal(ObjectAddress *thisobj, Oid nspOid);
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
#endif /* ALTER_H */
*** a/src/include/commands/conversioncmds.h
--- b/src/include/commands/conversioncmds.h
***************
*** 23,27 **** extern void RenameConversion(List *name, const char *newname);
--- 23,28 ----
extern void AlterConversionOwner(List *name, Oid newOwnerId);
extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
extern void AlterConversionNamespace(List *name, const char *newschema);
+ extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 66,71 **** extern void DropCast(DropCastStmt *stmt);
--- 66,72 ----
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
const char *newschema);
+ extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
***************
*** 79,84 **** extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
--- 80,87 ----
extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
+ extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
+ extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
***************
*** 102,111 **** extern void RenameOpFamily(List *name, const char *access_method, const char *ne
--- 105,116 ----
extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
extern void AlterOpClassNamespace(List *name, char *access_method, const char *newschema);
+ extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema);
+ extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
/* commands/tsearchcmds.c */
extern void DefineTSParser(List *names, List *parameters);
***************
*** 113,118 **** extern void RenameTSParser(List *oldname, const char *newname);
--- 118,125 ----
extern void AlterTSParserNamespace(List *name, const char *newschema);
extern void RemoveTSParsers(DropStmt *drop);
extern void RemoveTSParserById(Oid prsId);
+ extern void AlterTSParserNamespace(List *name, const char *newschema);
+ extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
extern void DefineTSDictionary(List *names, List *parameters);
extern void RenameTSDictionary(List *oldname, const char *newname);
***************
*** 121,135 **** extern void RemoveTSDictionaryById(Oid dictId);
--- 128,146 ----
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
+ extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
extern void DefineTSTemplate(List *names, List *parameters);
extern void RenameTSTemplate(List *oldname, const char *newname);
extern void AlterTSTemplateNamespace(List *name, const char *newschema);
+ extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
extern void RemoveTSTemplates(DropStmt *stmt);
extern void RemoveTSTemplateById(Oid tmplId);
extern void DefineTSConfiguration(List *names, List *parameters);
extern void RenameTSConfiguration(List *oldname, const char *newname);
+ extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
+ extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
extern void RemoveTSConfigurations(DropStmt *stmt);
extern void RemoveTSConfigurationById(Oid cfgId);
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
*** /dev/null
--- b/src/include/commands/extension.h
***************
*** 0 ****
--- 1,71 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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/dependency.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 CreateExtensionAddress;
+ bool create_extension;
+ bool create_extension_with_user_data;
+
+ typedef struct ExtensionControlFile
+ {
+ char *name; /* name of the extension */
+ char *script; /* filename of the install script to execute */
+ char *version; /* version "name" of the extension */
+ char *comment; /* the control file can also contain a comment */
+ int encoding; /* encoding of the given script */
+ bool relocatable; /* when true, ALTER EXTENSION SET SCHEMA is supported */
+ } ExtensionControlFile;
+
+ /* expansible list of extensions */
+ typedef struct ExtensionList
+ {
+ ExtensionControlFile *exts; /* => palloc'd array */
+ int numrefs; /* current number of extensions */
+ int maxrefs; /* current size of palloc'd array */
+ } ExtensionList;
+
+
+ typedef struct extension_objects_fctx
+ {
+ ObjectAddresses *targetObjects;
+ int i;
+ } extension_objects_fctx;
+
+ extern void CreateExtension(CreateExtensionStmt *stmt);
+ extern void DropExtension(DropExtensionStmt *stmt);
+
+ extern Oid get_extension_oid(const char *extname, bool missing_ok);
+ extern char *get_extension_name(Oid ext_oid);
+ extern bool extension_relocatable_p(Oid ext_oid);
+ extern void RemoveExtensionById(Oid extId);
+ extern void AlterExtensionNamespace(List *name, const char *newschema);
+ extern void AlterExtensionNamespace_oid(Oid extensionOid, Oid nspOid);
+
+
+ #endif /* EXTENSION_H */
*** a/src/include/commands/tablecmds.h
--- b/src/include/commands/tablecmds.h
***************
*** 34,40 **** extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
extern void AlterTableNamespace(RangeVar *relation, const char *newschema,
ObjectType stmttype, LOCKMODE lockmode);
! extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
Oid oldNspOid, Oid newNspOid,
bool hasDependEntry);
--- 34,40 ----
extern void AlterTableNamespace(RangeVar *relation, const char *newschema,
ObjectType stmttype, LOCKMODE lockmode);
! extern Oid AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
Oid oldNspOid, Oid newNspOid,
bool hasDependEntry);
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 41,47 **** extern void AlterTypeOwner(List *names, Oid newOwnerId);
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
bool hasDependEntry);
extern void AlterTypeNamespace(List *names, const char *newschema);
! extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType);
--- 41,48 ----
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
bool hasDependEntry);
extern void AlterTypeNamespace(List *names, const char *newschema);
! extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid);
! extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 354,359 **** typedef enum NodeTag
--- 354,361 ----
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
T_CreateForeignTableStmt,
+ T_CreateExtensionStmt,
+ T_DropExtensionStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1064,1069 **** typedef enum ObjectType
--- 1064,1070 ----
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DOMAIN,
+ OBJECT_EXTENSION,
OBJECT_FDW,
OBJECT_FOREIGN_SERVER,
OBJECT_FOREIGN_TABLE,
***************
*** 1493,1498 **** typedef struct Constraint
--- 1494,1519 ----
} Constraint;
/* ----------------------
+ * Create/Drop Extension Statements
+ * ----------------------
+ */
+
+ typedef struct CreateExtensionStmt
+ {
+ NodeTag type;
+ char *extname;
+ List *options; /* List of DefElem nodes */
+ } 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
***************
*** 1062,1065 **** extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
--- 1062,1071 ----
/* 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);
+ extern Datum pg_extension_objects(PG_FUNCTION_ARGS);
+
#endif /* BUILTINS_H */
*** /dev/null
--- b/src/include/utils/genfile.h
***************
*** 0 ****
--- 1,25 ----
+ /*--------------------------------------------------------------------
+ * 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
+
+ /*
+ * We need this in extension.c in the SRF that lists available extensions.
+ */
+ typedef struct directory_fctx
+ {
+ char *location;
+ DIR *dirdesc;
+ } directory_fctx;
+
+ #endif /* GENFILE_H */
*** a/src/makefiles/pgxs.mk
--- b/src/makefiles/pgxs.mk
***************
*** 82,88 **** ifdef PG_CPPFLAGS
override CPPFLAGS := $(PG_CPPFLAGS) $(CPPFLAGS)
endif
! all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES))
ifdef MODULE_big
# shared library parameters
--- 82,88 ----
override CPPFLAGS := $(PG_CPPFLAGS) $(CPPFLAGS)
endif
! all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES)) $(addsuffix .control, $(EXTENSION))
ifdef MODULE_big
# shared library parameters
***************
*** 93,100 **** 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)'"; \
--- 93,105 ----
all: all-lib
endif # MODULE_big
install: all installdirs
+ ifneq (,$(EXTENSION))
+ @for file in $(addsuffix .control, $(EXTENSION)); do \
+ echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
+ $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
+ done
+ endif # EXTENSION
ifneq (,$(DATA)$(DATA_built))
@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
***************
*** 167,174 **** endif # MODULE_big
uninstall:
! ifneq (,$(DATA)$(DATA_built))
! rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built)))
endif
ifneq (,$(DATA_TSEARCH))
rm -f $(addprefix '$(DESTDIR)$(datadir)/tsearch_data'/, $(notdir $(DATA_TSEARCH)))
--- 172,179 ----
uninstall:
! ifneq (,$(DATA)$(DATA_built)$(EXTENSION))
! rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built) $(addsuffix .control, $(EXTENSION))))
endif
ifneq (,$(DATA_TSEARCH))
rm -f $(addprefix '$(DESTDIR)$(datadir)/tsearch_data'/, $(notdir $(DATA_TSEARCH)))
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1279,1284 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
--- 1279,1285 ----
viewname | definition
-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
+ pg_available_extensions | SELECT e.name, e.version, e.relocatable, e.comment, e.installed FROM pg_extensions() e(name, version, relocatable, comment, installed);
pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS tablespace, pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
***************
*** 100,105 **** SELECT relname, relhasindex
--- 100,106 ----
pg_depend | t
pg_description | t
pg_enum | t
+ pg_extension | t
pg_foreign_data_wrapper | t
pg_foreign_server | t
pg_foreign_table | t