+ functions, new operators, and probably new index operator classes.
+ your database.
+ that might contain more or different objects than the old version.
+ other files available when loading such a dump into a new database.
+ contained in an extension, except by dropping the whole extension.
+ you concurrently make the same change in the extension's script file.
+ defaults can be overridden by the control file.
+ a single word or number.
+ directory.
+ The version of the extension. Any string can be given.
+ command.
+ extensions must be installed before this one can be installed.
+ be specified if the script file contains any non-ASCII characters.
+ database it is loaded into.
+ extension is not relocatable.
+ See below for more information.
+ This parameter can only be set for non-relocatable extensions.
+ and not any other. See below for more information.
+ script file is implicitly executed within a transaction block.
+ within the script file to set the comment.
- to the backend; these C functions can then call C++ functions.
- C++ code.
+ at any time, even after it's been loaded into a database.
+ file.
- in such cases, you must check for failures in your C++ code, e.g.
- unroll a C++ call stack with non-POD objects.
+ of limited use since the schema name is determined by the control file.
+ already exist, but in the other two cases it must already exist.
+ visible to the new extension's script file.
+ extensions.
+ and reload.
+ Any number of tables can be marked this way.
+SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');
+ in the rows created by the extension's script.
+ configuration table to ensure that modified rows are marked correctly.
+ values are automatically coerced to text for storage.
- Add data and documentation installation location control to
- linkend="xfunc-c-pgxs">
PGXS> Makefiles
- (Mark Cave-Ayland)
+ Add data and documentation installation location control to
+
PGXS> Makefiles (Mark Cave-Ayland)
&dfunc;
-
-
Extension Building Infrastructure
-
-
-
-
- If you are thinking about distributing your
-
PostgreSQL> extension modules, setting up a
- portable build system for them can be fairly difficult. Therefore
- the
PostgreSQL> installation provides a build
- infrastructure for extensions, called
PGXS , so
- that simple extension modules can be built simply against an
- already installed server. Note that this infrastructure is not
- intended to be a universal build system framework that can be used
- to build all software interfacing to
PostgreSQL>;
- it simply automates common build rules for simple server extension
- modules. For more complicated packages, you need to write your
- own build system.
-
-
- To use the infrastructure for your extension, you must write a
- simple makefile. In that makefile, you need to set some variables
- and finally include the global
PGXS makefile.
- Here is an example that builds an extension module named
- isbn_issn consisting of a shared library, an
- SQL script, and a documentation text file:
-MODULES = isbn_issn
-DATA_built = isbn_issn.sql
-DOCS = README.isbn_issn
-
-PG_CONFIG = pg_config
-PGXS := $(shell $(PG_CONFIG) --pgxs)
-include $(PGXS)
-
- The last three lines should always be the same. Earlier in the
- file, you assign variables or add custom
-
-
- Set one of these three variables to specify what is built:
-
-
-
- MODULES
-
- list of shared objects to be built from source files with same
- stem (do not include suffix in this list)
-
-
-
-
-
- MODULE_big
-
- a shared object to build from multiple source files
- (list object files in OBJS )
-
-
-
-
-
- PROGRAM
-
- a binary program to build
- (list object files in OBJS )
-
-
-
-
-
- The following variables can also be set:
-
-
-
- MODULEDIR
-
- subdirectory into which DATA and DOCS files should be
- installed (if not set, default is contrib )
-
-
-
-
-
- DATA
-
- random files to install into prefix /share/$MODULEDIR
-
-
-
-
-
- DATA_built
-
- random files to install into
- prefix /share/$MODULEDIR ,
- which need to be built first
-
-
-
-
-
- DATA_TSEARCH
-
- random files to install under
- prefix /share/tsearch_data
-
-
-
-
-
- DOCS
-
- random files to install under
- prefix /doc/$MODULEDIR
-
-
-
-
-
- SCRIPTS
-
- script files (not binaries) to install into
- prefix /bin
-
-
-
-
-
- SCRIPTS_built
-
- script files (not binaries) to install into
- prefix /bin ,
- which need to be built first
-
-
-
-
-
- REGRESS
-
- list of regression test cases (without suffix), see below
-
-
-
-
-
- EXTRA_CLEAN
-
- extra files to remove in make clean
-
-
-
-
-
- PG_CPPFLAGS
-
- will be added to CPPFLAGS
-
-
-
-
-
- PG_LIBS
-
- will be added to PROGRAM link line
-
-
-
-
-
- SHLIB_LINK
-
- will be added to MODULE_big link line
-
-
-
-
-
- PG_CONFIG
-
- path to
pg_config> program for the
-
PostgreSQL installation to build against
- (typically just pg_config> to use the first one in your
- PATH>)
-
-
-
-
-
-
- Put this makefile as Makefile in the directory
- which holds your extension. Then you can do
- make to compile, and later make
- install to install your module. By default, the extension is
- compiled and installed for the
-
PostgreSQL installation that
- corresponds to the first pg_config program
- found in your path. You can use a different installation by
- setting PG_CONFIG to point to its
- pg_config program, either within the makefile
- or on the make command line.
-
-
-
- Changing PG_CONFIG only works when building
- against
PostgreSQL 8.3 or later.
- With older releases it does not work to set it to anything except
- pg_config>; you must alter your PATH>
- to select the installation to build against.
-
-
-
- The scripts listed in the REGRESS> variable are used for
- regression testing of your module, just like make
- installcheck is used for the main
-
PostgreSQL server. For this to work you need
- to have a subdirectory named sql/ in your extension's
- directory, within which you put one file for each group of tests you want
- to run. The files should have extension .sql , which
- should not be included in the REGRESS list in the
- makefile. For each test there should be a file containing the expected
- result in a subdirectory named expected/ , with extension
- .out . The tests are run by executing make
- installcheck, and the resulting output will be compared to the
- expected files. The differences will be written to the file
- regression.diffs in diff -c format.
- Note that trying to run a test which is missing the expected file will be
- reported as trouble
, so make sure you have all expected
- files.
-
-
-
- The easiest way of creating the expected files is creating empty files,
- then carefully inspecting the result files after a test run (to be found
- in the results/ directory), and copying them to
- expected/ if they match what you want from the test.
-
-
-
-
-
-
Composite-type Arguments
+
+
+
Using C++ for Extensibility
+
+
+
+
+ Although the
PostgreSQL backend is written in
+ C, it is possible to write extensions in C++ if these guidelines are
+ followed:
+
+
+
+ All functions accessed by the backend must present a C interface
+ to the backend; these C functions can then call C++ functions.
+ For example, extern C> linkage is required for
+ backend-accessed functions. This is also necessary for any
+ functions that are passed as pointers between the backend and
+ C++ code.
+
+
+
+ Free memory using the appropriate deallocation method. For example,
+ most backend memory is allocated using palloc()>, so use
+ pfree()> to free it. Using C++
+ delete> in such cases will fail.
+
+
+
+ Prevent exceptions from propagating into the C code (use a catch-all
+ block at the top level of all extern C> functions). This
+ is necessary even if the C++ code does not explicitly throw any
+ exceptions, because events like out-of-memory can still throw
+ exceptions. Any exceptions must be caught and appropriate errors
+ passed back to the C interface. If possible, compile C++ with
+ -fno-exceptions> to eliminate exceptions entirely; in such
+ cases, you must check for failures in your C++ code, e.g. check for
+ NULL returned by new()>.
+
+
+
+ If calling backend functions from C++ code, be sure that the
+ C++ call stack contains only plain old data structures
+ (
POD>). This is necessary because backend errors
+ generate a distant longjmp()> that does not properly
+ unroll a C++ call stack with non-POD objects.
+
+
+
+
+
+ In summary, it is best to place C++ code behind a wall of
+ extern C> functions that interface to the backend,
+ and avoid exception, memory, and call stack leakage.
+
+
+
pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
- pg_ts_parser.h pg_ts_template.h \
+ pg_ts_parser.h pg_ts_template.h pg_extension.h \
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_collation.h \
#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"
#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"
#define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */
#define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */
#define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */
+#define DEPFLAG_EXTENSION 0x0010 /* reached via extension dependency */
/* expansible list of ObjectAddresses */
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
- ForeignTableRelationId, /* OCLASS_FOREIGN_TABLE */
- DefaultAclRelationId /* OCLASS_DEFACL */
+ DefaultAclRelationId, /* OCLASS_DEFACL */
+ ExtensionRelationId /* OCLASS_EXTENSION */
};
/* no problem */
break;
case DEPENDENCY_INTERNAL:
+ case DEPENDENCY_EXTENSION:
/*
* This object is part of the internal implementation of
- * another object. We have three cases:
+ * another object, or is part of the extension that is the
+ * other object. We have three cases:
*
* 1. At the outermost recursion level, disallow the DROP. (We
* just ereport here, rather than proceeding, since no other
case DEPENDENCY_INTERNAL:
subflags = DEPFLAG_INTERNAL;
break;
+ case DEPENDENCY_EXTENSION:
+ subflags = DEPFLAG_EXTENSION;
+ break;
case DEPENDENCY_PIN:
/*
/*
* If, at any stage of the recursive search, we reached the object via
- * an AUTO or INTERNAL dependency, then it's okay to delete it even in
- * RESTRICT mode.
+ * an AUTO, INTERNAL, or EXTENSION dependency, then it's okay to
+ * delete it even in RESTRICT mode.
*/
- if (extra->flags & (DEPFLAG_AUTO | DEPFLAG_INTERNAL))
+ if (extra->flags & (DEPFLAG_AUTO |
+ DEPFLAG_INTERNAL |
+ DEPFLAG_EXTENSION))
{
/*
* auto-cascades are reported at DEBUG2, not msglevel. We don't
RemoveDefaultACLById(object->objectId);
break;
+ case OCLASS_EXTENSION:
+ RemoveExtensionById(object->objectId);
+ break;
+
default:
elog(ERROR, "unrecognized object class: %u",
object->classId);
case UserMappingRelationId:
return OCLASS_USER_MAPPING;
- case ForeignTableRelationId:
- Assert(object->objectSubId == 0);
- return OCLASS_FOREIGN_TABLE;
-
case DefaultAclRelationId:
return OCLASS_DEFACL;
+
+ case ExtensionRelationId:
+ return OCLASS_EXTENSION;
}
/* shouldn't get here */
break;
}
+ case OCLASS_EXTENSION:
+ {
+ char *extname;
+
+ extname = get_extension_name(object->objectId);
+ if (!extname)
+ elog(ERROR, "cache lookup failed for extension %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("extension %s"), extname);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
* entry, so we needn't record them here. Likewise, TOAST tables don't
* need a namespace dependency (they live in a pinned namespace) nor an
* owner dependency (they depend indirectly through the parent table), nor
- * should they have any ACL entries.
+ * should they have any ACL entries. The same applies for extension
+ * dependencies.
*
* Also, skip this in bootstrap mode, since we don't make dependencies
* while bootstrapping.
recordDependencyOnOwner(RelationRelationId, relid, ownerid);
+ recordDependencyOnCurrentExtension(&myself);
+
if (reloftypeid)
{
referenced.classId = TypeRelationId;
#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"
#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"
address = get_object_address_relobject(objtype, objname, &relation);
break;
case OBJECT_DATABASE:
+ case OBJECT_EXTENSION:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
case OBJECT_SCHEMA:
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;
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);
case TSConfigRelationId:
cache = TSCONFIGOID;
break;
+ case ExtensionRelationId:
+ indexoid = ExtensionOidIndexId;
+ break;
default:
elog(ERROR, "unrecognized classid: %u", address.classId);
}
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* Post creation hook for new conversion */
InvokeObjectAccessHook(OAT_POST_CREATE,
ConversionRelationId, HeapTupleGetOid(tup), 0);
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
+#include "catalog/pg_extension.h"
+#include "commands/extension.h"
#include "miscadmin.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
heap_close(dependDesc, RowExclusiveLock);
}
+/*
+ * If we are executing a CREATE EXTENSION operation, mark the given object
+ * as being a member of the extension. Otherwise, do nothing.
+ *
+ * This must be called during creation of any user-definable object type
+ * that could be a member of an extension.
+ */
+void
+recordDependencyOnCurrentExtension(const ObjectAddress *object)
+{
+ if (creating_extension)
+ {
+ ObjectAddress extension;
+
+ extension.classId = ExtensionRelationId;
+ extension.objectId = CurrentExtensionObject;
+ extension.objectSubId = 0;
+
+ recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
+ }
+}
+
/*
* deleteDependencyRecordsFor -- delete all records with given depender
* classId/objectId. Returns the number of records deleted.
* This is used when redefining an existing object. Links leading to the
* object do not change, and links leading from it will be recreated
* (possibly with some differences from before).
+ *
+ * If skipExtensionDeps is true, we do not delete any dependencies that
+ * show that the given object is a member of an extension. This avoids
+ * needing a lot of extra logic to fetch and recreate that dependency.
*/
long
-deleteDependencyRecordsFor(Oid classId, Oid objectId)
+deleteDependencyRecordsFor(Oid classId, Oid objectId,
+ bool skipExtensionDeps)
{
long count = 0;
Relation depRel;
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
+ if (skipExtensionDeps &&
+ ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
+ continue;
+
simple_heap_delete(depRel, &tup->t_self);
count++;
}
*/
+/*
+ * Find the extension containing the specified object, if any
+ *
+ * Returns the OID of the extension, or InvalidOid if the object does not
+ * belong to any extension.
+ *
+ * Extension membership is marked by an EXTENSION dependency from the object
+ * to the extension. Note that the result will be indeterminate if pg_depend
+ * contains links from this object to more than one extension ... but that
+ * should never happen.
+ */
+Oid
+getExtensionOfObject(Oid classId, Oid objectId)
+{
+ Oid result = InvalidOid;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = heap_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == ExtensionRelationId &&
+ depform->deptype == DEPENDENCY_EXTENSION)
+ {
+ result = depform->refobjid;
+ break; /* no need to keep scanning */
+ }
+ }
+
+ systable_endscan(scan);
+
+ heap_close(depRel, AccessShareLock);
+
+ return result;
+}
+
/*
* Detect whether a sequence is marked as "owned" by a column
*
Datum values[Natts_pg_namespace];
NameData nname;
TupleDesc tupDesc;
+ ObjectAddress myself;
int i;
/* sanity checks */
heap_close(nspdesc, RowExclusiveLock);
- /* Record dependency on owner */
+ /* Record dependencies */
+ myself.classId = NamespaceRelationId;
+ myself.objectId = nspoid;
+ myself.objectSubId = 0;
+
+ /* dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* Post creation hook for new schema */
InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
myself.objectSubId = 0;
/* In case we are updating a shell, delete any existing entries */
- deleteDependencyRecordsFor(myself.classId, myself.objectId);
+ deleteDependencyRecordsFor(myself.classId, myself.objectId, false );
deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
/* Dependency on namespace */
/* Dependency on owner */
recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
oper->oprowner);
+
+ /* Dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
}
* Create dependencies for the new function. If we are updating an
* existing function, first delete any existing pg_depend entries.
* (However, since we are not changing ownership or permissions, the
- * shared dependencies do *not* need to change, and we leave them alone.)
+ * shared dependencies do *not* need to change, and we leave them alone.
+ * We also don't change any pre-existing extension-membership dependency.)
*/
if (is_update)
- deleteDependencyRecordsFor(ProcedureRelationId, retval);
+ deleteDependencyRecordsFor(ProcedureRelationId, retval, true );
myself.classId = ProcedureRelationId;
myself.objectId = retval;
nnewmembers, newmembers);
}
+ /* dependency on extension */
+ if (!is_update)
+ recordDependencyOnCurrentExtension(&myself);
+
heap_freetuple(tup);
/* Post creation hook for new function */
*
* If rebuild is true, we remove existing dependencies and rebuild them
* from scratch. This is needed for ALTER TYPE, and also when replacing
- * a shell type.
+ * a shell type. We don't remove/rebuild extension dependencies, though.
*/
void
GenerateTypeDependencies(Oid typeNamespace,
if (rebuild)
{
- deleteDependencyRecordsFor(TypeRelationId, typeObjectId);
+ deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true );
deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0);
}
* For a relation rowtype (that's not a composite type), we should skip
* these because we'll depend on them indirectly through the pg_class
* entry. Likewise, skip for implicit arrays since we'll depend on them
- * through the element type.
+ * through the element type. The same goes for extension membership.
*/
if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) &&
!isImplicitArray)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnOwner(TypeRelationId, typeObjectId, owner);
+
+ /* dependency on extension */
+ if (!rebuild)
+ recordDependencyOnCurrentExtension(&myself);
}
/* Normal dependencies on the I/O functions */
CREATE VIEW pg_cursors AS
SELECT * FROM pg_cursor() AS C;
+CREATE VIEW pg_available_extensions AS
+ SELECT E.name, E.version, X.extversion AS installed,
+ N.nspname AS schema, E.relocatable, E.comment
+ FROM pg_available_extensions() AS E
+ LEFT JOIN pg_extension AS X ON E.name = X.extname
+ LEFT JOIN pg_namespace AS N on N.oid = X.extnamespace;
+
CREATE VIEW pg_prepared_xacts AS
SELECT P.transaction, P.gid, P.prepared,
U.rolname AS owner, D.datname AS database
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 \
+ dbcommands.o define.o discard.o explain.o extension.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 \
#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"
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);
}
}
+/*
+ * Change an object's namespace given its classOid and object Oid.
+ *
+ * Objects that don't have a namespace should be ignored.
+ *
+ * This function is currently used only by ALTER EXTENSION SET SCHEMA,
+ * so it only needs to cover object types that can be members of an
+ * extension, and it doesn't have to deal with certain special cases
+ * such as not wanting to process array types --- those should never
+ * be direct members of an extension anyway.
+ *
+ * Returns the OID of the object's previous namespace, or InvalidOid if
+ * object doesn't have a schema.
+ */
+Oid
+AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
+{
+ Oid oldNspOid = InvalidOid;
+ ObjectAddress dep;
+
+ dep.classId = classId;
+ dep.objectId = objid;
+ dep.objectSubId = 0;
+
+ switch (getObjectClass(&dep))
+ {
+ case OCLASS_CLASS:
+ {
+ Relation rel;
+ Relation classRel;
+
+ rel = relation_open(objid, AccessExclusiveLock);
+ oldNspOid = RelationGetNamespace(rel);
+
+ classRel = heap_open(RelationRelationId, RowExclusiveLock);
+
+ AlterRelationNamespaceInternal(classRel,
+ objid,
+ oldNspOid,
+ nspOid,
+ true);
+
+ heap_close(classRel, RowExclusiveLock);
+
+ relation_close(rel, NoLock);
+ break;
+ }
+
+ case OCLASS_PROC:
+ oldNspOid = AlterFunctionNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TYPE:
+ oldNspOid = AlterTypeNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_CONVERSION:
+ oldNspOid = AlterConversionNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPERATOR:
+ oldNspOid = AlterOperatorNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPCLASS:
+ oldNspOid = AlterOpClassNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPFAMILY:
+ oldNspOid = AlterOpFamilyNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSPARSER:
+ oldNspOid = AlterTSParserNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSDICT:
+ oldNspOid = AlterTSDictionaryNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSTEMPLATE:
+ oldNspOid = AlterTSTemplateNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSCONFIG:
+ oldNspOid = AlterTSConfigurationNamespace_oid(objid, nspOid);
+ break;
+
+ default:
+ break;
+ }
+
+ return oldNspOid;
+}
+
/*
* Generic function to change the namespace of a given object, for simple
- * cases (won't work for tables or functions, objects which have more than 2
- * key-attributes to use when searching for their syscache entries --- we
- * don't want nor need to get this generic here).
+ * cases (won't work for tables, nor other cases where we need to do more
+ * than change the namespace column of a single catalog entry).
*
* The AlterFooNamespace() calls just above will call a function whose job
* is to lookup the arguments for the generic function here.
*
- * Relation must already by open, it's the responsibility of the caller to
- * close it.
+ * rel: catalog relation containing object (RowExclusiveLock'd by caller)
+ * oidCacheId: syscache that indexes this catalog by OID
+ * nameCacheId: syscache that indexes this catalog by name and namespace
+ * (pass -1 if there is none)
+ * objid: OID of object to change the namespace of
+ * nspOid: OID of new namespace
+ * Anum_name: column number of catalog's name column
+ * Anum_namespace: column number of catalog's namespace column
+ * Anum_owner: column number of catalog's owner column, or -1 if none
+ * acl_kind: ACL type for object, or -1 if none assigned
+ *
+ * If the object does not have an owner or permissions, pass -1 for
+ * Anum_owner and acl_kind. In this case the calling user must be superuser.
+ *
+ * Returns the OID of the object's previous namespace.
*/
-vo id
-AlterObjectNamespace(Relation rel, int c acheId,
- Oid classId, Oid objid, Oid nspOid,
+O id
+AlterObjectNamespace(Relation rel, int oidCacheId, int nameC acheId,
+ Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
- AclObjectKind acl_kind,
- bool superuser_only)
+ AclObjectKind acl_kind)
{
+ Oid classId = RelationGetRelid(rel);
Oid oldNspOid;
Datum name, namespace;
bool isnull;
- HeapTuple tup, newtup = NULL ;
+ HeapTuple tup, newtup;
Datum *values;
bool *nulls;
bool *replaces;
- tup = SearchSysCacheCopy1(c acheId, ObjectIdGetDatum(objid));
+ tup = SearchSysCacheCopy1(oidC acheId, ObjectIdGetDatum(objid));
if (!HeapTupleIsValid(tup)) /* should not happen */
- elog(ERROR, "cache lookup failed for object %u: %s ",
- objid, getObjectDescriptionOids(classId, objid ));
+ elog(ERROR, "cache lookup failed for object %u of catalog \"%s\" ",
+ objid, RelationGetRelationName(rel ));
- name = heap_getattr(tup, Anum_name, rel->rd_att, &isnull);
- namespace = heap_getattr(tup, Anum_namespace, rel->rd_att, &isnull);
+ name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
+ namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
oldNspOid = DatumGetObjectId(namespace);
/* Check basic namespace related issues */
CheckSetNamespace(oldNspOid, nspOid, classId, objid);
- /* check for duplicate name (more friendly than unique-index failure) */
- if (SearchSysCacheExists2(cacheId, name, ObjectIdGetDatum(nspOid)))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("%s already exists in schema \"%s\"",
- getObjectDescriptionOids(classId, objid),
- get_namespace_name(nspOid))));
-
- /* Superusers can always do it */
+ /* Permission checks ... superusers can always do it */
if (!superuser())
{
Datum owner;
Oid ownerId;
AclResult aclresult;
- if (superuser_only)
+ /* Fail if object does not have an explicit owner */
+ if (Anum_owner <= 0)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to SET SCHEMA of %s",
getObjectDescriptionOids(classId, objid)))));
/* Otherwise, must be owner of the existing object */
- owner = heap_getattr(tup, Anum_owner, rel->rd_att, &isnull);
+ owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
ownerId = DatumGetObjectId(owner);
if (!has_privs_of_role(GetUserId(), ownerId))
aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind,
NameStr(*(DatumGetName(name))));
- /* owner must have CREATE privilege on namespace */
- aclresult = pg_namespace_aclcheck(oldN spOid, GetUserId(), ACL_CREATE);
+ /* User must have CREATE privilege on new namespace */
+ aclresult = pg_namespace_aclcheck(n spOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
- get_namespace_name(oldN spOid));
+ get_namespace_name(n spOid));
}
- /* Prepare to update tuple */
- values = palloc0(rel->rd_att->natts * sizeof(Datum));
- nulls = palloc0(rel->rd_att->natts * sizeof(bool));
- replaces = palloc0(rel->rd_att->natts * sizeof(bool));
- values[Anum_namespace - 1] = nspOid;
+ /*
+ * Check for duplicate name (more friendly than unique-index failure).
+ * Since this is just a friendliness check, we can just skip it in cases
+ * where there isn't a suitable syscache available.
+ */
+ if (nameCacheId >= 0 &&
+ SearchSysCacheExists2(nameCacheId, name, ObjectIdGetDatum(nspOid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("%s already exists in schema \"%s\"",
+ getObjectDescriptionOids(classId, objid),
+ get_namespace_name(nspOid))));
+
+ /* Build modified tuple */
+ values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
+ nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
+ replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
+ values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
replaces[Anum_namespace - 1] = true;
- newtup = heap_modify_tuple(tup, rel->rd_att, values, nulls, replaces);
+ newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
+ values, nulls, replaces);
/* Perform actual update */
simple_heap_update(rel, &tup->t_self, newtup);
/* update dependencies to point to the new schema */
changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid);
+
+ return oldNspOid;
}
if (relform1->reltoastrelid)
{
count = deleteDependencyRecordsFor(RelationRelationId,
- relform1->reltoastrelid);
+ relform1->reltoastrelid,
+ false);
if (count != 1)
elog(ERROR, "expected one dependency record for TOAST table, found %ld",
count);
if (relform2->reltoastrelid)
{
count = deleteDependencyRecordsFor(RelationRelationId,
- relform2->reltoastrelid);
+ relform2->reltoastrelid,
+ false);
if (count != 1)
elog(ERROR, "expected one dependency record for TOAST table, found %ld",
count);
(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,
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, CONVOID, ConversionRelationId, convOid, nspOid,
+ AlterObjectNamespace(rel, CONVOID, CONNAMENSP,
+ convOid, nspOid,
Anum_pg_conversion_conname,
Anum_pg_conversion_connamespace,
Anum_pg_conversion_conowner,
- ACL_KIND_CONVERSION,
- false);
+ ACL_KIND_CONVERSION);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * 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, CONNAMENSP,
+ convOid, newNspOid,
+ Anum_pg_conversion_conname,
+ Anum_pg_conversion_connamespace,
+ Anum_pg_conversion_conowner,
+ ACL_KIND_CONVERSION);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * extension.c
+ * Commands to manipulate extensions
+ *
+ * Extensions in PostgreSQL allow management of collections of SQL objects.
+ *
+ * All we need internally to manage an extension is an OID so that the
+ * dependent objects can be associated with it. An extension is created by
+ * populating the pg_extension catalog from a "control" file.
+ * The extension control file is parsed with the same parser we use for
+ * postgresql.conf and recovery.conf. An extension also has an installation
+ * script file, containing SQL commands to create the extension's objects.
+ *
+ * Portions Copyright (c) 1996-2011, 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 "access/sysattr.h"
+#include "access/xact.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 "commands/trigger.h"
+#include "executor/executor.h"
+#include "funcapi.h"
+#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "tcop/tcopprot.h"
+#include "tcop/utility.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/tqual.h"
+
+
+bool creating_extension = false;
+Oid CurrentExtensionObject = InvalidOid;
+
+/*
+ * Internal data structure to hold the results of parsing a control file
+ */
+typedef struct ExtensionControlFile
+{
+ char *name; /* name of the extension */
+ char *script; /* filename of the installation script */
+ char *version; /* version ID, if any */
+ char *comment; /* comment, if any */
+ char *schema; /* target schema (allowed if !relocatable) */
+ bool relocatable; /* is ALTER EXTENSION SET SCHEMA supported? */
+ int encoding; /* encoding of the script file, or -1 */
+ List *requires; /* names of prerequisite extensions */
+} ExtensionControlFile;
+
+
+/*
+ * 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;
+ SysScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_extension_extname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(extname));
+
+ scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
+ SnapshotNow, 1, entry);
+
+ tuple = systable_getnext(scandesc);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = HeapTupleGetOid(tuple);
+ else
+ result = InvalidOid;
+
+ systable_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;
+ SysScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(ext_oid));
+
+ scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
+ SnapshotNow, 1, entry);
+
+ tuple = systable_getnext(scandesc);
+
+ /* 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;
+
+ systable_endscan(scandesc);
+
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * get_extension_schema - given an extension OID, fetch its extnamespace
+ *
+ * Returns InvalidOid if no such extension.
+ */
+static Oid
+get_extension_schema(Oid ext_oid)
+{
+ Oid result;
+ Relation rel;
+ SysScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(ext_oid));
+
+ scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
+ SnapshotNow, 1, entry);
+
+ tuple = systable_getnext(scandesc);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
+ else
+ result = InvalidOid;
+
+ systable_endscan(scandesc);
+
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * Utility functions to handle extension-related path names
+ */
+static bool
+is_extension_control_filename(const char *filename)
+{
+ const char *extension = strrchr(filename, '.');
+
+ return (extension != NULL) && (strcmp(extension, ".control") == 0);
+}
+
+static char *
+get_extension_control_directory(void)
+{
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ get_share_path(my_exec_path, sharepath);
+ result = (char *) 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 = (char *) 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. If 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 pstrdup(filename);
+
+ get_share_path(my_exec_path, sharepath);
+ result = (char *) palloc(MAXPGPATH);
+ snprintf(result, MAXPGPATH, "%s/contrib/%s", sharepath, filename);
+
+ return result;
+}
+
+
+/*
+ * Read the control file for the specified extension.
+ *
+ * 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 worry about
+ * memory allocation risks here. Also note that we don't worry about
+ * what encoding it's in; all values are expected to be ASCII.
+ */
+static ExtensionControlFile *
+read_extension_control_file(const char *extname)
+{
+ char *filename = get_extension_control_filename(extname);
+ FILE *file;
+ ExtensionControlFile *control;
+ ConfigVariable *item,
+ *head = NULL,
+ *tail = NULL;
+
+ /*
+ * Parse the file content, using GUC's file parsing code
+ */
+ if ((file = AllocateFile(filename, "r")) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open extension control file \"%s\": %m",
+ filename)));
+
+ ParseConfigFp(file, filename, 0, ERROR, &head, &tail);
+
+ FreeFile(file);
+
+ /*
+ * Set up default values. Pointer fields are initially null.
+ */
+ control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
+ control->name = pstrdup(extname);
+ control->relocatable = false;
+ control->encoding = -1;
+
+ /*
+ * Convert the ConfigVariable list into ExtensionControlFile entries.
+ */
+ for (item = head; item != NULL; 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, "schema") == 0)
+ {
+ control->schema = pstrdup(item->value);
+ }
+ else if (strcmp(item->name, "relocatable") == 0)
+ {
+ if (!parse_bool(item->value, &control->relocatable))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" requires a Boolean value",
+ item->name)));
+ }
+ 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",
+ item->value)));
+ }
+ else if (strcmp(item->name, "requires") == 0)
+ {
+ /* Need a modifiable copy of string */
+ char *rawnames = pstrdup(item->value);
+
+ /* Parse string into list of identifiers */
+ if (!SplitIdentifierString(rawnames, ',', &control->requires))
+ {
+ /* syntax error in name list */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" must be a list of extension names",
+ item->name)));
+ }
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized parameter \"%s\" in file \"%s\"",
+ item->name, filename)));
+ }
+
+ FreeConfigVariables(head);
+
+ if (control->relocatable && control->schema != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
+
+ /*
+ * script defaults to ${extension-name}.sql
+ */
+ if (control->script == NULL)
+ {
+ char script[MAXPGPATH];
+
+ snprintf(script, MAXPGPATH, "%s.sql", control->name);
+ control->script = pstrdup(script);
+ }
+
+ return control;
+}
+
+/*
+ * Read the SQL script into a string, and convert to database encoding
+ */
+static char *
+read_extension_script_file(const ExtensionControlFile *control,
+ const char *filename)
+{
+ int src_encoding;
+ int dest_encoding = GetDatabaseEncoding();
+ bytea *content;
+ char *src_str;
+ char *dest_str;
+ int len;
+
+ content = read_binary_file(filename, 0, -1);
+
+ /* use database encoding if not given */
+ if (control->encoding < 0)
+ src_encoding = dest_encoding;
+ else
+ src_encoding = control->encoding;
+
+ /* make sure that source string is valid in the expected encoding */
+ 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 no conversion happened, we have to arrange for null termination */
+ if (dest_str == src_str)
+ {
+ dest_str = (char *) palloc(len + 1);
+ memcpy(dest_str, src_str, len);
+ dest_str[len] = '\0';
+ }
+
+ return dest_str;
+}
+
+/*
+ * Execute given SQL string.
+ *
+ * filename is used only to report errors.
+ *
+ * Note: it's tempting to just use SPI to execute the string, but that does
+ * not work very well. The really serious problem is that SPI will parse,
+ * analyze, and plan the whole string before executing any of it; of course
+ * this fails if there are any plannable statements referring to objects
+ * created earlier in the script. A lesser annoyance is that SPI insists
+ * on printing the whole string as errcontext in case of any error, and that
+ * could be very long.
+ */
+static void
+execute_sql_string(const char *sql, const char *filename)
+{
+ List *raw_parsetree_list;
+ DestReceiver *dest;
+ ListCell *lc1;
+
+ /*
+ * Parse the SQL string into a list of raw parse trees.
+ */
+ raw_parsetree_list = pg_parse_query(sql);
+
+ /* All output from SELECTs goes to the bit bucket */
+ dest = CreateDestReceiver(DestNone);
+
+ /*
+ * Do parse analysis, rule rewrite, planning, and execution for each raw
+ * parsetree. We must fully execute each query before beginning parse
+ * analysis on the next one, since there may be interdependencies.
+ */
+ foreach(lc1, raw_parsetree_list)
+ {
+ Node *parsetree = (Node *) lfirst(lc1);
+ List *stmt_list;
+ ListCell *lc2;
+
+ stmt_list = pg_analyze_and_rewrite(parsetree,
+ sql,
+ NULL,
+ 0);
+ stmt_list = pg_plan_queries(stmt_list, 0, NULL);
+
+ foreach(lc2, stmt_list)
+ {
+ Node *stmt = (Node *) lfirst(lc2);
+
+ if (IsA(stmt, TransactionStmt))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("transaction control statements are not allowed within an extension script")));
+
+ CommandCounterIncrement();
+
+ PushActiveSnapshot(GetTransactionSnapshot());
+
+ if (IsA(stmt, PlannedStmt) &&
+ ((PlannedStmt *) stmt)->utilityStmt == NULL)
+ {
+ QueryDesc *qdesc;
+
+ qdesc = CreateQueryDesc((PlannedStmt *) stmt,
+ sql,
+ GetActiveSnapshot(), NULL,
+ dest, NULL, 0);
+
+ AfterTriggerBeginQuery();
+ ExecutorStart(qdesc, 0);
+ ExecutorRun(qdesc, ForwardScanDirection, 0);
+ AfterTriggerEndQuery(qdesc->estate);
+ ExecutorEnd(qdesc);
+
+ FreeQueryDesc(qdesc);
+ }
+ else
+ {
+ ProcessUtility(stmt,
+ sql,
+ NULL,
+ false, /* not top level */
+ dest,
+ NULL);
+ }
+
+ PopActiveSnapshot();
+ }
+ }
+
+ /* Be sure to advance the command counter after the last script command */
+ CommandCounterIncrement();
+}
+
+/*
+ * Execute the extension's script file
+ */
+static void
+execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
+ List *requiredSchemas,
+ const char *schemaName, Oid schemaOid)
+{
+ char *filename = get_extension_absolute_path(control->script);
+ char *save_client_min_messages = NULL,
+ *save_log_min_messages = NULL,
+ *save_search_path;
+ StringInfoData pathbuf;
+ ListCell *lc;
+
+ /*
+ * Force client_min_messages and log_min_messages to be at least WARNING,
+ * so that we won't spam the user with useless NOTICE messages from common
+ * script actions like creating shell types.
+ *
+ * We use the equivalent of SET LOCAL to ensure the setting is undone
+ * upon error.
+ */
+ if (client_min_messages < WARNING)
+ {
+ save_client_min_messages =
+ pstrdup(GetConfigOption("client_min_messages", false));
+ (void) set_config_option("client_min_messages", "warning",
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_LOCAL, true);
+ }
+
+ if (log_min_messages < WARNING)
+ {
+ save_log_min_messages =
+ pstrdup(GetConfigOption("log_min_messages", false));
+ (void) set_config_option("log_min_messages", "warning",
+ PGC_SUSET, PGC_S_SESSION,
+ GUC_ACTION_LOCAL, true);
+ }
+
+ /*
+ * Set up the search path to contain the target schema, then the schemas
+ * of any prerequisite extensions, and nothing else. In particular this
+ * makes the target schema be the default creation target namespace.
+ *
+ * Note: it might look tempting to use PushOverrideSearchPath for this,
+ * but we cannot do that. We have to actually set the search_path GUC
+ * in case the extension script examines or changes it.
+ */
+ save_search_path = pstrdup(GetConfigOption("search_path", false));
+
+ initStringInfo(&pathbuf);
+ appendStringInfoString(&pathbuf, quote_identifier(schemaName));
+ foreach(lc, requiredSchemas)
+ {
+ Oid reqschema = lfirst_oid(lc);
+ char *reqname = get_namespace_name(reqschema);
+
+ if (reqname)
+ appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
+ }
+
+ (void) set_config_option("search_path", pathbuf.data,
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_LOCAL, true);
+
+ /*
+ * Set creating_extension and related variables so that
+ * recordDependencyOnCurrentExtension and other functions do the right
+ * things. On failure, ensure we reset these variables.
+ */
+ creating_extension = true;
+ CurrentExtensionObject = extensionOid;
+ PG_TRY();
+ {
+ char *sql = read_extension_script_file(control, filename);
+
+ /*
+ * If it's not relocatable, substitute the target schema name for
+ * occcurrences of @extschema@.
+ *
+ * For a relocatable extension, we just run the script as-is.
+ * There cannot be any need for @extschema@, else it wouldn't
+ * be relocatable.
+ */
+ if (!control->relocatable)
+ {
+ const char *qSchemaName = quote_identifier(schemaName);
+
+ sql = text_to_cstring(
+ DatumGetTextPP(
+ DirectFunctionCall3(replace_text,
+ CStringGetTextDatum(sql),
+ CStringGetTextDatum("@extschema@"),
+ CStringGetTextDatum(qSchemaName))));
+
+ }
+
+ execute_sql_string(sql, filename);
+ }
+ PG_CATCH();
+ {
+ creating_extension = false;
+ CurrentExtensionObject = InvalidOid;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ creating_extension = false;
+ CurrentExtensionObject = InvalidOid;
+
+ /*
+ * Restore GUC variables for the remainder of the current transaction.
+ * Again use SET LOCAL, so we won't affect the session value.
+ */
+ (void) set_config_option("search_path", save_search_path,
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_LOCAL, true);
+
+ if (save_client_min_messages != NULL)
+ (void) set_config_option("client_min_messages", save_client_min_messages,
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_LOCAL, true);
+ if (save_log_min_messages != NULL)
+ (void) set_config_option("log_min_messages", save_log_min_messages,
+ PGC_SUSET, PGC_S_SESSION,
+ GUC_ACTION_LOCAL, true);
+}
+
+/*
+ * CREATE EXTENSION
+ */
+void
+CreateExtension(CreateExtensionStmt *stmt)
+{
+ DefElem *d_schema = NULL;
+ char *schemaName;
+ Oid schemaOid;
+ Oid extowner = GetUserId();
+ ExtensionControlFile *control;
+ List *requiredExtensions;
+ List *requiredSchemas;
+ Relation rel;
+ Datum values[Natts_pg_extension];
+ bool nulls[Natts_pg_extension];
+ HeapTuple tuple;
+ Oid extensionOid;
+ ObjectAddress myself;
+ ObjectAddress nsp;
+ ListCell *lc;
+
+ /* 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.")));
+
+ /*
+ * We use global variables to track the extension being created, so we
+ * can create only one extension at the same time.
+ */
+ if (creating_extension)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("nested CREATE EXTENSION is not supported")));
+
+ /*
+ * Check for duplicate extension name. The unique index on
+ * pg_extension.extname would catch this anyway, and serves as a backstop
+ * in case of race conditions; but this is a friendlier error message.
+ */
+ if (get_extension_oid(stmt->extname, true) != InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("extension \"%s\" already exists", stmt->extname)));
+
+ /*
+ * Read the control file. Note we assume that it does not contain
+ * any non-ASCII data, so there is no need to worry about encoding
+ * at this point.
+ */
+ control = read_extension_control_file(stmt->extname);
+
+ /*
+ * Read the statement option list
+ */
+ foreach(lc, stmt->options)
+ {
+ DefElem *defel = (DefElem *) lfirst(lc);
+
+ if (strcmp(defel->defname, "schema") == 0)
+ {
+ if (d_schema)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ d_schema = defel;
+ }
+ else
+ elog(ERROR, "unrecognized option: %s", defel->defname);
+ }
+
+ /*
+ * Determine the target schema to install the extension into
+ */
+ if (d_schema && d_schema->arg)
+ {
+ /*
+ * User given schema, CREATE EXTENSION ... WITH SCHEMA ...
+ *
+ * It's an error to give a schema different from control->schema if
+ * control->schema is specified.
+ */
+ schemaName = strVal(d_schema->arg);
+
+ if (control->schema != NULL &&
+ strcmp(control->schema, schemaName) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("extension \"%s\" must be installed in schema \"%s\"",
+ control->name,
+ control->schema)));
+
+ /* If the user is giving us the schema name, it must exist already */
+ schemaOid = get_namespace_oid(schemaName, false);
+ }
+ else if (control->schema != NULL)
+ {
+ /*
+ * The extension is not relocatable and the author gave us a schema
+ * for it. We create the schema here if it does not already exist.
+ */
+ schemaName = control->schema;
+ schemaOid = get_namespace_oid(schemaName, true);
+
+ if (schemaOid == InvalidOid)
+ {
+ schemaOid = NamespaceCreate(schemaName, extowner);
+ /* Advance cmd counter to make the namespace visible */
+ CommandCounterIncrement();
+ }
+ }
+ else
+ {
+ /*
+ * Else, use the current default creation namespace, which is the
+ * first explicit entry in the search_path.
+ */
+ List *search_path = fetch_search_path(false);
+
+ if (search_path == NIL) /* probably can't happen */
+ elog(ERROR, "there is no default creation target");
+ schemaOid = linitial_oid(search_path);
+ schemaName = get_namespace_name(schemaOid);
+ if (schemaName == NULL) /* recently-deleted namespace? */
+ elog(ERROR, "there is no default creation target");
+
+ list_free(search_path);
+ }
+
+ /*
+ * If we didn't already know user is superuser, we would probably want
+ * to do pg_namespace_aclcheck(schemaOid, extowner, ACL_CREATE) here.
+ */
+
+ /*
+ * Look up the prerequisite extensions, and build lists of their OIDs
+ * and the OIDs of their target schemas.
+ */
+ requiredExtensions = NIL;
+ requiredSchemas = NIL;
+ foreach(lc, control->requires)
+ {
+ char *curreq = (char *) lfirst(lc);
+ Oid reqext;
+ Oid reqschema;
+
+ /*
+ * We intentionally don't use get_extension_oid's default error
+ * message here, because it would be confusing in this context.
+ */
+ reqext = get_extension_oid(curreq, true);
+ if (!OidIsValid(reqext))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("required extension \"%s\" is not installed",
+ curreq)));
+ reqschema = get_extension_schema(reqext);
+ requiredExtensions = lappend_oid(requiredExtensions, reqext);
+ requiredSchemas = lappend_oid(requiredSchemas, reqschema);
+ }
+
+ /*
+ * Insert new tuple into pg_extension.
+ */
+ rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+
+ values[Anum_pg_extension_extname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(control->name));
+ values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extowner);
+ values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
+ values[Anum_pg_extension_extrelocatable - 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);
+
+ nulls[Anum_pg_extension_extconfig - 1] = true;
+ nulls[Anum_pg_extension_extcondition - 1] = true;
+
+ tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+ extensionOid = simple_heap_insert(rel, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+ heap_close(rel, RowExclusiveLock);
+
+ /*
+ * Apply any comment on extension
+ */
+ if (control->comment != NULL)
+ CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
+
+ /*
+ * Record dependencies on owner, schema, and prerequisite extensions
+ */
+ recordDependencyOnOwner(ExtensionRelationId, extensionOid, extowner);
+
+ myself.classId = ExtensionRelationId;
+ myself.objectId = extensionOid;
+ myself.objectSubId = 0;
+
+ nsp.classId = NamespaceRelationId;
+ nsp.objectId = schemaOid;
+ nsp.objectSubId = 0;
+
+ recordDependencyOn(&myself, &nsp, DEPENDENCY_NORMAL);
+
+ foreach(lc, requiredExtensions)
+ {
+ Oid reqext = lfirst_oid(lc);
+ ObjectAddress otherext;
+
+ otherext.classId = ExtensionRelationId;
+ otherext.objectId = reqext;
+ otherext.objectSubId = 0;
+
+ recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
+ }
+
+ /*
+ * Finally, execute the extension script to create the member objects
+ */
+ execute_extension_script(extensionOid, control, requiredSchemas,
+ schemaName, schemaOid);
+}
+
+
+/*
+ * RemoveExtensions
+ * Implements DROP EXTENSION.
+ */
+void
+RemoveExtensions(DropStmt *drop)
+{
+ ObjectAddresses *objects;
+ ListCell *cell;
+
+ /*
+ * First we identify all the extensions, then we delete them in a single
+ * performMultipleDeletions() call. This is to avoid unwanted DROP
+ * RESTRICT errors if one of the extensions depends on another.
+ */
+ objects = new_object_addresses();
+
+ foreach(cell, drop->objects)
+ {
+ List *names = (List *) lfirst(cell);
+ char *extensionName;
+ Oid extensionId;
+ ObjectAddress object;
+
+ if (list_length(names) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("extension name cannot be qualified")));
+ extensionName = strVal(linitial(names));
+
+ extensionId = get_extension_oid(extensionName, drop->missing_ok);
+
+ if (!OidIsValid(extensionId))
+ {
+ ereport(NOTICE,
+ (errmsg("extension \"%s\" does not exist, skipping",
+ extensionName)));
+ continue;
+ }
+
+ /*
+ * Permission check. For now, insist on superuser-ness; later we
+ * might want to relax that to being owner of the extension.
+ */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to drop extension \"%s\"",
+ extensionName),
+ errhint("Must be superuser to drop an extension.")));
+
+ object.classId = ExtensionRelationId;
+ object.objectId = extensionId;
+ object.objectSubId = 0;
+
+ add_exact_object_address(&object, objects);
+ }
+
+ /*
+ * Do the deletions. Objects contained in the extension(s) are removed by
+ * means of their dependency links to the extensions.
+ */
+ performMultipleDeletions(objects, drop->behavior);
+
+ free_object_addresses(objects);
+}
+
+
+/*
+ * Guts of extension deletion.
+ *
+ * All we need do here is remove the pg_extension tuple itself. Everything
+ * else is taken care of by the dependency infrastructure.
+ */
+void
+RemoveExtensionById(Oid extId)
+{
+ Relation rel;
+ SysScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(extId));
+ scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
+ SnapshotNow, 1, entry);
+
+ tuple = systable_getnext(scandesc);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ simple_heap_delete(rel, &tuple->t_self);
+
+ systable_endscan(scandesc);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * This function lists the extensions available in the control directory
+ * (each of which might or might not actually be installed). We parse each
+ * available control file and report the interesting fields.
+ *
+ * The system view pg_available_extensions provides a user interface to this
+ * SRF, adding information about whether the extensions are installed in the
+ * current DB.
+ */
+Datum
+pg_available_extensions(PG_FUNCTION_ARGS)
+{
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ MemoryContext per_query_ctx;
+ MemoryContext oldcontext;
+ char *location;
+ DIR *dir;
+ struct dirent *de;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to list available extensions"))));
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not " \
+ "allowed in this context")));
+
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ location = get_extension_control_directory();
+ dir = AllocateDir(location);
+
+ /*
+ * If the control directory doesn't exist, we want to silently return
+ * an empty set. Any other error will be reported by ReadDir.
+ */
+ if (dir == NULL && errno == ENOENT)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ while ((de = ReadDir(dir, location)) != NULL)
+ {
+ ExtensionControlFile *control;
+ char *extname;
+ Datum values[4];
+ bool nulls[4];
+
+ if (!is_extension_control_filename(de->d_name))
+ continue;
+
+ /* extract extension name from 'name.control' filename */
+ extname = pstrdup(de->d_name);
+ *strrchr(extname, '.') = '\0';
+
+ control = read_extension_control_file(extname);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+
+ /* name */
+ values[0] = DirectFunctionCall1(namein,
+ CStringGetDatum(control->name));
+ /* version */
+ if (control->version == NULL)
+ nulls[1] = true;
+ else
+ values[1] = CStringGetTextDatum(control->version);
+ /* relocatable */
+ values[2] = BoolGetDatum(control->relocatable);
+ /* comment */
+ if (control->comment == NULL)
+ nulls[3] = true;
+ else
+ values[3] = CStringGetTextDatum(control->comment);
+
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ }
+
+ FreeDir(dir);
+ }
+
+ /* clean up and return the tuplestore */
+ tuplestore_donestoring(tupstore);
+
+ return (Datum) 0;
+}
+
+/*
+ * pg_extension_config_dump
+ *
+ * Record information about a configuration table that belongs to an
+ * extension being created, but whose contents should be dumped in whole
+ * or in part during pg_dump.
+ */
+Datum
+pg_extension_config_dump(PG_FUNCTION_ARGS)
+{
+ Oid tableoid = PG_GETARG_OID(0);
+ text *wherecond = PG_GETARG_TEXT_P(1);
+ char *tablename;
+ Relation extRel;
+ ScanKeyData key[1];
+ SysScanDesc extScan;
+ HeapTuple extTup;
+ Datum arrayDatum;
+ Datum elementDatum;
+ int arrayIndex;
+ bool isnull;
+ Datum repl_val[Natts_pg_extension];
+ bool repl_null[Natts_pg_extension];
+ bool repl_repl[Natts_pg_extension];
+ ArrayType *a;
+
+ /*
+ * We only allow this to be called from an extension's SQL script.
+ * We shouldn't need any permissions check beyond that.
+ */
+ if (!creating_extension)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("pg_extension_config_dump() can only be called "
+ "from a SQL script executed by CREATE EXTENSION")));
+
+ /*
+ * Check that the table exists and is a member of the extension being
+ * created. This ensures that we don't need to register a dependency
+ * to protect the extconfig entry.
+ */
+ tablename = get_rel_name(tableoid);
+ if (tablename == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("OID %u does not refer to a table", tableoid)));
+ if (getExtensionOfObject(RelationRelationId, tableoid) !=
+ CurrentExtensionObject)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("table \"%s\" is not a member of the extension being created",
+ tablename)));
+
+ /*
+ * Add the table OID and WHERE condition to the extension's extconfig
+ * and extcondition arrays.
+ */
+
+ /* Find the pg_extension tuple */
+ extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(CurrentExtensionObject));
+
+ extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
+ SnapshotNow, 1, key);
+
+ extTup = systable_getnext(extScan);
+
+ if (!HeapTupleIsValid(extTup)) /* should not happen */
+ elog(ERROR, "extension with oid %u does not exist",
+ CurrentExtensionObject);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+
+ /* Build or modify the extconfig value */
+ elementDatum = ObjectIdGetDatum(tableoid);
+
+ arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
+ RelationGetDescr(extRel), &isnull);
+ if (isnull)
+ {
+ a = construct_array(&elementDatum, 1,
+ OIDOID,
+ sizeof(Oid), true, 'i');
+ }
+ else
+ {
+ a = DatumGetArrayTypeP(arrayDatum);
+ Assert(ARR_ELEMTYPE(a) == OIDOID);
+ Assert(ARR_NDIM(a) == 1);
+ Assert(ARR_LBOUND(a)[0] == 1);
+
+ arrayIndex = ARR_DIMS(a)[0] + 1; /* add after end */
+
+ a = array_set(a, 1, &arrayIndex,
+ elementDatum,
+ false,
+ -1 /* varlena array */ ,
+ sizeof(Oid) /* OID's typlen */ ,
+ true /* OID's typbyval */ ,
+ 'i' /* OID's typalign */ );
+ }
+ repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
+ repl_repl[Anum_pg_extension_extconfig - 1] = true;
+
+ /* Build or modify the extcondition value */
+ elementDatum = PointerGetDatum(wherecond);
+
+ arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
+ RelationGetDescr(extRel), &isnull);
+ if (isnull)
+ {
+ a = construct_array(&elementDatum, 1,
+ TEXTOID,
+ -1, false, 'i');
+ }
+ else
+ {
+ a = DatumGetArrayTypeP(arrayDatum);
+ Assert(ARR_ELEMTYPE(a) == TEXTOID);
+ Assert(ARR_NDIM(a) == 1);
+ Assert(ARR_LBOUND(a)[0] == 1);
+
+ arrayIndex = ARR_DIMS(a)[0] + 1; /* add after end */
+
+ a = array_set(a, 1, &arrayIndex,
+ elementDatum,
+ false,
+ -1 /* varlena array */ ,
+ -1 /* TEXT's typlen */ ,
+ false /* TEXT's typbyval */ ,
+ 'i' /* TEXT's typalign */ );
+ }
+ repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
+ repl_repl[Anum_pg_extension_extcondition - 1] = true;
+
+ extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
+ repl_val, repl_null, repl_repl);
+
+ simple_heap_update(extRel, &extTup->t_self, extTup);
+ CatalogUpdateIndexes(extRel, extTup);
+
+ systable_endscan(extScan);
+
+ heap_close(extRel, RowExclusiveLock);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Execute ALTER EXTENSION SET SCHEMA
+ */
+void
+AlterExtensionNamespace(List *names, const char *newschema)
+{
+ char *extensionName;
+ Oid extensionOid;
+ Oid nspOid;
+ Oid oldNspOid = InvalidOid;
+ Relation extRel;
+ ScanKeyData key[2];
+ SysScanDesc extScan;
+ HeapTuple extTup;
+ Form_pg_extension extForm;
+ Relation depRel;
+ SysScanDesc depScan;
+ HeapTuple depTup;
+
+ if (list_length(names) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("extension name cannot be qualified")));
+ extensionName = strVal(linitial(names));
+
+ extensionOid = get_extension_oid(extensionName, false);
+
+ nspOid = LookupCreationNamespace(newschema);
+
+ /* this might later become an ownership test */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to use ALTER EXTENSION"))));
+
+ /* Locate the pg_extension tuple */
+ extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(extensionOid));
+
+ extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
+ SnapshotNow, 1, key);
+
+ extTup = systable_getnext(extScan);
+
+ if (!HeapTupleIsValid(extTup)) /* should not happen */
+ elog(ERROR, "extension with oid %u does not exist", extensionOid);
+
+ /* Copy tuple so we can modify it below */
+ extTup = heap_copytuple(extTup);
+ extForm = (Form_pg_extension) GETSTRUCT(extTup);
+
+ systable_endscan(extScan);
+
+ /*
+ * If the extension is already in the target schema, just silently
+ * do nothing.
+ */
+ if (extForm->extnamespace == nspOid)
+ {
+ heap_close(extRel, RowExclusiveLock);
+ return;
+ }
+
+ /* Check extension is supposed to be relocatable */
+ if (!extForm->extrelocatable)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("extension \"%s\" does not support SET SCHEMA",
+ NameStr(extForm->extname))));
+
+ /*
+ * Scan pg_depend to find objects that depend directly on the extension,
+ * and alter each one's schema.
+ */
+ depRel = heap_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(ExtensionRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(extensionOid));
+
+ depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
+ {
+ Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
+ ObjectAddress dep;
+ Oid dep_oldNspOid;
+
+ /*
+ * Ignore non-membership dependencies. (Currently, the only other
+ * case we could see here is a normal dependency from another
+ * extension.)
+ */
+ if (pg_depend->deptype != DEPENDENCY_EXTENSION)
+ continue;
+
+ dep.classId = pg_depend->classid;
+ dep.objectId = pg_depend->objid;
+ dep.objectSubId = pg_depend->objsubid;
+
+ if (dep.objectSubId != 0) /* should not happen */
+ elog(ERROR, "extension should not have a sub-object dependency");
+
+ dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
+ dep.objectId,
+ nspOid);
+
+ /*
+ * Remember previous namespace of first object that has one
+ */
+ if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid)
+ oldNspOid = dep_oldNspOid;
+
+ /*
+ * If not all the objects had the same old namespace (ignoring any
+ * that are not in namespaces), complain.
+ */
+ if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("extension \"%s\" does not support SET SCHEMA",
+ NameStr(extForm->extname)),
+ errdetail("%s is not in the extension's schema \"%s\"",
+ getObjectDescription(&dep),
+ get_namespace_name(oldNspOid))));
+ }
+
+ systable_endscan(depScan);
+
+ relation_close(depRel, AccessShareLock);
+
+ /* Now adjust pg_extension.extnamespace */
+ extForm->extnamespace = nspOid;
+
+ simple_heap_update(extRel, &extTup->t_self, extTup);
+ CatalogUpdateIndexes(extRel, extTup);
+
+ heap_close(extRel, RowExclusiveLock);
+
+ /* update dependencies to point to the new schema */
+ changeDependencyFor(ExtensionRelationId, extensionOid,
+ NamespaceRelationId, oldNspOid, nspOid);
+}
Oid fdwvalidator;
Datum fdwoptions;
Oid ownerId;
+ ObjectAddress myself;
+ ObjectAddress referenced;
/* Must be super user */
if (!superuser())
heap_freetuple(tuple);
+ /* record dependencies */
+ myself.classId = ForeignDataWrapperRelationId;
+ myself.objectId = fdwId;
+ myself.objectSubId = 0;
+
if (fdwvalidator)
{
- ObjectAddress myself;
- ObjectAddress referenced;
-
- myself.classId = ForeignDataWrapperRelationId;
- myself.objectId = fdwId;
- myself.objectSubId = 0;
-
referenced.classId = ProcedureRelationId;
referenced.objectId = fdwvalidator;
referenced.objectSubId = 0;
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* Post creation hook for new foreign data wrapper */
InvokeObjectAccessHook(OAT_POST_CREATE,
ForeignDataWrapperRelationId, fdwId, 0);
heap_freetuple(tuple);
- /* Add dependency on FDW and owner */
+ /* record dependencies */
myself.classId = ForeignServerRelationId;
myself.objectId = srvId;
myself.objectSubId = 0;
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* Post creation hook for new foreign server */
InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (OidIsValid(useId))
+ {
/* Record the mapped user dependency */
recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new user mapping */
InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* Post creation hook for new cast */
InvokeObjectAccessHook(OAT_POST_CREATE,
CastRelationId, myself.objectId, 0);
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)
else
procOid = LookupFuncNameTypeNames(name, argtypes, false);
- /* check permissions on function */
- if (!pg_proc_ownercheck(procOid, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
- NameListToString(name));
+ /* get schema OID and check its permissions */
+ 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);
proc = (Form_pg_proc) GETSTRUCT(tup);
- oldNspOid = proc->pronamespace;
+ /* check permissions on function */
+ if (!pg_proc_ownercheck(procOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ NameStr(proc->proname));
- /* get schema OID and check its permissions */
- nspOid = LookupCreationNamespace(newschema);
+ oldNspOid = proc->pronamespace;
/* common checks on switching namespaces */
CheckSetNamespace(oldNspOid, nspOid, ProcedureRelationId, procOid);
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function \"%s\" already exists in schema \"%s\"",
NameStr(proc->proname),
- newschema )));
+ get_namespace_name(nspOid) )));
/* OK, modify the pg_proc row */
if (changeDependencyFor(ProcedureRelationId, procOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "failed to change schema dependency for function \"%s\"",
- NameListToString( name));
+ NameStr(proc->pro name));
heap_freetuple(tup);
heap_close(procRel, RowExclusiveLock);
+
+ return oldNspOid;
}
/* dependency on owner */
recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* Post creation hook for new operator family */
InvokeObjectAccessHook(OAT_POST_CREATE,
OperatorFamilyRelationId, opfamilyoid, 0);
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* Post creation hook for new operator class */
InvokeObjectAccessHook(OAT_POST_CREATE,
OperatorClassRelationId, opclassoid, 0);
{
Oid amOid;
Relation rel;
- Oid oid;
+ Oid opclassO id;
Oid nspOid;
amOid = get_am_oid(access_method, false);
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
- /* Look up the opclass. */
- oid = get_opclass_oid(amOid, name, false);
+ /* Look up the opclass */
+ opclassO id = get_opclass_oid(amOid, name, false);
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, CLAOID, OperatorClassRelationId,
- oid, nspOid,
- Anum_pg_opfamily_opfname,
- Anum_pg_opfamily_opfnamespace,
- Anum_pg_opfamily_opfowner,
- ACL_KIND_OPCLASS,
- false);
+ AlterObjectNamespace(rel, CLAOID, -1,
+ opclassOid, nspOid,
+ Anum_pg_opclass_opcname,
+ Anum_pg_opclass_opcnamespace,
+ Anum_pg_opclass_opcowner,
+ ACL_KIND_OPCLASS);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+Oid
+AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
+{
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, CLAOID, -1,
+ opclassOid, newNspOid,
+ Anum_pg_opclass_opcname,
+ Anum_pg_opclass_opcnamespace,
+ Anum_pg_opclass_opcowner,
+ ACL_KIND_OPCLASS);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
/*
{
Oid amOid;
Relation rel;
+ Oid opfamilyOid;
Oid nspOid;
- Oid oid;
amOid = get_am_oid(access_method, false);
rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
/* Look up the opfamily */
- oid = get_opfamily_oid(amOid, name, false);
+ opfamilyO id = get_opfamily_oid(amOid, name, false);
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, OPFAMILYOID, OperatorFamilyRelationId ,
- oid, nspOid,
+ AlterObjectNamespace(rel, OPFAMILYOID, -1 ,
+ opfamilyO id, nspOid,
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner,
- ACL_KIND_OPFAMILY,
- false);
+ ACL_KIND_OPFAMILY);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+Oid
+AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid)
+{
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, OPFAMILYOID, -1,
+ opfamilyOid, newNspOid,
+ Anum_pg_opfamily_opfname,
+ Anum_pg_opfamily_opfnamespace,
+ Anum_pg_opfamily_opfowner,
+ ACL_KIND_OPFAMILY);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, OPEROID, OperatorRelationId, operOid, nspOid,
+ AlterObjectNamespace(rel, OPEROID, -1,
+ operOid, nspOid,
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
- ACL_KIND_OPER,
- false);
+ ACL_KIND_OPER);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+Oid
+AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
+{
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(OperatorRelationId, RowExclusiveLock);
+
+ oldNspOid = AlterObjectNamespace(rel, OPEROID, -1,
+ operOid, newNspOid,
+ Anum_pg_operator_oprname,
+ Anum_pg_operator_oprnamespace,
+ Anum_pg_operator_oprowner,
+ ACL_KIND_OPER);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
* Create dependencies for the new language. If we are updating an
* existing language, first delete any existing pg_depend entries.
* (However, since we are not changing ownership or permissions, the
- * shared dependencies do *not* need to change, and we leave them alone.)
+ * shared dependencies do *not* need to change, and we leave them alone.
+ * We also don't change any pre-existing extension-membership dependency.)
*/
myself.classId = LanguageRelationId;
myself.objectId = HeapTupleGetOid(tup);
myself.objectSubId = 0;
if (is_update)
- deleteDependencyRecordsFor(myself.classId, myself.objectId);
+ deleteDependencyRecordsFor(myself.classId, myself.objectId, true );
/* dependency on owner of language */
if (!is_update)
recordDependencyOnOwner(myself.classId, myself.objectId,
languageOwner);
+ /* dependency on extension */
+ if (!is_update)
+ recordDependencyOnCurrentExtension(&myself);
+
/* dependency on the PL handler function */
referenced.classId = ProcedureRelationId;
referenced.objectId = handlerOid;
case OCLASS_FOREIGN_SERVER:
case OCLASS_USER_MAPPING:
case OCLASS_DEFACL:
+ case OCLASS_EXTENSION:
/*
* We don't expect any of these sorts of objects to depend on
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* dependencies on functions */
referenced.classId = ProcedureRelationId;
referenced.objectSubId = 0;
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, TSPARSEROID, TSParserRelationId, prsId, nspOid,
+ AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP,
+ prsId, nspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
- -1, -1, true );
+ -1, -1);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+Oid
+AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
+{
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSParserRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP,
+ prsId, newNspOid,
+ Anum_pg_ts_parser_prsname,
+ Anum_pg_ts_parser_prsnamespace,
+ -1, -1);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
/* ---------------------- TS Dictionary commands -----------------------*/
/* dependency on owner */
recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* dependency on template */
referenced.classId = TSTemplateRelationId;
referenced.objectId = dict->dicttemplate;
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, TSDICTOID, TSDictionaryRelationId, dictId, nspOid,
+ AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP,
+ dictId, nspOid,
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
- ACL_KIND_TSDICTIONARY,
- true);
+ ACL_KIND_TSDICTIONARY);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+Oid
+AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
+{
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP,
+ dictId, newNspOid,
+ Anum_pg_ts_dict_dictname,
+ Anum_pg_ts_dict_dictnamespace,
+ Anum_pg_ts_dict_dictowner,
+ ACL_KIND_TSDICTIONARY);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
/*
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself);
+
/* dependencies on functions */
referenced.classId = ProcedureRelationId;
referenced.objectSubId = 0;
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, TSTEMPLATEOID, TSTemplateRelationId ,
+ AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP ,
tmplId, nspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
- -1, -1, true );
+ -1, -1);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+Oid
+AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
+{
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP,
+ tmplId, newNspOid,
+ Anum_pg_ts_template_tmplname,
+ Anum_pg_ts_template_tmplnamespace,
+ -1, -1);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
/*
myself.objectId = HeapTupleGetOid(tuple);
myself.objectSubId = 0;
- /* for ALTER case, first flush old dependencies */
+ /* for ALTER case, first flush old dependencies, except extension deps */
if (removeOld)
{
- deleteDependencyRecordsFor(myself.classId, myself.objectId);
+ deleteDependencyRecordsFor(myself.classId, myself.objectId, true );
deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
}
/* dependency on owner */
recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner);
+ /* dependency on extension */
+ if (!removeOld)
+ recordDependencyOnCurrentExtension(&myself);
+
/* dependency on parser */
referenced.classId = TSParserRelationId;
referenced.objectId = cfg->cfgparser;
/* get schema OID */
nspOid = LookupCreationNamespace(newschema);
- AlterObjectNamespace(rel, TSCONFIGOID, TSConfigRelationId, cfgId, nspOid,
+ AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP,
+ cfgId, nspOid,
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
- ACL_KIND_TSCONFIGURATION,
- false);
+ ACL_KIND_TSCONFIGURATION);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+}
+
+Oid
+AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
+{
+ Oid oldNspOid;
+ Relation rel;
+
+ rel = heap_open(TSConfigRelationId, RowExclusiveLock);
+
+ oldNspOid =
+ AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP,
+ cfgId, newNspOid,
+ Anum_pg_ts_config_cfgname,
+ Anum_pg_ts_config_cfgnamespace,
+ Anum_pg_ts_config_cfgowner,
+ ACL_KIND_TSCONFIGURATION);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return oldNspOid;
}
/*
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);
+ /* 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));
- /* 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)
format_type_be(elemOid))));
/* and do the work */
- AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
+ return AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
}
/*
* If errorOnTableType is TRUE, the function errors out if the type is
* a table type. ALTER TABLE has to be used to move a table to a new
* namespace.
+ *
+ * Returns the type's old namespace OID.
*/
-vo id
+O id
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType)
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
+
+ return oldNspOid;
}
return newnode;
}
+static CreateExtensionStmt *
+_copyCreateExtensionStmt(CreateExtensionStmt *from)
+{
+ CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
+
+ COPY_STRING_FIELD(extname);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
static CreateFdwStmt *
_copyCreateFdwStmt(CreateFdwStmt *from)
{
case T_AlterTableSpaceOptionsStmt:
retval = _copyAlterTableSpaceOptionsStmt(from);
break;
+ case T_CreateExtensionStmt:
+ retval = _copyCreateExtensionStmt(from);
+ break;
case T_CreateFdwStmt:
retval = _copyCreateFdwStmt(from);
break;
return true;
}
+static bool
+_equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
+{
+ COMPARE_STRING_FIELD(extname);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b)
{
case T_AlterTableSpaceOptionsStmt:
retval = _equalAlterTableSpaceOptionsStmt(a, b);
break;
+ case T_CreateExtensionStmt:
+ retval = _equalCreateExtensionStmt(a, b);
+ break;
case T_CreateFdwStmt:
retval = _equalCreateFdwStmt(a, b);
break;
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
- CreateDomainStmt CreateGroupStmt CreateOpClassStmt
+ CreateDomainStmt CreateExtensionStmt Create GroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
%type opt_drop_behavior
%type createdb_opt_list alterdb_opt_list copy_opt_list
- transaction_mode_list
+ transaction_mode_list create_extension_opt_list
%type createdb_opt_item alterdb_opt_item copy_opt_item
- transaction_mode_item
+ transaction_mode_item create_extension_opt_item
%type opt_lock lock_type cast_context
%type vacuum_option_list vacuum_option_elem
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
+ 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
| CreateCastStmt
| CreateConversionStmt
| CreateDomainStmt
+ | CreateExtensionStmt
| CreateFdwStmt
| CreateForeignServerStmt
| CreateForeignTableStmt
}
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * CREATE EXTENSION extension
+ * [ WITH ] [ 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:
+ SCHEMA opt_equal name
+ {
+ $$ = makeDefElem("schema", (Node *)makeString($3));
+ }
+ ;
+
/*****************************************************************************
*
* QUERY:
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
| VIEW { $$ = OBJECT_VIEW; }
| INDEX { $$ = OBJECT_INDEX; }
- | TYPE_P { $$ = OBJECT_TYPE; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
+ | TYPE_P { $$ = OBJECT_TYPE; }
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| SCHEMA { $$ = OBJECT_SCHEMA; }
+ | EXTENSION { $$ = OBJECT_EXTENSION; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
*
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
- * CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
+ * CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
* TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION |
* FOREIGN TABLE ] |
| VIEW { $$ = OBJECT_VIEW; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
+ | EXTENSION { $$ = OBJECT_EXTENSION; }
| ROLE { $$ = OBJECT_ROLE; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
;
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);
| EXCLUSIVE
| EXECUTE
| EXPLAIN
+ | EXTENSION
| EXTERNAL
| FAMILY
| FIRST_P
/* If replacing, get rid of old dependencies and make new ones */
if (is_update)
- deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId);
+ deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false );
/*
* Install dependency on rule's relation to ensure it will go away on
#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"
case T_ReassignOwnedStmt:
case T_AlterTSDictionaryStmt:
case T_AlterTSConfigurationStmt:
+ case T_CreateExtensionStmt:
case T_CreateFdwStmt:
case T_AlterFdwStmt:
case T_DropFdwStmt:
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
break;
+ case T_CreateExtensionStmt:
+ CreateExtension((CreateExtensionStmt *) parsetree);
+ break;
+
case T_CreateFdwStmt:
CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
break;
RemoveTSConfigurations(stmt);
break;
+ case OBJECT_EXTENSION:
+ RemoveExtensions(stmt);
+ break;
+
default:
elog(ERROR, "unrecognized drop object type: %d",
(int) stmt->removeType);
tag = "ALTER TABLESPACE";
break;
+ case T_CreateExtensionStmt:
+ tag = "CREATE EXTENSION";
+ break;
+
case T_CreateFdwStmt:
tag = "CREATE FOREIGN DATA WRAPPER";
break;
case OBJECT_FOREIGN_TABLE:
tag = "DROP FOREIGN TABLE";
break;
+ case OBJECT_EXTENSION:
+ tag = "DROP EXTENSION";
+ break;
default:
tag = "???";
}
case OBJECT_DOMAIN:
tag = "ALTER DOMAIN";
break;
+ case OBJECT_EXTENSION:
+ tag = "ALTER EXTENSION";
+ break;
case OBJECT_OPERATOR:
tag = "ALTER OPERATOR";
break;
lev = LOGSTMT_DDL;
break;
+ case T_CreateExtensionStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreateFdwStmt:
case T_AlterFdwStmt:
case T_DropFdwStmt:
/*
* Read a section of a file, returning it as bytea
*
- * We read the whole of the file when bytes_to_read is nagative.
+ * Caller is responsible for all permissions checking.
+ *
+ * We read the whole of the file when bytes_to_read is negative.
*/
-static bytea *
-read_binary_file(text *filename_t , int64 seek_offset, int64 bytes_to_read)
+bytea *
+read_binary_file(const char *filename , int64 seek_offset, int64 bytes_to_read)
{
bytea *buf;
size_t nbytes;
FILE *file;
- char *filename;
-
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to read files"))));
-
- filename = convert_and_check_filename(filename_t);
if (bytes_to_read < 0)
{
SET_VARSIZE(buf, nbytes + VARHDRSZ);
FreeFile(file);
- pfree(filename);
return buf;
}
* in the database encoding.
*/
static text *
-read_text_file(text *filename, int64 seek_offset, int64 bytes_to_read)
+read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
{
- bytea *buf = read_binary_file(filename, seek_offset, bytes_to_read);
+ bytea *buf;
+
+ buf = read_binary_file(filename, seek_offset, bytes_to_read);
/* Make sure the input is valid */
pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
text *filename_t = PG_GETARG_TEXT_P(0);
int64 seek_offset = PG_GETARG_INT64(1);
int64 bytes_to_read = PG_GETARG_INT64(2);
+ char *filename;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to read files"))));
+
+ filename = convert_and_check_filename(filename_t);
if (bytes_to_read < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("requested length cannot be negative")));
- PG_RETURN_TEXT_P(read_text_file(filename_t , seek_offset, bytes_to_read));
+ PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
}
/*
pg_read_file_all(PG_FUNCTION_ARGS)
{
text *filename_t = PG_GETARG_TEXT_P(0);
+ char *filename;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to read files"))));
+
+ filename = convert_and_check_filename(filename_t);
- PG_RETURN_TEXT_P(read_text_file(filename_t , 0, -1));
+ PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
}
/*
text *filename_t = PG_GETARG_TEXT_P(0);
int64 seek_offset = PG_GETARG_INT64(1);
int64 bytes_to_read = PG_GETARG_INT64(2);
+ char *filename;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to read files"))));
+
+ filename = convert_and_check_filename(filename_t);
if (bytes_to_read < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("requested length cannot be negative")));
- PG_RETURN_BYTEA_P(read_binary_file(filename_t , seek_offset, bytes_to_read));
+ PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
}
/*
pg_read_binary_file_all(PG_FUNCTION_ARGS)
{
text *filename_t = PG_GETARG_TEXT_P(0);
+ char *filename;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to read files"))));
+
+ filename = convert_and_check_filename(filename_t);
- PG_RETURN_BYTEA_P(read_binary_file(filename_t , 0, -1));
+ PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
}
/*
RuleInfo *ruleinfo;
ProcLangInfo *proclanginfo;
CastInfo *castinfo;
+ ExtensionInfo *extinfo;
OpclassInfo *opcinfo;
OpfamilyInfo *opfinfo;
ConvInfo *convinfo;
int numRules;
int numProcLangs;
int numCasts;
+ int numExtensions;
int numOpclasses;
int numOpfamilies;
int numConversions;
write_msg(NULL, "reading type casts\n");
castinfo = getCasts(&numCasts);
+ /* this must be after getTables */
+ if (g_verbose)
+ write_msg(NULL, "reading extensions\n");
+ extinfo = getExtensions(&numExtensions);
+
/* Link tables to parents, mark parents of target tables interesting */
if (g_verbose)
write_msg(NULL, "finding inheritance relationships\n");
static int collectSecLabels(Archive *fout, SecLabelItem **items);
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
+static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
static void dumpType(Archive *fout, TypeInfo *tyinfo);
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
static void getDependencies(void);
static void getDomainConstraints(TypeInfo *tyinfo);
static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
+static void makeTableDataInfo(TableInfo *tbinfo, bool oids);
static void getTableDataFKConstraints(void);
static char *format_function_arguments(FuncInfo *finfo, char *funcargs);
static char *format_function_arguments_old(FuncInfo *finfo, int nallargs,
/*
* Collect dependency data to assist in ordering the objects.
+ *
+ * (In 9.1 and later, this also marks extension member objects as
+ * not to be dumped.)
*/
getDependencies();
classname),
column_list);
}
+ else if (tdinfo->filtercond)
+ {
+ /* Note: this syntax is only supported in 8.2 and up */
+ appendPQExpBufferStr(q, "COPY (SELECT ");
+ /* klugery to get rid of parens in column list */
+ if (strlen(column_list) > 2)
+ {
+ appendPQExpBufferStr(q, column_list + 1);
+ q->data[q->len - 1] = ' ';
+ }
+ else
+ appendPQExpBufferStr(q, "* ");
+ appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
+ fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
+ classname),
+ tdinfo->filtercond);
+ }
else
{
appendPQExpBuffer(q, "COPY %s %s TO stdout;",
fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
classname));
}
+ if (tdinfo->filtercond)
+ appendPQExpBuffer(q, " %s", tdinfo->filtercond);
res = PQexec(g_conn, q->data);
check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
dumpTableData(Archive *fout, TableDataInfo *tdinfo)
{
TableInfo *tbinfo = tdinfo->tdtable;
- PQExpBuffer copyBuf = createPQExpBuffer() ;
+ PQExpBuffer copyBuf;
DataDumperPtr dumpFn;
char *copyStmt;
+ if (!tdinfo->dobj.dump)
+ return;
+
+ copyBuf = createPQExpBuffer();
+
if (!dump_inserts)
{
/* Dump/restore using COPY */
&& no_unlogged_table_data)
continue;
- if (tblinfo[i].dobj.dump)
- {
- TableDataInfo *tdinfo;
+ if (tblinfo[i].dobj.dump && tblinfo[i].dataObj == NULL)
+ makeTableDataInfo(&(tblinfo[i]), oids);
+ }
+}
- tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
+/*
+ * Make a dumpable object for the data of this specific table
+ */
+static void
+makeTableDataInfo(TableInfo *tbinfo, bool oids)
+{
+ TableDataInfo *tdinfo;
- tdinfo->dobj.objType = DO_TABLE_DATA ;
+ tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo)) ;
- /*
- * Note: use tableoid 0 so that this object won't be mistaken for
- * something that pg_depend entries apply to.
- */
- tdinfo->dobj.catId.tableoid = 0;
- tdinfo->dobj.catId.oid = tblinfo[i].dobj.catId.oid;
- AssignDumpId(&tdinfo->dobj);
- tdinfo->dobj.name = tblinfo[i].dobj.name;
- tdinfo->dobj.namespace = tblinfo[i].dobj.namespace;
- tdinfo->tdtable = &(tblinfo[i]);
- tdinfo->oids = oids;
- addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId);
-
- tblinfo[i].dataObj = tdinfo;
- }
- }
+ tdinfo->dobj.objType = DO_TABLE_DATA;
+
+ /*
+ * Note: use tableoid 0 so that this object won't be mistaken for
+ * something that pg_depend entries apply to.
+ */
+ tdinfo->dobj.catId.tableoid = 0;
+ tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
+ AssignDumpId(&tdinfo->dobj);
+ tdinfo->dobj.name = tbinfo->dobj.name;
+ tdinfo->dobj.namespace = tbinfo->dobj.namespace;
+ tdinfo->dobj.dump = true;
+ tdinfo->tdtable = tbinfo;
+ tdinfo->oids = oids;
+ tdinfo->ext_config = false; /* might get set later */
+ tdinfo->filtercond = NULL; /* might get set later */
+ addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
+
+ tbinfo->dataObj = tdinfo;
}
/*
return NULL; /* keep compiler quiet */
}
+/*
+ * 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;
+ int j;
+ PQExpBuffer query;
+ ExtensionInfo *extinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_extname;
+ int i_nspname;
+ int i_extconfig;
+ int i_extcondition;
+
+ /*
+ * Before 9.1, there are no extensions.
+ */
+ if (g_fout->remoteVersion < 90100)
+ {
+ *numExtensions = 0;
+ return NULL;
+ }
+
+ query = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT x.tableoid, x.oid, "
+ "x.extname, n.nspname, x.extconfig, x.extcondition "
+ "FROM pg_extension x "
+ "JOIN pg_namespace n ON n.oid = x.extnamespace");
+
+ 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_nspname = PQfnumber(res, "nspname");
+ i_extconfig = PQfnumber(res, "extconfig");
+ i_extcondition = PQfnumber(res, "extcondition");
+
+ for (i = 0; i < ntups; i++)
+ {
+ char *extconfig;
+ char *extcondition;
+ char **extconfigarray = NULL;
+ char **extconditionarray = NULL;
+ int nconfigitems;
+ int nconditionitems;
+
+ 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].namespace = strdup(PQgetvalue(res, i, i_nspname));
+
+ /* For the moment, all extensions are considered dumpable */
+ extinfo->dobj.dump = true;
+
+ /*
+ * Find and mark any configuration tables for this extension.
+ *
+ * Note that we create TableDataInfo objects even in schemaOnly mode,
+ * ie, user data in a configuration table is treated like schema data.
+ * This seems appropriate since system data in a config table would
+ * get reloaded by CREATE EXTENSION.
+ */
+ extconfig = PQgetvalue(res, i, i_extconfig);
+ extcondition = PQgetvalue(res, i, i_extcondition);
+ if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) &&
+ parsePGArray(extcondition, &extconditionarray, &nconditionitems) &&
+ nconfigitems == nconditionitems)
+ {
+ for (j = 0; j < nconfigitems; j++)
+ {
+ TableInfo *configtbl;
+
+ configtbl = findTableByOid(atooid(extconfigarray[j]));
+ if (configtbl && configtbl->dataObj == NULL)
+ {
+ makeTableDataInfo(configtbl, false);
+ configtbl->dataObj->ext_config = true;
+ if (strlen(extconditionarray[j]) > 0)
+ configtbl->dataObj->filtercond = strdup(extconditionarray[j]);
+ }
+ }
+ }
+ if (extconfigarray)
+ free(extconfigarray);
+ if (extconditionarray)
+ free(extconditionarray);
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ *numExtensions = ntups;
+
+ return extinfo;
+}
+
/*
* getTypes:
* read all types in the system catalogs and return them in the
else
planginfo[i].lanowner = strdup("");
+ /* Assume it should be dumped (getDependencies may override this) */
+ planginfo[i].dobj.dump = true;
+
if (g_fout->remoteVersion < 70300)
{
/*
castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
+ /* Assume it should be dumped (getDependencies may override this) */
+ castinfo[i].dobj.dump = true;
+
/*
* Try to name cast as concatenation of typnames. This is only used
* for purposes of sorting. If we fail to find either type, the name
case DO_NAMESPACE:
dumpNamespace(fout, (NamespaceInfo *) dobj);
break;
+ case DO_EXTENSION:
+ dumpExtension(fout, (ExtensionInfo *) dobj);
+ break;
case DO_TYPE:
dumpType(fout, (TypeInfo *) dobj);
break;
destroyPQExpBuffer(delq);
}
+/*
+ * 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;\n",
+ qextname, fmtId(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 and Security Labels */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "EXTENSION %s", qextname);
+ dumpComment(fout, q->data,
+ NULL, "",
+ extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ NULL, "",
+ extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+
+ free(qextname);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+}
+
/*
* dumpType
* writes out to fout the queries to recreate a user-defined type
FuncInfo *inlineInfo = NULL;
FuncInfo *validatorInfo = NULL;
- if (dataOnly)
+ /* Skip if not to be dumped */
+ if (!plang->dobj.dump || dataOnly)
return;
/*
TypeInfo *sourceInfo;
TypeInfo *targetInfo;
- if (dataOnly)
+ /* Skip if not to be dumped */
+ if (!cast->dobj.dump || dataOnly)
return;
if (OidIsValid(cast->castfunc))
else
/* normal case */
addObjectDependency(dobj, refdobj->dumpId);
+
+ /*
+ * If it's an extension-membership dependency, mark the member
+ * object as not to be dumped. We still need the dependency links,
+ * though, to ensure that sorting is done correctly.
+ */
+ if (deptype == 'e')
+ {
+ dobj->dump = false;
+ if (dobj->objType == DO_TABLE)
+ {
+ /* Mark the data as not to be dumped either, unless config */
+ TableDataInfo *tdinfo = ((TableInfo *) dobj)->dataObj;
+
+ if (tdinfo && !tdinfo->ext_config)
+ tdinfo->dobj.dump = false;
+ }
+ }
}
PQclear(res);
{
/* When modifying this enum, update priority tables in pg_dump_sort.c! */
DO_NAMESPACE,
+ DO_EXTENSION,
DO_TYPE,
DO_SHELL_TYPE,
DO_FUNC,
char *nspacl;
} NamespaceInfo;
+typedef struct _extensionInfo
+{
+ DumpableObject dobj;
+ char *namespace; /* schema containing extension's objects */
+} ExtensionInfo;
+
typedef struct _typeInfo
{
DumpableObject dobj;
DumpableObject dobj;
TableInfo *tdtable; /* link to table to dump */
bool oids; /* include OIDs in data? */
+ bool ext_config; /* is table an extension config table? */
+ char *filtercond; /* WHERE condition to limit rows dumped */
} TableDataInfo;
typedef struct _indxInfo
* version specific routines
*/
extern NamespaceInfo *getNamespaces(int *numNamespaces);
+extern ExtensionInfo *getExtensions(int *numExtensions);
extern TypeInfo *getTypes(int *numTypes);
extern FuncInfo *getFuncs(int *numFuncs);
extern AggInfo *getAggregates(int *numAggregates);
* Sort priority for object types when dumping a pre-7.3 database.
* Objects are sorted by priority levels, and within an equal priority level
* by OID. (This is a relatively crude hack to provide semi-reasonable
- * behavior for old databases without full dependency info.) Note: text
- * search, foreign-data, and default ACL objects can't really happen here,
+ * behavior for old databases without full dependency info.) Note: extensions,
+ * text search, foreign-data, and default ACL objects can't really happen here,
* so the rather bogus priorities for them don't matter.
*/
static const int oldObjectTypePriority[] =
{
1, /* DO_NAMESPACE */
+ 1, /* DO_EXTENSION */
2, /* DO_TYPE */
2, /* DO_SHELL_TYPE */
2, /* DO_FUNC */
static const int newObjectTypePriority[] =
{
1, /* DO_NAMESPACE */
- 3, /* DO_TYPE */
- 3, /* DO_SHELL_TYPE */
- 4, /* DO_FUNC */
- 5, /* DO_AGG */
- 6, /* DO_OPERATOR */
- 7, /* DO_OPCLASS */
- 7, /* DO_OPFAMILY */
- 9, /* DO_CONVERSION */
- 16, /* DO_TABLE */
- 18, /* DO_ATTRDEF */
- 23, /* DO_INDEX */
- 24, /* DO_RULE */
- 25, /* DO_TRIGGER */
- 22, /* DO_CONSTRAINT */
- 26, /* DO_FK_CONSTRAINT */
+ 3, /* DO_EXTENSION */
+ 4, /* DO_TYPE */
+ 4, /* DO_SHELL_TYPE */
+ 5, /* DO_FUNC */
+ 6, /* DO_AGG */
+ 7, /* DO_OPERATOR */
+ 8, /* DO_OPCLASS */
+ 8, /* DO_OPFAMILY */
+ 10, /* DO_CONVERSION */
+ 17, /* DO_TABLE */
+ 19, /* DO_ATTRDEF */
+ 24, /* DO_INDEX */
+ 25, /* DO_RULE */
+ 26, /* DO_TRIGGER */
+ 23, /* DO_CONSTRAINT */
+ 27, /* DO_FK_CONSTRAINT */
2, /* DO_PROCLANG */
- 8 , /* DO_CAST */
- 20 , /* DO_TABLE_DATA */
- 17 , /* DO_DUMMY_TYPE */
- 10 , /* DO_TSPARSER */
- 12 , /* DO_TSDICT */
- 11 , /* DO_TSTEMPLATE */
- 13 , /* DO_TSCONFIG */
- 14 , /* DO_FDW */
- 15 , /* DO_FOREIGN_SERVER */
- 27 , /* DO_DEFAULT_ACL */
- 19 , /* DO_BLOB */
- 21 /* DO_BLOB_DATA */
+ 9 , /* DO_CAST */
+ 21 , /* DO_TABLE_DATA */
+ 18 , /* DO_DUMMY_TYPE */
+ 11 , /* DO_TSPARSER */
+ 13 , /* DO_TSDICT */
+ 12 , /* DO_TSTEMPLATE */
+ 14 , /* DO_TSCONFIG */
+ 15 , /* DO_FDW */
+ 16 , /* DO_FOREIGN_SERVER */
+ 28 , /* DO_DEFAULT_ACL */
+ 20 , /* DO_BLOB */
+ 22 /* DO_BLOB_DATA */
};
"SCHEMA %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
+ case DO_EXTENSION:
+ snprintf(buf, bufsize,
+ "EXTENSION %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
case DO_TYPE:
snprintf(buf, bufsize,
"TYPE %s (ID %d OID %u)",
break;
}
break;
+ case 'x': /* Extensions */
+ if (show_verbose)
+ success = listExtensionContents(pattern);
+ else
+ success = listExtensions(pattern);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
}
const char *cfgname,
const char *pnspname, const char *prsname);
static void printACLColumn(PQExpBuffer buf, const char *colname);
+static bool listOneExtensionContents(const char *extname, const char *oid);
/*----------------
if (pset.sversion < 90100)
{
- fprintf(stderr, _("The server (version %d.%d) does not support foreign table.\n"),
+ fprintf(stderr, _("The server (version %d.%d) does not support foreign tables .\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
return true;
}
+/*
+ * \dx
+ *
+ * Briefly describes installed extensions.
+ */
+bool
+listExtensions(const char *pattern)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ if (pset.sversion < 90100)
+ {
+ fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
+ pset.sversion / 10000, (pset.sversion / 100) % 100);
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT e.extname AS \"%s\", "
+ "e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n"
+ "FROM pg_catalog.pg_extension e "
+ "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
+ "LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid "
+ "AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n",
+ gettext_noop("Name"),
+ gettext_noop("Version"),
+ gettext_noop("Schema"),
+ gettext_noop("Description"));
+
+ processSQLNamePattern(pset.db, &buf, pattern,
+ false, false,
+ NULL, "e.extname", NULL,
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of installed extensions");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
+/*
+ * \dx+
+ *
+ * List contents of installed extensions.
+ */
+bool
+listExtensionContents(const char *pattern)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ int i;
+
+ if (pset.sversion < 90100)
+ {
+ fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
+ pset.sversion / 10000, (pset.sversion / 100) % 100);
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT e.extname, e.oid\n"
+ "FROM pg_catalog.pg_extension e\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern,
+ false, false,
+ NULL, "e.extname", NULL,
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ if (PQntuples(res) == 0)
+ {
+ if (!pset.quiet)
+ {
+ if (pattern)
+ fprintf(stderr, _("Did not find any extension named \"%s\".\n"),
+ pattern);
+ else
+ fprintf(stderr, _("Did not find any extensions.\n"));
+ }
+ PQclear(res);
+ return false;
+ }
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ const char *extname;
+ const char *oid;
+
+ extname = PQgetvalue(res, i, 0);
+ oid = PQgetvalue(res, i, 1);
+
+ if (!listOneExtensionContents(extname, oid))
+ {
+ PQclear(res);
+ return false;
+ }
+ if (cancel_pressed)
+ {
+ PQclear(res);
+ return false;
+ }
+ }
+
+ PQclear(res);
+ return true;
+}
+
+static bool
+listOneExtensionContents(const char *extname, const char *oid)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ char title[1024];
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
+ "FROM pg_catalog.pg_depend\n"
+ "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
+ "ORDER BY 1;",
+ gettext_noop("Object Description"),
+ oid);
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ snprintf(title, sizeof(title), _("Objects in extension \"%s\""), extname);
+ myopt.title = title;
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* printACLColumn
*
/* \dL */
extern bool listLanguages(const char *pattern, bool verbose, bool showSystem);
+/* \dx */
+extern bool listExtensions(const char *pattern);
+
+/* \dx+ */
+extern bool listExtensionContents(const char *pattern);
+
#endif /* DESCRIBE_H */
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[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
" FROM pg_catalog.pg_proc "\
" WHERE proname='%s'"
+#define Query_for_list_of_extensions \
+" SELECT pg_catalog.quote_ident(extname) "\
+" FROM pg_catalog.pg_extension "\
+" WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'"
+
+#define Query_for_list_of_available_extensions \
+" SELECT pg_catalog.quote_ident(name) "\
+" FROM pg_catalog.pg_available_extensions "\
+" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed IS NULL"
+
/*
* This is a list of all "things" in Pgsql, which can show up after CREATE or
* DROP; and there is also a query to get a list of them.
{"DATABASE", Query_for_list_of_databases},
{"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
{"DOMAIN", NULL, &Query_for_list_of_domains},
+ {"EXTENSION", Query_for_list_of_extensions},
{"FOREIGN DATA WRAPPER", NULL, NULL},
{"FOREIGN TABLE", NULL, NULL},
{"FUNCTION", NULL, &Query_for_list_of_functions},
pg_strcasecmp(prev3_wd, "TABLE") != 0)
{
static const char *const list_ALTER[] =
- {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
- "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
- "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
+ {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
+ "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
+ "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
+ "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
+ "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
+ "USER", "USER MAPPING FOR", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
COMPLETE_WITH_LIST(list_ALTERDATABASE);
}
+ /* ALTER EXTENSION */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
+ COMPLETE_WITH_CONST("SET SCHEMA");
+
/* ALTER FOREIGN */
else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
pg_strcasecmp(prev_wd, "FOREIGN") == 0)
pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
+ /* CREATE EXTENSION */
+ /* Complete with available extensions rather than installed ones. */
+ else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev_wd, "EXTENSION") == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
+ /* CREATE EXTENSION */
+ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
+ COMPLETE_WITH_CONST("WITH SCHEMA");
+
/* CREATE FOREIGN */
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
pg_strcasecmp(prev_wd, "FOREIGN") == 0)
else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
(pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
+ pg_strcasecmp(prev2_wd, "EXTENSION") == 0 ||
pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201102083
+#define CATALOG_VERSION_NO 201102084
#endif
* Example: a trigger that's created to enforce a foreign-key constraint
* is made internally dependent on the constraint's pg_constraint entry.
*
+ * DEPENDENCY_EXTENSION ('e'): the dependent object is a member of the
+ * extension that is the referenced object. The dependent object can be
+ * dropped only via DROP EXTENSION on the referenced object. Functionally
+ * this dependency type acts the same as an internal dependency, but it's
+ * kept separate for clarity and to simplify pg_dump.
+ *
* DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
* is a signal that the system itself depends on the referenced object,
* and so that object must never be deleted. Entries of this type are
DEPENDENCY_NORMAL = 'n',
DEPENDENCY_AUTO = 'a',
DEPENDENCY_INTERNAL = 'i',
+ DEPENDENCY_EXTENSION = 'e',
DEPENDENCY_PIN = 'p'
} DependencyType;
OCLASS_FDW, /* pg_foreign_data_wrapper */
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
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;
int nreferenced,
DependencyType behavior);
-extern long deleteDependencyRecordsFor(Oid classId, Oid objectId);
+extern void recordDependencyOnCurrentExtension(const ObjectAddress *object);
+
+extern long deleteDependencyRecordsFor(Oid classId, Oid objectId,
+ bool skipExtensionDeps);
extern long changeDependencyFor(Oid classId, Oid objectId,
Oid refClassId, Oid oldRefObjectId,
Oid newRefObjectId);
+extern Oid getExtensionOfObject(Oid classId, Oid objectId);
+
extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
extern void markSequenceUnowned(Oid seqId);
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, 3080, on pg_extension using btree(oid oid_ops));
+#define ExtensionOidIndexId 3080
+
+DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
+#define ExtensionNameIndexId 3081
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pg_extension.h
+ * definition of the system "extension" relation (pg_extension)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2011, 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 3079
+
+CATALOG(pg_extension,3079)
+{
+ NameData extname; /* extension name */
+ Oid extowner; /* extension owner */
+ Oid extnamespace; /* namespace of contained objects */
+ bool extrelocatable; /* if true, allow ALTER EXTENSION SET SCHEMA */
+
+ /*
+ * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
+ */
+ text extversion; /* extension version ID, if any */
+ Oid extconfig[1]; /* dumpable configuration tables */
+ text extcondition[1]; /* WHERE clauses for config tables */
+} 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 7
+#define Anum_pg_extension_extname 1
+#define Anum_pg_extension_extowner 2
+#define Anum_pg_extension_extnamespace 3
+#define Anum_pg_extension_extrelocatable 4
+#define Anum_pg_extension_extversion 5
+#define Anum_pg_extension_extconfig 6
+#define Anum_pg_extension_extcondition 7
+
+/* ----------------
+ * pg_extension has no initial contents
+ * ----------------
+ */
+
+#endif /* PG_EXTENSION_H */
DATA(insert OID = 2987 ( btrecordcmp PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "2249 2249" _null_ _null_ _null_ _null_ btrecordcmp _null_ _null_ _null_ ));
DESCR("btree less-equal-greater");
+/* Extensions */
+DATA(insert OID = 3082 ( pg_available_extensions PGNSP PGUID 12 10 100 0 f f f t t s 0 0 2249 "" "{19,25,16,25}" "{o,o,o,o}" "{name,version,relocatable,comment}" _null_ pg_available_extensions _null_ _null_ _null_ ));
+DESCR("list available extensions");
+DATA(insert OID = 3083 ( pg_extension_config_dump PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ pg_extension_config_dump _null_ _null_ _null_ ));
+DESCR("flag an extension's table contents to be emitted by pg_dump");
+
/* SQL-spec window functions */
DATA(insert OID = 3100 ( row_number PGNSP PGUID 12 1 0 0 f t f f f i 0 0 20 "" _null_ _null_ _null_ _null_ window_row_number _null_ _null_ _null_ ));
DESCR("row number within partition");
extern void ExecRenameStmt(RenameStmt *stmt);
extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
-extern void AlterObjectNamespace(Relation rel, int cacheId,
- Oid classId, Oid objid, Oid nsp Id,
- int Anum_name, int Anum_namespace, int Anum_owner ,
- AclObjectKind acl_kind ,
- bool superuser_only );
+extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid);
+extern Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCache Id,
+ Oid objid, Oid nspOid ,
+ int Anum_name, int Anum_namespace, int Anum_owner ,
+ AclObjectKind acl_kind );
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
#endif /* ALTER_H */
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 */
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);
TypeName *typename2, Oid newOwnerId);
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
+extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
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 void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema);
+extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
extern Oid get_am_oid(const char *amname, bool missing_ok);
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 void DefineTSParser(List *names, List *parameters);
extern void RenameTSParser(List *oldname, const char *newname);
extern void AlterTSParserNamespace(List *name, const char *newschema);
+extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
extern void RemoveTSParsers(DropStmt *drop);
extern void RemoveTSParserById(Oid prsId);
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 AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
+extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
extern text *serialize_deflist(List *deflist);
extern List *deserialize_deflist(Datum txt);
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * extension.h
+ * Extension management commands (create/drop extension).
+ *
+ *
+ * Portions Copyright (c) 1996-2011, 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 "nodes/parsenodes.h"
+
+
+/*
+ * creating_extension is only true while running a CREATE EXTENSION command.
+ * It instructs recordDependencyOnCurrentExtension() to register a dependency
+ * on the current pg_extension object for each SQL object created by its
+ * installation script.
+ */
+extern bool creating_extension;
+extern Oid CurrentExtensionObject;
+
+
+extern void CreateExtension(CreateExtensionStmt *stmt);
+
+extern void RemoveExtensions(DropStmt *stmt);
+extern void RemoveExtensionById(Oid extId);
+
+extern Oid get_extension_oid(const char *extname, bool missing_ok);
+extern char *get_extension_name(Oid ext_oid);
+
+extern void AlterExtensionNamespace(List *names, const char *newschema);
+
+#endif /* EXTENSION_H */
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
bool hasDependEntry);
extern void AlterTypeNamespace(List *names, const char *newschema);
-extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
+extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid);
+extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType);
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
T_CreateForeignTableStmt,
+ T_CreateExtensionStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DOMAIN,
+ OBJECT_EXTENSION,
OBJECT_FDW,
OBJECT_FOREIGN_SERVER,
OBJECT_FOREIGN_TABLE,
bool isReset;
} AlterTableSpaceOptionsStmt;
+/* ----------------------
+ * Create Extension Statement
+ * ----------------------
+ */
+
+typedef struct CreateExtensionStmt
+{
+ NodeTag type;
+ char *extname;
+ List *options; /* List of DefElem nodes */
+} CreateExtensionStmt;
+
/* ----------------------
* Create/Drop FOREIGN DATA WRAPPER Statements
* ----------------------
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)
extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
/* genfile.c */
+extern bytea *read_binary_file(const char *filename,
+ int64 seek_offset, int64 bytes_to_read);
extern Datum pg_stat_file(PG_FUNCTION_ARGS);
extern Datum pg_read_file(PG_FUNCTION_ARGS);
extern Datum pg_read_file_all(PG_FUNCTION_ARGS);
/* commands/constraint.c */
extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
+/* commands/extension.c */
+extern Datum pg_available_extensions(PG_FUNCTION_ARGS);
+extern Datum pg_extension_config_dump(PG_FUNCTION_ARGS);
+
/* commands/prepare.c */
extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
#
# Set one of these three variables to specify what is built:
#
-# MODULES -- list of shared objects to be built from source files with
-# same stem (do not include suffix in this list)
-# MODULE_big -- a shared object to build from multiple source files
+# MODULES -- list of shared-library objects to be built from source files
+# with same stem (do not include library suffixes in this list)
+# MODULE_big -- a shared library to build from multiple source files
# (list object files in OBJS)
-# PROGRAM -- a binary program to build (list object files in OBJS)
+# PROGRAM -- an executable program to build (list object files in OBJS)
#
# The following variables can also be set:
#
-# MODULEDIR -- subdirectory into which DATA and DOCS files should be
-# installed (if not set, default is "contrib")
+# MODULEDIR -- subdirectory into which EXTENSION, DATA and DOCS files
+# should be installed (if not set, default is "contrib")
+# EXTENSION -- name of extension (there must be a $EXTENSION.control file)
# DATA -- random files to install into $PREFIX/share/$MODULEDIR
# DATA_built -- random files to install into $PREFIX/share/$MODULEDIR,
# which need to be built first
override CPPFLAGS := $(PG_CPPFLAGS) $(CPPFLAGS)
endif
-all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES))
+all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES)) $(addsuffix .control, $(EXTENSION))
ifdef MODULE_big
# shared library parameters
install: all installdirs
-ifneq (,$(DATA)$(DATA_built))
- @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
+ifneq (,$(DATA)$(DATA_built)$(EXTENSION) )
+ @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) $(addsuffix .control, $(EXTENSION)) ; do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
done
uninstall:
-ifneq (,$(DATA)$(DATA_built))
- rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built)))
+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)))
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, x.extversion AS installed, n.nspname AS schema, e.relocatable, e.comment FROM ((pg_available_extensions() e(name, version, relocatable, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))) LEFT JOIN pg_namespace n ON ((n.oid = x.extnamespace)));
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"));
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
-(58 rows)
+(59 rows)
SELECT tablename, rulename, definition FROM pg_rules
ORDER BY tablename, rulename;
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