extension.v0.patch
text/x-patch
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
Series: patch v0
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 1 | 0 |
| src/backend/catalog/dependency.c | 24 | 0 |
| src/backend/catalog/heap.c | 8 | 0 |
| src/backend/catalog/Makefile | 1 | 0 |
| src/backend/catalog/objectaddress.c | 14 | 0 |
| src/backend/catalog/pg_aggregate.c | 7 | 0 |
| src/backend/catalog/pg_conversion.c | 7 | 0 |
| src/backend/catalog/pg_namespace.c | 13 | 0 |
| src/backend/catalog/pg_operator.c | 7 | 0 |
| src/backend/catalog/pg_proc.c | 7 | 0 |
| src/backend/catalog/pg_type.c | 8 | 0 |
| src/backend/commands/comment.c | 6 | 0 |
| src/backend/commands/extension.c | 456 | 0 |
| src/backend/commands/foreigncmds.c | 19 | 0 |
| src/backend/commands/functioncmds.c | 6 | 1 |
| src/backend/commands/Makefile | 2 | 0 |
| src/backend/commands/opclasscmds.c | 13 | 0 |
| src/backend/commands/proclang.c | 7 | 0 |
| src/backend/commands/tsearchcmds.c | 25 | 0 |
| src/backend/nodes/copyfuncs.c | 22 | 0 |
| src/backend/nodes/equalfuncs.c | 18 | 0 |
| src/backend/parser/gram.y | 47 | 0 |
| src/backend/tcop/utility.c | 27 | 0 |
| src/backend/utils/adt/genfile.c | 193 | 0 |
| src/backend/utils/misc/cfparser.c | 112 | 0 |
| src/backend/utils/misc/Makefile | 1 | 0 |
| src/bin/pg_dump/common.c | 6 | 0 |
| src/bin/pg_dump/pg_dump.c | 467 | 0 |
| src/bin/pg_dump/pg_dump.h | 10 | 0 |
| src/bin/pg_dump/pg_dump_sort.c | 7 | 0 |
| src/bin/psql/command.c | 3 | 0 |
| src/bin/psql/describe.c | 45 | 0 |
| src/bin/psql/describe.h | 3 | 0 |
| src/bin/psql/help.c | 1 | 0 |
| src/include/catalog/dependency.h | 1 | 0 |
| src/include/catalog/indexing.h | 6 | 0 |
| src/include/catalog/pg_extension.h | 61 | 0 |
| src/include/catalog/pg_proc.h | 6 | 0 |
| src/include/catalog/toasting.h | 1 | 0 |
| src/include/commands/extension.h | 49 | 0 |
| src/include/nodes/nodes.h | 2 | 0 |
| src/include/nodes/parsenodes.h | 20 | 0 |
| src/include/parser/kwlist.h | 1 | 0 |
| src/include/utils/builtins.h | 1 | 0 |
| src/include/utils/cfparser.h | 18 | 0 |
| src/makefiles/pgxs.mk | 18 | 0 |
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 55,60 ****
--- 55,61 ----
#include "utils/guc.h"
#include "utils/ps_status.h"
#include "utils/relmapper.h"
+ #include "utils/cfparser.h"
#include "pg_trace.h"
***************
*** 5018,5117 **** str_time(pg_time_t tnow)
}
/*
- * Parse one line from recovery.conf. 'cmdline' is the raw line from the
- * file. If the line is parsed successfully, returns true, false indicates
- * syntax error. On success, *key_p and *value_p are set to the parameter
- * name and value on the line, respectively. If the line is an empty line,
- * consisting entirely of whitespace and comments, function returns true
- * and *keyp_p and *value_p are set to NULL.
- *
- * The pointers returned in *key_p and *value_p point to an internal buffer
- * that is valid only until the next call of parseRecoveryCommandFile().
- */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- char *ptr;
- char *bufp;
- char *key;
- char *value;
- static char *buf = NULL;
-
- *key_p = *value_p = NULL;
-
- /*
- * Allocate the buffer on first use. It's used to hold both the parameter
- * name and value.
- */
- if (buf == NULL)
- buf = malloc(MAXPGPATH + 1);
- bufp = buf;
-
- /* Skip any whitespace at the beginning of line */
- for (ptr = cmdline; *ptr; ptr++)
- {
- if (!isspace((unsigned char) *ptr))
- break;
- }
- /* Ignore empty lines */
- if (*ptr == '\0' || *ptr == '#')
- return true;
-
- /* Read the parameter name */
- key = bufp;
- while (*ptr && !isspace((unsigned char) *ptr) &&
- *ptr != '=' && *ptr != '\'')
- *(bufp++) = *(ptr++);
- *(bufp++) = '\0';
-
- /* Skip to the beginning quote of the parameter value */
- ptr = strchr(ptr, '\'');
- if (!ptr)
- return false;
- ptr++;
-
- /* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- value = bufp;
- for (;;)
- {
- if (*ptr == '\'')
- {
- ptr++;
- if (*ptr == '\'')
- *(bufp++) = '\'';
- else
- {
- /* end of parameter */
- *bufp = '\0';
- break;
- }
- }
- else if (*ptr == '\0')
- return false; /* unterminated quoted string */
- else
- *(bufp++) = *ptr;
-
- ptr++;
- }
- *(bufp++) = '\0';
-
- /* Check that there's no garbage after the value */
- while (*ptr)
- {
- if (*ptr == '#')
- break;
- if (!isspace((unsigned char) *ptr))
- return false;
- ptr++;
- }
-
- /* Success! */
- *key_p = key;
- *value_p = value;
- return true;
- }
-
- /*
* See if there is a recovery command file (recovery.conf), and if so
* read in parameters for archive recovery and XLOG streaming.
*
--- 5019,5024 ----
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 39,44 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
--- 39,45 ----
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_default_acl.h pg_seclabel.h \
+ pg_extension.h \
toasting.h indexing.h \
)
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 34,39 ****
--- 34,40 ----
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
***************
*** 55,60 ****
--- 56,62 ----
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+ #include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
***************
*** 152,158 **** static const Oid object_classes[MAX_OCLASS] = {
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
! DefaultAclRelationId /* OCLASS_DEFACL */
};
--- 154,161 ----
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
! DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId /* OCLASS_EXTENSION */
};
***************
*** 1148,1153 **** doDeletion(const ObjectAddress *object)
--- 1151,1160 ----
RemoveUserMappingById(object->objectId);
break;
+ case OCLASS_EXTENSION:
+ RemoveExtensionById(object->objectId);
+ break;
+
case OCLASS_DEFACL:
RemoveDefaultACLById(object->objectId);
break;
***************
*** 2082,2087 **** getObjectClass(const ObjectAddress *object)
--- 2089,2098 ----
case DefaultAclRelationId:
Assert(object->objectSubId == 0);
return OCLASS_DEFACL;
+
+ case ExtensionRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_EXTENSION;
}
/* shouldn't get here */
***************
*** 2563,2568 **** getObjectDescription(const ObjectAddress *object)
--- 2574,2591 ----
break;
}
+ case OCLASS_EXTENSION:
+ {
+ char *extension;
+
+ extension = get_extension_name(object->objectId);
+ if (!extension)
+ elog(ERROR, "cache lookup failed for extension %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("extension %s"), extension);
+ break;
+ }
+
case OCLASS_TBLSPACE:
{
char *tblspace;
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 49,54 ****
--- 49,55 ----
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
#include "catalog/storage.h"
+ #include "commands/extension.h"
#include "commands/tablecmds.h"
#include "commands/typecmds.h"
#include "miscadmin.h"
***************
*** 1139,1145 **** heap_create_with_catalog(const char *relname,
/*
* Make a dependency link to force the relation to be deleted if its
* namespace is. Also make a dependency link to its owner, as well as
! * dependencies for any roles mentioned in the default ACL.
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
--- 1140,1147 ----
/*
* Make a dependency link to force the relation to be deleted if its
* namespace is. Also make a dependency link to its owner, as well as
! * dependencies for any roles mentioned in the default ACL. Record a
! * dependancy to the extension we're part of if any.
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
***************
*** 1186,1191 **** heap_create_with_catalog(const char *relname,
--- 1188,1198 ----
0, NULL,
nnewmembers, newmembers);
}
+
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 28,33 ****
--- 28,34 ----
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+ #include "catalog/pg_extension.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
***************
*** 47,52 ****
--- 48,54 ----
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
+ #include "commands/extension.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "nodes/makefuncs.h"
***************
*** 128,133 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 130,136 ----
address = get_object_address_relobject(objtype, objname, &relation);
break;
case OBJECT_DATABASE:
+ case OBJECT_EXTENSION:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
case OBJECT_SCHEMA:
***************
*** 266,271 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 269,277 ----
case OBJECT_DATABASE:
msg = gettext_noop("database name cannot be qualified");
break;
+ case OBJECT_EXTENSION:
+ msg = gettext_noop("extension name cannot be qualified");
+ break;
case OBJECT_TABLESPACE:
msg = gettext_noop("tablespace name cannot be qualified");
break;
***************
*** 298,303 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 304,314 ----
address.objectId = get_database_oid(name, false);
address.objectSubId = 0;
break;
+ case OBJECT_EXTENSION:
+ address.classId = ExtensionRelationId;
+ address.objectId = get_extension_oid(name, false);
+ address.objectSubId = 0;
+ break;
case OBJECT_TABLESPACE:
address.classId = TableSpaceRelationId;
address.objectId = get_tablespace_oid(name, false);
***************
*** 635,640 **** object_exists(ObjectAddress address)
--- 646,654 ----
case TSConfigRelationId:
cache = TSCONFIGOID;
break;
+ case ExtensionRelationId:
+ indexoid = ExtensionOidIndexId;
+ break;
default:
elog(ERROR, "unrecognized classid: %u", address.classId);
}
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
***************
*** 32,37 ****
--- 32,38 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
***************
*** 293,298 **** AggregateCreate(const char *aggName,
--- 294,305 ----
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* Depends on currently installed extension, if any */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/catalog/pg_conversion.c
--- b/src/backend/catalog/pg_conversion.c
***************
*** 30,35 ****
--- 30,36 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
/*
* ConversionCreate
***************
*** 131,136 **** ConversionCreate(const char *conname, Oid connamespace,
--- 132,143 ----
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner);
+ /* Depends on currently installed extension, if any */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 21,26 ****
--- 21,27 ----
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
/* ----------------
***************
*** 75,79 **** NamespaceCreate(const char *nspName, Oid ownerId)
--- 76,92 ----
/* Record dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+ /* Record dependency on extension, if we're in a CREATE EXTENSION */
+ if (create_extension != NULL )
+ {
+ ObjectAddress myself;
+
+ myself.classId = NamespaceRelationId;
+ myself.objectId = nspoid;
+ myself.objectSubId = 0;
+
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
return nspoid;
}
*** a/src/backend/catalog/pg_operator.c
--- b/src/backend/catalog/pg_operator.c
***************
*** 33,38 ****
--- 33,39 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
static Oid OperatorGet(const char *operatorName,
***************
*** 846,849 **** makeOperatorDependencies(HeapTuple tuple)
--- 847,856 ----
/* Dependency on owner */
recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
oper->oprowner);
+
+ /* Dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 35,40 ****
--- 35,41 ----
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
***************
*** 614,619 **** ProcedureCreate(const char *procedureName,
--- 615,626 ----
nnewmembers, newmembers);
}
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 31,36 ****
--- 31,38 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
+
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
***************
*** 625,630 **** GenerateTypeDependencies(Oid typeNamespace,
--- 627,638 ----
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 14,20 **** include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
constraint.o conversioncmds.o copy.o \
! dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
--- 14,21 ----
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
constraint.o conversioncmds.o copy.o \
! dbcommands.o define.o discard.o \
! extension.o explain.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
*** a/src/backend/commands/comment.c
--- b/src/backend/commands/comment.c
***************
*** 142,147 **** CommentObject(CommentStmt *stmt)
--- 142,153 ----
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to comment on procedural language")));
break;
+ case OBJECT_EXTENSION:
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on extension")));
+ break;
case OBJECT_OPCLASS:
if (!pg_opclass_ownercheck(address.objectId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
*** /dev/null
--- b/src/backend/commands/extension.c
***************
*** 0 ****
--- 1,456 ----
+ /*-------------------------------------------------------------------------
+ *
+ * extension.c
+ * Commands to manipulate extensions
+ *
+ * Extensions in PostgreSQL allow user defined behavior plugged in at
+ * runtime. They will typically create SQL objects that you want to avoid
+ * dumping, issuing a single CREATE EXTENSION foo; command instead.
+ *
+ * All we need internally to manage an extension is an OID so that the
+ * dependant objects can get associated with it. An extension is created by
+ * populating the pg_extension catalog from a "control" file, containing
+ * some parameters.
+ *
+ * The extension control file format is the most simple name = value, we
+ * don't need anything more there. The SQL file to execute commands from is
+ * hardcoded to `pg_config --sharedir`/<extension>.install.sql.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/commands/extension.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include <unistd.h>
+ #include <dirent.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+
+ #include "nodes/parsenodes.h"
+ #include "miscadmin.h"
+ #include "catalog/pg_extension.h"
+ #include "commands/extension.h"
+ #include "storage/fd.h"
+ #include "utils/builtins.h"
+ #include "utils/cfparser.h"
+ #include "utils/rel.h"
+ #include "access/heapam.h"
+ #include "catalog/indexing.h"
+ #include "catalog/dependency.h"
+ #include "utils/memutils.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "access/sysattr.h"
+
+ /*
+ * See commands/extension.h for details.
+ */
+ ObjectAddress *create_extension = NULL;
+
+ char *
+ get_extension_install_filename(const char *extname)
+ {
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ get_share_path(my_exec_path, sharepath);
+ result = palloc(MAXPGPATH);
+ snprintf(result, MAXPGPATH, "%s/contrib/%s.sql", sharepath, extname);
+
+ return result;
+ }
+
+ char *
+ get_extension_control_filename(const char *extname)
+ {
+ char sharepath[MAXPGPATH];
+ char *result;
+
+ get_share_path(my_exec_path, sharepath);
+ result = palloc(MAXPGPATH);
+ snprintf(result, MAXPGPATH, "%s/contrib/%s.control", sharepath, extname);
+
+ return result;
+ }
+
+ /*
+ * The control file is supposed to be very short, half a dozen lines, and
+ * reading it is only allowed to superuser, so we don't even try to minimize
+ * memory allocation risks here.
+ *
+ * Also note that only happens in CREATE EXTENSION.
+ */
+ ExtensionControlFile *
+ read_extension_control_file(const char *filename)
+ {
+ int64 fsize = -1;
+ struct stat fst;
+ FILE *file;
+ char line[MAXPGPATH];
+ bool syntaxError = false;
+ ExtensionControlFile *control =
+ (ExtensionControlFile *)palloc(sizeof(ExtensionControlFile));
+
+ /*
+ * Stat the file to get its size, then read its content into memory so
+ * that we can "parse" it.
+ */
+ if (stat(filename, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+
+ fsize = Int64GetDatum((int64) fst.st_size);
+
+ if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ filename)));
+
+ if (ferror(file))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+
+ /*
+ * Prepare our ExtensionControlFile structure with default values, then
+ * "parse" the control file.
+ *
+ * The parsing uses directly the code for parsing Recovery Commands as
+ * we're abusing the exact same format here.
+ */
+ control->name = NULL;
+ control->version = NULL;
+ control->custom_class = NULL;
+
+ while (fgets(line, sizeof(line), file) != NULL)
+ {
+ char *tok1;
+ char *tok2;
+
+ if (!parseRecoveryCommandFileLine(line, &tok1, &tok2))
+ {
+ syntaxError = true;
+ break;
+ }
+ if (tok1 == NULL)
+ continue;
+
+ if (strcmp(tok1, "name") == 0)
+ {
+ control->name = pstrdup(tok2);
+ }
+ if (strcmp(tok1, "version") == 0)
+ {
+ control->version = pstrdup(tok2);
+ }
+ if (strcmp(tok1, "custom_class") == 0)
+ {
+ control->custom_class = pstrdup(tok2);
+ }
+ }
+
+ if( control->name == NULL )
+ ereport(FATAL,
+ (errmsg("Missing a name parameter in file: %s", filename)));
+
+ if (syntaxError)
+ ereport(FATAL,
+ (errmsg("syntax error in extension control file: %s", line),
+ errhint("Lines should have the format parameter = 'value'.")));
+
+ FreeFile(file);
+ return control;
+ }
+
+ /*
+ * Create an extension
+ *
+ * Only superusers can create an extension.
+ */
+ void
+ CreateExtension(CreateExtensionStmt *stmt)
+ {
+ ExtensionControlFile *control;
+ Relation rel;
+ Datum values[Natts_pg_extension];
+ bool nulls[Natts_pg_extension];
+ HeapTuple tuple;
+ Oid extensionoid;
+ text *filename;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create extension \"%s\"",
+ stmt->extname),
+ errhint("Must be superuser to create an extension.")));
+
+ if( InvalidOid != get_extension_oid(stmt->extname, true) )
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("extension \"%s\" already exists", stmt->extname)));
+ }
+
+ control = read_extension_control_file(
+ get_extension_control_filename(stmt->extname));
+
+ /*
+ * Insert tuple into pg_extension.
+ *
+ * We use ExclusiveLock here so that there's only CREATE EXTENSION
+ * possible at any given time. It's necessary because of the way the
+ * global variable create_extension is handled, and it seems like a
+ * restriction that's easy to live with.
+ */
+ rel = heap_open(ExtensionRelationId, ExclusiveLock);
+
+ memset(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_extension_extname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(control->name));
+
+ if( control->version == NULL )
+ nulls[Anum_pg_extension_extversion - 1] = true;
+ else
+ values[Anum_pg_extension_extversion - 1] =
+ CStringGetTextDatum(control->version);
+
+ if( control->custom_class == NULL )
+ nulls[Anum_pg_extension_custom_class - 1] = true;
+ else
+ values[Anum_pg_extension_custom_class - 1] =
+ CStringGetTextDatum(control->custom_class);
+
+ tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ extensionoid = simple_heap_insert(rel, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+ heap_freetuple(tuple);
+ heap_close(rel, ExclusiveLock);
+
+ /*
+ * Set the global create_extension so that any object created in the
+ * script has a dependancy recorded towards the extension here.
+ */
+ create_extension = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ create_extension->classId = ExtensionRelationId;
+ create_extension->objectId = extensionoid;
+ create_extension->objectSubId = 0;
+
+ elog(NOTICE,
+ "Installing extension '%s' from '%s'",
+ stmt->extname,
+ get_extension_install_filename(stmt->extname));
+
+ filename = cstring_to_text(get_extension_install_filename(stmt->extname));
+ DirectFunctionCall1(pg_execute_from_file, PointerGetDatum(filename));
+
+ /* reset the current_extension dependancy tracker */
+ pfree(create_extension);
+ create_extension = NULL;
+
+ return;
+ }
+
+ /*
+ * Drop an extension
+ */
+ void
+ DropExtension(DropExtensionStmt *stmt)
+ {
+ char *extname = stmt->extname;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+ Oid extensionoid;
+ ObjectAddress object;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to drop extension \"%s\"",
+ stmt->extname),
+ errhint("Must be superuser to drop an extension.")));
+
+ /*
+ * Find the target tuple
+ */
+ rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_extension_extname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(extname));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ if (!stmt->missing_ok)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("extension \"%s\" does not exist", extname)));
+ }
+ else
+ {
+ ereport(NOTICE,
+ (errmsg("extension \"%s\" does not exist, skipping",
+ extname)));
+ /* XXX I assume I need one or both of these next two calls */
+ heap_endscan(scandesc);
+ heap_close(rel, RowExclusiveLock);
+ }
+ return;
+ }
+ extensionoid = HeapTupleGetOid(tuple);
+ heap_endscan(scandesc);
+ heap_close(rel, RowExclusiveLock);
+
+ /*
+ * Do the deletion
+ */
+ object.classId = ExtensionRelationId;
+ object.objectId = extensionoid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+
+ return;
+ }
+
+ /*
+ * get_extension_oid - given an extension name, look up the OID
+ *
+ * If missing_ok is false, throw an error if extension name not found. If
+ * true, just return InvalidOid.
+ */
+ Oid
+ get_extension_oid(const char *extname, bool missing_ok)
+ {
+ Oid result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_extension_extname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(extname));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = HeapTupleGetOid(tuple);
+ else
+ result = InvalidOid;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ if (!OidIsValid(result) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("extension \"%s\" does not exist",
+ extname)));
+
+ return result;
+ }
+
+ /*
+ * get_extension_name - given an extension OID, look up the name
+ *
+ * Returns a palloc'd string, or NULL if no such tablespace.
+ */
+ char *
+ get_extension_name(Oid ext_oid)
+ {
+ char *result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ /*
+ * Search pg_extension. We use a heapscan here even though there is an
+ * index on oid, on the theory that pg_extension will usually have just a
+ * few entries and so an indexed lookup is a waste of effort.
+ */
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(ext_oid));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
+ else
+ result = NULL;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return result;
+ }
+
+ /*
+ * Drop extension by OID. This is called to clean up dependencies.
+ */
+ char *
+ RemoveExtensionById(Oid extId)
+ {
+ char *result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ /*
+ * Search pg_extension. We use a heapscan here even though there is an
+ * index on oid, on the theory that pg_extension will usually have just a
+ * few entries and so an indexed lookup is a waste of effort.
+ */
+ rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(extId));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
+ else
+ result = NULL;
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return result;
+ }
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
***************
*** 32,37 ****
--- 32,38 ----
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+ #include "commands/extension.h"
/*
***************
*** 411,416 **** CreateForeignDataWrapper(CreateFdwStmt *stmt)
--- 412,423 ----
referenced.objectId = fdwvalidator;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
***************
*** 696,701 **** CreateForeignServer(CreateForeignServerStmt *stmt)
--- 703,714 ----
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, NoLock);
}
***************
*** 967,972 **** CreateUserMapping(CreateUserMappingStmt *stmt)
--- 980,991 ----
/* Record the mapped user dependency */
recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, NoLock);
}
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 61,67 ****
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
!
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
Oid newOwnerId);
--- 61,67 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
! #include "commands/extension.h"
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
Oid newOwnerId);
***************
*** 1744,1749 **** CreateCast(CreateCastStmt *stmt)
--- 1744,1754 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_freetuple(tuple);
heap_close(relation, RowExclusiveLock);
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 42,47 ****
--- 42,48 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
/*
***************
*** 306,311 **** CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
--- 307,318 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, RowExclusiveLock);
return opfamilyoid;
***************
*** 693,698 **** DefineOpClass(CreateOpClassStmt *stmt)
--- 700,711 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, RowExclusiveLock);
}
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
***************
*** 36,41 ****
--- 36,42 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
typedef struct
***************
*** 425,430 **** create_proc_lang(const char *languageName, bool replace,
--- 426,437 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
heap_close(rel, RowExclusiveLock);
}
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 46,51 ****
--- 46,52 ----
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "commands/extension.h"
static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
***************
*** 154,159 **** makeParserDependencies(HeapTuple tuple)
--- 155,166 ----
referenced.objectId = prs->prsheadline;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* dependency on extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 423,428 **** makeDictionaryDependencies(HeapTuple tuple)
--- 430,441 ----
referenced.objectId = dict->dicttemplate;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on extension, if in create extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 965,970 **** makeTSTemplateDependencies(HeapTuple tuple)
--- 978,989 ----
referenced.objectId = tmpl->tmplinit;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* dependency on extension, if in create extension */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
}
/*
***************
*** 1289,1294 **** makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
--- 1308,1319 ----
/* Record 'em (this includes duplicate elimination) */
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+ /* dependencies on extension being created, if any */
+ if (create_extension != NULL )
+ {
+ recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ }
+
free_object_addresses(addrs);
}
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3087,3092 **** _copyDiscardStmt(DiscardStmt *from)
--- 3087,3114 ----
return newnode;
}
+ static CreateExtensionStmt *
+ _copyCreateExtensionStmt(CreateExtensionStmt *from)
+ {
+ CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
+
+ COPY_STRING_FIELD(extname);
+
+ return newnode;
+ }
+
+ static DropExtensionStmt *
+ _copyDropExtensionStmt(DropExtensionStmt *from)
+ {
+ DropExtensionStmt *newnode = makeNode(DropExtensionStmt);
+
+ COPY_STRING_FIELD(extname);
+ COPY_SCALAR_FIELD(missing_ok);
+ COPY_SCALAR_FIELD(behavior);
+
+ return newnode;
+ }
+
static CreateTableSpaceStmt *
_copyCreateTableSpaceStmt(CreateTableSpaceStmt *from)
{
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1587,1592 **** _equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b)
--- 1587,1610 ----
}
static bool
+ _equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
+ {
+ COMPARE_STRING_FIELD(extname);
+
+ return true;
+ }
+
+ static bool
+ _equalDropExtensionStmt(DropExtensionStmt *a, DropExtensionStmt *b)
+ {
+ COMPARE_STRING_FIELD(extname);
+ COMPARE_SCALAR_FIELD(missing_ok);
+ COMPARE_SCALAR_FIELD(behavior);
+
+ return true;
+ }
+
+ static bool
_equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a,
AlterTableSpaceOptionsStmt *b)
{
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 190,196 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
! CreateDomainStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
--- 190,196 ----
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
! CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
***************
*** 198,204 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
--- 198,204 ----
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt DropExtensionStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
***************
*** 484,490 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
! EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
FREEZE FROM FULL FUNCTION FUNCTIONS
--- 484,490 ----
DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
! EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
FREEZE FROM FULL FUNCTION FUNCTIONS
***************
*** 680,685 **** stmt :
--- 680,686 ----
| CreateCastStmt
| CreateConversionStmt
| CreateDomainStmt
+ | CreateExtensionStmt
| CreateFdwStmt
| CreateForeignServerStmt
| CreateFunctionStmt
***************
*** 705,710 **** stmt :
--- 706,712 ----
| DoStmt
| DropAssertStmt
| DropCastStmt
+ | DropExtensionStmt
| DropFdwStmt
| DropForeignServerStmt
| DropGroupStmt
***************
*** 3081,3086 **** opt_procedural:
--- 3083,3128 ----
/*****************************************************************************
*
* QUERY:
+ * CREATE EXTENSION extension
+ *
+ *****************************************************************************/
+
+ CreateExtensionStmt: CREATE EXTENSION name
+ {
+ CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ n->extname = $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * DROP EXTENSION <extension> [ RESTRICT | CASCADE ]
+ *
+ ****************************************************************************/
+
+ DropExtensionStmt: DROP EXTENSION name opt_drop_behavior
+ {
+ DropExtensionStmt *n = makeNode(DropExtensionStmt);
+ n->extname = $3;
+ n->missing_ok = false;
+ n->behavior = $4;
+ $$ = (Node *) n;
+ }
+ | DROP EXTENSION IF_P EXISTS name opt_drop_behavior
+ {
+ DropExtensionStmt *n = makeNode(DropExtensionStmt);
+ n->extname = $5;
+ n->missing_ok = true;
+ n->behavior = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY:
* CREATE TABLESPACE tablespace LOCATION '/path/to/tablespace/'
*
*****************************************************************************/
***************
*** 4184,4190 **** opt_restart_seqs:
*
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
! * CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
* TEXT SEARCH TEMPLATE |
* TEXT SEARCH CONFIGURATION ] <objname> |
--- 4226,4232 ----
*
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
! * CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
* TEXT SEARCH TEMPLATE |
* TEXT SEARCH CONFIGURATION ] <objname> |
***************
*** 4363,4368 **** comment_type:
--- 4405,4411 ----
| VIEW { $$ = OBJECT_VIEW; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
+ | EXTENSION { $$ = OBJECT_EXTENSION; }
| ROLE { $$ = OBJECT_ROLE; }
;
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 32,37 ****
--- 32,38 ----
#include "commands/defrem.h"
#include "commands/discard.h"
#include "commands/explain.h"
+ #include "commands/extension.h"
#include "commands/lockcmds.h"
#include "commands/portalcmds.h"
#include "commands/prepare.h"
***************
*** 175,180 **** check_xact_readonly(Node *parsetree)
--- 176,182 ----
case T_CreateConversionStmt:
case T_CreatedbStmt:
case T_CreateDomainStmt:
+ case T_CreateExtensionStmt:
case T_CreateFunctionStmt:
case T_CreateRoleStmt:
case T_IndexStmt:
***************
*** 194,199 **** check_xact_readonly(Node *parsetree)
--- 196,202 ----
case T_DropCastStmt:
case T_DropStmt:
case T_DropdbStmt:
+ case T_DropExtensionStmt:
case T_DropTableSpaceStmt:
case T_RemoveFuncStmt:
case T_DropRoleStmt:
***************
*** 557,562 **** standard_ProcessUtility(Node *parsetree,
--- 560,573 ----
}
break;
+ case T_CreateExtensionStmt:
+ CreateExtension((CreateExtensionStmt *) parsetree);
+ break;
+
+ case T_DropExtensionStmt:
+ DropExtension((DropExtensionStmt *) parsetree);
+ break;
+
case T_CreateTableSpaceStmt:
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
***************
*** 1498,1503 **** CreateCommandTag(Node *parsetree)
--- 1509,1522 ----
tag = "CREATE TABLE";
break;
+ case T_CreateExtensionStmt:
+ tag = "CREATE EXTENSION";
+ break;
+
+ case T_DropExtensionStmt:
+ tag = "DROP EXTENSION";
+ break;
+
case T_CreateTableSpaceStmt:
tag = "CREATE TABLESPACE";
break;
***************
*** 2292,2297 **** GetCommandLogLevel(Node *parsetree)
--- 2311,2324 ----
lev = LOGSTMT_DDL;
break;
+ case T_CreateExtensionStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_DropExtensionStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreateTableSpaceStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
* Copyright (c) 2004-2010, PostgreSQL Global Development Group
*
* Author: Andreas Pflug <pgadmin@pse-consulting.de>
+ * Dimitri Fontaine <dimitri@2ndQuadrant.fr>
*
* IDENTIFICATION
* src/backend/utils/adt/genfile.c
***************
*** 30,35 ****
--- 31,46 ----
#include "utils/memutils.h"
#include "utils/timestamp.h"
+ #include "tcop/pquery.h"
+ #include "tcop/tcopprot.h"
+ #include "tcop/utility.h"
+ #include "access/transam.h"
+ #include "access/xact.h"
+ #include "utils/resowner.h"
+ #include "utils/snapmgr.h"
+ #include "parser/analyze.h"
+ #include "access/printtup.h"
+
typedef struct
{
char *location;
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 275,459 ----
SRF_RETURN_DONE(funcctx);
}
+
+ /*
+ * Read a file then execute the SQL commands it contains.
+ */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ text *filename_t = PG_GETARG_TEXT_P(0);
+ char *filename;
+ FILE *file;
+ int64 fsize = -1, nbytes;
+ struct stat fst;
+ char *query_string = NULL;
+
+ CommandDest dest = DestNone;
+ MemoryContext oldcontext;
+ List *parsetree_list;
+ ListCell *parsetree_item;
+ bool save_log_statement_stats = log_statement_stats;
+ bool was_logged = false;
+ bool isTopLevel;
+ char msec_str[32];
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to get file information"))));
+
+ /*
+ * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ * uses that too. Don't double check the PATH. Also note that
+ * extension's install files are not in $PGDATA but `pg_config
+ * --sharedir`.
+ */
+ filename = text_to_cstring(filename_t);
+
+ if (stat(filename, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+
+ fsize = Int64GetDatum((int64) fst.st_size);
+
+ if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ filename)));
+
+ if (ferror(file))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+
+ query_string = (char *)palloc((fsize+1)*sizeof(char));
+ memset(query_string, 0, fsize+1);
+ nbytes = fread(query_string, 1, (size_t) fsize, file);
+ pg_verifymbstr(query_string, nbytes, false);
+ FreeFile(file);
+
+ /*
+ elog(NOTICE, "pg_execute_from_file('%s') read %d/%d bytes:", filename, nbytes, fsize);
+ elog(NOTICE, "%s", query_string);
+ */
+
+ /*
+ * Code pasted from postgres.c:exec_simple_query, main differences are:
+ * - don't override unnamed portal, name it after filename instead
+ * - don't start nor finish a transaction
+ * - don't set stats or tracing markers
+ */
+ oldcontext = MemoryContextSwitchTo(MessageContext);
+ parsetree_list = pg_parse_query(query_string);
+ MemoryContextSwitchTo(oldcontext);
+
+ isTopLevel = false;
+
+ foreach(parsetree_item, parsetree_list)
+ {
+ Node *parsetree = (Node *) lfirst(parsetree_item);
+ bool snapshot_set = false;
+ const char *commandTag;
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+ List *querytree_list,
+ *plantree_list;
+ Portal portal;
+ DestReceiver *receiver;
+ int16 format = 0; /* TEXT */
+
+ commandTag = CreateCommandTag(parsetree);
+
+ /* If we got a cancel signal in parsing or prior command, quit */
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Set up a snapshot if parse analysis/planning will need one.
+ */
+ if (analyze_requires_snapshot(parsetree))
+ {
+ PushActiveSnapshot(GetTransactionSnapshot());
+ snapshot_set = true;
+ }
+
+ /*
+ * OK to analyze, rewrite, and plan this query.
+ *
+ * Switch to appropriate context for constructing querytrees (again,
+ * these must outlive the execution context).
+ */
+ oldcontext = MemoryContextSwitchTo(MessageContext);
+
+ querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+ NULL, 0);
+
+ plantree_list = pg_plan_queries(querytree_list, 0, NULL);
+
+ /* Done with the snapshot used for parsing/planning */
+ if (snapshot_set)
+ PopActiveSnapshot();
+
+ /* If we got a cancel signal in analysis or planning, quit */
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Create a portal to run the query or queries in. Name if after the
+ * given filename. If there already is one, silently drop it.
+ */
+ portal = CreatePortal(filename, true, true);
+ /* Don't display the portal in pg_cursors */
+ portal->visible = false;
+
+ /*
+ * We don't have to copy anything into the portal, because everything
+ * we are passing here is in MessageContext, which will outlive the
+ * portal anyway.
+ */
+ PortalDefineQuery(portal,
+ NULL,
+ query_string,
+ commandTag,
+ plantree_list,
+ NULL);
+
+ /*
+ * Start the portal. No parameters here.
+ */
+ PortalStart(portal, NULL, InvalidSnapshot);
+ PortalSetResultFormat(portal, 1, &format);
+
+ /*
+ * Now we can create the destination receiver object.
+ */
+ receiver = CreateDestReceiver(dest);
+ if (dest == DestRemote)
+ SetRemoteDestReceiverParams(receiver, portal);
+
+ /*
+ * Switch back to transaction context for execution.
+ */
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * Run the portal to completion, and then drop it (and the receiver).
+ */
+ (void) PortalRun(portal,
+ FETCH_ALL,
+ isTopLevel,
+ receiver,
+ receiver,
+ completionTag);
+
+ (*receiver->rDestroy) (receiver);
+
+ PortalDrop(portal, false);
+
+ if (!IsA(parsetree, TransactionStmt) && lnext(parsetree_item) != NULL)
+ {
+ CommandCounterIncrement();
+ }
+ }
+ PG_RETURN_VOID();
+ }
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
! rbtree.o
# This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h.
--- 15,21 ----
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
! rbtree.o cfparser.o
# This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h.
*** /dev/null
--- b/src/backend/utils/misc/cfparser.c
***************
*** 0 ****
--- 1,112 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cfparser.c
+ * Function for parsing RecoveryCommandFile lines
+ *
+ * This very simple file format (varible = value) is now also used in the
+ * extension control file format
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/cfparser.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #include "postgres.h"
+
+ /*
+ * Parse one line from recovery.conf. 'cmdline' is the raw line from the
+ * file. If the line is parsed successfully, returns true, false indicates
+ * syntax error. On success, *key_p and *value_p are set to the parameter
+ * name and value on the line, respectively. If the line is an empty line,
+ * consisting entirely of whitespace and comments, function returns true
+ * and *keyp_p and *value_p are set to NULL.
+ *
+ * The pointers returned in *key_p and *value_p point to an internal buffer
+ * that is valid only until the next call of parseRecoveryCommandFile().
+ */
+ bool
+ parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
+ {
+ char *ptr;
+ char *bufp;
+ char *key;
+ char *value;
+ static char *buf = NULL;
+
+ *key_p = *value_p = NULL;
+
+ /*
+ * Allocate the buffer on first use. It's used to hold both the parameter
+ * name and value.
+ */
+ if (buf == NULL)
+ buf = malloc(MAXPGPATH + 1);
+ bufp = buf;
+
+ /* Skip any whitespace at the beginning of line */
+ for (ptr = cmdline; *ptr; ptr++)
+ {
+ if (!isspace((unsigned char) *ptr))
+ break;
+ }
+ /* Ignore empty lines */
+ if (*ptr == '\0' || *ptr == '#')
+ return true;
+
+ /* Read the parameter name */
+ key = bufp;
+ while (*ptr && !isspace((unsigned char) *ptr) &&
+ *ptr != '=' && *ptr != '\'')
+ *(bufp++) = *(ptr++);
+ *(bufp++) = '\0';
+
+ /* Skip to the beginning quote of the parameter value */
+ ptr = strchr(ptr, '\'');
+ if (!ptr)
+ return false;
+ ptr++;
+
+ /* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
+ value = bufp;
+ for (;;)
+ {
+ if (*ptr == '\'')
+ {
+ ptr++;
+ if (*ptr == '\'')
+ *(bufp++) = '\'';
+ else
+ {
+ /* end of parameter */
+ *bufp = '\0';
+ break;
+ }
+ }
+ else if (*ptr == '\0')
+ return false; /* unterminated quoted string */
+ else
+ *(bufp++) = *ptr;
+
+ ptr++;
+ }
+ *(bufp++) = '\0';
+
+ /* Check that there's no garbage after the value */
+ while (*ptr)
+ {
+ if (*ptr == '#')
+ break;
+ if (!isspace((unsigned char) *ptr))
+ return false;
+ ptr++;
+ }
+
+ /* Success! */
+ *key_p = key;
+ *value_p = value;
+ return true;
+ }
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
***************
*** 78,83 **** static int strInArray(const char *pattern, char **arr, int arr_size);
--- 78,84 ----
TableInfo *
getSchemaData(int *numTablesPtr)
{
+ ExtensionInfo *extinfo;
NamespaceInfo *nsinfo;
AggInfo *agginfo;
InhInfo *inhinfo;
***************
*** 94,99 **** getSchemaData(int *numTablesPtr)
--- 95,101 ----
FdwInfo *fdwinfo;
ForeignServerInfo *srvinfo;
DefaultACLInfo *daclinfo;
+ int numExtensions;
int numNamespaces;
int numAggregates;
int numInherits;
***************
*** 112,117 **** getSchemaData(int *numTablesPtr)
--- 114,123 ----
int numDefaultACLs;
if (g_verbose)
+ write_msg(NULL, "reading extensions\n");
+ extinfo = getExtensions(&numExtensions);
+
+ if (g_verbose)
write_msg(NULL, "reading schemas\n");
nsinfo = getNamespaces(&numNamespaces);
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 157,162 **** static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
--- 157,163 ----
SecLabelItem **items);
static int collectSecLabels(Archive *fout, SecLabelItem **items);
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
+ static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
static void dumpType(Archive *fout, TypeInfo *tyinfo);
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
***************
*** 2376,2381 **** binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, Oid pg_class_oid,
--- 2377,2456 ----
}
/*
+ * getExtensions:
+ * read all extensions in the system catalogs and return them in the
+ * ExtensionInfo* structure
+ *
+ * numExtensions is set to the number of extensions read in
+ */
+ ExtensionInfo *
+ getExtensions(int *numExtensions)
+ {
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query;
+ ExtensionInfo *extinfo = NULL;
+ int i_tableoid;
+ int i_oid;
+ int i_extname;
+ int i_extversion;
+ int i_extcclass;
+
+ /*
+ * Before 9.1, there are no extensions.
+ */
+ if (g_fout->remoteVersion < 90100)
+ {
+ *numExtensions = 0;
+ return extinfo;
+ }
+
+ query = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ /*
+ * we fetch all namespaces including system ones, so that every object we
+ * read in can be linked to a containing namespace.
+ */
+ appendPQExpBuffer(query,
+ "SELECT tableoid, oid, extname, extversion, custom_class "
+ "FROM pg_extension");
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ extinfo = (ExtensionInfo *) malloc(ntups * sizeof(ExtensionInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_extname = PQfnumber(res, "extname");
+ i_extversion = PQfnumber(res, "extversion");
+ i_extcclass = PQfnumber(res, "custom_class");
+
+ for (i = 0; i < ntups; i++)
+ {
+ extinfo[i].dobj.objType = DO_EXTENSION;
+ extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&extinfo[i].dobj);
+ extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname));
+ extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion));
+ extinfo[i].custom_class = strdup(PQgetvalue(res, i, i_extcclass));
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ *numExtensions = ntups;
+ return extinfo;
+ }
+
+ /*
* getNamespaces:
* read all namespaces in the system catalogs and return them in the
* NamespaceInfo* structure
***************
*** 2439,2448 **** getNamespaces(int *numNamespaces)
* we fetch all namespaces including system ones, so that every object we
* read in can be linked to a containing namespace.
*/
! appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
! "(%s nspowner) AS rolname, "
! "nspacl FROM pg_namespace",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 2514,2569 ----
* we fetch all namespaces including system ones, so that every object we
* read in can be linked to a containing namespace.
*/
! if (g_fout->remoteVersion >= 90100)
! {
! /*
! * So we want the namespaces, but we want to filter out any
! * namespace created by an extension's script. That's unless the
! * user went over his head and created objects into the extension's
! * schema: we now want the schema not to be filtered out to avoid:
! *
! * pg_dump: schema with OID 77869 does not exist
! */
! appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
! "(%s nspowner) AS rolname, "
! "n.nspacl "
! " FROM pg_namespace n "
! " WHERE n.nspname != 'information_schema' "
! " AND CASE WHEN (SELECT count(*) "
! " FROM pg_depend "
! " WHERE refobjid = n.oid and deptype != 'p') > 0 "
! " THEN EXISTS( "
! "WITH RECURSIVE depends AS ( "
! " select n.oid as nsp, objid, refobjid, array[refobjid] as deps "
! " from pg_depend "
! " where refobjid = n.oid and deptype != 'p' "
! " UNION ALL "
! " select p.nsp, p.objid, d.refobjid, deps || d.refobjid "
! " from pg_depend d JOIN depends p ON d.objid = p.objid "
! " where d.deptype != 'p' and not d.refobjid = any(deps) "
! ") "
! " SELECT nsp, objid, array_agg(distinct refobjid) "
! " FROM depends "
! "GROUP BY nsp, objid "
! " HAVING NOT array_agg(distinct refobjid) && array(select oid from pg_extension) "
! ") "
! " ELSE true "
! " END "
! "UNION ALL "
! "SELECT n.tableoid, n.oid, n.nspname, "
! "(%s nspowner) AS rolname, "
! "nspacl FROM pg_namespace n "
! "WHERE n.nspname = 'information_schema'",
! username_subquery,
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
! "(%s nspowner) AS rolname, "
! "nspacl FROM pg_namespace",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 2559,2565 **** getTypes(int *numTypes)
* we include even the built-in types because those may be used as array
* elements by user-defined types
*
! * we filter out the built-in types when we dump out the types
*
* same approach for undefined (shell) types and array types
*
--- 2680,2687 ----
* we include even the built-in types because those may be used as array
* elements by user-defined types
*
! * we filter out the built-in types when we dump out the types, and from
! * 9.1 we also filter out types that depend on an extension
*
* same approach for undefined (shell) types and array types
*
***************
*** 2574,2580 **** getTypes(int *numTypes)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, "
--- 2696,2721 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
! "t.typnamespace, "
! "(%s typowner) AS rolname, "
! "t.typinput::oid AS typinput, "
! "t.typoutput::oid AS typoutput, t.typelem, t.typrelid, "
! "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
! "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
! "t.typtype, t.typisdefined, "
! "t.typname[0] = '_' AND t.typelem != 0 AND "
! "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
! "FROM pg_type t "
! "LEFT JOIN pg_depend d ON d.objid = t.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, "
***************
*** 2806,2812 **** getOperators(int *numOprs)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
"oprnamespace, "
--- 2947,2966 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.oprname, "
! "o.oprnamespace, "
! "(%s oprowner) AS rolname, "
! "o.oprcode::oid AS oprcode "
! "FROM pg_operator o "
! "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
"oprnamespace, "
***************
*** 2985,2991 **** getOpclasses(int *numOpclasses)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
"opcnamespace, "
--- 3139,3157 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.opcname, "
! "o.opcnamespace, "
! "(%s opcowner) AS rolname "
! "FROM pg_opclass o "
! "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
"opcnamespace, "
***************
*** 3091,3101 **** getOpfamilies(int *numOpfamilies)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
! "opfnamespace, "
! "(%s opfowner) AS rolname "
! "FROM pg_opfamily",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 3257,3282 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.opfname, "
! "o.opfnamespace, "
! "(%s opfowner) AS rolname "
! "FROM pg_opfamily o "
! "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
! "opfnamespace, "
! "(%s opfowner) AS rolname "
! "FROM pg_opfamily",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 3169,3175 **** getAggregates(int *numAggs)
/* find all user-defined aggregates */
! if (g_fout->remoteVersion >= 80200)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
"pronamespace AS aggnamespace, "
--- 3350,3372 ----
/* find all user-defined aggregates */
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT a.tableoid, a.oid, a.proname AS aggname, "
! "a.pronamespace AS aggnamespace, "
! "a.pronargs, a.proargtypes, "
! "(%s proowner) AS rolname, "
! "a.proacl AS aggacl "
! "FROM pg_proc a "
! "LEFT JOIN pg_depend d ON d.objid = a.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL AND proisagg "
! "AND pronamespace != "
! "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 80200)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
"pronamespace AS aggnamespace, "
***************
*** 3315,3321 **** getFuncs(int *numFuncs)
/* find all user-defined funcs */
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
--- 3512,3535 ----
/* find all user-defined funcs */
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query,
! "SELECT f.tableoid, f.oid, f.proname, f.prolang, "
! "f.pronargs, f.proargtypes, f.prorettype, f.proacl, "
! "f.pronamespace, "
! "(%s proowner) AS rolname "
! "FROM pg_proc f "
! "LEFT JOIN pg_depend d ON d.objid = f.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL AND NOT proisagg "
! "AND pronamespace != "
! "(SELECT oid FROM pg_namespace "
! "WHERE nspname = 'pg_catalog')",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
***************
*** 3477,3483 **** getTables(int *numTables)
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
! if (g_fout->remoteVersion >= 90000)
{
/*
* Left join to pick up dependency info linking sequences to their
--- 3691,3734 ----
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
! if (g_fout->remoteVersion >= 90100)
! {
! /*
! * Left join to pick up dependency info linking sequences to their
! * owning column, if any (note this dependency is AUTO as of 8.2)
! */
! appendPQExpBuffer(query,
! "SELECT c.tableoid, c.oid, c.relname, "
! "c.relacl, c.relkind, c.relnamespace, "
! "(%s c.relowner) AS rolname, "
! "c.relchecks, c.relhastriggers, "
! "c.relhasindex, c.relhasrules, c.relhasoids, "
! "c.relfrozenxid, "
! "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
! "d.refobjid AS owning_tab, "
! "d.refobjsubid AS owning_col, "
! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
! "array_to_string(c.reloptions, ', ') AS reloptions, "
! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
! "FROM pg_class c "
! "LEFT JOIN pg_depend d ON "
! "(c.relkind = '%c' AND "
! "d.classid = c.tableoid AND d.objid = c.oid AND "
! "d.objsubid = 0 AND "
! "d.refclassid = c.tableoid AND d.deptype = 'a') "
! "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
! "LEFT JOIN pg_depend dx ON dx.objid = c.oid and dx.deptype = 'i' "
! "and dx.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON dx.refobjid = x.oid "
! "WHERE x.oid IS NULL "
! "AND c.relkind in ('%c', '%c', '%c', '%c') "
! "ORDER BY c.oid",
! username_subquery,
! RELKIND_SEQUENCE,
! RELKIND_RELATION, RELKIND_SEQUENCE,
! RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
! }
! else if (g_fout->remoteVersion >= 90000)
{
/*
* Left join to pick up dependency info linking sequences to their
***************
*** 4838,4844 **** getProcLangs(int *numProcLangs)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90000)
{
/* pg_language has a laninline column */
appendPQExpBuffer(query, "SELECT tableoid, oid, "
--- 5089,5110 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! /* pg_language has a laninline column */
! appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, "
! "l.lanname, l.lanpltrusted, l.lanplcallfoid, "
! "l.laninline, l.lanvalidator, l.lanacl, "
! "(%s lanowner) AS lanowner "
! "FROM pg_language l "
! "LEFT JOIN pg_depend d ON d.objid = l.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL and lanispl "
! "ORDER BY oid",
! username_subquery);
! }
! else if (g_fout->remoteVersion >= 90000)
{
/* pg_language has a laninline column */
appendPQExpBuffer(query, "SELECT tableoid, oid, "
***************
*** 4992,4998 **** getCasts(int *numCasts)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
--- 5258,5276 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, "
! "c.castsource, c.casttarget, c.castfunc, c.castcontext, "
! "c.castmethod "
! "FROM pg_cast c "
! "LEFT JOIN pg_depend d ON d.objid = c.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL "
! "ORDER BY 3,4");
! }
! else if (g_fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
***************
*** 5586,5595 **** getTSParsers(int *numTSParsers)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, "
! "prsstart::oid, prstoken::oid, "
! "prsend::oid, prsheadline::oid, prslextype::oid "
! "FROM pg_ts_parser");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 5864,5887 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, p.prsname, p.prsnamespace, "
! "p.prsstart::oid, p.prstoken::oid, "
! "p.prsend::oid, p.prsheadline::oid, p.prslextype::oid "
! "FROM pg_ts_parser p "
! "LEFT JOIN pg_depend d ON d.objid = p.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL");
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, "
! "prsstart::oid, prstoken::oid, "
! "prsend::oid, prsheadline::oid, prslextype::oid "
! "FROM pg_ts_parser");
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5668,5678 **** getTSDictionaries(int *numTSDicts)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
! "dictnamespace, (%s dictowner) AS rolname, "
! "dicttemplate, dictinitoption "
! "FROM pg_ts_dict",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 5960,5985 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT tsd.tableoid, tsd.oid, tsd.dictname, "
! "tsd.dictnamespace, (%s dictowner) AS rolname, "
! "tsd.dicttemplate, tsd.dictinitoption "
! "FROM pg_ts_dict tsd "
! "LEFT JOIN pg_depend d ON d.objid = tsd.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
! "dictnamespace, (%s dictowner) AS rolname, "
! "dicttemplate, dictinitoption "
! "FROM pg_ts_dict",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5749,5757 **** getTSTemplates(int *numTSTemplates)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, "
! "tmplnamespace, tmplinit::oid, tmpllexize::oid "
! "FROM pg_ts_template");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6056,6077 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.tmplname, "
! "t.tmplnamespace, t.tmplinit::oid, t.tmpllexize::oid "
! "FROM pg_ts_template t "
! "LEFT JOIN pg_depend d ON d.objid = t.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL");
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, "
! "tmplnamespace, tmplinit::oid, tmpllexize::oid "
! "FROM pg_ts_template");
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5823,5832 **** getTSConfigurations(int *numTSConfigs)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
! "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
! "FROM pg_ts_config",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6143,6166 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.cfgname, "
! "c.cfgnamespace, (%s cfgowner) AS rolname, c.cfgparser "
! "FROM pg_ts_config c "
! "LEFT JOIN pg_depend d ON d.objid = c.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
! "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
! "FROM pg_ts_config",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5899,5911 **** getForeignDataWrappers(int *numForeignDataWrappers)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
! "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
! "FROM pg_foreign_data_wrapper",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6233,6262 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT fdw.tableoid, fdw.oid, fdw.fdwname, "
! "(%s fdwowner) AS rolname, fdw.fdwvalidator::pg_catalog.regproc, fdwacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fdw.fdwoptions)), ', ') AS fdwoptions "
! "FROM pg_foreign_data_wrapper fdw "
! "LEFT JOIN pg_depend d ON d.objid = fdw.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
! "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
! "FROM pg_foreign_data_wrapper",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5983,5996 **** getForeignServers(int *numForeignServers)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
! "(%s srvowner) AS rolname, "
! "srvfdw, srvtype, srvversion, srvacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(srvoptions)), ', ') AS srvoptions "
! "FROM pg_foreign_server",
! username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6334,6365 ----
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! if (g_fout->remoteVersion >= 90100)
! {
! appendPQExpBuffer(query, "SELECT fs.tableoid, fs.oid, fs.srvname, "
! "(%s srvowner) AS rolname, "
! "fs.srvfdw, fs.srvtype, fs.srvversion, fs.srvacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(fs.srvoptions)), ', ') AS srvoptions "
! "FROM pg_foreign_server fs "
! "LEFT JOIN pg_depend d ON d.objid = fs.oid and d.deptype = 'i' "
! "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! "WHERE x.oid IS NULL",
! username_subquery);
! }
! else
! {
! appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
! "(%s srvowner) AS rolname, "
! "srvfdw, srvtype, srvversion, srvacl,"
! "array_to_string(ARRAY("
! " SELECT option_name || ' ' || quote_literal(option_value) "
! " FROM pg_options_to_table(srvoptions)), ', ') AS srvoptions "
! "FROM pg_foreign_server",
! username_subquery);
! }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 6467,6472 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6836,6844 ----
{
switch (dobj->objType)
{
+ case DO_EXTENSION:
+ dumpExtension(fout, (ExtensionInfo *) dobj);
+ break;
case DO_NAMESPACE:
dumpNamespace(fout, (NamespaceInfo *) dobj);
break;
***************
*** 6563,6568 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6935,6986 ----
}
/*
+ * 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;\n", qextname);
+
+ ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
+ extinfo->dobj.name,
+ NULL, NULL,
+ "",
+ false, "EXTENSION", SECTION_PRE_DATA,
+ q->data, delq->data, NULL,
+ extinfo->dobj.dependencies, extinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Extension Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "EXTENSION %s", qextname);
+ dumpComment(fout, q->data,
+ NULL, "",
+ extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+
+ free(qextname);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ }
+
+ /*
* dumpNamespace
* writes out to fout the queries to recreate a user-defined namespace
*/
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 88,93 **** typedef struct SimpleStringList
--- 88,94 ----
typedef enum
{
/* When modifying this enum, update priority tables in pg_dump_sort.c! */
+ DO_EXTENSION,
DO_NAMESPACE,
DO_TYPE,
DO_SHELL_TYPE,
***************
*** 132,137 **** typedef struct _dumpableObject
--- 133,146 ----
int allocDeps; /* allocated size of dependencies[] */
} DumpableObject;
+ typedef struct _extensionInfo
+ {
+ DumpableObject dobj;
+ char *extname;
+ char *extversion;
+ char *custom_class;
+ } ExtensionInfo;
+
typedef struct _namespaceInfo
{
DumpableObject dobj;
***************
*** 511,516 **** extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs);
--- 520,526 ----
/*
* version specific routines
*/
+ extern ExtensionInfo *getExtensions(int *numExtensions);
extern NamespaceInfo *getNamespaces(int *numNamespaces);
extern TypeInfo *getTypes(int *numTypes);
extern FuncInfo *getFuncs(int *numFuncs);
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 28,33 **** static const char *modulename = gettext_noop("sorter");
--- 28,34 ----
*/
static const int oldObjectTypePriority[] =
{
+ 1, /* DO_EXTENSION */
1, /* DO_NAMESPACE */
2, /* DO_TYPE */
2, /* DO_SHELL_TYPE */
***************
*** 65,70 **** static const int oldObjectTypePriority[] =
--- 66,72 ----
*/
static const int newObjectTypePriority[] =
{
+ 1, /* DO_EXTENSION */
1, /* DO_NAMESPACE */
3, /* DO_TYPE */
3, /* DO_SHELL_TYPE */
***************
*** 1018,1023 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1020,1030 ----
{
switch (obj->objType)
{
+ case DO_EXTENSION:
+ snprintf(buf, bufsize,
+ "EXTENSION %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
case DO_NAMESPACE:
snprintf(buf, bufsize,
"SCHEMA %s (ID %d OID %u)",
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 488,493 **** exec_command(const char *cmd,
--- 488,496 ----
break;
}
break;
+ case 'x': /* Extensions */
+ success = listExtensions(pattern, show_verbose);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
}
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2720,2725 **** listSchemas(const char *pattern, bool verbose)
--- 2720,2770 ----
return true;
}
+ /*
+ * \dx
+ *
+ * Describes extensions
+ */
+ bool
+ listExtensions(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT x.extname AS \"%s\", x.extversion AS \"%s\", "
+ "x.custom_class AS \"%s\", d.description as \"%s\" "
+ " FROM pg_catalog.pg_extension x \n"
+ " LEFT JOIN pg_description d on x.oid = d.objoid "
+ " and d.classoid = 3696",
+ gettext_noop("Name"),
+ gettext_noop("Version"),
+ gettext_noop("Custom Class"),
+ gettext_noop("Description"));
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "x.extname", NULL,
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of extensions");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
/*
* \dFp
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
***************
*** 54,59 **** extern bool listTSDictionaries(const char *pattern, bool verbose);
--- 54,62 ----
/* \dFt */
extern bool listTSTemplates(const char *pattern, bool verbose);
+ /* \dx */
+ bool listExtensions(const char *pattern, bool verbose);
+
/* \l */
extern bool listAllDbs(bool verbose);
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 219,224 **** slashUsage(unsigned short int pager)
--- 219,225 ----
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
fprintf(output, _(" \\du[+] [PATTERN] list roles (users)\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
+ fprintf(output, _(" \\dx [PATTERN] list extensions\n"));
fprintf(output, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 138,143 **** typedef enum ObjectClass
--- 138,144 ----
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
OCLASS_USER_MAPPING, /* pg_user_mapping */
OCLASS_DEFACL, /* pg_default_acl */
+ OCLASS_EXTENSION, /* pg_extension */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 284,289 **** DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol
--- 284,295 ----
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
#define SecLabelObjectIndexId 3597
+ DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3695, on pg_extension using btree(oid oid_ops));
+ #define ExtensionOidIndexId 3695
+
+ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3697, on pg_extension using btree(extname name_ops));
+ #define ExtensionNameIndexId 3697
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
*** /dev/null
--- b/src/include/catalog/pg_extension.h
***************
*** 0 ****
--- 1,61 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_extension.h
+ * definition of the system "extension" relation (pg_extension)
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_extension.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_EXTENSION_H
+ #define PG_EXTENSION_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_extension definition. cpp turns this into
+ * typedef struct FormData_pg_extension
+ * ----------------
+ */
+ #define ExtensionRelationId 3696
+
+ CATALOG(pg_extension,3696)
+ {
+ NameData extname; /* name of the extension */
+ text extversion; /* version "name" of the extension */
+ text custom_class; /* custom class used by the extension */
+ } FormData_pg_extension;
+
+ /* ----------------
+ * Form_pg_extension corresponds to a pointer to a tuple with
+ * the format of pg_extension relation.
+ * ----------------
+ */
+ typedef FormData_pg_extension *Form_pg_extension;
+
+ /* ----------------
+ * compiler constants for pg_extension
+ * ----------------
+ */
+
+ #define Natts_pg_extension 3
+ #define Anum_pg_extension_extname 1
+ #define Anum_pg_extension_extversion 2
+ #define Anum_pg_extension_custom_class 3
+
+ /* ----------------
+ * initial contents of pg_extension
+ * ----------------
+ */
+
+ /* btree
+ DATA(insert ("PostgreSQL" PG_VERSION _null_));
+ */
+ #endif /* PG_EXTENSION_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
DATA(insert OID = 2622 ( pg_rotate_logfile PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
DESCR("rotate log file");
! DATA(insert OID = 2623 ( pg_stat_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
DESCR("sleep for the specified time in seconds");
DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
DESCR("convert boolean to text");
--- 3386,3401 ----
DATA(insert OID = 2622 ( pg_rotate_logfile PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
DESCR("rotate log file");
! DATA(insert OID = 2623 ( pg_stat_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
DESCR("convert boolean to text");
*** a/src/include/catalog/toasting.h
--- b/src/include/catalog/toasting.h
***************
*** 43,48 **** extern void BootstrapToastTable(char *relName,
--- 43,49 ----
DECLARE_TOAST(pg_attrdef, 2830, 2831);
DECLARE_TOAST(pg_constraint, 2832, 2833);
DECLARE_TOAST(pg_description, 2834, 2835);
+ DECLARE_TOAST(pg_extension, 3698, 3699);
DECLARE_TOAST(pg_proc, 2836, 2837);
DECLARE_TOAST(pg_rewrite, 2838, 2839);
DECLARE_TOAST(pg_seclabel, 3598, 3599);
*** /dev/null
--- b/src/include/commands/extension.h
***************
*** 0 ****
--- 1,49 ----
+ /*-------------------------------------------------------------------------
+ *
+ * extension.h
+ * Extension management commands (create/drop extension).
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/extension.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef EXTENSION_H
+ #define EXTENSION_H
+
+ #include "catalog/objectaddress.h"
+
+ /*
+ * create_extension is only set when running a CREATE EXTENSION command, it
+ * allows to register the (INTERNAL) dependancies between the pg_extension
+ * row and the SQL objects created by its installation script.
+ *
+ * For that to work out, all CREATE commands have been modified so that they
+ * will inquire about create_extension and call recordDependencyOn() when
+ * it's set.
+ */
+ extern ObjectAddress *create_extension;
+
+ typedef struct ExtensionControlFile
+ {
+ char *name; /* name of the extension */
+ char *version; /* version "name" of the extension */
+ char *custom_class; /* custom class used by the extension */
+ } ExtensionControlFile;
+
+ char *get_extension_install_filename(const char *extname);
+ char *get_extension_control_filename(const char *extname);
+ char * extension_parse_control_line(const char *content, ExtensionControlFile *control);
+ ExtensionControlFile *read_extension_control_file(const char *filename);
+ extern void CreateExtension(CreateExtensionStmt *stmt);
+ extern void DropExtension(DropExtensionStmt *stmt);
+
+ Oid get_extension_oid(const char *extname, bool missing_ok);
+ char * get_extension_name(Oid ext_oid);
+ char * RemoveExtensionById(Oid extId);
+
+
+ #endif /* EXTENSION_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 348,353 **** typedef enum NodeTag
--- 348,355 ----
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
+ T_CreateExtensionStmt,
+ T_DropExtensionStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1053,1058 **** typedef enum ObjectType
--- 1053,1059 ----
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DOMAIN,
+ OBJECT_EXTENSION,
OBJECT_FDW,
OBJECT_FOREIGN_SERVER,
OBJECT_FUNCTION,
***************
*** 1479,1484 **** typedef struct Constraint
--- 1480,1504 ----
} Constraint;
/* ----------------------
+ * Create/Drop Extension Statements
+ * ----------------------
+ */
+
+ typedef struct CreateExtensionStmt
+ {
+ NodeTag type;
+ char *extname;
+ } CreateExtensionStmt;
+
+ typedef struct DropExtensionStmt
+ {
+ NodeTag type;
+ char *extname;
+ bool missing_ok; /* skip error if missing? */
+ DropBehavior behavior; /* drop behavior - cascade/restrict */
+ } DropExtensionStmt;
+
+ /* ----------------------
* Create/Drop Table Space Statements
* ----------------------
*/
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 150,155 **** PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD)
--- 150,156 ----
PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD)
PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD)
+ PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD)
PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD)
PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD)
PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD)
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
extern Datum pg_stat_file(PG_FUNCTION_ARGS);
extern Datum pg_read_file(PG_FUNCTION_ARGS);
extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
/* misc.c */
extern Datum current_database(PG_FUNCTION_ARGS);
*** /dev/null
--- b/src/include/utils/cfparser.h
***************
*** 0 ****
--- 1,18 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cfparser.h
+ * Function for parsing RecoveryCommandFile lines
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/cfparser.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef CFPARSER_H
+ #define CFPARSER_H
+
+ bool parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p);
+
+ #endif /* CFPARSER_H */
*** a/src/makefiles/pgxs.mk
--- b/src/makefiles/pgxs.mk
***************
*** 93,102 **** include $(top_srcdir)/src/Makefile.shlib
all: all-lib
endif # MODULE_big
! install: all installdirs
! ifneq (,$(DATA)$(DATA_built))
! @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
done
--- 93,116 ----
all: all-lib
endif # MODULE_big
+ # create extension support
+ ifndef CONTROL
+ ifdef MODULE_big
+ CONTROL = $(MODULE_big).control
+ EXTENSION = $(MODULE_big)
+ else
+ CONTROL = $(MODULES).control
+ EXTENSION = $(MODULES)
+ endif
+ endif
! $(CONTROL):
! touch .control
! test ! -f $@ && echo "name = '$(EXTENSION)' \nversion = '$(MAJORVERSION)'" > $@
!
! install: all installdirs $(CONTROL)
! ifneq (,$(DATA)$(DATA_built)$(CONTROL))
! @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) $(CONTROL); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
done
***************
*** 221,226 **** ifeq ($(PORTNAME), win)
--- 235,241 ----
rm -f regress.def
endif
endif # REGRESS
+ if [ -f .control ]; then rm -f .control $(CONTROL); fi
ifdef MODULE_big
clean: clean-lib