*** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** *** 2767,2772 **** --- 2767,2827 ---- + + <structname>pg_extension</structname> + + + pg_extension + + + + The catalog pg_extension stores information + about the installed extension. See for + details about extensions. + + + + <structname>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. + + + + +
+
+ + <structname>pg_foreign_data_wrapper</structname> *************** *** 6083,6088 **** --- 6138,6148 ---- + pg_available_extensions + available and installed extensions + + + pg_group groups of database users *************** *** 6292,6297 **** --- 6352,6428 ---- + + <structname>pg_available_extensions</structname> + + + pg_available_extensions + + + + The pg_available_extensions view lists the + extensions that are currently available and installed. + + + + <structname>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. + + +
+ <structname>pg_group</structname> *** 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 + + + Name Return Type Description + + + + + + + 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 ---- void Obtain 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