#include "postgres.h"
#include <dirent.h>
#include <limits.h>
#include <unistd.h>
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/comment.h"
#include "commands/extension.h"
#include "commands/schemacmds.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/tqual.h"
Go to the source code of this file.
typedef struct ExtensionControlFile ExtensionControlFile |
typedef struct ExtensionVersionInfo ExtensionVersionInfo |
Definition at line 2403 of file extension.c.
References AccessShareLock, ACL_CREATE, ACL_KIND_EXTENSION, ACL_KIND_NAMESPACE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, AlterObjectNamespace_oid(), Anum_pg_depend_refclassid, Anum_pg_depend_refobjid, BTEqualStrategyNumber, CatalogUpdateIndexes(), changeDependencyFor(), ObjectAddress::classId, DEPENDENCY_EXTENSION, DependReferenceIndexId, DependRelationId, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, ExtensionOidIndexId, ExtensionRelationId, get_extension_oid(), get_namespace_name(), getExtensionOfObject(), getObjectDescription(), GETSTRUCT, GetUserId(), heap_close, heap_copytuple(), heap_open(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHook, linitial, list_length(), LookupCreationNamespace(), NamespaceRelationId, NameStr, new_object_addresses(), ObjectAddress::objectId, ObjectIdAttributeNumber, ObjectIdGetDatum, ObjectAddress::objectSubId, pg_extension_ownercheck(), pg_namespace_aclcheck(), relation_close(), RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.
Referenced by ExecAlterObjectSchemaStmt().
{ char *extensionName; Oid extensionOid; Oid nspOid; Oid oldNspOid = InvalidOid; AclResult aclresult; Relation extRel; ScanKeyData key[2]; SysScanDesc extScan; HeapTuple extTup; Form_pg_extension extForm; Relation depRel; SysScanDesc depScan; HeapTuple depTup; ObjectAddresses *objsMoved; if (list_length(names) != 1) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("extension name cannot be qualified"))); extensionName = strVal(linitial(names)); extensionOid = get_extension_oid(extensionName, false); nspOid = LookupCreationNamespace(newschema); /* * Permission check: must own extension. Note that we don't bother to * check ownership of the individual member objects ... */ if (!pg_extension_ownercheck(extensionOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION, extensionName); /* Permission check: must have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, newschema); /* * If the schema is currently a member of the extension, disallow moving * the extension into the schema. That would create a dependency loop. */ if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot move extension \"%s\" into schema \"%s\" " "because the extension contains the schema", extensionName, newschema))); /* Locate the pg_extension tuple */ extRel = heap_open(ExtensionRelationId, RowExclusiveLock); ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(extensionOid)); extScan = systable_beginscan(extRel, ExtensionOidIndexId, true, SnapshotNow, 1, key); extTup = systable_getnext(extScan); if (!HeapTupleIsValid(extTup)) /* should not happen */ elog(ERROR, "extension with oid %u does not exist", extensionOid); /* Copy tuple so we can modify it below */ extTup = heap_copytuple(extTup); extForm = (Form_pg_extension) GETSTRUCT(extTup); systable_endscan(extScan); /* * If the extension is already in the target schema, just silently do * nothing. */ if (extForm->extnamespace == nspOid) { heap_close(extRel, RowExclusiveLock); return InvalidOid; } /* Check extension is supposed to be relocatable */ if (!extForm->extrelocatable) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("extension \"%s\" does not support SET SCHEMA", NameStr(extForm->extname)))); objsMoved = new_object_addresses(); /* * Scan pg_depend to find objects that depend directly on the extension, * and alter each one's schema. */ depRel = heap_open(DependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ExtensionRelationId)); ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(extensionOid)); depScan = systable_beginscan(depRel, DependReferenceIndexId, true, SnapshotNow, 2, key); while (HeapTupleIsValid(depTup = systable_getnext(depScan))) { Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup); ObjectAddress dep; Oid dep_oldNspOid; /* * Ignore non-membership dependencies. (Currently, the only other * case we could see here is a normal dependency from another * extension.) */ if (pg_depend->deptype != DEPENDENCY_EXTENSION) continue; dep.classId = pg_depend->classid; dep.objectId = pg_depend->objid; dep.objectSubId = pg_depend->objsubid; if (dep.objectSubId != 0) /* should not happen */ elog(ERROR, "extension should not have a sub-object dependency"); /* Relocate the object */ dep_oldNspOid = AlterObjectNamespace_oid(dep.classId, dep.objectId, nspOid, objsMoved); /* * Remember previous namespace of first object that has one */ if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid) oldNspOid = dep_oldNspOid; /* * If not all the objects had the same old namespace (ignoring any * that are not in namespaces), complain. */ if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("extension \"%s\" does not support SET SCHEMA", NameStr(extForm->extname)), errdetail("%s is not in the extension's schema \"%s\"", getObjectDescription(&dep), get_namespace_name(oldNspOid)))); } systable_endscan(depScan); relation_close(depRel, AccessShareLock); /* Now adjust pg_extension.extnamespace */ extForm->extnamespace = nspOid; simple_heap_update(extRel, &extTup->t_self, extTup); CatalogUpdateIndexes(extRel, extTup); heap_close(extRel, RowExclusiveLock); /* update dependencies to point to the new schema */ changeDependencyFor(ExtensionRelationId, extensionOid, NamespaceRelationId, oldNspOid, nspOid); InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0); return extensionOid; }
static void ApplyExtensionUpdates | ( | Oid | extensionOid, | |
ExtensionControlFile * | pcontrol, | |||
const char * | initialVersion, | |||
List * | updateVersions | |||
) | [static] |
Definition at line 2730 of file extension.c.
References Anum_pg_extension_extrelocatable, Anum_pg_extension_extversion, BoolGetDatum, BTEqualStrategyNumber, CatalogUpdateIndexes(), ObjectAddress::classId, CStringGetTextDatum, deleteDependencyRecordsForClass(), DEPENDENCY_NORMAL, elog, ereport, errcode(), errmsg(), ERROR, execute_extension_script(), ExtensionOidIndexId, ExtensionRelationId, get_extension_oid(), get_extension_schema(), get_namespace_name(), GETSTRUCT, heap_close, heap_modify_tuple(), heap_open(), HeapTupleIsValid, InvokeObjectPostAlterHook, lappend_oid(), lfirst, lfirst_oid, ObjectAddress::objectId, ObjectIdAttributeNumber, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, read_extension_aux_control_file(), recordDependencyOn(), RelationGetDescr, ExtensionControlFile::relocatable, ExtensionControlFile::requires, RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, and values.
Referenced by CreateExtension(), and ExecAlterExtensionStmt().
{ const char *oldVersionName = initialVersion; ListCell *lcv; foreach(lcv, updateVersions) { char *versionName = (char *) lfirst(lcv); ExtensionControlFile *control; char *schemaName; Oid schemaOid; List *requiredExtensions; List *requiredSchemas; Relation extRel; ScanKeyData key[1]; SysScanDesc extScan; HeapTuple extTup; Form_pg_extension extForm; Datum values[Natts_pg_extension]; bool nulls[Natts_pg_extension]; bool repl[Natts_pg_extension]; ObjectAddress myself; ListCell *lc; /* * Fetch parameters for specific version (pcontrol is not changed) */ control = read_extension_aux_control_file(pcontrol, versionName); /* Find the pg_extension tuple */ extRel = heap_open(ExtensionRelationId, RowExclusiveLock); ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(extensionOid)); extScan = systable_beginscan(extRel, ExtensionOidIndexId, true, SnapshotNow, 1, key); extTup = systable_getnext(extScan); if (!HeapTupleIsValid(extTup)) /* should not happen */ elog(ERROR, "extension with oid %u does not exist", extensionOid); extForm = (Form_pg_extension) GETSTRUCT(extTup); /* * Determine the target schema (set by original install) */ schemaOid = extForm->extnamespace; schemaName = get_namespace_name(schemaOid); /* * Modify extrelocatable and extversion in the pg_extension tuple */ memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); memset(repl, 0, sizeof(repl)); values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(control->relocatable); repl[Anum_pg_extension_extrelocatable - 1] = true; values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(versionName); repl[Anum_pg_extension_extversion - 1] = true; extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), values, nulls, repl); simple_heap_update(extRel, &extTup->t_self, extTup); CatalogUpdateIndexes(extRel, extTup); systable_endscan(extScan); heap_close(extRel, RowExclusiveLock); /* * Look up the prerequisite extensions for this version, and build * lists of their OIDs and the OIDs of their target schemas. */ requiredExtensions = NIL; requiredSchemas = NIL; foreach(lc, control->requires) { char *curreq = (char *) lfirst(lc); Oid reqext; Oid reqschema; /* * We intentionally don't use get_extension_oid's default error * message here, because it would be confusing in this context. */ reqext = get_extension_oid(curreq, true); if (!OidIsValid(reqext)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("required extension \"%s\" is not installed", curreq))); reqschema = get_extension_schema(reqext); requiredExtensions = lappend_oid(requiredExtensions, reqext); requiredSchemas = lappend_oid(requiredSchemas, reqschema); } /* * Remove and recreate dependencies on prerequisite extensions */ deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid, ExtensionRelationId, DEPENDENCY_NORMAL); myself.classId = ExtensionRelationId; myself.objectId = extensionOid; myself.objectSubId = 0; foreach(lc, requiredExtensions) { Oid reqext = lfirst_oid(lc); ObjectAddress otherext; otherext.classId = ExtensionRelationId; otherext.objectId = reqext; otherext.objectSubId = 0; recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL); } InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0); /* * Finally, execute the update script file */ execute_extension_script(extensionOid, control, oldVersionName, versionName, requiredSchemas, schemaName, schemaOid); /* * Update prior-version name and loop around. Since * execute_sql_string did a final CommandCounterIncrement, we can * update the pg_extension row again. */ oldVersionName = versionName; } }
static void check_valid_extension_name | ( | const char * | extensionname | ) | [static] |
Definition at line 236 of file extension.c.
References ereport, errcode(), errdetail(), errmsg(), ERROR, first_dir_separator(), and NULL.
Referenced by CreateExtension(), and pg_extension_update_paths().
{ int namelen = strlen(extensionname); /* * Disallow empty names (the parser rejects empty identifiers anyway, but * let's check). */ if (namelen == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension name: \"%s\"", extensionname), errdetail("Extension names must not be empty."))); /* * No double dashes, since that would make script filenames ambiguous. */ if (strstr(extensionname, "--")) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension name: \"%s\"", extensionname), errdetail("Extension names must not contain \"--\"."))); /* * No leading or trailing dash either. (We could probably allow this, but * it would require much care in filename parsing and would make filenames * visually if not formally ambiguous. Since there's no real-world use * case, let's just forbid it.) */ if (extensionname[0] == '-' || extensionname[namelen - 1] == '-') ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension name: \"%s\"", extensionname), errdetail("Extension names must not begin or end with \"-\"."))); /* * No directory separators either (this is sufficient to prevent ".." * style attacks). */ if (first_dir_separator(extensionname) != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension name: \"%s\"", extensionname), errdetail("Extension names must not contain directory separator characters."))); }
static void check_valid_version_name | ( | const char * | versionname | ) | [static] |
Definition at line 283 of file extension.c.
References ereport, errcode(), errdetail(), errmsg(), ERROR, first_dir_separator(), and NULL.
Referenced by CreateExtension(), and ExecAlterExtensionStmt().
{ int namelen = strlen(versionname); /* * Disallow empty names (we could possibly allow this, but there seems * little point). */ if (namelen == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension version name: \"%s\"", versionname), errdetail("Version names must not be empty."))); /* * No double dashes, since that would make script filenames ambiguous. */ if (strstr(versionname, "--")) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension version name: \"%s\"", versionname), errdetail("Version names must not contain \"--\"."))); /* * No leading or trailing dash either. */ if (versionname[0] == '-' || versionname[namelen - 1] == '-') ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension version name: \"%s\"", versionname), errdetail("Version names must not begin or end with \"-\"."))); /* * No directory separators either (this is sufficient to prevent ".." * style attacks). */ if (first_dir_separator(versionname) != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid extension version name: \"%s\"", versionname), errdetail("Version names must not contain directory separator characters."))); }
Oid CreateExtension | ( | CreateExtensionStmt * | stmt | ) |
Definition at line 1178 of file extension.c.
References ApplyExtensionUpdates(), DefElem::arg, Assert, CreateSchemaStmt::authid, check_valid_extension_name(), check_valid_version_name(), ExtensionControlFile::comment, CreateComments(), CreateSchemaCommand(), creating_extension, ExtensionControlFile::default_version, DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, execute_extension_script(), ExtensionRelationId, CreateExtensionStmt::extname, fetch_search_path(), get_extension_oid(), get_extension_schema(), get_namespace_name(), get_namespace_oid(), GetUserId(), identify_update_path(), CreateSchemaStmt::if_not_exists, CreateExtensionStmt::if_not_exists, InsertExtensionTuple(), InvalidOid, lappend_oid(), lfirst, linitial, linitial_oid, list_delete_first(), list_free(), list_length(), makeNode, ExtensionControlFile::name, NIL, NOTICE, NULL, OidIsValid, CreateExtensionStmt::options, PointerGetDatum, read_extension_aux_control_file(), read_extension_control_file(), ExtensionControlFile::relocatable, ExtensionControlFile::requires, ExtensionControlFile::schema, CreateSchemaStmt::schemaElts, CreateSchemaStmt::schemaname, and strVal.
Referenced by ProcessUtilitySlow().
{ DefElem *d_schema = NULL; DefElem *d_new_version = NULL; DefElem *d_old_version = NULL; char *schemaName; Oid schemaOid; char *versionName; char *oldVersionName; Oid extowner = GetUserId(); ExtensionControlFile *pcontrol; ExtensionControlFile *control; List *updateVersions; List *requiredExtensions; List *requiredSchemas; Oid extensionOid; ListCell *lc; /* Check extension name validity before any filesystem access */ check_valid_extension_name(stmt->extname); /* * Check for duplicate extension name. The unique index on * pg_extension.extname would catch this anyway, and serves as a backstop * in case of race conditions; but this is a friendlier error message, and * besides we need a check to support IF NOT EXISTS. */ if (get_extension_oid(stmt->extname, true) != InvalidOid) { if (stmt->if_not_exists) { ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("extension \"%s\" already exists, skipping", stmt->extname))); return InvalidOid; } else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("extension \"%s\" already exists", stmt->extname))); } /* * We use global variables to track the extension being created, so we can * create only one extension at the same time. */ if (creating_extension) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("nested CREATE EXTENSION is not supported"))); /* * Read the primary control file. Note we assume that it does not contain * any non-ASCII data, so there is no need to worry about encoding at this * point. */ pcontrol = read_extension_control_file(stmt->extname); /* * Read the statement option list */ foreach(lc, stmt->options) { DefElem *defel = (DefElem *) lfirst(lc); if (strcmp(defel->defname, "schema") == 0) { if (d_schema) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); d_schema = defel; } else if (strcmp(defel->defname, "new_version") == 0) { if (d_new_version) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); d_new_version = defel; } else if (strcmp(defel->defname, "old_version") == 0) { if (d_old_version) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); d_old_version = defel; } else elog(ERROR, "unrecognized option: %s", defel->defname); } /* * Determine the version to install */ if (d_new_version && d_new_version->arg) versionName = strVal(d_new_version->arg); else if (pcontrol->default_version) versionName = pcontrol->default_version; else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("version to install must be specified"))); versionName = NULL; /* keep compiler quiet */ } check_valid_version_name(versionName); /* * Determine the (unpackaged) version to update from, if any, and then * figure out what sequence of update scripts we need to apply. */ if (d_old_version && d_old_version->arg) { oldVersionName = strVal(d_old_version->arg); check_valid_version_name(oldVersionName); if (strcmp(oldVersionName, versionName) == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("FROM version must be different from installation target version \"%s\"", versionName))); updateVersions = identify_update_path(pcontrol, oldVersionName, versionName); if (list_length(updateVersions) == 1) { /* * Simple case where there's just one update script to run. We * will not need any follow-on update steps. */ Assert(strcmp((char *) linitial(updateVersions), versionName) == 0); updateVersions = NIL; } else { /* * Multi-step sequence. We treat this as installing the version * that is the target of the first script, followed by successive * updates to the later versions. */ versionName = (char *) linitial(updateVersions); updateVersions = list_delete_first(updateVersions); } } else { oldVersionName = NULL; updateVersions = NIL; } /* * Fetch control parameters for installation target version */ control = read_extension_aux_control_file(pcontrol, versionName); /* * Determine the target schema to install the extension into */ if (d_schema && d_schema->arg) { /* * User given schema, CREATE EXTENSION ... WITH SCHEMA ... * * It's an error to give a schema different from control->schema if * control->schema is specified. */ schemaName = strVal(d_schema->arg); if (control->schema != NULL && strcmp(control->schema, schemaName) != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("extension \"%s\" must be installed in schema \"%s\"", control->name, control->schema))); /* If the user is giving us the schema name, it must exist already */ schemaOid = get_namespace_oid(schemaName, false); } else if (control->schema != NULL) { /* * The extension is not relocatable and the author gave us a schema * for it. We create the schema here if it does not already exist. */ schemaName = control->schema; schemaOid = get_namespace_oid(schemaName, true); if (schemaOid == InvalidOid) { CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt); csstmt->schemaname = schemaName; csstmt->authid = NULL; /* will be created by current user */ csstmt->schemaElts = NIL; csstmt->if_not_exists = false; CreateSchemaCommand(csstmt, NULL); /* * CreateSchemaCommand includes CommandCounterIncrement, so new * schema is now visible */ schemaOid = get_namespace_oid(schemaName, false); } } else { /* * Else, use the current default creation namespace, which is the * first explicit entry in the search_path. */ List *search_path = fetch_search_path(false); if (search_path == NIL) /* probably can't happen */ elog(ERROR, "there is no default creation target"); schemaOid = linitial_oid(search_path); schemaName = get_namespace_name(schemaOid); if (schemaName == NULL) /* recently-deleted namespace? */ elog(ERROR, "there is no default creation target"); list_free(search_path); } /* * We don't check creation rights on the target namespace here. If the * extension script actually creates any objects there, it will fail if * the user doesn't have such permissions. But there are cases such as * procedural languages where it's convenient to set schema = pg_catalog * yet we don't want to restrict the command to users with ACL_CREATE for * pg_catalog. */ /* * Look up the prerequisite extensions, and build lists of their OIDs and * the OIDs of their target schemas. */ requiredExtensions = NIL; requiredSchemas = NIL; foreach(lc, control->requires) { char *curreq = (char *) lfirst(lc); Oid reqext; Oid reqschema; /* * We intentionally don't use get_extension_oid's default error * message here, because it would be confusing in this context. */ reqext = get_extension_oid(curreq, true); if (!OidIsValid(reqext)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("required extension \"%s\" is not installed", curreq))); reqschema = get_extension_schema(reqext); requiredExtensions = lappend_oid(requiredExtensions, reqext); requiredSchemas = lappend_oid(requiredSchemas, reqschema); } /* * Insert new tuple into pg_extension, and create dependency entries. */ extensionOid = InsertExtensionTuple(control->name, extowner, schemaOid, control->relocatable, versionName, PointerGetDatum(NULL), PointerGetDatum(NULL), requiredExtensions); /* * Apply any control-file comment on extension */ if (control->comment != NULL) CreateComments(extensionOid, ExtensionRelationId, 0, control->comment); /* * Execute the installation script file */ execute_extension_script(extensionOid, control, oldVersionName, versionName, requiredSchemas, schemaName, schemaOid); /* * If additional update scripts have to be executed, apply the updates as * though a series of ALTER EXTENSION UPDATE commands were given */ ApplyExtensionUpdates(extensionOid, pcontrol, versionName, updateVersions); return extensionOid; }
Oid ExecAlterExtensionContentsStmt | ( | AlterExtensionContentsStmt * | stmt | ) |
Definition at line 2884 of file extension.c.
References ACL_KIND_EXTENSION, aclcheck_error(), ACLCHECK_NOT_OWNER, AlterExtensionContentsStmt::action, check_object_ownership(), ObjectAddress::classId, deleteDependencyRecordsForClass(), DEPENDENCY_EXTENSION, elog, ereport, errcode(), errmsg(), ERROR, extension_config_remove(), ExtensionRelationId, AlterExtensionContentsStmt::extname, get_extension_name(), get_extension_oid(), get_extension_schema(), get_namespace_name(), get_object_address(), getExtensionOfObject(), getObjectDescription(), GetUserId(), InvokeObjectPostAlterHook, NamespaceRelationId, NoLock, NULL, AlterExtensionContentsStmt::objargs, ObjectAddress::objectId, ObjectAddress::objectSubId, AlterExtensionContentsStmt::objname, AlterExtensionContentsStmt::objtype, OidIsValid, pg_extension_ownercheck(), recordDependencyOn(), relation_close(), RelationRelationId, and ShareUpdateExclusiveLock.
Referenced by ProcessUtilitySlow().
{ ObjectAddress extension; ObjectAddress object; Relation relation; Oid oldExtension; extension.classId = ExtensionRelationId; extension.objectId = get_extension_oid(stmt->extname, false); extension.objectSubId = 0; /* Permission check: must own extension */ if (!pg_extension_ownercheck(extension.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION, stmt->extname); /* * Translate the parser representation that identifies the object into an * ObjectAddress. get_object_address() will throw an error if the object * does not exist, and will also acquire a lock on the object to guard * against concurrent DROP and ALTER EXTENSION ADD/DROP operations. */ object = get_object_address(stmt->objtype, stmt->objname, stmt->objargs, &relation, ShareUpdateExclusiveLock, false); /* Permission check: must own target object, too */ check_object_ownership(GetUserId(), stmt->objtype, object, stmt->objname, stmt->objargs, relation); /* * Check existing extension membership. */ oldExtension = getExtensionOfObject(object.classId, object.objectId); if (stmt->action > 0) { /* * ADD, so complain if object is already attached to some extension. */ if (OidIsValid(oldExtension)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("%s is already a member of extension \"%s\"", getObjectDescription(&object), get_extension_name(oldExtension)))); /* * Prevent a schema from being added to an extension if the schema * contains the extension. That would create a dependency loop. */ if (object.classId == NamespaceRelationId && object.objectId == get_extension_schema(extension.objectId)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot add schema \"%s\" to extension \"%s\" " "because the schema contains the extension", get_namespace_name(object.objectId), stmt->extname))); /* * OK, add the dependency. */ recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION); } else { /* * DROP, so complain if it's not a member. */ if (oldExtension != extension.objectId) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("%s is not a member of extension \"%s\"", getObjectDescription(&object), stmt->extname))); /* * OK, drop the dependency. */ if (deleteDependencyRecordsForClass(object.classId, object.objectId, ExtensionRelationId, DEPENDENCY_EXTENSION) != 1) elog(ERROR, "unexpected number of extension dependency records"); /* * If it's a relation, it might have an entry in the extension's * extconfig array, which we must remove. */ if (object.classId == RelationRelationId) extension_config_remove(extension.objectId, object.objectId); } InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0); /* * If get_object_address() opened the relation for us, we close it to keep * the reference count correct - but we retain any locks acquired by * get_object_address() until commit time, to guard against concurrent * activity. */ if (relation != NULL) relation_close(relation, NoLock); return extension.objectId; }
Oid ExecAlterExtensionStmt | ( | AlterExtensionStmt * | stmt | ) |
Definition at line 2585 of file extension.c.
References AccessShareLock, ACL_KIND_EXTENSION, aclcheck_error(), ACLCHECK_NOT_OWNER, Anum_pg_extension_extname, Anum_pg_extension_extversion, ApplyExtensionUpdates(), DefElem::arg, BTEqualStrategyNumber, check_valid_version_name(), creating_extension, CStringGetDatum, DatumGetTextPP, ExtensionControlFile::default_version, DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, ExtensionNameIndexId, ExtensionRelationId, AlterExtensionStmt::extname, GetUserId(), heap_close, heap_getattr, heap_open(), HeapTupleGetOid, HeapTupleIsValid, identify_update_path(), lfirst, NOTICE, AlterExtensionStmt::options, pg_extension_ownercheck(), read_extension_control_file(), RelationGetDescr, ScanKeyInit(), SnapshotNow, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), and text_to_cstring().
Referenced by ProcessUtilitySlow().
{ DefElem *d_new_version = NULL; char *versionName; char *oldVersionName; ExtensionControlFile *control; Oid extensionOid; Relation extRel; ScanKeyData key[1]; SysScanDesc extScan; HeapTuple extTup; List *updateVersions; Datum datum; bool isnull; ListCell *lc; /* * We use global variables to track the extension being created, so we can * create/update only one extension at the same time. */ if (creating_extension) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("nested ALTER EXTENSION is not supported"))); /* * Look up the extension --- it must already exist in pg_extension */ extRel = heap_open(ExtensionRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_extension_extname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(stmt->extname)); extScan = systable_beginscan(extRel, ExtensionNameIndexId, true, SnapshotNow, 1, key); extTup = systable_getnext(extScan); if (!HeapTupleIsValid(extTup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("extension \"%s\" does not exist", stmt->extname))); extensionOid = HeapTupleGetOid(extTup); /* * Determine the existing version we are updating from */ datum = heap_getattr(extTup, Anum_pg_extension_extversion, RelationGetDescr(extRel), &isnull); if (isnull) elog(ERROR, "extversion is null"); oldVersionName = text_to_cstring(DatumGetTextPP(datum)); systable_endscan(extScan); heap_close(extRel, AccessShareLock); /* Permission check: must own extension */ if (!pg_extension_ownercheck(extensionOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION, stmt->extname); /* * Read the primary control file. Note we assume that it does not contain * any non-ASCII data, so there is no need to worry about encoding at this * point. */ control = read_extension_control_file(stmt->extname); /* * Read the statement option list */ foreach(lc, stmt->options) { DefElem *defel = (DefElem *) lfirst(lc); if (strcmp(defel->defname, "new_version") == 0) { if (d_new_version) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); d_new_version = defel; } else elog(ERROR, "unrecognized option: %s", defel->defname); } /* * Determine the version to update to */ if (d_new_version && d_new_version->arg) versionName = strVal(d_new_version->arg); else if (control->default_version) versionName = control->default_version; else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("version to install must be specified"))); versionName = NULL; /* keep compiler quiet */ } check_valid_version_name(versionName); /* * If we're already at that version, just say so */ if (strcmp(oldVersionName, versionName) == 0) { ereport(NOTICE, (errmsg("version \"%s\" of extension \"%s\" is already installed", versionName, stmt->extname))); return InvalidOid; } /* * Identify the series of update script files we need to execute */ updateVersions = identify_update_path(control, oldVersionName, versionName); /* * Update the pg_extension row and execute the update scripts, one at a * time */ ApplyExtensionUpdates(extensionOid, control, oldVersionName, updateVersions); return extensionOid; }
static void execute_extension_script | ( | Oid | extensionOid, | |
ExtensionControlFile * | control, | |||
const char * | from_version, | |||
const char * | version, | |||
List * | requiredSchemas, | |||
const char * | schemaName, | |||
Oid | schemaOid | |||
) | [static] |
Definition at line 773 of file extension.c.
References appendStringInfo(), appendStringInfoString(), AtEOXact_GUC(), C_COLLATION_OID, client_min_messages, creating_extension, CStringGetTextDatum, CurrentExtensionObject, StringInfoData::data, DatumGetTextPP, DirectFunctionCall3, DirectFunctionCall4Coll(), ereport, errcode(), errhint(), errmsg(), ERROR, execute_sql_string(), filename, get_extension_script_filename(), get_namespace_name(), GUC_ACTION_SAVE, initStringInfo(), lfirst_oid, log_min_messages, ExtensionControlFile::module_pathname, ExtensionControlFile::name, NewGUCNestLevel(), NULL, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PGC_S_SESSION, PGC_SUSET, PGC_USERSET, quote_identifier(), read_extension_script_file(), ExtensionControlFile::relocatable, replace_text(), set_config_option(), superuser(), ExtensionControlFile::superuser, text_to_cstring(), textregexreplace(), and WARNING.
Referenced by ApplyExtensionUpdates(), and CreateExtension().
{ char *filename; int save_nestlevel; StringInfoData pathbuf; ListCell *lc; /* * Enforce superuser-ness if appropriate. We postpone this check until * here so that the flag is correctly associated with the right script(s) * if it's set in secondary control files. */ if (control->superuser && !superuser()) { if (from_version == NULL) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create extension \"%s\"", control->name), errhint("Must be superuser to create this extension."))); else ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to update extension \"%s\"", control->name), errhint("Must be superuser to update this extension."))); } filename = get_extension_script_filename(control, from_version, version); /* * Force client_min_messages and log_min_messages to be at least WARNING, * so that we won't spam the user with useless NOTICE messages from common * script actions like creating shell types. * * We use the equivalent of a function SET option to allow the setting to * persist for exactly the duration of the script execution. guc.c also * takes care of undoing the setting on error. */ save_nestlevel = NewGUCNestLevel(); if (client_min_messages < WARNING) (void) set_config_option("client_min_messages", "warning", PGC_USERSET, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0); if (log_min_messages < WARNING) (void) set_config_option("log_min_messages", "warning", PGC_SUSET, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0); /* * Set up the search path to contain the target schema, then the schemas * of any prerequisite extensions, and nothing else. In particular this * makes the target schema be the default creation target namespace. * * Note: it might look tempting to use PushOverrideSearchPath for this, * but we cannot do that. We have to actually set the search_path GUC in * case the extension script examines or changes it. In any case, the * GUC_ACTION_SAVE method is just as convenient. */ initStringInfo(&pathbuf); appendStringInfoString(&pathbuf, quote_identifier(schemaName)); foreach(lc, requiredSchemas) { Oid reqschema = lfirst_oid(lc); char *reqname = get_namespace_name(reqschema); if (reqname) appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname)); } (void) set_config_option("search_path", pathbuf.data, PGC_USERSET, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0); /* * Set creating_extension and related variables so that * recordDependencyOnCurrentExtension and other functions do the right * things. On failure, ensure we reset these variables. */ creating_extension = true; CurrentExtensionObject = extensionOid; PG_TRY(); { char *c_sql = read_extension_script_file(control, filename); Datum t_sql; /* We use various functions that want to operate on text datums */ t_sql = CStringGetTextDatum(c_sql); /* * Reduce any lines beginning with "\echo" to empty. This allows * scripts to contain messages telling people not to run them via * psql, which has been found to be necessary due to old habits. */ t_sql = DirectFunctionCall4Coll(textregexreplace, C_COLLATION_OID, t_sql, CStringGetTextDatum("^\\\\echo.*$"), CStringGetTextDatum(""), CStringGetTextDatum("ng")); /* * If it's not relocatable, substitute the target schema name for * occurrences of @extschema@. * * For a relocatable extension, we needn't do this. There cannot be * any need for @extschema@, else it wouldn't be relocatable. */ if (!control->relocatable) { const char *qSchemaName = quote_identifier(schemaName); t_sql = DirectFunctionCall3(replace_text, t_sql, CStringGetTextDatum("@extschema@"), CStringGetTextDatum(qSchemaName)); } /* * If module_pathname was set in the control file, substitute its * value for occurrences of MODULE_PATHNAME. */ if (control->module_pathname) { t_sql = DirectFunctionCall3(replace_text, t_sql, CStringGetTextDatum("MODULE_PATHNAME"), CStringGetTextDatum(control->module_pathname)); } /* And now back to C string */ c_sql = text_to_cstring(DatumGetTextPP(t_sql)); execute_sql_string(c_sql, filename); } PG_CATCH(); { creating_extension = false; CurrentExtensionObject = InvalidOid; PG_RE_THROW(); } PG_END_TRY(); creating_extension = false; CurrentExtensionObject = InvalidOid; /* * Restore the GUC variables we set above. */ AtEOXact_GUC(true, save_nestlevel); }
static void execute_sql_string | ( | const char * | sql, | |
const char * | filename | |||
) | [static] |
Definition at line 688 of file extension.c.
References CommandCounterIncrement(), CreateDestReceiver(), CreateQueryDesc(), DestNone, ereport, errcode(), errmsg(), ERROR, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), ForwardScanDirection, FreeQueryDesc(), GetActiveSnapshot(), GetTransactionSnapshot(), IsA, lfirst, NULL, pg_analyze_and_rewrite(), pg_parse_query(), pg_plan_queries(), PopActiveSnapshot(), PROCESS_UTILITY_QUERY, ProcessUtility(), and PushActiveSnapshot().
Referenced by execute_extension_script().
{ List *raw_parsetree_list; DestReceiver *dest; ListCell *lc1; /* * Parse the SQL string into a list of raw parse trees. */ raw_parsetree_list = pg_parse_query(sql); /* All output from SELECTs goes to the bit bucket */ dest = CreateDestReceiver(DestNone); /* * Do parse analysis, rule rewrite, planning, and execution for each raw * parsetree. We must fully execute each query before beginning parse * analysis on the next one, since there may be interdependencies. */ foreach(lc1, raw_parsetree_list) { Node *parsetree = (Node *) lfirst(lc1); List *stmt_list; ListCell *lc2; stmt_list = pg_analyze_and_rewrite(parsetree, sql, NULL, 0); stmt_list = pg_plan_queries(stmt_list, 0, NULL); foreach(lc2, stmt_list) { Node *stmt = (Node *) lfirst(lc2); if (IsA(stmt, TransactionStmt)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("transaction control statements are not allowed within an extension script"))); CommandCounterIncrement(); PushActiveSnapshot(GetTransactionSnapshot()); if (IsA(stmt, PlannedStmt) && ((PlannedStmt *) stmt)->utilityStmt == NULL) { QueryDesc *qdesc; qdesc = CreateQueryDesc((PlannedStmt *) stmt, sql, GetActiveSnapshot(), NULL, dest, NULL, 0); ExecutorStart(qdesc, 0); ExecutorRun(qdesc, ForwardScanDirection, 0); ExecutorFinish(qdesc); ExecutorEnd(qdesc); FreeQueryDesc(qdesc); } else { ProcessUtility(stmt, sql, PROCESS_UTILITY_QUERY, NULL, dest, NULL); } PopActiveSnapshot(); } } /* Be sure to advance the command counter after the last script command */ CommandCounterIncrement(); }
Definition at line 2231 of file extension.c.
References Anum_pg_extension_extcondition, Anum_pg_extension_extconfig, ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_LBOUND, ARR_NDIM, BTEqualStrategyNumber, CatalogUpdateIndexes(), construct_array(), DatumGetArrayTypeP, deconstruct_array(), elog, ERROR, ExtensionOidIndexId, ExtensionRelationId, heap_close, heap_getattr, heap_modify_tuple(), heap_open(), HeapTupleIsValid, i, ObjectIdAttributeNumber, ObjectIdGetDatum, OIDOID, PointerGetDatum, RelationGetDescr, RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, and TEXTOID.
Referenced by ExecAlterExtensionContentsStmt().
{ Relation extRel; ScanKeyData key[1]; SysScanDesc extScan; HeapTuple extTup; Datum arrayDatum; int arrayLength; int arrayIndex; bool isnull; Datum repl_val[Natts_pg_extension]; bool repl_null[Natts_pg_extension]; bool repl_repl[Natts_pg_extension]; ArrayType *a; /* Find the pg_extension tuple */ extRel = heap_open(ExtensionRelationId, RowExclusiveLock); ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(extensionoid)); extScan = systable_beginscan(extRel, ExtensionOidIndexId, true, SnapshotNow, 1, key); extTup = systable_getnext(extScan); if (!HeapTupleIsValid(extTup)) /* should not happen */ elog(ERROR, "extension with oid %u does not exist", extensionoid); /* Search extconfig for the tableoid */ arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig, RelationGetDescr(extRel), &isnull); if (isnull) { /* nothing to do */ a = NULL; arrayLength = 0; arrayIndex = -1; } else { Oid *arrayData; int i; a = DatumGetArrayTypeP(arrayDatum); arrayLength = ARR_DIMS(a)[0]; if (ARR_NDIM(a) != 1 || ARR_LBOUND(a)[0] != 1 || arrayLength < 0 || ARR_HASNULL(a) || ARR_ELEMTYPE(a) != OIDOID) elog(ERROR, "extconfig is not a 1-D Oid array"); arrayData = (Oid *) ARR_DATA_PTR(a); arrayIndex = -1; /* flag for no deletion needed */ for (i = 0; i < arrayLength; i++) { if (arrayData[i] == tableoid) { arrayIndex = i; /* index to remove */ break; } } } /* If tableoid is not in extconfig, nothing to do */ if (arrayIndex < 0) { systable_endscan(extScan); heap_close(extRel, RowExclusiveLock); return; } /* Modify or delete the extconfig value */ memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); if (arrayLength <= 1) { /* removing only element, just set array to null */ repl_null[Anum_pg_extension_extconfig - 1] = true; } else { /* squeeze out the target element */ Datum *dvalues; bool *dnulls; int nelems; int i; deconstruct_array(a, OIDOID, sizeof(Oid), true, 'i', &dvalues, &dnulls, &nelems); /* We already checked there are no nulls, so ignore dnulls */ for (i = arrayIndex; i < arrayLength - 1; i++) dvalues[i] = dvalues[i + 1]; a = construct_array(dvalues, arrayLength - 1, OIDOID, sizeof(Oid), true, 'i'); repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a); } repl_repl[Anum_pg_extension_extconfig - 1] = true; /* Modify or delete the extcondition value */ arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition, RelationGetDescr(extRel), &isnull); if (isnull) { elog(ERROR, "extconfig and extcondition arrays do not match"); } else { a = DatumGetArrayTypeP(arrayDatum); if (ARR_NDIM(a) != 1 || ARR_LBOUND(a)[0] != 1 || ARR_HASNULL(a) || ARR_ELEMTYPE(a) != TEXTOID) elog(ERROR, "extcondition is not a 1-D text array"); if (ARR_DIMS(a)[0] != arrayLength) elog(ERROR, "extconfig and extcondition arrays do not match"); } if (arrayLength <= 1) { /* removing only element, just set array to null */ repl_null[Anum_pg_extension_extcondition - 1] = true; } else { /* squeeze out the target element */ Datum *dvalues; bool *dnulls; int nelems; int i; deconstruct_array(a, TEXTOID, -1, false, 'i', &dvalues, &dnulls, &nelems); /* We already checked there are no nulls, so ignore dnulls */ for (i = arrayIndex; i < arrayLength - 1; i++) dvalues[i] = dvalues[i + 1]; a = construct_array(dvalues, arrayLength - 1, TEXTOID, -1, false, 'i'); repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a); } repl_repl[Anum_pg_extension_extcondition - 1] = true; extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), repl_val, repl_null, repl_repl); simple_heap_update(extRel, &extTup->t_self, extTup); CatalogUpdateIndexes(extRel, extTup); systable_endscan(extScan); heap_close(extRel, RowExclusiveLock); }
static List * find_update_path | ( | List * | evi_list, | |
ExtensionVersionInfo * | evi_start, | |||
ExtensionVersionInfo * | evi_target, | |||
bool | reinitialize | |||
) | [static] |
Definition at line 1102 of file extension.c.
References Assert, ExtensionVersionInfo::distance, ExtensionVersionInfo::distance_known, get_nearest_unprocessed_vertex(), lcons(), lfirst, ExtensionVersionInfo::name, NULL, ExtensionVersionInfo::previous, and ExtensionVersionInfo::reachable.
Referenced by identify_update_path(), and pg_extension_update_paths().
{ List *result; ExtensionVersionInfo *evi; ListCell *lc; /* Caller error if start == target */ Assert(evi_start != evi_target); if (reinitialize) { foreach(lc, evi_list) { evi = (ExtensionVersionInfo *) lfirst(lc); evi->distance_known = false; evi->distance = INT_MAX; evi->previous = NULL; } } evi_start->distance = 0; while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL) { if (evi->distance == INT_MAX) break; /* all remaining vertices are unreachable */ evi->distance_known = true; if (evi == evi_target) break; /* found shortest path to target */ foreach(lc, evi->reachable) { ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc); int newdist; newdist = evi->distance + 1; if (newdist < evi2->distance) { evi2->distance = newdist; evi2->previous = evi; } else if (newdist == evi2->distance && evi2->previous != NULL && strcmp(evi->name, evi2->previous->name) < 0) { /* * Break ties in favor of the version name that comes first * according to strcmp(). This behavior is undocumented and * users shouldn't rely on it. We do it just to ensure that * if there is a tie, the update path that is chosen does not * depend on random factors like the order in which directory * entries get visited. */ evi2->previous = evi; } } } /* Return NIL if target is not reachable from start */ if (!evi_target->distance_known) return NIL; /* Build and return list of version names representing the update path */ result = NIL; for (evi = evi_target; evi != evi_start; evi = evi->previous) result = lcons(evi->name, result); return result; }
static void get_available_versions_for_extension | ( | ExtensionControlFile * | pcontrol, | |
Tuplestorestate * | tupstore, | |||
TupleDesc | tupdesc | |||
) | [static] |
Definition at line 1827 of file extension.c.
References AllocateDir(), BoolGetDatum, ExtensionControlFile::comment, construct_array(), CStringGetDatum, CStringGetTextDatum, dirent::d_name, DirectFunctionCall1, FreeDir(), get_extension_script_directory(), is_extension_script_filename(), lfirst, list_length(), ExtensionControlFile::name, NAMEDATALEN, namein(), NAMEOID, NIL, NULL, palloc(), PointerGetDatum, pstrdup(), read_extension_aux_control_file(), ReadDir(), ExtensionControlFile::relocatable, ExtensionControlFile::requires, ExtensionControlFile::schema, ExtensionControlFile::superuser, tuplestore_putvalues(), and values.
Referenced by pg_available_extension_versions().
{ int extnamelen = strlen(pcontrol->name); char *location; DIR *dir; struct dirent *de; location = get_extension_script_directory(pcontrol); dir = AllocateDir(location); /* Note this will fail if script directory doesn't exist */ while ((de = ReadDir(dir, location)) != NULL) { ExtensionControlFile *control; char *vername; Datum values[7]; bool nulls[7]; /* must be a .sql file ... */ if (!is_extension_script_filename(de->d_name)) continue; /* ... matching extension name followed by separator */ if (strncmp(de->d_name, pcontrol->name, extnamelen) != 0 || de->d_name[extnamelen] != '-' || de->d_name[extnamelen + 1] != '-') continue; /* extract version name from 'extname--something.sql' filename */ vername = pstrdup(de->d_name + extnamelen + 2); *strrchr(vername, '.') = '\0'; /* ignore it if it's an update script */ if (strstr(vername, "--")) continue; /* * Fetch parameters for specific version (pcontrol is not changed) */ control = read_extension_aux_control_file(pcontrol, vername); memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); /* name */ values[0] = DirectFunctionCall1(namein, CStringGetDatum(control->name)); /* version */ values[1] = CStringGetTextDatum(vername); /* superuser */ values[2] = BoolGetDatum(control->superuser); /* relocatable */ values[3] = BoolGetDatum(control->relocatable); /* schema */ if (control->schema == NULL) nulls[4] = true; else values[4] = DirectFunctionCall1(namein, CStringGetDatum(control->schema)); /* requires */ if (control->requires == NIL) nulls[5] = true; else { Datum *datums; int ndatums; ArrayType *a; ListCell *lc; ndatums = list_length(control->requires); datums = (Datum *) palloc(ndatums * sizeof(Datum)); ndatums = 0; foreach(lc, control->requires) { char *curreq = (char *) lfirst(lc); datums[ndatums++] = DirectFunctionCall1(namein, CStringGetDatum(curreq)); } a = construct_array(datums, ndatums, NAMEOID, NAMEDATALEN, false, 'c'); values[5] = PointerGetDatum(a); } /* comment */ if (control->comment == NULL) nulls[6] = true; else values[6] = CStringGetTextDatum(control->comment); tuplestore_putvalues(tupstore, tupdesc, values, nulls); } FreeDir(dir); }
static ExtensionVersionInfo* get_ext_ver_info | ( | const char * | versionname, | |
List ** | evi_list | |||
) | [static] |
Definition at line 939 of file extension.c.
References ExtensionVersionInfo::distance, ExtensionVersionInfo::distance_known, ExtensionVersionInfo::installable, lappend(), lfirst, ExtensionVersionInfo::name, palloc(), ExtensionVersionInfo::previous, pstrdup(), and ExtensionVersionInfo::reachable.
Referenced by get_ext_ver_list(), and identify_update_path().
{ ExtensionVersionInfo *evi; ListCell *lc; foreach(lc, *evi_list) { evi = (ExtensionVersionInfo *) lfirst(lc); if (strcmp(evi->name, versionname) == 0) return evi; } evi = (ExtensionVersionInfo *) palloc(sizeof(ExtensionVersionInfo)); evi->name = pstrdup(versionname); evi->reachable = NIL; evi->installable = false; /* initialize for later application of Dijkstra's algorithm */ evi->distance_known = false; evi->distance = INT_MAX; evi->previous = NULL; *evi_list = lappend(*evi_list, evi); return evi; }
static List* get_ext_ver_list | ( | ExtensionControlFile * | control | ) | [static] |
Definition at line 1000 of file extension.c.
References AllocateDir(), dirent::d_name, FreeDir(), get_ext_ver_info(), get_extension_script_directory(), ExtensionVersionInfo::installable, is_extension_script_filename(), lappend(), ExtensionControlFile::name, NULL, pstrdup(), ExtensionVersionInfo::reachable, and ReadDir().
Referenced by identify_update_path(), and pg_extension_update_paths().
{ List *evi_list = NIL; int extnamelen = strlen(control->name); char *location; DIR *dir; struct dirent *de; location = get_extension_script_directory(control); dir = AllocateDir(location); while ((de = ReadDir(dir, location)) != NULL) { char *vername; char *vername2; ExtensionVersionInfo *evi; ExtensionVersionInfo *evi2; /* must be a .sql file ... */ if (!is_extension_script_filename(de->d_name)) continue; /* ... matching extension name followed by separator */ if (strncmp(de->d_name, control->name, extnamelen) != 0 || de->d_name[extnamelen] != '-' || de->d_name[extnamelen + 1] != '-') continue; /* extract version name(s) from 'extname--something.sql' filename */ vername = pstrdup(de->d_name + extnamelen + 2); *strrchr(vername, '.') = '\0'; vername2 = strstr(vername, "--"); if (!vername2) { /* It's an install, not update, script; record its version name */ evi = get_ext_ver_info(vername, &evi_list); evi->installable = true; continue; } *vername2 = '\0'; /* terminate first version */ vername2 += 2; /* and point to second */ /* if there's a third --, it's bogus, ignore it */ if (strstr(vername2, "--")) continue; /* Create ExtensionVersionInfos and link them together */ evi = get_ext_ver_info(vername, &evi_list); evi2 = get_ext_ver_info(vername2, &evi_list); evi->reachable = lappend(evi->reachable, evi2); } FreeDir(dir); return evi_list; }
static char* get_extension_aux_control_filename | ( | ExtensionControlFile * | control, | |
const char * | version | |||
) | [static] |
Definition at line 396 of file extension.c.
References get_extension_script_directory(), MAXPGPATH, ExtensionControlFile::name, palloc(), pfree(), and snprintf().
Referenced by parse_extension_control_file().
static char* get_extension_control_directory | ( | void | ) | [static] |
Definition at line 346 of file extension.c.
References get_share_path(), MAXPGPATH, my_exec_path, palloc(), and snprintf().
Referenced by get_extension_script_directory(), pg_available_extension_versions(), and pg_available_extensions().
{ char sharepath[MAXPGPATH]; char *result; get_share_path(my_exec_path, sharepath); result = (char *) palloc(MAXPGPATH); snprintf(result, MAXPGPATH, "%s/extension", sharepath); return result; }
static char* get_extension_control_filename | ( | const char * | extname | ) | [static] |
Definition at line 359 of file extension.c.
References get_share_path(), MAXPGPATH, my_exec_path, palloc(), and snprintf().
Referenced by parse_extension_control_file().
{ char sharepath[MAXPGPATH]; char *result; get_share_path(my_exec_path, sharepath); result = (char *) palloc(MAXPGPATH); snprintf(result, MAXPGPATH, "%s/extension/%s.control", sharepath, extname); return result; }
char* get_extension_name | ( | Oid | ext_oid | ) |
Definition at line 160 of file extension.c.
References AccessShareLock, BTEqualStrategyNumber, ExtensionOidIndexId, ExtensionRelationId, GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, NameStr, ObjectIdAttributeNumber, ObjectIdGetDatum, pstrdup(), ScanKeyInit(), SnapshotNow, systable_beginscan(), systable_endscan(), and systable_getnext().
Referenced by ExecAlterExtensionContentsStmt(), getObjectDescription(), getObjectIdentity(), recordDependencyOnCurrentExtension(), and RemoveExtensionById().
{ char *result; Relation rel; SysScanDesc scandesc; HeapTuple tuple; ScanKeyData entry[1]; rel = heap_open(ExtensionRelationId, AccessShareLock); ScanKeyInit(&entry[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ext_oid)); scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, SnapshotNow, 1, entry); tuple = systable_getnext(scandesc); /* We assume that there can be at most one matching tuple */ if (HeapTupleIsValid(tuple)) result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname)); else result = NULL; systable_endscan(scandesc); heap_close(rel, AccessShareLock); return result; }
Definition at line 115 of file extension.c.
References AccessShareLock, Anum_pg_extension_extname, BTEqualStrategyNumber, CStringGetDatum, ereport, errcode(), errmsg(), ERROR, ExtensionNameIndexId, ExtensionRelationId, heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, OidIsValid, ScanKeyInit(), SnapshotNow, systable_beginscan(), systable_endscan(), and systable_getnext().
Referenced by AlterExtensionNamespace(), ApplyExtensionUpdates(), create_empty_extension(), CreateExtension(), ExecAlterExtensionContentsStmt(), and get_object_address_unqualified().
{ Oid result; Relation rel; SysScanDesc scandesc; HeapTuple tuple; ScanKeyData entry[1]; rel = heap_open(ExtensionRelationId, AccessShareLock); ScanKeyInit(&entry[0], Anum_pg_extension_extname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(extname)); scandesc = systable_beginscan(rel, ExtensionNameIndexId, true, SnapshotNow, 1, entry); tuple = systable_getnext(scandesc); /* We assume that there can be at most one matching tuple */ if (HeapTupleIsValid(tuple)) result = HeapTupleGetOid(tuple); else result = InvalidOid; systable_endscan(scandesc); heap_close(rel, AccessShareLock); if (!OidIsValid(result) && !missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("extension \"%s\" does not exist", extname))); return result; }
Definition at line 199 of file extension.c.
References AccessShareLock, BTEqualStrategyNumber, ExtensionOidIndexId, ExtensionRelationId, GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, ObjectIdAttributeNumber, ObjectIdGetDatum, ScanKeyInit(), SnapshotNow, systable_beginscan(), systable_endscan(), and systable_getnext().
Referenced by ApplyExtensionUpdates(), CreateExtension(), and ExecAlterExtensionContentsStmt().
{ Oid result; Relation rel; SysScanDesc scandesc; HeapTuple tuple; ScanKeyData entry[1]; rel = heap_open(ExtensionRelationId, AccessShareLock); ScanKeyInit(&entry[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ext_oid)); scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, SnapshotNow, 1, entry); tuple = systable_getnext(scandesc); /* We assume that there can be at most one matching tuple */ if (HeapTupleIsValid(tuple)) result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace; else result = InvalidOid; systable_endscan(scandesc); heap_close(rel, AccessShareLock); return result; }
static char* get_extension_script_directory | ( | ExtensionControlFile * | control | ) | [static] |
Definition at line 373 of file extension.c.
References ExtensionControlFile::directory, get_extension_control_directory(), get_share_path(), is_absolute_path, MAXPGPATH, my_exec_path, palloc(), pstrdup(), and snprintf().
Referenced by get_available_versions_for_extension(), get_ext_ver_list(), get_extension_aux_control_filename(), and get_extension_script_filename().
{ char sharepath[MAXPGPATH]; char *result; /* * The directory parameter can be omitted, absolute, or relative to the * installation's share directory. */ if (!control->directory) return get_extension_control_directory(); if (is_absolute_path(control->directory)) return pstrdup(control->directory); get_share_path(my_exec_path, sharepath); result = (char *) palloc(MAXPGPATH); snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory); return result; }
static char* get_extension_script_filename | ( | ExtensionControlFile * | control, | |
const char * | from_version, | |||
const char * | version | |||
) | [static] |
Definition at line 414 of file extension.c.
References get_extension_script_directory(), MAXPGPATH, ExtensionControlFile::name, palloc(), pfree(), and snprintf().
Referenced by execute_extension_script().
{ char *result; char *scriptdir; scriptdir = get_extension_script_directory(control); result = (char *) palloc(MAXPGPATH); if (from_version) snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql", scriptdir, control->name, from_version, version); else snprintf(result, MAXPGPATH, "%s/%s--%s.sql", scriptdir, control->name, version); pfree(scriptdir); return result; }
static ExtensionVersionInfo* get_nearest_unprocessed_vertex | ( | List * | evi_list | ) | [static] |
Definition at line 972 of file extension.c.
References ExtensionVersionInfo::distance, ExtensionVersionInfo::distance_known, lfirst, and NULL.
Referenced by find_update_path().
{ ExtensionVersionInfo *evi = NULL; ListCell *lc; foreach(lc, evi_list) { ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc); /* only vertices whose distance is still uncertain are candidates */ if (evi2->distance_known) continue; /* remember the closest such vertex */ if (evi == NULL || evi->distance > evi2->distance) evi = evi2; } return evi; }
static List* identify_update_path | ( | ExtensionControlFile * | control, | |
const char * | oldVersion, | |||
const char * | newVersion | |||
) | [static] |
Definition at line 1063 of file extension.c.
References ereport, errcode(), errmsg(), ERROR, find_update_path(), get_ext_ver_info(), get_ext_ver_list(), ExtensionControlFile::name, and NIL.
Referenced by CreateExtension(), and ExecAlterExtensionStmt().
{ List *result; List *evi_list; ExtensionVersionInfo *evi_start; ExtensionVersionInfo *evi_target; /* Extract the version update graph from the script directory */ evi_list = get_ext_ver_list(control); /* Initialize start and end vertices */ evi_start = get_ext_ver_info(oldVersion, &evi_list); evi_target = get_ext_ver_info(newVersion, &evi_list); /* Find shortest path */ result = find_update_path(evi_list, evi_start, evi_target, false); if (result == NIL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"", control->name, oldVersion, newVersion))); return result; }
Oid InsertExtensionTuple | ( | const char * | extName, | |
Oid | extOwner, | |||
Oid | schemaOid, | |||
bool | relocatable, | |||
const char * | extVersion, | |||
Datum | extConfig, | |||
Datum | extCondition, | |||
List * | requiredExtensions | |||
) |
Definition at line 1491 of file extension.c.
References Anum_pg_extension_extcondition, Anum_pg_extension_extconfig, Anum_pg_extension_extname, Anum_pg_extension_extnamespace, Anum_pg_extension_extowner, Anum_pg_extension_extrelocatable, Anum_pg_extension_extversion, BoolGetDatum, CatalogUpdateIndexes(), ObjectAddress::classId, CStringGetDatum, CStringGetTextDatum, DEPENDENCY_NORMAL, DirectFunctionCall1, ExtensionRelationId, heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), InvokeObjectPostCreateHook, lfirst_oid, namein(), NULL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, PointerGetDatum, RelationData::rd_att, recordDependencyOn(), recordDependencyOnOwner(), RowExclusiveLock, simple_heap_insert(), and values.
Referenced by create_empty_extension(), and CreateExtension().
{ Oid extensionOid; Relation rel; Datum values[Natts_pg_extension]; bool nulls[Natts_pg_extension]; HeapTuple tuple; ObjectAddress myself; ObjectAddress nsp; ListCell *lc; /* * Build and insert the pg_extension tuple */ rel = heap_open(ExtensionRelationId, RowExclusiveLock); memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); values[Anum_pg_extension_extname - 1] = DirectFunctionCall1(namein, CStringGetDatum(extName)); values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner); values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid); values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable); values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion); if (extConfig == PointerGetDatum(NULL)) nulls[Anum_pg_extension_extconfig - 1] = true; else values[Anum_pg_extension_extconfig - 1] = extConfig; if (extCondition == PointerGetDatum(NULL)) nulls[Anum_pg_extension_extcondition - 1] = true; else values[Anum_pg_extension_extcondition - 1] = extCondition; tuple = heap_form_tuple(rel->rd_att, values, nulls); extensionOid = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); heap_freetuple(tuple); heap_close(rel, RowExclusiveLock); /* * Record dependencies on owner, schema, and prerequisite extensions */ recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner); myself.classId = ExtensionRelationId; myself.objectId = extensionOid; myself.objectSubId = 0; nsp.classId = NamespaceRelationId; nsp.objectId = schemaOid; nsp.objectSubId = 0; recordDependencyOn(&myself, &nsp, DEPENDENCY_NORMAL); foreach(lc, requiredExtensions) { Oid reqext = lfirst_oid(lc); ObjectAddress otherext; otherext.classId = ExtensionRelationId; otherext.objectId = reqext; otherext.objectSubId = 0; recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL); } /* Post creation hook for new extension */ InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0); return extensionOid; }
static bool is_extension_control_filename | ( | const char * | filename | ) | [static] |
Definition at line 330 of file extension.c.
References NULL.
Referenced by pg_available_extension_versions(), and pg_available_extensions().
static bool is_extension_script_filename | ( | const char * | filename | ) | [static] |
Definition at line 338 of file extension.c.
References NULL.
Referenced by get_available_versions_for_extension(), and get_ext_ver_list().
static void parse_extension_control_file | ( | ExtensionControlFile * | control, | |
const char * | version | |||
) | [static] |
Definition at line 446 of file extension.c.
References AllocateFile(), ExtensionControlFile::comment, ExtensionControlFile::default_version, ExtensionControlFile::directory, ExtensionControlFile::encoding, ereport, errcode(), errcode_for_file_access(), errmsg(), ERROR, filename, FreeConfigVariables(), FreeFile(), get_extension_aux_control_filename(), get_extension_control_filename(), ExtensionControlFile::module_pathname, ConfigVariable::name, ExtensionControlFile::name, ConfigVariable::next, NULL, parse_bool(), ParseConfigFp(), pfree(), pg_valid_server_encoding(), pstrdup(), ExtensionControlFile::relocatable, ExtensionControlFile::requires, ExtensionControlFile::schema, SplitIdentifierString(), ExtensionControlFile::superuser, and ConfigVariable::value.
Referenced by read_extension_aux_control_file(), and read_extension_control_file().
{ char *filename; FILE *file; ConfigVariable *item, *head = NULL, *tail = NULL; /* * Locate the file to read. Auxiliary files are optional. */ if (version) filename = get_extension_aux_control_filename(control, version); else filename = get_extension_control_filename(control->name); if ((file = AllocateFile(filename, "r")) == NULL) { if (version && errno == ENOENT) { /* no auxiliary file for this version */ pfree(filename); return; } ereport(ERROR, (errcode_for_file_access(), errmsg("could not open extension control file \"%s\": %m", filename))); } /* * Parse the file content, using GUC's file parsing code. We need not * check the return value since any errors will be thrown at ERROR level. */ (void) ParseConfigFp(file, filename, 0, ERROR, &head, &tail); FreeFile(file); /* * Convert the ConfigVariable list into ExtensionControlFile entries. */ for (item = head; item != NULL; item = item->next) { if (strcmp(item->name, "directory") == 0) { if (version) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("parameter \"%s\" cannot be set in a secondary extension control file", item->name))); control->directory = pstrdup(item->value); } else if (strcmp(item->name, "default_version") == 0) { if (version) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("parameter \"%s\" cannot be set in a secondary extension control file", item->name))); control->default_version = pstrdup(item->value); } else if (strcmp(item->name, "module_pathname") == 0) { control->module_pathname = pstrdup(item->value); } else if (strcmp(item->name, "comment") == 0) { control->comment = pstrdup(item->value); } else if (strcmp(item->name, "schema") == 0) { control->schema = pstrdup(item->value); } else if (strcmp(item->name, "relocatable") == 0) { if (!parse_bool(item->value, &control->relocatable)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("parameter \"%s\" requires a Boolean value", item->name))); } else if (strcmp(item->name, "superuser") == 0) { if (!parse_bool(item->value, &control->superuser)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("parameter \"%s\" requires a Boolean value", item->name))); } else if (strcmp(item->name, "encoding") == 0) { control->encoding = pg_valid_server_encoding(item->value); if (control->encoding < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("\"%s\" is not a valid encoding name", item->value))); } else if (strcmp(item->name, "requires") == 0) { /* Need a modifiable copy of string */ char *rawnames = pstrdup(item->value); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawnames, ',', &control->requires)) { /* syntax error in name list */ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("parameter \"%s\" must be a list of extension names", item->name))); } } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized parameter \"%s\" in file \"%s\"", item->name, filename))); } FreeConfigVariables(head); if (control->relocatable && control->schema != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true"))); pfree(filename); }
Datum pg_available_extension_versions | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1740 of file extension.c.
References AllocateDir(), ReturnSetInfo::allowedModes, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errmsg(), ERROR, FreeDir(), get_available_versions_for_extension(), get_call_result_type(), get_extension_control_directory(), is_extension_control_filename(), IsA, MemoryContextSwitchTo(), NULL, pstrdup(), read_extension_control_file(), ReadDir(), ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, tuplestore_begin_heap(), tuplestore_donestoring, and work_mem.
{ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; char *location; DIR *dir; struct dirent *de; /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); location = get_extension_control_directory(); dir = AllocateDir(location); /* * If the control directory doesn't exist, we want to silently return an * empty set. Any other error will be reported by ReadDir. */ if (dir == NULL && errno == ENOENT) { /* do nothing */ } else { while ((de = ReadDir(dir, location)) != NULL) { ExtensionControlFile *control; char *extname; if (!is_extension_control_filename(de->d_name)) continue; /* extract extension name from 'name.control' filename */ extname = pstrdup(de->d_name); *strrchr(extname, '.') = '\0'; /* ignore it if it's an auxiliary control file */ if (strstr(extname, "--")) continue; /* read the control file */ control = read_extension_control_file(extname); /* scan extension's script directory for install scripts */ get_available_versions_for_extension(control, tupstore, tupdesc); } FreeDir(dir); } /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; }
Datum pg_available_extensions | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1631 of file extension.c.
References AllocateDir(), ReturnSetInfo::allowedModes, ExtensionControlFile::comment, CStringGetDatum, CStringGetTextDatum, ExtensionControlFile::default_version, DirectFunctionCall1, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errmsg(), ERROR, FreeDir(), get_call_result_type(), get_extension_control_directory(), is_extension_control_filename(), IsA, MemoryContextSwitchTo(), ExtensionControlFile::name, namein(), NULL, pstrdup(), read_extension_control_file(), ReadDir(), ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), values, and work_mem.
{ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; char *location; DIR *dir; struct dirent *de; /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); location = get_extension_control_directory(); dir = AllocateDir(location); /* * If the control directory doesn't exist, we want to silently return an * empty set. Any other error will be reported by ReadDir. */ if (dir == NULL && errno == ENOENT) { /* do nothing */ } else { while ((de = ReadDir(dir, location)) != NULL) { ExtensionControlFile *control; char *extname; Datum values[3]; bool nulls[3]; if (!is_extension_control_filename(de->d_name)) continue; /* extract extension name from 'name.control' filename */ extname = pstrdup(de->d_name); *strrchr(extname, '.') = '\0'; /* ignore it if it's an auxiliary control file */ if (strstr(extname, "--")) continue; control = read_extension_control_file(extname); memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); /* name */ values[0] = DirectFunctionCall1(namein, CStringGetDatum(control->name)); /* default_version */ if (control->default_version == NULL) nulls[1] = true; else values[1] = CStringGetTextDatum(control->default_version); /* comment */ if (control->comment == NULL) nulls[2] = true; else values[2] = CStringGetTextDatum(control->comment); tuplestore_putvalues(tupstore, tupdesc, values, nulls); } FreeDir(dir); } /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; }
Datum pg_extension_config_dump | ( | PG_FUNCTION_ARGS | ) |
Definition at line 2043 of file extension.c.
References Anum_pg_extension_extcondition, Anum_pg_extension_extconfig, ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_LBOUND, ARR_NDIM, array_set(), BTEqualStrategyNumber, CatalogUpdateIndexes(), construct_array(), creating_extension, CurrentExtensionObject, DatumGetArrayTypeP, elog, ereport, errcode(), errmsg(), ERROR, ExtensionOidIndexId, ExtensionRelationId, get_rel_name(), getExtensionOfObject(), heap_close, heap_getattr, heap_modify_tuple(), heap_open(), HeapTupleIsValid, i, NULL, ObjectIdAttributeNumber, ObjectIdGetDatum, OIDOID, PG_GETARG_OID, PG_GETARG_TEXT_P, PG_RETURN_VOID, PointerGetDatum, RelationGetDescr, RelationRelationId, RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, and TEXTOID.
{ Oid tableoid = PG_GETARG_OID(0); text *wherecond = PG_GETARG_TEXT_P(1); char *tablename; Relation extRel; ScanKeyData key[1]; SysScanDesc extScan; HeapTuple extTup; Datum arrayDatum; Datum elementDatum; int arrayLength; int arrayIndex; bool isnull; Datum repl_val[Natts_pg_extension]; bool repl_null[Natts_pg_extension]; bool repl_repl[Natts_pg_extension]; ArrayType *a; /* * We only allow this to be called from an extension's SQL script. We * shouldn't need any permissions check beyond that. */ if (!creating_extension) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("pg_extension_config_dump() can only be called " "from an SQL script executed by CREATE EXTENSION"))); /* * Check that the table exists and is a member of the extension being * created. This ensures that we don't need to register an additional * dependency to protect the extconfig entry. */ tablename = get_rel_name(tableoid); if (tablename == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("OID %u does not refer to a table", tableoid))); if (getExtensionOfObject(RelationRelationId, tableoid) != CurrentExtensionObject) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("table \"%s\" is not a member of the extension being created", tablename))); /* * Add the table OID and WHERE condition to the extension's extconfig and * extcondition arrays. * * If the table is already in extconfig, treat this as an update of the * WHERE condition. */ /* Find the pg_extension tuple */ extRel = heap_open(ExtensionRelationId, RowExclusiveLock); ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(CurrentExtensionObject)); extScan = systable_beginscan(extRel, ExtensionOidIndexId, true, SnapshotNow, 1, key); extTup = systable_getnext(extScan); if (!HeapTupleIsValid(extTup)) /* should not happen */ elog(ERROR, "extension with oid %u does not exist", CurrentExtensionObject); memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); /* Build or modify the extconfig value */ elementDatum = ObjectIdGetDatum(tableoid); arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig, RelationGetDescr(extRel), &isnull); if (isnull) { /* Previously empty extconfig, so build 1-element array */ arrayLength = 0; arrayIndex = 1; a = construct_array(&elementDatum, 1, OIDOID, sizeof(Oid), true, 'i'); } else { /* Modify or extend existing extconfig array */ Oid *arrayData; int i; a = DatumGetArrayTypeP(arrayDatum); arrayLength = ARR_DIMS(a)[0]; if (ARR_NDIM(a) != 1 || ARR_LBOUND(a)[0] != 1 || arrayLength < 0 || ARR_HASNULL(a) || ARR_ELEMTYPE(a) != OIDOID) elog(ERROR, "extconfig is not a 1-D Oid array"); arrayData = (Oid *) ARR_DATA_PTR(a); arrayIndex = arrayLength + 1; /* set up to add after end */ for (i = 0; i < arrayLength; i++) { if (arrayData[i] == tableoid) { arrayIndex = i + 1; /* replace this element instead */ break; } } a = array_set(a, 1, &arrayIndex, elementDatum, false, -1 /* varlena array */ , sizeof(Oid) /* OID's typlen */ , true /* OID's typbyval */ , 'i' /* OID's typalign */ ); } repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a); repl_repl[Anum_pg_extension_extconfig - 1] = true; /* Build or modify the extcondition value */ elementDatum = PointerGetDatum(wherecond); arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition, RelationGetDescr(extRel), &isnull); if (isnull) { if (arrayLength != 0) elog(ERROR, "extconfig and extcondition arrays do not match"); a = construct_array(&elementDatum, 1, TEXTOID, -1, false, 'i'); } else { a = DatumGetArrayTypeP(arrayDatum); if (ARR_NDIM(a) != 1 || ARR_LBOUND(a)[0] != 1 || ARR_HASNULL(a) || ARR_ELEMTYPE(a) != TEXTOID) elog(ERROR, "extcondition is not a 1-D text array"); if (ARR_DIMS(a)[0] != arrayLength) elog(ERROR, "extconfig and extcondition arrays do not match"); /* Add or replace at same index as in extconfig */ a = array_set(a, 1, &arrayIndex, elementDatum, false, -1 /* varlena array */ , -1 /* TEXT's typlen */ , false /* TEXT's typbyval */ , 'i' /* TEXT's typalign */ ); } repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a); repl_repl[Anum_pg_extension_extcondition - 1] = true; extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), repl_val, repl_null, repl_repl); simple_heap_update(extRel, &extTup->t_self, extTup); CatalogUpdateIndexes(extRel, extTup); systable_endscan(extScan); heap_close(extRel, RowExclusiveLock); PG_RETURN_VOID(); }
Datum pg_extension_update_paths | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1929 of file extension.c.
References ReturnSetInfo::allowedModes, appendStringInfoString(), check_valid_extension_name(), CStringGetTextDatum, StringInfoData::data, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errmsg(), ERROR, find_update_path(), get_call_result_type(), get_ext_ver_list(), initStringInfo(), IsA, lfirst, MemoryContextSwitchTo(), ExtensionVersionInfo::name, NameStr, NIL, NULL, pfree(), PG_GETARG_NAME, read_extension_control_file(), ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), values, and work_mem.
{ Name extname = PG_GETARG_NAME(0); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; List *evi_list; ExtensionControlFile *control; ListCell *lc1; /* Check extension name validity before any filesystem access */ check_valid_extension_name(NameStr(*extname)); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); /* Read the extension's control file */ control = read_extension_control_file(NameStr(*extname)); /* Extract the version update graph from the script directory */ evi_list = get_ext_ver_list(control); /* Iterate over all pairs of versions */ foreach(lc1, evi_list) { ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc1); ListCell *lc2; foreach(lc2, evi_list) { ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2); List *path; Datum values[3]; bool nulls[3]; if (evi1 == evi2) continue; /* Find shortest path from evi1 to evi2 */ path = find_update_path(evi_list, evi1, evi2, true); /* Emit result row */ memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); /* source */ values[0] = CStringGetTextDatum(evi1->name); /* target */ values[1] = CStringGetTextDatum(evi2->name); /* path */ if (path == NIL) nulls[2] = true; else { StringInfoData pathbuf; ListCell *lcv; initStringInfo(&pathbuf); /* The path doesn't include start vertex, but show it */ appendStringInfoString(&pathbuf, evi1->name); foreach(lcv, path) { char *versionName = (char *) lfirst(lcv); appendStringInfoString(&pathbuf, "--"); appendStringInfoString(&pathbuf, versionName); } values[2] = CStringGetTextDatum(pathbuf.data); pfree(pathbuf.data); } tuplestore_putvalues(tupstore, tupdesc, values, nulls); } } /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; }
static ExtensionControlFile* read_extension_aux_control_file | ( | const ExtensionControlFile * | pcontrol, | |
const char * | version | |||
) | [static] |
Definition at line 611 of file extension.c.
References palloc(), and parse_extension_control_file().
Referenced by ApplyExtensionUpdates(), CreateExtension(), and get_available_versions_for_extension().
{ ExtensionControlFile *acontrol; /* * Flat-copy the struct. Pointer fields share values with original. */ acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile)); memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile)); /* * Parse the auxiliary control file, overwriting struct fields */ parse_extension_control_file(acontrol, version); return acontrol; }
static ExtensionControlFile* read_extension_control_file | ( | const char * | extname | ) | [static] |
Definition at line 583 of file extension.c.
References ExtensionControlFile::encoding, ExtensionControlFile::name, NULL, palloc0(), parse_extension_control_file(), pstrdup(), ExtensionControlFile::relocatable, and ExtensionControlFile::superuser.
Referenced by CreateExtension(), ExecAlterExtensionStmt(), pg_available_extension_versions(), pg_available_extensions(), and pg_extension_update_paths().
{ ExtensionControlFile *control; /* * Set up default values. Pointer fields are initially null. */ control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile)); control->name = pstrdup(extname); control->relocatable = false; control->superuser = true; control->encoding = -1; /* * Parse the primary control file. */ parse_extension_control_file(control, NULL); return control; }
static char* read_extension_script_file | ( | const ExtensionControlFile * | control, | |
const char * | filename | |||
) | [static] |
Definition at line 634 of file extension.c.
References ExtensionControlFile::encoding, GetDatabaseEncoding(), palloc(), pg_do_encoding_conversion(), pg_verify_mbstr_len(), read_binary_file(), VARDATA_ANY, and VARSIZE_ANY_EXHDR.
Referenced by execute_extension_script().
{ int src_encoding; int dest_encoding = GetDatabaseEncoding(); bytea *content; char *src_str; char *dest_str; int len; content = read_binary_file(filename, 0, -1); /* use database encoding if not given */ if (control->encoding < 0) src_encoding = dest_encoding; else src_encoding = control->encoding; /* make sure that source string is valid in the expected encoding */ len = VARSIZE_ANY_EXHDR(content); src_str = VARDATA_ANY(content); pg_verify_mbstr_len(src_encoding, src_str, len, false); /* convert the encoding to the database encoding */ dest_str = (char *) pg_do_encoding_conversion((unsigned char *) src_str, len, src_encoding, dest_encoding); /* if no conversion happened, we have to arrange for null termination */ if (dest_str == src_str) { dest_str = (char *) palloc(len + 1); memcpy(dest_str, src_str, len); dest_str[len] = '\0'; } return dest_str; }
void RemoveExtensionById | ( | Oid | extId | ) |
Definition at line 1577 of file extension.c.
References BTEqualStrategyNumber, CurrentExtensionObject, ereport, errcode(), errmsg(), ERROR, ExtensionOidIndexId, ExtensionRelationId, get_extension_name(), heap_close, heap_open(), HeapTupleIsValid, ObjectIdAttributeNumber, ObjectIdGetDatum, RowExclusiveLock, ScanKeyInit(), simple_heap_delete(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.
Referenced by doDeletion().
{ Relation rel; SysScanDesc scandesc; HeapTuple tuple; ScanKeyData entry[1]; /* * Disallow deletion of any extension that's currently open for insertion; * else subsequent executions of recordDependencyOnCurrentExtension() * could create dangling pg_depend records that refer to a no-longer-valid * pg_extension OID. This is needed not so much because we think people * might write "DROP EXTENSION foo" in foo's own script files, as because * errors in dependency management in extension script files could give * rise to cases where an extension is dropped as a result of recursing * from some contained object. Because of that, we must test for the case * here, not at some higher level of the DROP EXTENSION command. */ if (extId == CurrentExtensionObject) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot drop extension \"%s\" because it is being modified", get_extension_name(extId)))); rel = heap_open(ExtensionRelationId, RowExclusiveLock); ScanKeyInit(&entry[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(extId)); scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, SnapshotNow, 1, entry); tuple = systable_getnext(scandesc); /* We assume that there can be at most one matching tuple */ if (HeapTupleIsValid(tuple)) simple_heap_delete(rel, &tuple->t_self); systable_endscan(scandesc); heap_close(rel, RowExclusiveLock); }
bool creating_extension = false |
Definition at line 60 of file extension.c.
Referenced by CreateExtension(), ExecAlterExtensionStmt(), execute_extension_script(), findDependentObjects(), pg_extension_config_dump(), and recordDependencyOnCurrentExtension().
Oid CurrentExtensionObject = InvalidOid |
Definition at line 61 of file extension.c.
Referenced by execute_extension_script(), findDependentObjects(), pg_extension_config_dump(), recordDependencyOnCurrentExtension(), and RemoveExtensionById().