/* 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);
/* *Testwhetherthegivenextensionexists(notwhetherit'sinstalled) * *Thischecksfortheexistenceofamatchingcontrolfileintheextension *directory.That'snotabulletproofcheck,sincethefilemightbe *invalid,butthisisonlyusedforhintssoitdoesn'thavetobe100% *right.
*/ bool
extension_file_exists(constchar *extensionName)
{ bool result = false;
List *locations;
DIR *dir; struct dirent *de;
locations = get_extension_control_directories();
foreach_ptr(char, location, locations)
{
dir = AllocateDir(location);
/* *Ifthecontroldirectorydoesn'texist,wewanttosilentlyreturn *false.AnyothererrorwillbereportedbyReadDir.
*/ if (dir == NULL && errno == ENOENT)
{ /* do nothing */
} else
{ while ((de = ReadDir(dir, location)) != NULL)
{ 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;
/* done if it matches request */ if (strcmp(extname, extensionName) == 0)
{
result = true; break;
}
}
FreeDir(dir);
} if (result) break;
}
return result;
}
/*
* Convert a list of extension names to a name[] Datum
*/
static Datum
convert_requires_to_datum(List *requires)
{
Datum *datums;
int ndatums;
ArrayType *a;
ListCell *lc;
/*
* This function reports the version update paths that exist for the
* specified extension.
*/
Datum
pg_extension_update_paths(PG_FUNCTION_ARGS)
{
Name extname = PG_GETARG_NAME(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
List *evi_list;
ExtensionControlFile *control;
ListCell *lc1;
/* Check extension name validity before any filesystem access */
check_valid_extension_name(NameStr(*extname));
/* Build tuplestore to hold the result rows */
InitMaterializedSRF(fcinfo, 0);
/* 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, false, true);
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
*
* Record information about a configuration table that belongs to an
* extension being created, but whose contents should be dumped in whole
* or in part during pg_dump.
*/
Datum
pg_extension_config_dump(PG_FUNCTION_ARGS)
{
Oid tableoid = PG_GETARG_OID(0);
text *wherecond = PG_GETARG_TEXT_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;
/*
* 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("%s can only be called from an SQL script executed by CREATE EXTENSION", "pg_extension_config_dump()")));
/*
* 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 = table_open(ExtensionRelationId, RowExclusiveLock);
/* 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;
/*
* pg_get_loaded_modules
*
* SQL-callable function to get per-loaded-module information. Modules
* (shared libraries) aren't necessarily one-to-one with extensions, but
* they're sufficiently closely related to make this file a good home.
*/
Datum
pg_get_loaded_modules(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
DynamicFileList *file_scanner;
/* Build tuplestore to hold the result rows */
InitMaterializedSRF(fcinfo, 0);
/* 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);
/*
* extension_config_remove
*
* Remove the specified table OID from extension's extconfig, if present.
* This is not currently exposed as a function, but it could be;
* for now, we just invoke it from ALTER EXTENSION DROP.
*/
static void
extension_config_remove(Oid extensionoid, Oid tableoid)
{
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 = table_open(ExtensionRelationId, RowExclusiveLock);
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 own extension. Note that we don't bother to
* check ownership of the individual member objects ...
*/
if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
extensionName);
/* 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);
/*
* 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 = table_open(ExtensionRelationId, RowExclusiveLock);
if (!HeapTupleIsValid(extTup)) /* should not happen */
elog(ERROR, "could not find tuple for extension %u",
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)
{
table_close(extRel, RowExclusiveLock);
return InvalidObjectAddress;
}
/* 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;
/*
* Scan pg_depend to find objects that depend directly on the extension,
* and alter each one's schema.
*/
depRel = table_open(DependRelationId, AccessShareLock);
while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
{
Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
ObjectAddress dep;
Oid dep_oldNspOid;
/*
* If a dependent extension has a no_relocate request for this
* extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
* the same loop that's actually executing the renames: we may detect
* the error condition only after having expended a fair amount of
* work. However, the alternative is to do two scans of pg_depend,
* which seems like optimizing for failure cases. The rename work
* will all roll back cleanly enough if we do fail here.)
*/
if (pg_depend->deptype == DEPENDENCY_NORMAL &&
pg_depend->classid == ExtensionRelationId)
{
char *depextname = get_extension_name(pg_depend->objid);
ExtensionControlFile *dcontrol;
ListCell *lc;
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, 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;
/*
* If not all the objects had the same old namespace (ignoring any
* that are not in namespaces or are dependent types), 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));
/*
* 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 = table_open(ExtensionRelationId, AccessShareLock);
/*
* 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);
table_close(extRel, AccessShareLock);
/* Permission check: must own extension */
if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_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);
/*
* 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 InvalidObjectAddress;
}
/*
* 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,
NULL, false, false);
/*
* Apply a series of update scripts as though individual ALTER EXTENSION
* UPDATE commands had been given, including altering the pg_extension row
* and dependencies each time.
*
* This might be more work than necessary, but it ensures that old update
* scripts don't break if newer versions have different control parameters.
*/
static void
ApplyExtensionUpdates(Oid extensionOid,
ExtensionControlFile *pcontrol,
const char *initialVersion,
List *updateVersions,
char *origSchemaName,
bool cascade,
bool is_create)
{
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 = table_open(ExtensionRelationId, RowExclusiveLock);
/*
* Look up the prerequisite extensions for this version, install them
* if necessary, 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;
/*
* 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;
}
}
/*
* Execute ALTER EXTENSION ADD/DROP
*
* Return value is the address of the altered extension.
*
* objAddr is an output argument which, if not NULL, is set to the address of
* the added/dropped object.
*/
ObjectAddress
ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
ObjectAddress *objAddr)
{
ObjectAddress extension;
ObjectAddress object;
Relation relation;
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;
}
/*
* Find the extension and acquire a lock on it, to ensure it doesn't get
* dropped concurrently. A sharable lock seems sufficient: there's no
* reason not to allow other sorts of manipulations, such as add/drop of
* other objects, to occur concurrently. Concurrently adding/dropping the
* *same* object would be bad, but we prevent that by using a non-sharable
* lock on the individual object, below.
*/
extension = get_object_address(OBJECT_EXTENSION,
(Node *) makeString(stmt->extname),
&relation, AccessShareLock, false);
/* Permission check: must own extension */
if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_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->object,
&relation, ShareUpdateExclusiveLock, false);
Assert(object.objectSubId == 0);
if (objAddr)
*objAddr = object;
/* Permission check: must own target object, too */
check_object_ownership(GetUserId(), stmt->objtype, object,
stmt->object, relation);
/* Do the update, recursing to any dependent objects */
ExecAlterExtensionContentsRecurse(stmt, extension, object);
/* Finish up */
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;
}
/*
* ExecAlterExtensionContentsRecurse
* Subroutine for ExecAlterExtensionContentsStmt
*
* Do the bare alteration of object's membership in extension,
* without permission checks. Recurse to dependent objects, if any.
*/
static void
ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
ObjectAddress extension,
ObjectAddress object)
{
Oid oldExtension;
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, false),
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);
/*
* Also record the initial ACL on the object, if any.
*
* Note that this will handle the object's ACLs, as well as any ACLs
* on object subIds. (In other words, when the object is a table,
* this will record the table's ACL and the ACLs for the columns on
* the table, if any).
*/
recordExtObjInitPriv(object.objectId, object.classId);
}
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, false),
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);
/*
* Remove all the initial ACLs, if any.
*
* Note that this will remove the object's ACLs, as well as any ACLs
* on object subIds. (In other words, when the object is a table,
* this will remove the table's ACL and the ACLs for the columns on
* the table, if any).
*/
removeExtObjInitPriv(object.objectId, object.classId);
}
/*
* Recurse to any dependent objects; currently, this includes the array
* type of a base type, the multirange type associated with a range type,
* and the rowtype of a table.
*/
if (object.classId == TypeRelationId)
{
ObjectAddress depobject;
/* 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);
}
}
/*
* Read the whole of file into memory.
*
* The file contents are returned as a single palloc'd chunk. For convenience
* of the callers, an extra \0 byte is added to the end. That is not counted
* in the length returned into *length.
*/
static char *
read_whole_file(const char *filename, int *length)
{
char *buf;
FILE *file;
size_t bytes_to_read;
struct stat fst;
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)));
if (ferror(file))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m", filename)));
FreeFile(file);
buf[bytes_to_read] = '\0';
/*
* On Windows, manually convert Windows-style newlines (\r\n) to the Unix
* convention of \n only. This avoids gotchas due to script files
* possibly getting converted when being transferred between platforms.
* Ideally we'd do this by using text mode to read the file, but that also
* causes control-Z to be treated as end-of-file. Historically we've
* allowed control-Z in script files, so breaking that seems unwise.
*/
#ifdef WIN32
{
char *s,
*d;
for (s = d = buf; *s; s++)
{
if (!(*s == '\r' && s[1] == '\n'))
*d++ = *s;
}
*d = '\0';
bytes_to_read = d - buf;
}
#endif
*length = bytes_to_read;
return buf;
}
static ExtensionControlFile *
new_ExtensionControlFile(const char *extname)
{
/*
* Set up default values. Pointer fields are initially null.
*/
ExtensionControlFile *control = palloc0_object(ExtensionControlFile);
/*
* Work in a very similar way with find_in_path but it receives an already
* parsed List of paths to search the basename and it do not support macro
* replacement or custom error messages (for simplicity).
*
* By "already parsed List of paths" this function expected that paths already
* have all macros replaced.
*/
char *
find_in_paths(const char *basename, List *paths)
{
ListCell *cell;
/* 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
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.127Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.