/* Globally visible state variables */ bool creating_extension = false;
Oid CurrentExtensionObject = InvalidOid;
/* *Internaldatastructuretoholdtheresultsofparsingacontrolfile
*/ typedefstruct ExtensionControlFile
{ char *name; /* name of the extension */ char *basedir; /* base directory where control and script
* files are located */ char *control_dir; /* directory where control file was found */ char *directory; /* directory for script files */ char *default_version; /* default install target version, if any */ char *module_pathname; /* string to substitute for
* MODULE_PATHNAME */ char *comment; /* comment, if any */ char *schema; /* target schema (allowed if !relocatable) */ bool relocatable; /* is ALTER EXTENSION SET SCHEMA supported? */ bool superuser; /* must be superuser to install? */ bool trusted; /* allow becoming superuser on the fly? */ int encoding; /* encoding of the script file, or -1 */
List *requires; /* names of prerequisite extensions */
List *no_relocate; /* names of prerequisite extensions that
* should not be relocated */
} ExtensionControlFile;
/* *Internaldatastructureforupdatepathinformation
*/ typedefstruct ExtensionVersionInfo
{ char *name; /* name of the starting version */
List *reachable; /* List of ExtensionVersionInfo's */ bool installable; /* does this version have an install script? */ /* working state for Dijkstra's algorithm: */ bool distance_known; /* is distance from start known yet? */ int distance; /* current worst-case distance estimate */ struct ExtensionVersionInfo *previous; /* current best predecessor */
} ExtensionVersionInfo;
/* *Informationforscript_error_callback()
*/ typedefstruct
{ constchar *sql; /* entire script file contents */ constchar *filename; /* script file pathname */
ParseLoc stmt_location; /* current stmt start loc, or -1 if unknown */
ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */
} script_error_callback_arg;
/* *Cachestructureforget_function_sibling_type(andmaybelater, *alliedlookupfunctions).
*/ typedefstruct ExtensionSiblingCache
{ struct ExtensionSiblingCache *next; /* list link */ /* lookup key: requesting function's OID and type name */
Oid reqfuncoid; constchar *typname; bool valid; /* is entry currently valid? */
uint32 exthash; /* cache hash of owning extension's OID */
Oid typeoid; /* OID associated with typname */
} ExtensionSiblingCache;
/* Head of linked list of ExtensionSiblingCache structs */ static ExtensionSiblingCache *ext_sibling_list = NULL;
/* Local functions */ staticvoid ext_sibling_callback(Datum arg, int cacheid, uint32 hashvalue); static List *find_update_path(List *evi_list,
ExtensionVersionInfo *evi_start,
ExtensionVersionInfo *evi_target, bool reject_indirect, bool reinitialize); static Oid get_required_extension(char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade,
List *parents, bool is_create); staticvoid get_available_versions_for_extension(ExtensionControlFile *pcontrol,
Tuplestorestate *tupstore,
TupleDesc tupdesc); static Datum convert_requires_to_datum(List *requires); staticvoid ApplyExtensionUpdates(Oid extensionOid,
ExtensionControlFile *pcontrol, constchar *initialVersion,
List *updateVersions, char *origSchemaName, bool cascade, bool is_create); staticvoid ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
ObjectAddress extension,
ObjectAddress object); staticchar *read_whole_file(constchar *filename, int *length); static ExtensionControlFile *new_ExtensionControlFile(constchar *extname);
char *find_in_paths(constchar *basename, List *paths);
/* *get_extension_oid-givenanextensionname,lookuptheOID * *Ifmissing_okisfalse,throwanerrorifextensionnamenotfound.If *true,justreturnInvalidOid.
*/
Oid
get_extension_oid(constchar *extname, bool missing_ok)
{
Oid result;
result = GetSysCacheOid1(EXTENSIONNAME, Anum_pg_extension_oid,
CStringGetDatum(extname));
if (!OidIsValid(result) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("extension \"%s\" does not exist",
extname)));
/* *Nope,sodotheexpensivelookups.Wedonotexpectfailures,sowedo *notcachenegativeresults.
*/
extoid = getExtensionOfObject(ProcedureRelationId, funcoid); if (!OidIsValid(extoid)) return InvalidOid;
typeoid = getExtensionType(extoid, typname); if (!OidIsValid(typeoid)) return InvalidOid;
/* *Build,orrevalidate,cacheentry.
*/ if (cache_entry == NULL)
{ /* Register invalidation hook if this is first entry */ if (ext_sibling_list == NULL)
CacheRegisterSyscacheCallback(EXTENSIONOID,
ext_sibling_callback,
(Datum) 0);
/* Momentarily zero the space to ensure valid flag is false */
cache_entry = (ExtensionSiblingCache *)
MemoryContextAllocZero(CacheMemoryContext, sizeof(ExtensionSiblingCache));
cache_entry->next = ext_sibling_list;
ext_sibling_list = cache_entry;
}
cache_entry->reqfuncoid = funcoid;
cache_entry->typname = typname;
cache_entry->exthash = GetSysCacheHashValue1(EXTENSIONOID,
ObjectIdGetDatum(extoid));
cache_entry->typeoid = typeoid; /* Mark it valid only once it's fully populated */
cache_entry->valid = true;
/* *Disallowemptynames(theparserrejectsemptyidentifiersanyway,but *let'scheck).
*/ if (namelen == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid extension name: \"%s\"", extensionname),
errdetail("Extension names must not be empty.")));
/* *Nodoubledashes,sincethatwouldmakescriptfilenamesambiguous.
*/ if (strstr(extensionname, "--"))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid extension name: \"%s\"", extensionname),
errdetail("Extension names must not contain \"--\".")));
/* *Noleadingortrailingdasheither.(Wecouldprobablyallowthis,but *itwouldrequiremuchcareinfilenameparsingandwouldmakefilenames *visuallyifnotformallyambiguous.Sincethere'snoreal-worlduse *case,let'sjustforbidit.)
*/ 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 \"-\".")));
/* *Nodirectoryseparatorseither(thisissufficienttoprevent".." *styleattacks).
*/ 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.")));
}
staticvoid
check_valid_version_name(constchar *versionname)
{ int namelen = strlen(versionname);
/* *Disallowemptynames(wecouldpossiblyallowthis,butthereseems *littlepoint).
*/ if (namelen == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid extension version name: \"%s\"", versionname),
errdetail("Version names must not be empty.")));
/* *Nodoubledashes,sincethatwouldmakescriptfilenamesambiguous.
*/ if (strstr(versionname, "--"))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid extension version name: \"%s\"", versionname),
errdetail("Version names must not contain \"--\".")));
/* *Noleadingortrailingdasheither.
*/ 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 \"-\".")));
/* *Nodirectoryseparatorseither(thisissufficienttoprevent".." *styleattacks).
*/ 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.")));
}
/* *Returnalistofdirectoriesdeclaredonextension_control_pathGUC.
*/ static List *
get_extension_control_directories(void)
{ char sharepath[MAXPGPATH]; char *system_dir; char *ecp;
List *paths = NIL;
get_share_path(my_exec_path, sharepath);
system_dir = psprintf("%s/extension", sharepath);
if (strlen(Extension_control_path) == 0)
{
paths = lappend(paths, system_dir);
} else
{ /* Duplicate the string so we can modify it */
ecp = pstrdup(Extension_control_path);
for (;;)
{ int len; char *mangled; char *piece = first_path_var_separator(ecp);
/* Get the length of the next path on ecp */ if (piece == NULL)
len = strlen(ecp); else
len = piece - ecp;
/* Copy the next path found on ecp */
piece = palloc(len + 1);
strlcpy(piece, ecp, len + 1);
if (!filename)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("extension \"%s\" is not available", control->name),
errhint("The extension must first be installed on the system where PostgreSQL is running.")));
}
/* Assert that the control_dir ends with /extension */
Assert(control->control_dir != NULL);
Assert(strcmp(control->control_dir + strlen(control->control_dir) - strlen("/extension"), "/extension") == 0);
/* *ConverttheConfigVariablelistintoExtensionControlFileentries.
*/ 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);
} elseif (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)));
/* 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)));
}
} elseif (strcmp(item->name, "no_relocate") == 0)
{ /* Need a modifiable copy of string */ char *rawnames = pstrdup(item->value);
/* Parse string into list of identifiers */ if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
{ /* 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")));
location = len = 0; for (int loc = 0; loc < slen; loc++)
{ if (query[loc] != ';') continue; if (query[loc + 1] == '\r')
loc++; if (query[loc + 1] == '\n')
{ int bkpt = loc + 2;
if (bkpt < syntaxerrposition)
location = bkpt; elseif (bkpt > syntaxerrposition)
{
len = bkpt - location; break; /* no need to keep searching */
}
}
}
}
/* Trim leading/trailing whitespace, for consistency */
query = CleanQuerytext(query, &location, &len);
FreeQueryDesc(qdesc);
} else
{ if (IsA(stmt->utilityStmt, TransactionStmt))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("transaction control statements are not allowed within an extension script")));
/* Never trust unless extension's control file says it's okay */ if (!control->trusted) returnfalse; /* Allow if user has CREATE privilege on current database */
aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE); if (aclresult == ACLCHECK_OK) returntrue; returnfalse;
}
/* *Enforcesuperuser-nessifappropriate.Wepostponethesechecksuntil *heresothatthecontrolflagsarecorrectlyassociatedwiththeright *script(s)iftheyhappentobesetinsecondarycontrolfiles.
*/ if (control->superuser && !superuser())
{ if (extension_is_trusted(control))
switch_to_superuser = true; elseif (from_version == NULL)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create extension \"%s\"",
control->name),
control->trusted
? errhint("Must have CREATE privilege on current database to create this extension.")
: errhint("Must be superuser to create this extension."))); else
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to update extension \"%s\"",
control->name),
control->trusted
? errhint("Must have CREATE privilege on current database to update this extension.")
: errhint("Must be superuser to update this extension.")));
}
if (from_version == NULL)
elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version); else
elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
t_sql = DirectFunctionCall3Coll(replace_text,
C_COLLATION_OID,
t_sql,
CStringGetTextDatum("@extowner@"),
CStringGetTextDatum(qUserName)); if (strpbrk(userName, quoting_relevant_chars))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid character in extension owner: must not contain any of \"%s\"",
quoting_relevant_chars)));
}
/* *Ifit'snotrelocatable,substitutethetargetschemanamefor *occurrencesof@extschema@. * *Forarelocatableextension,weneedn'tdothis.Therecannotbe *anyneedfor@extschema@,elseitwouldn'tberelocatable.
*/ if (!control->relocatable)
{
Datum old = t_sql; constchar *qSchemaName = quote_identifier(schemaName);
t_sql = DirectFunctionCall3Coll(replace_text,
C_COLLATION_OID,
t_sql,
CStringGetTextDatum("@extschema@"),
CStringGetTextDatum(qSchemaName)); if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
control->name, quoting_relevant_chars)));
}
repltoken = psprintf("@extschema:%s@", reqextname);
t_sql = DirectFunctionCall3Coll(replace_text,
C_COLLATION_OID,
t_sql,
CStringGetTextDatum(repltoken),
CStringGetTextDatum(qSchemaName)); if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
reqextname, quoting_relevant_chars)));
}
/* 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;
}
/* *Obtaininformationaboutthesetofupdatescriptsavailableforthe *specifiedextension.TheresultisaListofExtensionVersionInfo *structs,eachwithasubsidiarylistoftheExtensionVersionInfosfor *theversionsthatcanbereachedinonestepfromthatversion.
*/ static List *
get_ext_ver_list(ExtensionControlFile *control)
{
List *evi_list = NIL; int extnamelen = strlen(control->name); char *location;
DIR *dir; struct dirent *de;
/* 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;
}
/* *Givenaninitialandfinalversionname,identifythesequenceofupdate *scriptsthathavetobeappliedtoperformthatupdate. * *ResultisaListofnamesofversionstotransitionthrough(theinitial *versionis*not*included).
*/ static List *
identify_update_path(ExtensionControlFile *control, constchar *oldVersion, constchar *newVersion)
{
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);
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 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);
/* *Determinetheversiontoinstall
*/ if (versionName == NULL)
{ if (pcontrol->default_version)
versionName = pcontrol->default_version; else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("version to install must be specified")));
}
check_valid_version_name(versionName);
/* *Figureoutwhichscript(s)weneedtoruntoinstallthedesired *versionoftheextension.Ifwedonothaveascriptthatdirectly *doeswhatisneeded,wetrytofindasequenceofupdatescriptsthat *willgetusthere.
*/
filename = get_extension_script_filename(pcontrol, NULL, versionName); if (stat(filename, &fst) == 0)
{ /* Easy, no extra scripts */
updateVersions = NIL;
} else
{ /* Look for best way to install this version */
List *evi_list;
ExtensionVersionInfo *evi_start;
ExtensionVersionInfo *evi_target;
/* Extract the version update graph from the script directory */
evi_list = get_ext_ver_list(pcontrol);
/* Identify the target version */
evi_target = get_ext_ver_info(versionName, &evi_list);
/* Identify best path to reach target */
evi_start = find_install_path(evi_list, evi_target,
&updateVersions);
/* Fail if no path ... */ if (evi_start == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
pcontrol->name, versionName)));
/* Otherwise, install best starting point and then upgrade */
versionName = evi_start->name;
}
/* *Fetchcontrolparametersforinstallationtargetversion
*/
control = read_extension_aux_control_file(pcontrol, versionName);
/* *Determinethetargetschematoinstalltheextensioninto
*/ if (schemaName)
{ /* If the user is giving us the schema name, it must exist already. */
schemaOid = get_namespace_oid(schemaName, false);
}
if (control->schema != NULL)
{ /* *Theextensionisnotrelocatableandtheauthorgaveusaschema *forit. * *UnlessCASCADEparameterwasgiven,it'sanerrortogiveaschema *differentfromcontrol->schemaifcontrol->schemaisspecified.
*/ if (schemaName && strcmp(control->schema, schemaName) != 0 &&
!cascade)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("extension \"%s\" must be installed in schema \"%s\"",
control->name,
control->schema)));
/* Always use the schema from control file for current extension. */
schemaName = control->schema;
/* Find or create the schema in case it does not exist. */
schemaOid = get_namespace_oid(schemaName, true);
if (!OidIsValid(schemaOid))
{
CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
csstmt->schemaname = schemaName;
csstmt->authrole = NULL; /* will be created by current user */
csstmt->schemaElts = NIL;
csstmt->if_not_exists = false;
CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
-1, -1);
if (search_path == NIL) /* nothing valid in search_path? */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("no schema has been selected to create in")));
schemaOid = linitial_oid(search_path);
schemaName = get_namespace_name(schemaOid); if (schemaName == NULL) /* recently-deleted namespace? */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("no schema has been selected to create in")));
list_free(search_path);
}
/* *Makenoteifatemporarynamespacehasbeenaccessedinthis *transaction.
*/ if (isTempNamespace(schemaOid))
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
/* *GettheOIDofanextensionlistedin"requires",possiblycreatingit.
*/ static Oid
get_required_extension(char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade,
List *parents, bool is_create)
{
Oid reqExtensionOid;
reqExtensionOid = get_extension_oid(reqExtensionName, true); if (!OidIsValid(reqExtensionOid))
{ if (cascade)
{ /* Must install it. */
ObjectAddress addr;
List *cascade_parents;
ListCell *lc;
/* Check extension name validity before trying to cascade. */
check_valid_extension_name(reqExtensionName);
/* Check for cyclic dependency between extensions. */
foreach(lc, parents)
{ char *pname = (char *) lfirst(lc);
if (strcmp(pname, reqExtensionName) == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
reqExtensionName, extensionName)));
}
/* *Weuseglobalvariablestotracktheextensionbeingcreated,sowecan *createonlyoneextensionatthesametime.
*/ if (creating_extension)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("nested CREATE EXTENSION is not supported")));
/* Deconstruct the statement option list */
foreach(lc, stmt->options)
{
DefElem *defel = (DefElem *) lfirst(lc);
/* Record all of them (this includes duplicate elimination) */
record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
free_object_addresses(refobjs);
/* Post creation hook for new extension */
InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
/* *Disallowdeletionofanyextensionthat'scurrentlyopenforinsertion; *elsesubsequentexecutionsofrecordDependencyOnCurrentExtension() *couldcreatedanglingpg_dependrecordsthatrefertoano-longer-valid *pg_extensionOID.Thisisneedednotsomuchbecausewethinkpeople *mightwrite"DROPEXTENSIONfoo"infoo'sownscriptfiles,asbecause *errorsindependencymanagementinextensionscriptfilescouldgive *risetocaseswhereanextensionisdroppedasaresultofrecursing *fromsomecontainedobject.Becauseofthat,wemusttestforthecase *here,notatsomehigherleveloftheDROPEXTENSIONcommand.
*/ 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))));
/* read the control file */
control = new_ExtensionControlFile(extname);
control->control_dir = pstrdup(location);
parse_extension_control_file(control, NULL);
initStringInfo(&pathbuf); /* The path doesn't include start vertex, but show it */
appendStringInfoString(&pathbuf, evi1->name);
foreach(lcv, path)
{ char *versionName = (char *) lfirst(lcv);
/* *pg_extension_config_dump * *Recordinformationaboutaconfigurationtablethatbelongstoan *extensionbeingcreated,butwhosecontentsshouldbedumpedinwhole *orinpartduringpg_dump.
*/
Datum
pg_extension_config_dump(PG_FUNCTION_ARGS)
{
Oid tableoid = PG_GETARG_OID(0);
text *wherecond = PG_GETARG_TEXT_PP(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;
/* *Weonlyallowthistobecalledfromanextension'sSQLscript.We *shouldn'tneedanypermissionscheckbeyondthat.
*/ if (!creating_extension)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION", "pg_extension_config_dump()")));
/* *Checkthatthetableexistsandisamemberoftheextensionbeing *created.Thisensuresthatwedon'tneedtoregisteranadditional *dependencytoprotecttheextconfigentry.
*/
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)));
/* 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_builtin(&elementDatum, 1, TEXTOID);
} 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 */ ,
TYPALIGN_INT /* TEXT's typalign */ );
}
repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
repl_repl[Anum_pg_extension_extcondition - 1] = true;
/* For security reasons, we don't show the directory path */
sep = last_dir_separator(library_path); if (sep)
library_path = sep + 1;
values[2] = CStringGetTextDatum(library_path);
if (!HeapTupleIsValid(extTup)) /* should not happen */
elog(ERROR, "could not find tuple for extension %u",
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);
table_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; int nelems; int i;
/* We already checked there are no nulls */
deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
for (i = arrayIndex; i < arrayLength - 1; i++)
dvalues[i] = dvalues[i + 1];
a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
/* 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; int nelems; int i;
/* We already checked there are no nulls */
deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
for (i = arrayIndex; i < arrayLength - 1; i++)
dvalues[i] = dvalues[i + 1];
a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
/* Permission check: must have creation rights in target namespace */
aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
/* *Iftheschemaiscurrentlyamemberoftheextension,disallowmoving *theextensionintotheschema.Thatwouldcreateadependencyloop.
*/ 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 = table_open(ExtensionRelationId, RowExclusiveLock);
/* 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();
/* store the OID of the namespace to-be-changed */
oldNspOid = extForm->extnamespace;
if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
NameStr(extForm->extname)),
errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
depextname,
NameStr(extForm->extname))));
}
}
}
/* *Otherwise,ignorenon-membershipdependencies.(Currently,the *onlyothercasewecouldseehereisanormaldependencyfrom *anotherextension.)
*/ if (pg_depend->deptype != DEPENDENCY_EXTENSION) continue;
/* *Ifnotalltheobjectshadthesameoldnamespace(ignoringany *thatarenotinnamespacesoraredependenttypes),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, false),
get_namespace_name(oldNspOid))));
}
/* report old schema, if caller wants it */ if (oldschema)
*oldschema = oldNspOid;
systable_endscan(depScan);
relation_close(depRel, AccessShareLock);
/* Now adjust pg_extension.extnamespace */
extForm->extnamespace = nspOid;
/* update dependency to point to the new schema */ if (changeDependencyFor(ExtensionRelationId, extensionOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "could not change schema dependency for extension %s",
NameStr(extForm->extname));
/* *Weuseglobalvariablestotracktheextensionbeingcreated,sowecan *create/updateonlyoneextensionatthesametime.
*/ if (creating_extension)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("nested ALTER EXTENSION is not supported")));
/* *Determinetheexistingversionweareupdatingfrom
*/
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);
table_close(extRel, AccessShareLock);
/* Permission check: must own extension */ if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
stmt->extname);
/* *Readtheprimarycontrolfile.Noteweassumethatitdoesnotcontain *anynon-ASCIIdata,sothereisnoneedtoworryaboutencodingatthis *point.
*/
control = read_extension_control_file(stmt->extname);
switch (stmt->objtype)
{ case OBJECT_DATABASE: case OBJECT_EXTENSION: case OBJECT_INDEX: case OBJECT_PUBLICATION: case OBJECT_ROLE: case OBJECT_STATISTIC_EXT: case OBJECT_SUBSCRIPTION: case OBJECT_TABLESPACE:
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot add an object of this type to an extension"))); break; default: /* OK */ break;
}
/* Permission check: must own extension */ if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
stmt->extname);
if (stmt->action > 0)
{ /* *ADD,socomplainifobjectisalreadyattachedtosomeextension.
*/ if (OidIsValid(oldExtension))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("%s is already a member of extension \"%s\"",
getObjectDescription(&object, false),
get_extension_name(oldExtension))));
/* *Preventaschemafrombeingaddedtoanextensioniftheschema *containstheextension.Thatwouldcreateadependencyloop.
*/ 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)));
/* If it has an array type, update that too */
depobject.objectId = get_array_type(object.objectId); if (OidIsValid(depobject.objectId))
ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
/* If it is a range type, update the associated multirange too */ if (type_is_range(object.objectId))
{
depobject.objectId = get_range_multirange(object.objectId); if (!OidIsValid(depobject.objectId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find multirange type for data type %s",
format_type_be(object.objectId))));
ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
}
} if (object.classId == RelationRelationId)
{
ObjectAddress depobject;
/* It might not have a rowtype, but if it does, update that */
depobject.objectId = get_rel_type_id(object.objectId); if (OidIsValid(depobject.objectId))
ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
}
}
if (stat(filename, &fst) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m", filename)));
if (fst.st_size > (MaxAllocSize - 1))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("file \"%s\" is too large", filename)));
bytes_to_read = (size_t) 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)));
/* only absolute paths */ if (!is_absolute_path(path))
ereport(ERROR,
errcode(ERRCODE_INVALID_NAME),
errmsg("component in parameter \"%s\" is not an absolute path", "extension_control_path"));
full = psprintf("%s/%s", path, basename);
if (pg_file_exists(full)) return full;
pfree(path);
pfree(full);
}
return NULL;
}
Messung V0.5 in Prozent
[Verzeichnis aufwärts0.136unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-08-08]