PostgreSQL Source Code git master
extension.c File Reference
#include "postgres.h"
#include <dirent.h>
#include <limits.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_database.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/defrem.h"
#include "commands/extension.h"
#include "commands/schemacmds.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "nodes/queryjumble.h"
#include "storage/fd.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/conffiles.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/varlena.h"
Include dependency graph for extension.c:

Go to the source code of this file.

Data Structures

struct  ExtensionControlFile
 
struct  ExtensionVersionInfo
 
struct  script_error_callback_arg
 

Typedefs

typedef struct ExtensionControlFile ExtensionControlFile
 
typedef struct ExtensionVersionInfo ExtensionVersionInfo
 

Functions

static Listfind_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)
 
static void get_available_versions_for_extension (ExtensionControlFile *pcontrol, Tuplestorestate *tupstore, TupleDesc tupdesc)
 
static Datum convert_requires_to_datum (List *requires)
 
static void ApplyExtensionUpdates (Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
 
static void ExecAlterExtensionContentsRecurse (AlterExtensionContentsStmt *stmt, ObjectAddress extension, ObjectAddress object)
 
static char * read_whole_file (const char *filename, int *length)
 
static ExtensionControlFilenew_ExtensionControlFile (const char *extname)
 
char * find_in_paths (const char *basename, List *paths)
 
Oid get_extension_oid (const char *extname, bool missing_ok)
 
char * get_extension_name (Oid ext_oid)
 
Oid get_extension_schema (Oid ext_oid)
 
static void check_valid_extension_name (const char *extensionname)
 
static void check_valid_version_name (const char *versionname)
 
static bool is_extension_control_filename (const char *filename)
 
static bool is_extension_script_filename (const char *filename)
 
static Listget_extension_control_directories (void)
 
static char * find_extension_control_filename (ExtensionControlFile *control)
 
static char * get_extension_script_directory (ExtensionControlFile *control)
 
static char * get_extension_aux_control_filename (ExtensionControlFile *control, const char *version)
 
static char * get_extension_script_filename (ExtensionControlFile *control, const char *from_version, const char *version)
 
static void parse_extension_control_file (ExtensionControlFile *control, const char *version)
 
static ExtensionControlFileread_extension_control_file (const char *extname)
 
static ExtensionControlFileread_extension_aux_control_file (const ExtensionControlFile *pcontrol, const char *version)
 
static char * read_extension_script_file (const ExtensionControlFile *control, const char *filename)
 
static void script_error_callback (void *arg)
 
static void execute_sql_string (const char *sql, const char *filename)
 
static bool extension_is_trusted (ExtensionControlFile *control)
 
static void execute_extension_script (Oid extensionOid, ExtensionControlFile *control, const char *from_version, const char *version, List *requiredSchemas, const char *schemaName)
 
static ExtensionVersionInfoget_ext_ver_info (const char *versionname, List **evi_list)
 
static ExtensionVersionInfoget_nearest_unprocessed_vertex (List *evi_list)
 
static Listget_ext_ver_list (ExtensionControlFile *control)
 
static Listidentify_update_path (ExtensionControlFile *control, const char *oldVersion, const char *newVersion)
 
static ExtensionVersionInfofind_install_path (List *evi_list, ExtensionVersionInfo *evi_target, List **best_path)
 
static ObjectAddress CreateExtensionInternal (char *extensionName, char *schemaName, const char *versionName, bool cascade, List *parents, bool is_create)
 
ObjectAddress CreateExtension (ParseState *pstate, CreateExtensionStmt *stmt)
 
ObjectAddress InsertExtensionTuple (const char *extName, Oid extOwner, Oid schemaOid, bool relocatable, const char *extVersion, Datum extConfig, Datum extCondition, List *requiredExtensions)
 
void RemoveExtensionById (Oid extId)
 
Datum pg_available_extensions (PG_FUNCTION_ARGS)
 
Datum pg_available_extension_versions (PG_FUNCTION_ARGS)
 
bool extension_file_exists (const char *extensionName)
 
Datum pg_extension_update_paths (PG_FUNCTION_ARGS)
 
Datum pg_extension_config_dump (PG_FUNCTION_ARGS)
 
Datum pg_get_loaded_modules (PG_FUNCTION_ARGS)
 
static void extension_config_remove (Oid extensionoid, Oid tableoid)
 
ObjectAddress AlterExtensionNamespace (const char *extensionName, const char *newschema, Oid *oldschema)
 
ObjectAddress ExecAlterExtensionStmt (ParseState *pstate, AlterExtensionStmt *stmt)
 
ObjectAddress ExecAlterExtensionContentsStmt (AlterExtensionContentsStmt *stmt, ObjectAddress *objAddr)
 

Variables

char * Extension_control_path
 
bool creating_extension = false
 
Oid CurrentExtensionObject = InvalidOid
 

Typedef Documentation

◆ ExtensionControlFile

◆ ExtensionVersionInfo

Function Documentation

◆ AlterExtensionNamespace()

ObjectAddress AlterExtensionNamespace ( const char *  extensionName,
const char *  newschema,
Oid oldschema 
)

Definition at line 3042 of file extension.c.

3043{
3044 Oid extensionOid;
3045 Oid nspOid;
3046 Oid oldNspOid;
3047 AclResult aclresult;
3048 Relation extRel;
3049 ScanKeyData key[2];
3050 SysScanDesc extScan;
3051 HeapTuple extTup;
3052 Form_pg_extension extForm;
3053 Relation depRel;
3054 SysScanDesc depScan;
3055 HeapTuple depTup;
3056 ObjectAddresses *objsMoved;
3057 ObjectAddress extAddr;
3058
3059 extensionOid = get_extension_oid(extensionName, false);
3060
3061 nspOid = LookupCreationNamespace(newschema);
3062
3063 /*
3064 * Permission check: must own extension. Note that we don't bother to
3065 * check ownership of the individual member objects ...
3066 */
3067 if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3069 extensionName);
3070
3071 /* Permission check: must have creation rights in target namespace */
3072 aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
3073 if (aclresult != ACLCHECK_OK)
3074 aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
3075
3076 /*
3077 * If the schema is currently a member of the extension, disallow moving
3078 * the extension into the schema. That would create a dependency loop.
3079 */
3080 if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
3081 ereport(ERROR,
3082 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3083 errmsg("cannot move extension \"%s\" into schema \"%s\" "
3084 "because the extension contains the schema",
3085 extensionName, newschema)));
3086
3087 /* Locate the pg_extension tuple */
3088 extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3089
3090 ScanKeyInit(&key[0],
3091 Anum_pg_extension_oid,
3092 BTEqualStrategyNumber, F_OIDEQ,
3093 ObjectIdGetDatum(extensionOid));
3094
3095 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3096 NULL, 1, key);
3097
3098 extTup = systable_getnext(extScan);
3099
3100 if (!HeapTupleIsValid(extTup)) /* should not happen */
3101 elog(ERROR, "could not find tuple for extension %u",
3102 extensionOid);
3103
3104 /* Copy tuple so we can modify it below */
3105 extTup = heap_copytuple(extTup);
3106 extForm = (Form_pg_extension) GETSTRUCT(extTup);
3107
3108 systable_endscan(extScan);
3109
3110 /*
3111 * If the extension is already in the target schema, just silently do
3112 * nothing.
3113 */
3114 if (extForm->extnamespace == nspOid)
3115 {
3117 return InvalidObjectAddress;
3118 }
3119
3120 /* Check extension is supposed to be relocatable */
3121 if (!extForm->extrelocatable)
3122 ereport(ERROR,
3123 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3124 errmsg("extension \"%s\" does not support SET SCHEMA",
3125 NameStr(extForm->extname))));
3126
3127 objsMoved = new_object_addresses();
3128
3129 /* store the OID of the namespace to-be-changed */
3130 oldNspOid = extForm->extnamespace;
3131
3132 /*
3133 * Scan pg_depend to find objects that depend directly on the extension,
3134 * and alter each one's schema.
3135 */
3136 depRel = table_open(DependRelationId, AccessShareLock);
3137
3138 ScanKeyInit(&key[0],
3139 Anum_pg_depend_refclassid,
3140 BTEqualStrategyNumber, F_OIDEQ,
3141 ObjectIdGetDatum(ExtensionRelationId));
3142 ScanKeyInit(&key[1],
3143 Anum_pg_depend_refobjid,
3144 BTEqualStrategyNumber, F_OIDEQ,
3145 ObjectIdGetDatum(extensionOid));
3146
3147 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
3148 NULL, 2, key);
3149
3150 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
3151 {
3152 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
3153 ObjectAddress dep;
3154 Oid dep_oldNspOid;
3155
3156 /*
3157 * If a dependent extension has a no_relocate request for this
3158 * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
3159 * the same loop that's actually executing the renames: we may detect
3160 * the error condition only after having expended a fair amount of
3161 * work. However, the alternative is to do two scans of pg_depend,
3162 * which seems like optimizing for failure cases. The rename work
3163 * will all roll back cleanly enough if we do fail here.)
3164 */
3165 if (pg_depend->deptype == DEPENDENCY_NORMAL &&
3166 pg_depend->classid == ExtensionRelationId)
3167 {
3168 char *depextname = get_extension_name(pg_depend->objid);
3169 ExtensionControlFile *dcontrol;
3170 ListCell *lc;
3171
3172 dcontrol = read_extension_control_file(depextname);
3173 foreach(lc, dcontrol->no_relocate)
3174 {
3175 char *nrextname = (char *) lfirst(lc);
3176
3177 if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
3178 {
3179 ereport(ERROR,
3180 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3181 errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
3182 NameStr(extForm->extname)),
3183 errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
3184 depextname,
3185 NameStr(extForm->extname))));
3186 }
3187 }
3188 }
3189
3190 /*
3191 * Otherwise, ignore non-membership dependencies. (Currently, the
3192 * only other case we could see here is a normal dependency from
3193 * another extension.)
3194 */
3195 if (pg_depend->deptype != DEPENDENCY_EXTENSION)
3196 continue;
3197
3198 dep.classId = pg_depend->classid;
3199 dep.objectId = pg_depend->objid;
3200 dep.objectSubId = pg_depend->objsubid;
3201
3202 if (dep.objectSubId != 0) /* should not happen */
3203 elog(ERROR, "extension should not have a sub-object dependency");
3204
3205 /* Relocate the object */
3206 dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
3207 dep.objectId,
3208 nspOid,
3209 objsMoved);
3210
3211 /*
3212 * If not all the objects had the same old namespace (ignoring any
3213 * that are not in namespaces or are dependent types), complain.
3214 */
3215 if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
3216 ereport(ERROR,
3217 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3218 errmsg("extension \"%s\" does not support SET SCHEMA",
3219 NameStr(extForm->extname)),
3220 errdetail("%s is not in the extension's schema \"%s\"",
3221 getObjectDescription(&dep, false),
3222 get_namespace_name(oldNspOid))));
3223 }
3224
3225 /* report old schema, if caller wants it */
3226 if (oldschema)
3227 *oldschema = oldNspOid;
3228
3229 systable_endscan(depScan);
3230
3232
3233 /* Now adjust pg_extension.extnamespace */
3234 extForm->extnamespace = nspOid;
3235
3236 CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3237
3239
3240 /* update dependency to point to the new schema */
3241 if (changeDependencyFor(ExtensionRelationId, extensionOid,
3242 NamespaceRelationId, oldNspOid, nspOid) != 1)
3243 elog(ERROR, "could not change schema dependency for extension %s",
3244 NameStr(extForm->extname));
3245
3246 InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3247
3248 ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
3249
3250 return extAddr;
3251}
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2639
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3821
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4075
Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: alter.c:625
#define NameStr(name)
Definition: c.h:717
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2502
@ DEPENDENCY_EXTENSION
Definition: dependency.h:38
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
static ExtensionControlFile * read_extension_control_file(const char *extname)
Definition: extension.c:700
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition: extension.c:167
char * get_extension_name(Oid ext_oid)
Definition: extension.c:189
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3506
Oid GetUserId(void)
Definition: miscinit.c:520
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:3428
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
@ OBJECT_SCHEMA
Definition: parsenodes.h:2353
@ OBJECT_EXTENSION
Definition: parsenodes.h:2332
#define ACL_CREATE
Definition: parsenodes.h:85
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:457
Oid getExtensionOfObject(Oid classId, Oid objectId)
Definition: pg_depend.c:732
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
FormData_pg_extension * Form_pg_extension
Definition: pg_extension.h:52
#define lfirst(lc)
Definition: pg_list.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
#define BTEqualStrategyNumber
Definition: stratnum.h:31
List *List * no_relocate
Definition: extension.c:100
ItemPointerData t_self
Definition: htup.h:65
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References AccessShareLock, ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, AlterObjectNamespace_oid(), BTEqualStrategyNumber, CatalogTupleUpdate(), changeDependencyFor(), ObjectAddress::classId, DEPENDENCY_EXTENSION, DEPENDENCY_NORMAL, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, get_extension_name(), get_extension_oid(), get_namespace_name(), getExtensionOfObject(), getObjectDescription(), GETSTRUCT(), GetUserId(), heap_copytuple(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, InvokeObjectPostAlterHook, sort-test::key, lfirst, LookupCreationNamespace(), NameStr, new_object_addresses(), ExtensionControlFile::no_relocate, object_aclcheck(), OBJECT_EXTENSION, object_ownercheck(), OBJECT_SCHEMA, ObjectAddressSet, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, read_extension_control_file(), relation_close(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ExecAlterObjectSchemaStmt().

◆ ApplyExtensionUpdates()

static void ApplyExtensionUpdates ( Oid  extensionOid,
ExtensionControlFile pcontrol,
const char *  initialVersion,
List updateVersions,
char *  origSchemaName,
bool  cascade,
bool  is_create 
)
static

Definition at line 3404 of file extension.c.

3411{
3412 const char *oldVersionName = initialVersion;
3413 ListCell *lcv;
3414
3415 foreach(lcv, updateVersions)
3416 {
3417 char *versionName = (char *) lfirst(lcv);
3418 ExtensionControlFile *control;
3419 char *schemaName;
3420 Oid schemaOid;
3421 List *requiredExtensions;
3422 List *requiredSchemas;
3423 Relation extRel;
3424 ScanKeyData key[1];
3425 SysScanDesc extScan;
3426 HeapTuple extTup;
3427 Form_pg_extension extForm;
3428 Datum values[Natts_pg_extension];
3429 bool nulls[Natts_pg_extension];
3430 bool repl[Natts_pg_extension];
3431 ObjectAddress myself;
3432 ListCell *lc;
3433
3434 /*
3435 * Fetch parameters for specific version (pcontrol is not changed)
3436 */
3437 control = read_extension_aux_control_file(pcontrol, versionName);
3438
3439 /* Find the pg_extension tuple */
3440 extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3441
3442 ScanKeyInit(&key[0],
3443 Anum_pg_extension_oid,
3444 BTEqualStrategyNumber, F_OIDEQ,
3445 ObjectIdGetDatum(extensionOid));
3446
3447 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3448 NULL, 1, key);
3449
3450 extTup = systable_getnext(extScan);
3451
3452 if (!HeapTupleIsValid(extTup)) /* should not happen */
3453 elog(ERROR, "could not find tuple for extension %u",
3454 extensionOid);
3455
3456 extForm = (Form_pg_extension) GETSTRUCT(extTup);
3457
3458 /*
3459 * Determine the target schema (set by original install)
3460 */
3461 schemaOid = extForm->extnamespace;
3462 schemaName = get_namespace_name(schemaOid);
3463
3464 /*
3465 * Modify extrelocatable and extversion in the pg_extension tuple
3466 */
3467 memset(values, 0, sizeof(values));
3468 memset(nulls, 0, sizeof(nulls));
3469 memset(repl, 0, sizeof(repl));
3470
3471 values[Anum_pg_extension_extrelocatable - 1] =
3472 BoolGetDatum(control->relocatable);
3473 repl[Anum_pg_extension_extrelocatable - 1] = true;
3474 values[Anum_pg_extension_extversion - 1] =
3475 CStringGetTextDatum(versionName);
3476 repl[Anum_pg_extension_extversion - 1] = true;
3477
3478 extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3479 values, nulls, repl);
3480
3481 CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3482
3483 systable_endscan(extScan);
3484
3486
3487 /*
3488 * Look up the prerequisite extensions for this version, install them
3489 * if necessary, and build lists of their OIDs and the OIDs of their
3490 * target schemas.
3491 */
3492 requiredExtensions = NIL;
3493 requiredSchemas = NIL;
3494 foreach(lc, control->requires)
3495 {
3496 char *curreq = (char *) lfirst(lc);
3497 Oid reqext;
3498 Oid reqschema;
3499
3500 reqext = get_required_extension(curreq,
3501 control->name,
3502 origSchemaName,
3503 cascade,
3504 NIL,
3505 is_create);
3506 reqschema = get_extension_schema(reqext);
3507 requiredExtensions = lappend_oid(requiredExtensions, reqext);
3508 requiredSchemas = lappend_oid(requiredSchemas, reqschema);
3509 }
3510
3511 /*
3512 * Remove and recreate dependencies on prerequisite extensions
3513 */
3514 deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
3515 ExtensionRelationId,
3517
3518 myself.classId = ExtensionRelationId;
3519 myself.objectId = extensionOid;
3520 myself.objectSubId = 0;
3521
3522 foreach(lc, requiredExtensions)
3523 {
3524 Oid reqext = lfirst_oid(lc);
3525 ObjectAddress otherext;
3526
3527 otherext.classId = ExtensionRelationId;
3528 otherext.objectId = reqext;
3529 otherext.objectSubId = 0;
3530
3531 recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
3532 }
3533
3534 InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3535
3536 /*
3537 * Finally, execute the update script file
3538 */
3539 execute_extension_script(extensionOid, control,
3540 oldVersionName, versionName,
3541 requiredSchemas,
3542 schemaName);
3543
3544 /*
3545 * Update prior-version name and loop around. Since
3546 * execute_sql_string did a final CommandCounterIncrement, we can
3547 * update the pg_extension row again.
3548 */
3549 oldVersionName = versionName;
3550 }
3551}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define CStringGetTextDatum(s)
Definition: builtins.h:97
Oid get_extension_schema(Oid ext_oid)
Definition: extension.c:211
static Oid get_required_extension(char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade, List *parents, bool is_create)
Definition: extension.c:1896
static ExtensionControlFile * read_extension_aux_control_file(const ExtensionControlFile *pcontrol, const char *version)
Definition: extension.c:719
static void execute_extension_script(Oid extensionOid, ExtensionControlFile *control, const char *from_version, const char *version, List *requiredSchemas, const char *schemaName)
Definition: extension.c:1069
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1210
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
#define NIL
Definition: pg_list.h:68
#define lfirst_oid(lc)
Definition: pg_list.h:174
uintptr_t Datum
Definition: postgres.h:69
static Datum BoolGetDatum(bool X)
Definition: postgres.h:107
#define RelationGetDescr(relation)
Definition: rel.h:542
Definition: pg_list.h:54

References BoolGetDatum(), BTEqualStrategyNumber, CatalogTupleUpdate(), ObjectAddress::classId, CStringGetTextDatum, deleteDependencyRecordsForClass(), DEPENDENCY_NORMAL, elog, ERROR, execute_extension_script(), get_extension_schema(), get_namespace_name(), get_required_extension(), GETSTRUCT(), heap_modify_tuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, sort-test::key, lappend_oid(), lfirst, lfirst_oid, ExtensionControlFile::name, NIL, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, read_extension_aux_control_file(), recordDependencyOn(), RelationGetDescr, ExtensionControlFile::relocatable, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), and values.

Referenced by CreateExtensionInternal(), and ExecAlterExtensionStmt().

◆ check_valid_extension_name()

static void check_valid_extension_name ( const char *  extensionname)
static

Definition at line 231 of file extension.c.

232{
233 int namelen = strlen(extensionname);
234
235 /*
236 * Disallow empty names (the parser rejects empty identifiers anyway, but
237 * let's check).
238 */
239 if (namelen == 0)
241 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
242 errmsg("invalid extension name: \"%s\"", extensionname),
243 errdetail("Extension names must not be empty.")));
244
245 /*
246 * No double dashes, since that would make script filenames ambiguous.
247 */
248 if (strstr(extensionname, "--"))
250 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
251 errmsg("invalid extension name: \"%s\"", extensionname),
252 errdetail("Extension names must not contain \"--\".")));
253
254 /*
255 * No leading or trailing dash either. (We could probably allow this, but
256 * it would require much care in filename parsing and would make filenames
257 * visually if not formally ambiguous. Since there's no real-world use
258 * case, let's just forbid it.)
259 */
260 if (extensionname[0] == '-' || extensionname[namelen - 1] == '-')
262 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
263 errmsg("invalid extension name: \"%s\"", extensionname),
264 errdetail("Extension names must not begin or end with \"-\".")));
265
266 /*
267 * No directory separators either (this is sufficient to prevent ".."
268 * style attacks).
269 */
270 if (first_dir_separator(extensionname) != NULL)
272 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
273 errmsg("invalid extension name: \"%s\"", extensionname),
274 errdetail("Extension names must not contain directory separator characters.")));
275}
char * first_dir_separator(const char *filename)
Definition: path.c:110

References ereport, errcode(), errdetail(), errmsg(), ERROR, and first_dir_separator().

Referenced by CreateExtension(), get_required_extension(), and pg_extension_update_paths().

◆ check_valid_version_name()

static void check_valid_version_name ( const char *  versionname)
static

Definition at line 278 of file extension.c.

279{
280 int namelen = strlen(versionname);
281
282 /*
283 * Disallow empty names (we could possibly allow this, but there seems
284 * little point).
285 */
286 if (namelen == 0)
288 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
289 errmsg("invalid extension version name: \"%s\"", versionname),
290 errdetail("Version names must not be empty.")));
291
292 /*
293 * No double dashes, since that would make script filenames ambiguous.
294 */
295 if (strstr(versionname, "--"))
297 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
298 errmsg("invalid extension version name: \"%s\"", versionname),
299 errdetail("Version names must not contain \"--\".")));
300
301 /*
302 * No leading or trailing dash either.
303 */
304 if (versionname[0] == '-' || versionname[namelen - 1] == '-')
306 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
307 errmsg("invalid extension version name: \"%s\"", versionname),
308 errdetail("Version names must not begin or end with \"-\".")));
309
310 /*
311 * No directory separators either (this is sufficient to prevent ".."
312 * style attacks).
313 */
314 if (first_dir_separator(versionname) != NULL)
316 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
317 errmsg("invalid extension version name: \"%s\"", versionname),
318 errdetail("Version names must not contain directory separator characters.")));
319}

References ereport, errcode(), errdetail(), errmsg(), ERROR, and first_dir_separator().

Referenced by CreateExtensionInternal(), and ExecAlterExtensionStmt().

◆ convert_requires_to_datum()

static Datum convert_requires_to_datum ( List requires)
static

Definition at line 2530 of file extension.c.

2531{
2532 Datum *datums;
2533 int ndatums;
2534 ArrayType *a;
2535 ListCell *lc;
2536
2537 ndatums = list_length(requires);
2538 datums = (Datum *) palloc(ndatums * sizeof(Datum));
2539 ndatums = 0;
2540 foreach(lc, requires)
2541 {
2542 char *curreq = (char *) lfirst(lc);
2543
2544 datums[ndatums++] =
2546 }
2547 a = construct_array_builtin(datums, ndatums, NAMEOID);
2548 return PointerGetDatum(a);
2549}
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3381
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:682
int a
Definition: isn.c:73
void * palloc(Size size)
Definition: mcxt.c:1945
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
static int list_length(const List *l)
Definition: pg_list.h:152
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355

References a, construct_array_builtin(), CStringGetDatum(), DirectFunctionCall1, lfirst, list_length(), namein(), palloc(), and PointerGetDatum().

Referenced by get_available_versions_for_extension().

◆ CreateExtension()

ObjectAddress CreateExtension ( ParseState pstate,
CreateExtensionStmt stmt 
)

Definition at line 1967 of file extension.c.

1968{
1969 DefElem *d_schema = NULL;
1970 DefElem *d_new_version = NULL;
1971 DefElem *d_cascade = NULL;
1972 char *schemaName = NULL;
1973 char *versionName = NULL;
1974 bool cascade = false;
1975 ListCell *lc;
1976
1977 /* Check extension name validity before any filesystem access */
1979
1980 /*
1981 * Check for duplicate extension name. The unique index on
1982 * pg_extension.extname would catch this anyway, and serves as a backstop
1983 * in case of race conditions; but this is a friendlier error message, and
1984 * besides we need a check to support IF NOT EXISTS.
1985 */
1986 if (get_extension_oid(stmt->extname, true) != InvalidOid)
1987 {
1988 if (stmt->if_not_exists)
1989 {
1992 errmsg("extension \"%s\" already exists, skipping",
1993 stmt->extname)));
1994 return InvalidObjectAddress;
1995 }
1996 else
1997 ereport(ERROR,
1999 errmsg("extension \"%s\" already exists",
2000 stmt->extname)));
2001 }
2002
2003 /*
2004 * We use global variables to track the extension being created, so we can
2005 * create only one extension at the same time.
2006 */
2008 ereport(ERROR,
2009 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2010 errmsg("nested CREATE EXTENSION is not supported")));
2011
2012 /* Deconstruct the statement option list */
2013 foreach(lc, stmt->options)
2014 {
2015 DefElem *defel = (DefElem *) lfirst(lc);
2016
2017 if (strcmp(defel->defname, "schema") == 0)
2018 {
2019 if (d_schema)
2020 errorConflictingDefElem(defel, pstate);
2021 d_schema = defel;
2022 schemaName = defGetString(d_schema);
2023 }
2024 else if (strcmp(defel->defname, "new_version") == 0)
2025 {
2026 if (d_new_version)
2027 errorConflictingDefElem(defel, pstate);
2028 d_new_version = defel;
2029 versionName = defGetString(d_new_version);
2030 }
2031 else if (strcmp(defel->defname, "cascade") == 0)
2032 {
2033 if (d_cascade)
2034 errorConflictingDefElem(defel, pstate);
2035 d_cascade = defel;
2036 cascade = defGetBoolean(d_cascade);
2037 }
2038 else
2039 elog(ERROR, "unrecognized option: %s", defel->defname);
2040 }
2041
2042 /* Call CreateExtensionInternal to do the real work. */
2043 return CreateExtensionInternal(stmt->extname,
2044 schemaName,
2045 versionName,
2046 cascade,
2047 NIL,
2048 true);
2049}
char * defGetString(DefElem *def)
Definition: define.c:35
bool defGetBoolean(DefElem *def)
Definition: define.c:94
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
Definition: define.c:371
#define NOTICE
Definition: elog.h:35
static void check_valid_extension_name(const char *extensionname)
Definition: extension.c:231
bool creating_extension
Definition: extension.c:77
static ObjectAddress CreateExtensionInternal(char *extensionName, char *schemaName, const char *versionName, bool cascade, List *parents, bool is_create)
Definition: extension.c:1657
#define stmt
Definition: indent_codes.h:59
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
char * defname
Definition: parsenodes.h:826

References check_valid_extension_name(), CreateExtensionInternal(), creating_extension, defGetBoolean(), defGetString(), DefElem::defname, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, errorConflictingDefElem(), get_extension_oid(), InvalidObjectAddress, InvalidOid, lfirst, NIL, NOTICE, and stmt.

Referenced by ProcessUtilitySlow().

◆ CreateExtensionInternal()

static ObjectAddress CreateExtensionInternal ( char *  extensionName,
char *  schemaName,
const char *  versionName,
bool  cascade,
List parents,
bool  is_create 
)
static

Definition at line 1657 of file extension.c.

1663{
1664 char *origSchemaName = schemaName;
1665 Oid schemaOid = InvalidOid;
1666 Oid extowner = GetUserId();
1667 ExtensionControlFile *pcontrol;
1668 ExtensionControlFile *control;
1669 char *filename;
1670 struct stat fst;
1671 List *updateVersions;
1672 List *requiredExtensions;
1673 List *requiredSchemas;
1674 Oid extensionOid;
1675 ObjectAddress address;
1676 ListCell *lc;
1677
1678 /*
1679 * Read the primary control file. Note we assume that it does not contain
1680 * any non-ASCII data, so there is no need to worry about encoding at this
1681 * point.
1682 */
1683 pcontrol = read_extension_control_file(extensionName);
1684
1685 /*
1686 * Determine the version to install
1687 */
1688 if (versionName == NULL)
1689 {
1690 if (pcontrol->default_version)
1691 versionName = pcontrol->default_version;
1692 else
1693 ereport(ERROR,
1694 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1695 errmsg("version to install must be specified")));
1696 }
1697 check_valid_version_name(versionName);
1698
1699 /*
1700 * Figure out which script(s) we need to run to install the desired
1701 * version of the extension. If we do not have a script that directly
1702 * does what is needed, we try to find a sequence of update scripts that
1703 * will get us there.
1704 */
1705 filename = get_extension_script_filename(pcontrol, NULL, versionName);
1706 if (stat(filename, &fst) == 0)
1707 {
1708 /* Easy, no extra scripts */
1709 updateVersions = NIL;
1710 }
1711 else
1712 {
1713 /* Look for best way to install this version */
1714 List *evi_list;
1715 ExtensionVersionInfo *evi_start;
1716 ExtensionVersionInfo *evi_target;
1717
1718 /* Extract the version update graph from the script directory */
1719 evi_list = get_ext_ver_list(pcontrol);
1720
1721 /* Identify the target version */
1722 evi_target = get_ext_ver_info(versionName, &evi_list);
1723
1724 /* Identify best path to reach target */
1725 evi_start = find_install_path(evi_list, evi_target,
1726 &updateVersions);
1727
1728 /* Fail if no path ... */
1729 if (evi_start == NULL)
1730 ereport(ERROR,
1731 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1732 errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
1733 pcontrol->name, versionName)));
1734
1735 /* Otherwise, install best starting point and then upgrade */
1736 versionName = evi_start->name;
1737 }
1738
1739 /*
1740 * Fetch control parameters for installation target version
1741 */
1742 control = read_extension_aux_control_file(pcontrol, versionName);
1743
1744 /*
1745 * Determine the target schema to install the extension into
1746 */
1747 if (schemaName)
1748 {
1749 /* If the user is giving us the schema name, it must exist already. */
1750 schemaOid = get_namespace_oid(schemaName, false);
1751 }
1752
1753 if (control->schema != NULL)
1754 {
1755 /*
1756 * The extension is not relocatable and the author gave us a schema
1757 * for it.
1758 *
1759 * Unless CASCADE parameter was given, it's an error to give a schema
1760 * different from control->schema if control->schema is specified.
1761 */
1762 if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1763 !cascade)
1764 ereport(ERROR,
1765 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1766 errmsg("extension \"%s\" must be installed in schema \"%s\"",
1767 control->name,
1768 control->schema)));
1769
1770 /* Always use the schema from control file for current extension. */
1771 schemaName = control->schema;
1772
1773 /* Find or create the schema in case it does not exist. */
1774 schemaOid = get_namespace_oid(schemaName, true);
1775
1776 if (!OidIsValid(schemaOid))
1777 {
1779
1780 csstmt->schemaname = schemaName;
1781 csstmt->authrole = NULL; /* will be created by current user */
1782 csstmt->schemaElts = NIL;
1783 csstmt->if_not_exists = false;
1784 CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
1785 -1, -1);
1786
1787 /*
1788 * CreateSchemaCommand includes CommandCounterIncrement, so new
1789 * schema is now visible.
1790 */
1791 schemaOid = get_namespace_oid(schemaName, false);
1792 }
1793 }
1794 else if (!OidIsValid(schemaOid))
1795 {
1796 /*
1797 * Neither user nor author of the extension specified schema; use the
1798 * current default creation namespace, which is the first explicit
1799 * entry in the search_path.
1800 */
1801 List *search_path = fetch_search_path(false);
1802
1803 if (search_path == NIL) /* nothing valid in search_path? */
1804 ereport(ERROR,
1805 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1806 errmsg("no schema has been selected to create in")));
1807 schemaOid = linitial_oid(search_path);
1808 schemaName = get_namespace_name(schemaOid);
1809 if (schemaName == NULL) /* recently-deleted namespace? */
1810 ereport(ERROR,
1811 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1812 errmsg("no schema has been selected to create in")));
1813
1814 list_free(search_path);
1815 }
1816
1817 /*
1818 * Make note if a temporary namespace has been accessed in this
1819 * transaction.
1820 */
1821 if (isTempNamespace(schemaOid))
1823
1824 /*
1825 * We don't check creation rights on the target namespace here. If the
1826 * extension script actually creates any objects there, it will fail if
1827 * the user doesn't have such permissions. But there are cases such as
1828 * procedural languages where it's convenient to set schema = pg_catalog
1829 * yet we don't want to restrict the command to users with ACL_CREATE for
1830 * pg_catalog.
1831 */
1832
1833 /*
1834 * Look up the prerequisite extensions, install them if necessary, and
1835 * build lists of their OIDs and the OIDs of their target schemas.
1836 */
1837 requiredExtensions = NIL;
1838 requiredSchemas = NIL;
1839 foreach(lc, control->requires)
1840 {
1841 char *curreq = (char *) lfirst(lc);
1842 Oid reqext;
1843 Oid reqschema;
1844
1845 reqext = get_required_extension(curreq,
1846 extensionName,
1847 origSchemaName,
1848 cascade,
1849 parents,
1850 is_create);
1851 reqschema = get_extension_schema(reqext);
1852 requiredExtensions = lappend_oid(requiredExtensions, reqext);
1853 requiredSchemas = lappend_oid(requiredSchemas, reqschema);
1854 }
1855
1856 /*
1857 * Insert new tuple into pg_extension, and create dependency entries.
1858 */
1859 address = InsertExtensionTuple(control->name, extowner,
1860 schemaOid, control->relocatable,
1861 versionName,
1862 PointerGetDatum(NULL),
1863 PointerGetDatum(NULL),
1864 requiredExtensions);
1865 extensionOid = address.objectId;
1866
1867 /*
1868 * Apply any control-file comment on extension
1869 */
1870 if (control->comment != NULL)
1871 CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
1872
1873 /*
1874 * Execute the installation script file
1875 */
1876 execute_extension_script(extensionOid, control,
1877 NULL, versionName,
1878 requiredSchemas,
1879 schemaName);
1880
1881 /*
1882 * If additional update scripts have to be executed, apply the updates as
1883 * though a series of ALTER EXTENSION UPDATE commands were given
1884 */
1885 ApplyExtensionUpdates(extensionOid, pcontrol,
1886 versionName, updateVersions,
1887 origSchemaName, cascade, is_create);
1888
1889 return address;
1890}
#define OidIsValid(objectId)
Definition: c.h:746
void CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
Definition: comment.c:143
ObjectAddress InsertExtensionTuple(const char *extName, Oid extOwner, Oid schemaOid, bool relocatable, const char *extVersion, Datum extConfig, Datum extCondition, List *requiredExtensions)
Definition: extension.c:2065
static void check_valid_version_name(const char *versionname)
Definition: extension.c:278
static ExtensionVersionInfo * find_install_path(List *evi_list, ExtensionVersionInfo *evi_target, List **best_path)
Definition: extension.c:1602
static ExtensionVersionInfo * get_ext_ver_info(const char *versionname, List **evi_list)
Definition: extension.c:1342
static List * get_ext_ver_list(ExtensionControlFile *control)
Definition: extension.c:1403
static char * get_extension_script_filename(ExtensionControlFile *control, const char *from_version, const char *version)
Definition: extension.c:475
static void ApplyExtensionUpdates(Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
Definition: extension.c:3404
void list_free(List *list)
Definition: list.c:1546
bool isTempNamespace(Oid namespaceId)
Definition: namespace.c:3649
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4819
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition: namespace.c:3535
#define makeNode(_type_)
Definition: nodes.h:161
static char * filename
Definition: pg_dumpall.c:123
#define linitial_oid(l)
Definition: pg_list.h:180
Oid CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString, int stmt_location, int stmt_len)
Definition: schemacmds.c:52
RoleSpec * authrole
Definition: parsenodes.h:2383
char * default_version
Definition: extension.c:90
#define stat
Definition: win32_port.h:274
int MyXactFlags
Definition: xact.c:136
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102

References ApplyExtensionUpdates(), CreateSchemaStmt::authrole, check_valid_version_name(), ExtensionControlFile::comment, CreateComments(), CreateSchemaCommand(), ExtensionControlFile::default_version, ereport, errcode(), errmsg(), ERROR, execute_extension_script(), fetch_search_path(), filename, find_install_path(), get_ext_ver_info(), get_ext_ver_list(), get_extension_schema(), get_extension_script_filename(), get_namespace_name(), get_namespace_oid(), get_required_extension(), GetUserId(), CreateSchemaStmt::if_not_exists, InsertExtensionTuple(), InvalidOid, isTempNamespace(), lappend_oid(), lfirst, linitial_oid, list_free(), makeNode, MyXactFlags, ExtensionControlFile::name, ExtensionVersionInfo::name, NIL, ObjectAddress::objectId, OidIsValid, PointerGetDatum(), read_extension_aux_control_file(), read_extension_control_file(), ExtensionControlFile::relocatable, ExtensionControlFile::schema, CreateSchemaStmt::schemaElts, CreateSchemaStmt::schemaname, stat, and XACT_FLAGS_ACCESSEDTEMPNAMESPACE.

Referenced by CreateExtension(), and get_required_extension().

◆ ExecAlterExtensionContentsRecurse()

static void ExecAlterExtensionContentsRecurse ( AlterExtensionContentsStmt stmt,
ObjectAddress  extension,
ObjectAddress  object 
)
static

Definition at line 3648 of file extension.c.

3651{
3652 Oid oldExtension;
3653
3654 /*
3655 * Check existing extension membership.
3656 */
3657 oldExtension = getExtensionOfObject(object.classId, object.objectId);
3658
3659 if (stmt->action > 0)
3660 {
3661 /*
3662 * ADD, so complain if object is already attached to some extension.
3663 */
3664 if (OidIsValid(oldExtension))
3665 ereport(ERROR,
3666 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3667 errmsg("%s is already a member of extension \"%s\"",
3668 getObjectDescription(&object, false),
3669 get_extension_name(oldExtension))));
3670
3671 /*
3672 * Prevent a schema from being added to an extension if the schema
3673 * contains the extension. That would create a dependency loop.
3674 */
3675 if (object.classId == NamespaceRelationId &&
3676 object.objectId == get_extension_schema(extension.objectId))
3677 ereport(ERROR,
3678 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3679 errmsg("cannot add schema \"%s\" to extension \"%s\" "
3680 "because the schema contains the extension",
3681 get_namespace_name(object.objectId),
3682 stmt->extname)));
3683
3684 /*
3685 * OK, add the dependency.
3686 */
3687 recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
3688
3689 /*
3690 * Also record the initial ACL on the object, if any.
3691 *
3692 * Note that this will handle the object's ACLs, as well as any ACLs
3693 * on object subIds. (In other words, when the object is a table,
3694 * this will record the table's ACL and the ACLs for the columns on
3695 * the table, if any).
3696 */
3697 recordExtObjInitPriv(object.objectId, object.classId);
3698 }
3699 else
3700 {
3701 /*
3702 * DROP, so complain if it's not a member.
3703 */
3704 if (oldExtension != extension.objectId)
3705 ereport(ERROR,
3706 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3707 errmsg("%s is not a member of extension \"%s\"",
3708 getObjectDescription(&object, false),
3709 stmt->extname)));
3710
3711 /*
3712 * OK, drop the dependency.
3713 */
3714 if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3715 ExtensionRelationId,
3717 elog(ERROR, "unexpected number of extension dependency records");
3718
3719 /*
3720 * If it's a relation, it might have an entry in the extension's
3721 * extconfig array, which we must remove.
3722 */
3723 if (object.classId == RelationRelationId)
3724 extension_config_remove(extension.objectId, object.objectId);
3725
3726 /*
3727 * Remove all the initial ACLs, if any.
3728 *
3729 * Note that this will remove the object's ACLs, as well as any ACLs
3730 * on object subIds. (In other words, when the object is a table,
3731 * this will remove the table's ACL and the ACLs for the columns on
3732 * the table, if any).
3733 */
3734 removeExtObjInitPriv(object.objectId, object.classId);
3735 }
3736
3737 /*
3738 * Recurse to any dependent objects; currently, this includes the array
3739 * type of a base type, the multirange type associated with a range type,
3740 * and the rowtype of a table.
3741 */
3742 if (object.classId == TypeRelationId)
3743 {
3744 ObjectAddress depobject;
3745
3746 depobject.classId = TypeRelationId;
3747 depobject.objectSubId = 0;
3748
3749 /* If it has an array type, update that too */
3750 depobject.objectId = get_array_type(object.objectId);
3751 if (OidIsValid(depobject.objectId))
3752 ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3753
3754 /* If it is a range type, update the associated multirange too */
3755 if (type_is_range(object.objectId))
3756 {
3757 depobject.objectId = get_range_multirange(object.objectId);
3758 if (!OidIsValid(depobject.objectId))
3759 ereport(ERROR,
3760 (errcode(ERRCODE_UNDEFINED_OBJECT),
3761 errmsg("could not find multirange type for data type %s",
3762 format_type_be(object.objectId))));
3763 ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3764 }
3765 }
3766 if (object.classId == RelationRelationId)
3767 {
3768 ObjectAddress depobject;
3769
3770 depobject.classId = TypeRelationId;
3771 depobject.objectSubId = 0;
3772
3773 /* It might not have a rowtype, but if it does, update that */
3774 depobject.objectId = get_rel_type_id(object.objectId);
3775 if (OidIsValid(depobject.objectId))
3776 ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3777 }
3778}
void recordExtObjInitPriv(Oid objoid, Oid classoid)
Definition: aclchk.c:4339
void removeExtObjInitPriv(Oid objoid, Oid classoid)
Definition: aclchk.c:4503
static void extension_config_remove(Oid extensionoid, Oid tableoid)
Definition: extension.c:2877
static void ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt, ObjectAddress extension, ObjectAddress object)
Definition: extension.c:3648
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
bool type_is_range(Oid typid)
Definition: lsyscache.c:2828
Oid get_rel_type_id(Oid relid)
Definition: lsyscache.c:2119
Oid get_range_multirange(Oid rangeOid)
Definition: lsyscache.c:3598
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2927

References ObjectAddress::classId, deleteDependencyRecordsForClass(), DEPENDENCY_EXTENSION, elog, ereport, errcode(), errmsg(), ERROR, ExecAlterExtensionContentsRecurse(), extension_config_remove(), format_type_be(), get_array_type(), get_extension_name(), get_extension_schema(), get_namespace_name(), get_range_multirange(), get_rel_type_id(), getExtensionOfObject(), getObjectDescription(), ObjectAddress::objectId, ObjectAddress::objectSubId, OidIsValid, recordDependencyOn(), recordExtObjInitPriv(), removeExtObjInitPriv(), stmt, and type_is_range().

Referenced by ExecAlterExtensionContentsRecurse(), and ExecAlterExtensionContentsStmt().

◆ ExecAlterExtensionContentsStmt()

ObjectAddress ExecAlterExtensionContentsStmt ( AlterExtensionContentsStmt stmt,
ObjectAddress objAddr 
)

Definition at line 3562 of file extension.c.

3564{
3565 ObjectAddress extension;
3566 ObjectAddress object;
3567 Relation relation;
3568
3569 switch (stmt->objtype)
3570 {
3571 case OBJECT_DATABASE:
3572 case OBJECT_EXTENSION:
3573 case OBJECT_INDEX:
3574 case OBJECT_PUBLICATION:
3575 case OBJECT_ROLE:
3578 case OBJECT_TABLESPACE:
3579 ereport(ERROR,
3580 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3581 errmsg("cannot add an object of this type to an extension")));
3582 break;
3583 default:
3584 /* OK */
3585 break;
3586 }
3587
3588 /*
3589 * Find the extension and acquire a lock on it, to ensure it doesn't get
3590 * dropped concurrently. A sharable lock seems sufficient: there's no
3591 * reason not to allow other sorts of manipulations, such as add/drop of
3592 * other objects, to occur concurrently. Concurrently adding/dropping the
3593 * *same* object would be bad, but we prevent that by using a non-sharable
3594 * lock on the individual object, below.
3595 */
3597 (Node *) makeString(stmt->extname),
3598 &relation, AccessShareLock, false);
3599
3600 /* Permission check: must own extension */
3601 if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
3603 stmt->extname);
3604
3605 /*
3606 * Translate the parser representation that identifies the object into an
3607 * ObjectAddress. get_object_address() will throw an error if the object
3608 * does not exist, and will also acquire a lock on the object to guard
3609 * against concurrent DROP and ALTER EXTENSION ADD/DROP operations.
3610 */
3611 object = get_object_address(stmt->objtype, stmt->object,
3612 &relation, ShareUpdateExclusiveLock, false);
3613
3614 Assert(object.objectSubId == 0);
3615 if (objAddr)
3616 *objAddr = object;
3617
3618 /* Permission check: must own target object, too */
3619 check_object_ownership(GetUserId(), stmt->objtype, object,
3620 stmt->object, relation);
3621
3622 /* Do the update, recursing to any dependent objects */
3623 ExecAlterExtensionContentsRecurse(stmt, extension, object);
3624
3625 /* Finish up */
3626 InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
3627
3628 /*
3629 * If get_object_address() opened the relation for us, we close it to keep
3630 * the reference count correct - but we retain any locks acquired by
3631 * get_object_address() until commit time, to guard against concurrent
3632 * activity.
3633 */
3634 if (relation != NULL)
3635 relation_close(relation, NoLock);
3636
3637 return extension;
3638}
Assert(PointerIsAligned(start, uint64))
#define NoLock
Definition: lockdefs.h:34
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
void check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, Node *object, Relation relation)
ObjectAddress get_object_address(ObjectType objtype, Node *object, Relation *relp, LOCKMODE lockmode, bool missing_ok)
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2359
@ OBJECT_ROLE
Definition: parsenodes.h:2350
@ OBJECT_INDEX
Definition: parsenodes.h:2337
@ OBJECT_DATABASE
Definition: parsenodes.h:2326
@ OBJECT_PUBLICATION
Definition: parsenodes.h:2347
@ OBJECT_SUBSCRIPTION
Definition: parsenodes.h:2355
@ OBJECT_STATISTIC_EXT
Definition: parsenodes.h:2356
Definition: nodes.h:135
String * makeString(char *str)
Definition: value.c:63

References AccessShareLock, aclcheck_error(), ACLCHECK_NOT_OWNER, Assert(), check_object_ownership(), ereport, errcode(), errmsg(), ERROR, ExecAlterExtensionContentsRecurse(), get_object_address(), GetUserId(), InvokeObjectPostAlterHook, makeString(), NoLock, OBJECT_DATABASE, OBJECT_EXTENSION, OBJECT_INDEX, object_ownercheck(), OBJECT_PUBLICATION, OBJECT_ROLE, OBJECT_STATISTIC_EXT, OBJECT_SUBSCRIPTION, OBJECT_TABLESPACE, ObjectAddress::objectId, relation_close(), ShareUpdateExclusiveLock, and stmt.

Referenced by ProcessUtilitySlow().

◆ ExecAlterExtensionStmt()

ObjectAddress ExecAlterExtensionStmt ( ParseState pstate,
AlterExtensionStmt stmt 
)

Definition at line 3257 of file extension.c.

3258{
3259 DefElem *d_new_version = NULL;
3260 char *versionName;
3261 char *oldVersionName;
3262 ExtensionControlFile *control;
3263 Oid extensionOid;
3264 Relation extRel;
3265 ScanKeyData key[1];
3266 SysScanDesc extScan;
3267 HeapTuple extTup;
3268 List *updateVersions;
3269 Datum datum;
3270 bool isnull;
3271 ListCell *lc;
3272 ObjectAddress address;
3273
3274 /*
3275 * We use global variables to track the extension being created, so we can
3276 * create/update only one extension at the same time.
3277 */
3279 ereport(ERROR,
3280 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3281 errmsg("nested ALTER EXTENSION is not supported")));
3282
3283 /*
3284 * Look up the extension --- it must already exist in pg_extension
3285 */
3286 extRel = table_open(ExtensionRelationId, AccessShareLock);
3287
3288 ScanKeyInit(&key[0],
3289 Anum_pg_extension_extname,
3290 BTEqualStrategyNumber, F_NAMEEQ,
3291 CStringGetDatum(stmt->extname));
3292
3293 extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
3294 NULL, 1, key);
3295
3296 extTup = systable_getnext(extScan);
3297
3298 if (!HeapTupleIsValid(extTup))
3299 ereport(ERROR,
3300 (errcode(ERRCODE_UNDEFINED_OBJECT),
3301 errmsg("extension \"%s\" does not exist",
3302 stmt->extname)));
3303
3304 extensionOid = ((Form_pg_extension) GETSTRUCT(extTup))->oid;
3305
3306 /*
3307 * Determine the existing version we are updating from
3308 */
3309 datum = heap_getattr(extTup, Anum_pg_extension_extversion,
3310 RelationGetDescr(extRel), &isnull);
3311 if (isnull)
3312 elog(ERROR, "extversion is null");
3313 oldVersionName = text_to_cstring(DatumGetTextPP(datum));
3314
3315 systable_endscan(extScan);
3316
3318
3319 /* Permission check: must own extension */
3320 if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3322 stmt->extname);
3323
3324 /*
3325 * Read the primary control file. Note we assume that it does not contain
3326 * any non-ASCII data, so there is no need to worry about encoding at this
3327 * point.
3328 */
3329 control = read_extension_control_file(stmt->extname);
3330
3331 /*
3332 * Read the statement option list
3333 */
3334 foreach(lc, stmt->options)
3335 {
3336 DefElem *defel = (DefElem *) lfirst(lc);
3337
3338 if (strcmp(defel->defname, "new_version") == 0)
3339 {
3340 if (d_new_version)
3341 errorConflictingDefElem(defel, pstate);
3342 d_new_version = defel;
3343 }
3344 else
3345 elog(ERROR, "unrecognized option: %s", defel->defname);
3346 }
3347
3348 /*
3349 * Determine the version to update to
3350 */
3351 if (d_new_version && d_new_version->arg)
3352 versionName = strVal(d_new_version->arg);
3353 else if (control->default_version)
3354 versionName = control->default_version;
3355 else
3356 {
3357 ereport(ERROR,
3358 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3359 errmsg("version to install must be specified")));
3360 versionName = NULL; /* keep compiler quiet */
3361 }
3362 check_valid_version_name(versionName);
3363
3364 /*
3365 * If we're already at that version, just say so
3366 */
3367 if (strcmp(oldVersionName, versionName) == 0)
3368 {
3370 (errmsg("version \"%s\" of extension \"%s\" is already installed",
3371 versionName, stmt->extname)));
3372 return InvalidObjectAddress;
3373 }
3374
3375 /*
3376 * Identify the series of update script files we need to execute
3377 */
3378 updateVersions = identify_update_path(control,
3379 oldVersionName,
3380 versionName);
3381
3382 /*
3383 * Update the pg_extension row and execute the update scripts, one at a
3384 * time
3385 */
3386 ApplyExtensionUpdates(extensionOid, control,
3387 oldVersionName, updateVersions,
3388 NULL, false, false);
3389
3390 ObjectAddressSet(address, ExtensionRelationId, extensionOid);
3391
3392 return address;
3393}
static List * identify_update_path(ExtensionControlFile *control, const char *oldVersion, const char *newVersion)
Definition: extension.c:1466
#define DatumGetTextPP(X)
Definition: fmgr.h:292
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:904
Node * arg
Definition: parsenodes.h:827
#define strVal(v)
Definition: value.h:82
char * text_to_cstring(const text *t)
Definition: varlena.c:225

References AccessShareLock, aclcheck_error(), ACLCHECK_NOT_OWNER, ApplyExtensionUpdates(), DefElem::arg, BTEqualStrategyNumber, check_valid_version_name(), creating_extension, CStringGetDatum(), DatumGetTextPP, ExtensionControlFile::default_version, DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, errorConflictingDefElem(), GETSTRUCT(), GetUserId(), heap_getattr(), HeapTupleIsValid, identify_update_path(), InvalidObjectAddress, sort-test::key, lfirst, NOTICE, OBJECT_EXTENSION, object_ownercheck(), ObjectAddressSet, read_extension_control_file(), RelationGetDescr, ScanKeyInit(), stmt, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and text_to_cstring().

Referenced by ProcessUtilitySlow().

◆ execute_extension_script()

static void execute_extension_script ( Oid  extensionOid,
ExtensionControlFile control,
const char *  from_version,
const char *  version,
List requiredSchemas,
const char *  schemaName 
)
static

Definition at line 1069 of file extension.c.

1074{
1075 bool switch_to_superuser = false;
1076 char *filename;
1077 Oid save_userid = 0;
1078 int save_sec_context = 0;
1079 int save_nestlevel;
1080 StringInfoData pathbuf;
1081 ListCell *lc;
1082 ListCell *lc2;
1083
1084 /*
1085 * Enforce superuser-ness if appropriate. We postpone these checks until
1086 * here so that the control flags are correctly associated with the right
1087 * script(s) if they happen to be set in secondary control files.
1088 */
1089 if (control->superuser && !superuser())
1090 {
1091 if (extension_is_trusted(control))
1092 switch_to_superuser = true;
1093 else if (from_version == NULL)
1094 ereport(ERROR,
1095 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1096 errmsg("permission denied to create extension \"%s\"",
1097 control->name),
1098 control->trusted
1099 ? errhint("Must have CREATE privilege on current database to create this extension.")
1100 : errhint("Must be superuser to create this extension.")));
1101 else
1102 ereport(ERROR,
1103 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1104 errmsg("permission denied to update extension \"%s\"",
1105 control->name),
1106 control->trusted
1107 ? errhint("Must have CREATE privilege on current database to update this extension.")
1108 : errhint("Must be superuser to update this extension.")));
1109 }
1110
1111 filename = get_extension_script_filename(control, from_version, version);
1112
1113 if (from_version == NULL)
1114 elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
1115 else
1116 elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
1117
1118 /*
1119 * If installing a trusted extension on behalf of a non-superuser, become
1120 * the bootstrap superuser. (This switch will be cleaned up automatically
1121 * if the transaction aborts, as will the GUC changes below.)
1122 */
1123 if (switch_to_superuser)
1124 {
1125 GetUserIdAndSecContext(&save_userid, &save_sec_context);
1126 SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID,
1127 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
1128 }
1129
1130 /*
1131 * Force client_min_messages and log_min_messages to be at least WARNING,
1132 * so that we won't spam the user with useless NOTICE messages from common
1133 * script actions like creating shell types.
1134 *
1135 * We use the equivalent of a function SET option to allow the setting to
1136 * persist for exactly the duration of the script execution. guc.c also
1137 * takes care of undoing the setting on error.
1138 *
1139 * log_min_messages can't be set by ordinary users, so for that one we
1140 * pretend to be superuser.
1141 */
1142 save_nestlevel = NewGUCNestLevel();
1143
1145 (void) set_config_option("client_min_messages", "warning",
1147 GUC_ACTION_SAVE, true, 0, false);
1149 (void) set_config_option_ext("log_min_messages", "warning",
1151 BOOTSTRAP_SUPERUSERID,
1152 GUC_ACTION_SAVE, true, 0, false);
1153
1154 /*
1155 * Similarly disable check_function_bodies, to ensure that SQL functions
1156 * won't be parsed during creation.
1157 */
1159 (void) set_config_option("check_function_bodies", "off",
1161 GUC_ACTION_SAVE, true, 0, false);
1162
1163 /*
1164 * Set up the search path to have the target schema first, making it be
1165 * the default creation target namespace. Then add the schemas of any
1166 * prerequisite extensions, unless they are in pg_catalog which would be
1167 * searched anyway. (Listing pg_catalog explicitly in a non-first
1168 * position would be bad for security.) Finally add pg_temp to ensure
1169 * that temp objects can't take precedence over others.
1170 */
1171 initStringInfo(&pathbuf);
1172 appendStringInfoString(&pathbuf, quote_identifier(schemaName));
1173 foreach(lc, requiredSchemas)
1174 {
1175 Oid reqschema = lfirst_oid(lc);
1176 char *reqname = get_namespace_name(reqschema);
1177
1178 if (reqname && strcmp(reqname, "pg_catalog") != 0)
1179 appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
1180 }
1181 appendStringInfoString(&pathbuf, ", pg_temp");
1182
1183 (void) set_config_option("search_path", pathbuf.data,
1185 GUC_ACTION_SAVE, true, 0, false);
1186
1187 /*
1188 * Set creating_extension and related variables so that
1189 * recordDependencyOnCurrentExtension and other functions do the right
1190 * things. On failure, ensure we reset these variables.
1191 */
1192 creating_extension = true;
1193 CurrentExtensionObject = extensionOid;
1194 PG_TRY();
1195 {
1196 char *c_sql = read_extension_script_file(control, filename);
1197 Datum t_sql;
1198
1199 /*
1200 * We filter each substitution through quote_identifier(). When the
1201 * arg contains one of the following characters, no one collection of
1202 * quoting can work inside $$dollar-quoted string literals$$,
1203 * 'single-quoted string literals', and outside of any literal. To
1204 * avoid a security snare for extension authors, error on substitution
1205 * for arguments containing these.
1206 */
1207 const char *quoting_relevant_chars = "\"$'\\";
1208
1209 /* We use various functions that want to operate on text datums */
1210 t_sql = CStringGetTextDatum(c_sql);
1211
1212 /*
1213 * Reduce any lines beginning with "\echo" to empty. This allows
1214 * scripts to contain messages telling people not to run them via
1215 * psql, which has been found to be necessary due to old habits.
1216 */
1218 C_COLLATION_OID,
1219 t_sql,
1220 CStringGetTextDatum("^\\\\echo.*$"),
1222 CStringGetTextDatum("ng"));
1223
1224 /*
1225 * If the script uses @extowner@, substitute the calling username.
1226 */
1227 if (strstr(c_sql, "@extowner@"))
1228 {
1229 Oid uid = switch_to_superuser ? save_userid : GetUserId();
1230 const char *userName = GetUserNameFromId(uid, false);
1231 const char *qUserName = quote_identifier(userName);
1232
1234 C_COLLATION_OID,
1235 t_sql,
1236 CStringGetTextDatum("@extowner@"),
1237 CStringGetTextDatum(qUserName));
1238 if (strpbrk(userName, quoting_relevant_chars))
1239 ereport(ERROR,
1240 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1241 errmsg("invalid character in extension owner: must not contain any of \"%s\"",
1242 quoting_relevant_chars)));
1243 }
1244
1245 /*
1246 * If it's not relocatable, substitute the target schema name for
1247 * occurrences of @extschema@.
1248 *
1249 * For a relocatable extension, we needn't do this. There cannot be
1250 * any need for @extschema@, else it wouldn't be relocatable.
1251 */
1252 if (!control->relocatable)
1253 {
1254 Datum old = t_sql;
1255 const char *qSchemaName = quote_identifier(schemaName);
1256
1258 C_COLLATION_OID,
1259 t_sql,
1260 CStringGetTextDatum("@extschema@"),
1261 CStringGetTextDatum(qSchemaName));
1262 if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
1263 ereport(ERROR,
1264 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1265 errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1266 control->name, quoting_relevant_chars)));
1267 }
1268
1269 /*
1270 * Likewise, substitute required extensions' schema names for
1271 * occurrences of @extschema:extension_name@.
1272 */
1273 Assert(list_length(control->requires) == list_length(requiredSchemas));
1274 forboth(lc, control->requires, lc2, requiredSchemas)
1275 {
1276 Datum old = t_sql;
1277 char *reqextname = (char *) lfirst(lc);
1278 Oid reqschema = lfirst_oid(lc2);
1279 char *schemaName = get_namespace_name(reqschema);
1280 const char *qSchemaName = quote_identifier(schemaName);
1281 char *repltoken;
1282
1283 repltoken = psprintf("@extschema:%s@", reqextname);
1285 C_COLLATION_OID,
1286 t_sql,
1287 CStringGetTextDatum(repltoken),
1288 CStringGetTextDatum(qSchemaName));
1289 if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
1290 ereport(ERROR,
1291 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1292 errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1293 reqextname, quoting_relevant_chars)));
1294 }
1295
1296 /*
1297 * If module_pathname was set in the control file, substitute its
1298 * value for occurrences of MODULE_PATHNAME.
1299 */
1300 if (control->module_pathname)
1301 {
1303 C_COLLATION_OID,
1304 t_sql,
1305 CStringGetTextDatum("MODULE_PATHNAME"),
1307 }
1308
1309 /* And now back to C string */
1310 c_sql = text_to_cstring(DatumGetTextPP(t_sql));
1311
1313 }
1314 PG_FINALLY();
1315 {
1316 creating_extension = false;
1318 }
1319 PG_END_TRY();
1320
1321 /*
1322 * Restore the GUC variables we set above.
1323 */
1324 AtEOXact_GUC(true, save_nestlevel);
1325
1326 /*
1327 * Restore authentication state if needed.
1328 */
1329 if (switch_to_superuser)
1330 SetUserIdAndSecContext(save_userid, save_sec_context);
1331}
int errhint(const char *fmt,...)
Definition: elog.c:1318
#define PG_TRY(...)
Definition: elog.h:371
#define WARNING
Definition: elog.h:36
#define PG_END_TRY(...)
Definition: elog.h:396
#define DEBUG1
Definition: elog.h:30
#define PG_FINALLY(...)
Definition: elog.h:388
static void execute_sql_string(const char *sql, const char *filename)
Definition: extension.c:917
Oid CurrentExtensionObject
Definition: extension.c:78
static char * read_extension_script_file(const ExtensionControlFile *control, const char *filename)
Definition: extension.c:742
static bool extension_is_trusted(ExtensionControlFile *control)
Definition: extension.c:1047
Datum DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4)
Definition: fmgr.c:859
Datum DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:834
int set_config_option_ext(const char *name, const char *value, GucContext context, GucSource source, Oid srole, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3382
int NewGUCNestLevel(void)
Definition: guc.c:2235
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2262
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3342
@ GUC_ACTION_SAVE
Definition: guc.h:205
@ PGC_S_SESSION
Definition: guc.h:126
@ PGC_SUSET
Definition: guc.h:78
@ PGC_USERSET
Definition: guc.h:79
bool check_function_bodies
Definition: guc_tables.c:528
int client_min_messages
Definition: guc_tables.c:540
int log_min_messages
Definition: guc_tables.c:539
#define SECURITY_LOCAL_USERID_CHANGE
Definition: miscadmin.h:318
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:663
char * GetUserNameFromId(Oid roleid, bool noerr)
Definition: miscinit.c:1039
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:670
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
Datum textregexreplace(PG_FUNCTION_ARGS)
Definition: regexp.c:658
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13019
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
char * module_pathname
Definition: extension.c:91
bool superuser(void)
Definition: superuser.c:46
Datum replace_text(PG_FUNCTION_ARGS)
Definition: varlena.c:4198

References appendStringInfo(), appendStringInfoString(), Assert(), AtEOXact_GUC(), check_function_bodies, client_min_messages, creating_extension, CStringGetTextDatum, CurrentExtensionObject, StringInfoData::data, DatumGetTextPP, DEBUG1, DirectFunctionCall3Coll(), DirectFunctionCall4Coll(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, execute_sql_string(), extension_is_trusted(), filename, forboth, get_extension_script_filename(), get_namespace_name(), GetUserId(), GetUserIdAndSecContext(), GetUserNameFromId(), GUC_ACTION_SAVE, initStringInfo(), InvalidOid, lfirst, lfirst_oid, list_length(), log_min_messages, ExtensionControlFile::module_pathname, ExtensionControlFile::name, NewGUCNestLevel(), PG_END_TRY, PG_FINALLY, PG_TRY, PGC_S_SESSION, PGC_SUSET, PGC_USERSET, psprintf(), quote_identifier(), read_extension_script_file(), ExtensionControlFile::relocatable, replace_text(), SECURITY_LOCAL_USERID_CHANGE, set_config_option(), set_config_option_ext(), SetUserIdAndSecContext(), ExtensionControlFile::superuser, superuser(), text_to_cstring(), textregexreplace(), ExtensionControlFile::trusted, and WARNING.

Referenced by ApplyExtensionUpdates(), and CreateExtensionInternal().

◆ execute_sql_string()

static void execute_sql_string ( const char *  sql,
const char *  filename 
)
static

Definition at line 917 of file extension.c.

918{
919 script_error_callback_arg callback_arg;
920 ErrorContextCallback scripterrcontext;
921 List *raw_parsetree_list;
923 ListCell *lc1;
924
925 /*
926 * Setup error traceback support for ereport().
927 */
928 callback_arg.sql = sql;
929 callback_arg.filename = filename;
930 callback_arg.stmt_location = -1;
931 callback_arg.stmt_len = -1;
932
933 scripterrcontext.callback = script_error_callback;
934 scripterrcontext.arg = (void *) &callback_arg;
935 scripterrcontext.previous = error_context_stack;
936 error_context_stack = &scripterrcontext;
937
938 /*
939 * Parse the SQL string into a list of raw parse trees.
940 */
941 raw_parsetree_list = pg_parse_query(sql);
942
943 /* All output from SELECTs goes to the bit bucket */
945
946 /*
947 * Do parse analysis, rule rewrite, planning, and execution for each raw
948 * parsetree. We must fully execute each query before beginning parse
949 * analysis on the next one, since there may be interdependencies.
950 */
951 foreach(lc1, raw_parsetree_list)
952 {
953 RawStmt *parsetree = lfirst_node(RawStmt, lc1);
954 MemoryContext per_parsetree_context,
955 oldcontext;
956 List *stmt_list;
957 ListCell *lc2;
958
959 /* Report location of this query for error context callback */
960 callback_arg.stmt_location = parsetree->stmt_location;
961 callback_arg.stmt_len = parsetree->stmt_len;
962
963 /*
964 * We do the work for each parsetree in a short-lived context, to
965 * limit the memory used when there are many commands in the string.
966 */
967 per_parsetree_context =
969 "execute_sql_string per-statement context",
971 oldcontext = MemoryContextSwitchTo(per_parsetree_context);
972
973 /* Be sure parser can see any DDL done so far */
975
976 stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
977 sql,
978 NULL,
979 0,
980 NULL);
981 stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
982
983 foreach(lc2, stmt_list)
984 {
986
988
990
991 if (stmt->utilityStmt == NULL)
992 {
993 QueryDesc *qdesc;
994
995 qdesc = CreateQueryDesc(stmt,
996 NULL,
997 sql,
998 GetActiveSnapshot(), NULL,
999 dest, NULL, NULL, 0);
1000
1001 if (!ExecutorStart(qdesc, 0))
1002 elog(ERROR, "ExecutorStart() failed unexpectedly");
1004 ExecutorFinish(qdesc);
1005 ExecutorEnd(qdesc);
1006
1007 FreeQueryDesc(qdesc);
1008 }
1009 else
1010 {
1011 if (IsA(stmt->utilityStmt, TransactionStmt))
1012 ereport(ERROR,
1013 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1014 errmsg("transaction control statements are not allowed within an extension script")));
1015
1017 sql,
1018 false,
1020 NULL,
1021 NULL,
1022 dest,
1023 NULL);
1024 }
1025
1027 }
1028
1029 /* Clean up per-parsetree context. */
1030 MemoryContextSwitchTo(oldcontext);
1031 MemoryContextDelete(per_parsetree_context);
1032 }
1033
1034 error_context_stack = scripterrcontext.previous;
1035
1036 /* Be sure to advance the command counter after the last script command */
1038}
DestReceiver * CreateDestReceiver(CommandDest dest)
Definition: dest.c:113
@ DestNone
Definition: dest.h:87
ErrorContextCallback * error_context_stack
Definition: elog.c:95
bool ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:128
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:538
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:475
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Definition: execMain.c:365
static void script_error_callback(void *arg)
Definition: extension.c:775
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define CURSOR_OPT_PARALLEL_OK
Definition: parsenodes.h:3385
#define lfirst_node(type, lc)
Definition: pg_list.h:176
List * pg_parse_query(const char *query_string)
Definition: postgres.c:603
List * pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:970
List * pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char *query_string, const Oid *paramTypes, int numParams, QueryEnvironment *queryEnv)
Definition: postgres.c:665
void FreeQueryDesc(QueryDesc *qdesc)
Definition: pquery.c:112
QueryDesc * CreateQueryDesc(PlannedStmt *plannedstmt, CachedPlan *cplan, const char *sourceText, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, ParamListInfo params, QueryEnvironment *queryEnv, int instrument_options)
Definition: pquery.c:72
@ ForwardScanDirection
Definition: sdir.h:28
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:787
struct ErrorContextCallback * previous
Definition: elog.h:296
void(* callback)(void *arg)
Definition: elog.h:297
ParseLoc stmt_location
Definition: parsenodes.h:2072
ParseLoc stmt_len
Definition: parsenodes.h:2073
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:499
@ PROCESS_UTILITY_QUERY
Definition: utility.h:23
void CommandCounterIncrement(void)
Definition: xact.c:1100

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, ErrorContextCallback::arg, ErrorContextCallback::callback, CommandCounterIncrement(), CreateDestReceiver(), CreateQueryDesc(), CurrentMemoryContext, CURSOR_OPT_PARALLEL_OK, generate_unaccent_rules::dest, DestNone, elog, ereport, errcode(), errmsg(), ERROR, error_context_stack, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), script_error_callback_arg::filename, filename, ForwardScanDirection, FreeQueryDesc(), GetActiveSnapshot(), GetTransactionSnapshot(), IsA, lfirst_node, MemoryContextDelete(), MemoryContextSwitchTo(), pg_analyze_and_rewrite_fixedparams(), pg_parse_query(), pg_plan_queries(), PopActiveSnapshot(), ErrorContextCallback::previous, PROCESS_UTILITY_QUERY, ProcessUtility(), PushActiveSnapshot(), script_error_callback(), script_error_callback_arg::sql, stmt, script_error_callback_arg::stmt_len, RawStmt::stmt_len, script_error_callback_arg::stmt_location, and RawStmt::stmt_location.

Referenced by execute_extension_script().

◆ extension_config_remove()

static void extension_config_remove ( Oid  extensionoid,
Oid  tableoid 
)
static

Definition at line 2877 of file extension.c.

2878{
2879 Relation extRel;
2880 ScanKeyData key[1];
2881 SysScanDesc extScan;
2882 HeapTuple extTup;
2883 Datum arrayDatum;
2884 int arrayLength;
2885 int arrayIndex;
2886 bool isnull;
2887 Datum repl_val[Natts_pg_extension];
2888 bool repl_null[Natts_pg_extension];
2889 bool repl_repl[Natts_pg_extension];
2890 ArrayType *a;
2891
2892 /* Find the pg_extension tuple */
2893 extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2894
2895 ScanKeyInit(&key[0],
2896 Anum_pg_extension_oid,
2897 BTEqualStrategyNumber, F_OIDEQ,
2898 ObjectIdGetDatum(extensionoid));
2899
2900 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2901 NULL, 1, key);
2902
2903 extTup = systable_getnext(extScan);
2904
2905 if (!HeapTupleIsValid(extTup)) /* should not happen */
2906 elog(ERROR, "could not find tuple for extension %u",
2907 extensionoid);
2908
2909 /* Search extconfig for the tableoid */
2910 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2911 RelationGetDescr(extRel), &isnull);
2912 if (isnull)
2913 {
2914 /* nothing to do */
2915 a = NULL;
2916 arrayLength = 0;
2917 arrayIndex = -1;
2918 }
2919 else
2920 {
2921 Oid *arrayData;
2922 int i;
2923
2924 a = DatumGetArrayTypeP(arrayDatum);
2925
2926 arrayLength = ARR_DIMS(a)[0];
2927 if (ARR_NDIM(a) != 1 ||
2928 ARR_LBOUND(a)[0] != 1 ||
2929 arrayLength < 0 ||
2930 ARR_HASNULL(a) ||
2931 ARR_ELEMTYPE(a) != OIDOID)
2932 elog(ERROR, "extconfig is not a 1-D Oid array");
2933 arrayData = (Oid *) ARR_DATA_PTR(a);
2934
2935 arrayIndex = -1; /* flag for no deletion needed */
2936
2937 for (i = 0; i < arrayLength; i++)
2938 {
2939 if (arrayData[i] == tableoid)
2940 {
2941 arrayIndex = i; /* index to remove */
2942 break;
2943 }
2944 }
2945 }
2946
2947 /* If tableoid is not in extconfig, nothing to do */
2948 if (arrayIndex < 0)
2949 {
2950 systable_endscan(extScan);
2952 return;
2953 }
2954
2955 /* Modify or delete the extconfig value */
2956 memset(repl_val, 0, sizeof(repl_val));
2957 memset(repl_null, false, sizeof(repl_null));
2958 memset(repl_repl, false, sizeof(repl_repl));
2959
2960 if (arrayLength <= 1)
2961 {
2962 /* removing only element, just set array to null */
2963 repl_null[Anum_pg_extension_extconfig - 1] = true;
2964 }
2965 else
2966 {
2967 /* squeeze out the target element */
2968 Datum *dvalues;
2969 int nelems;
2970 int i;
2971
2972 /* We already checked there are no nulls */
2973 deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
2974
2975 for (i = arrayIndex; i < arrayLength - 1; i++)
2976 dvalues[i] = dvalues[i + 1];
2977
2978 a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
2979
2980 repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2981 }
2982 repl_repl[Anum_pg_extension_extconfig - 1] = true;
2983
2984 /* Modify or delete the extcondition value */
2985 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2986 RelationGetDescr(extRel), &isnull);
2987 if (isnull)
2988 {
2989 elog(ERROR, "extconfig and extcondition arrays do not match");
2990 }
2991 else
2992 {
2993 a = DatumGetArrayTypeP(arrayDatum);
2994
2995 if (ARR_NDIM(a) != 1 ||
2996 ARR_LBOUND(a)[0] != 1 ||
2997 ARR_HASNULL(a) ||
2998 ARR_ELEMTYPE(a) != TEXTOID)
2999 elog(ERROR, "extcondition is not a 1-D text array");
3000 if (ARR_DIMS(a)[0] != arrayLength)
3001 elog(ERROR, "extconfig and extcondition arrays do not match");
3002 }
3003
3004 if (arrayLength <= 1)
3005 {
3006 /* removing only element, just set array to null */
3007 repl_null[Anum_pg_extension_extcondition - 1] = true;
3008 }
3009 else
3010 {
3011 /* squeeze out the target element */
3012 Datum *dvalues;
3013 int nelems;
3014 int i;
3015
3016 /* We already checked there are no nulls */
3017 deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
3018
3019 for (i = arrayIndex; i < arrayLength - 1; i++)
3020 dvalues[i] = dvalues[i + 1];
3021
3022 a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
3023
3024 repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
3025 }
3026 repl_repl[Anum_pg_extension_extcondition - 1] = true;
3027
3028 extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3029 repl_val, repl_null, repl_repl);
3030
3031 CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3032
3033 systable_endscan(extScan);
3034
3036}
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291
#define ARR_LBOUND(a)
Definition: array.h:296
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3697
int i
Definition: isn.c:77

References a, ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_LBOUND, ARR_NDIM, BTEqualStrategyNumber, CatalogTupleUpdate(), construct_array_builtin(), DatumGetArrayTypeP, deconstruct_array_builtin(), elog, ERROR, heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, i, sort-test::key, ObjectIdGetDatum(), PointerGetDatum(), RelationGetDescr, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ExecAlterExtensionContentsRecurse().

◆ extension_file_exists()

bool extension_file_exists ( const char *  extensionName)

Definition at line 2471 of file extension.c.

2472{
2473 bool result = false;
2474 List *locations;
2475 DIR *dir;
2476 struct dirent *de;
2477
2479
2480 foreach_ptr(char, location, locations)
2481 {
2482 dir = AllocateDir(location);
2483
2484 /*
2485 * If the control directory doesn't exist, we want to silently return
2486 * false. Any other error will be reported by ReadDir.
2487 */
2488 if (dir == NULL && errno == ENOENT)
2489 {
2490 /* do nothing */
2491 }
2492 else
2493 {
2494 while ((de = ReadDir(dir, location)) != NULL)
2495 {
2496 char *extname;
2497
2499 continue;
2500
2501 /* extract extension name from 'name.control' filename */
2502 extname = pstrdup(de->d_name);
2503 *strrchr(extname, '.') = '\0';
2504
2505 /* ignore it if it's an auxiliary control file */
2506 if (strstr(extname, "--"))
2507 continue;
2508
2509 /* done if it matches request */
2510 if (strcmp(extname, extensionName) == 0)
2511 {
2512 result = true;
2513 break;
2514 }
2515 }
2516
2517 FreeDir(dir);
2518 }
2519 if (result)
2520 break;
2521 }
2522
2523 return result;
2524}
static bool is_extension_control_filename(const char *filename)
Definition: extension.c:325
static List * get_extension_control_directories(void)
Definition: extension.c:344
int FreeDir(DIR *dir)
Definition: fd.c:3025
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2907
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2973
char * pstrdup(const char *in)
Definition: mcxt.c:2327
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
Definition: dirent.c:26
Definition: dirent.h:10
char d_name[MAX_PATH]
Definition: dirent.h:15

References AllocateDir(), dirent::d_name, foreach_ptr, FreeDir(), get_extension_control_directories(), is_extension_control_filename(), pstrdup(), and ReadDir().

Referenced by CreateFunction(), and ExecuteDoStmt().

◆ extension_is_trusted()

static bool extension_is_trusted ( ExtensionControlFile control)
static

Definition at line 1047 of file extension.c.

1048{
1049 AclResult aclresult;
1050
1051 /* Never trust unless extension's control file says it's okay */
1052 if (!control->trusted)
1053 return false;
1054 /* Allow if user has CREATE privilege on current database */
1055 aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
1056 if (aclresult == ACLCHECK_OK)
1057 return true;
1058 return false;
1059}
Oid MyDatabaseId
Definition: globals.c:95

References ACL_CREATE, ACLCHECK_OK, GetUserId(), MyDatabaseId, object_aclcheck(), and ExtensionControlFile::trusted.

Referenced by execute_extension_script().

◆ find_extension_control_filename()

static char * find_extension_control_filename ( ExtensionControlFile control)
static

Definition at line 412 of file extension.c.

413{
414 char *basename;
415 char *result;
416 List *paths;
417
418 Assert(control->name);
419
420 basename = psprintf("%s.control", control->name);
421
423 result = find_in_paths(basename, paths);
424
425 if (result)
426 {
427 const char *p;
428
429 p = strrchr(result, '/');
430 Assert(p);
431 control->control_dir = pnstrdup(result, p - result);
432 }
433
434 return result;
435}
char * find_in_paths(const char *basename, List *paths)
Definition: extension.c:3878
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:2338

References Assert(), ExtensionControlFile::control_dir, find_in_paths(), get_extension_control_directories(), ExtensionControlFile::name, pnstrdup(), and psprintf().

Referenced by parse_extension_control_file().

◆ find_in_paths()

char * find_in_paths ( const char *  basename,
List paths 
)

Definition at line 3878 of file extension.c.

3879{
3880 ListCell *cell;
3881
3882 foreach(cell, paths)
3883 {
3884 char *path = lfirst(cell);
3885 char *full;
3886
3887 Assert(path != NULL);
3888
3889 path = pstrdup(path);
3890 canonicalize_path(path);
3891
3892 /* only absolute paths */
3893 if (!is_absolute_path(path))
3894 ereport(ERROR,
3895 errcode(ERRCODE_INVALID_NAME),
3896 errmsg("component in parameter \"%s\" is not an absolute path", "extension_control_path"));
3897
3898 full = psprintf("%s/%s", path, basename);
3899
3900 if (pg_file_exists(full))
3901 return full;
3902
3903 pfree(path);
3904 pfree(full);
3905 }
3906
3907 return NULL;
3908}
bool pg_file_exists(const char *name)
Definition: fd.c:503
void pfree(void *pointer)
Definition: mcxt.c:2152
#define is_absolute_path(filename)
Definition: port.h:104
void canonicalize_path(char *path)
Definition: path.c:337

References Assert(), canonicalize_path(), ereport, errcode(), errmsg(), ERROR, is_absolute_path, lfirst, pfree(), pg_file_exists(), psprintf(), and pstrdup().

Referenced by find_extension_control_filename().

◆ find_install_path()

static ExtensionVersionInfo * find_install_path ( List evi_list,
ExtensionVersionInfo evi_target,
List **  best_path 
)
static

Definition at line 1602 of file extension.c.

1604{
1605 ExtensionVersionInfo *evi_start = NULL;
1606 ListCell *lc;
1607
1608 *best_path = NIL;
1609
1610 /*
1611 * We don't expect to be called for an installable target, but if we are,
1612 * the answer is easy: just start from there, with an empty update path.
1613 */
1614 if (evi_target->installable)
1615 return evi_target;
1616
1617 /* Consider all installable versions as start points */
1618 foreach(lc, evi_list)
1619 {
1621 List *path;
1622
1623 if (!evi1->installable)
1624 continue;
1625
1626 /*
1627 * Find shortest path from evi1 to evi_target; but no need to consider
1628 * paths going through other installable versions.
1629 */
1630 path = find_update_path(evi_list, evi1, evi_target, true, true);
1631 if (path == NIL)
1632 continue;
1633
1634 /* Remember best path */
1635 if (evi_start == NULL ||
1636 list_length(path) < list_length(*best_path) ||
1637 (list_length(path) == list_length(*best_path) &&
1638 strcmp(evi_start->name, evi1->name) < 0))
1639 {
1640 evi_start = evi1;
1641 *best_path = path;
1642 }
1643 }
1644
1645 return evi_start;
1646}
static List * find_update_path(List *evi_list, ExtensionVersionInfo *evi_start, ExtensionVersionInfo *evi_target, bool reject_indirect, bool reinitialize)
Definition: extension.c:1509

References find_update_path(), ExtensionVersionInfo::installable, lfirst, list_length(), ExtensionVersionInfo::name, and NIL.

Referenced by CreateExtensionInternal(), and get_available_versions_for_extension().

◆ find_update_path()

static List * find_update_path ( List evi_list,
ExtensionVersionInfo evi_start,
ExtensionVersionInfo evi_target,
bool  reject_indirect,
bool  reinitialize 
)
static

Definition at line 1509 of file extension.c.

1514{
1515 List *result;
1517 ListCell *lc;
1518
1519 /* Caller error if start == target */
1520 Assert(evi_start != evi_target);
1521 /* Caller error if reject_indirect and target is installable */
1522 Assert(!(reject_indirect && evi_target->installable));
1523
1524 if (reinitialize)
1525 {
1526 foreach(lc, evi_list)
1527 {
1528 evi = (ExtensionVersionInfo *) lfirst(lc);
1529 evi->distance_known = false;
1530 evi->distance = INT_MAX;
1531 evi->previous = NULL;
1532 }
1533 }
1534
1535 evi_start->distance = 0;
1536
1537 while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
1538 {
1539 if (evi->distance == INT_MAX)
1540 break; /* all remaining vertices are unreachable */
1541 evi->distance_known = true;
1542 if (evi == evi_target)
1543 break; /* found shortest path to target */
1544 foreach(lc, evi->reachable)
1545 {
1547 int newdist;
1548
1549 /* if reject_indirect, treat installable versions as unreachable */
1550 if (reject_indirect && evi2->installable)
1551 continue;
1552 newdist = evi->distance + 1;
1553 if (newdist < evi2->distance)
1554 {
1555 evi2->distance = newdist;
1556 evi2->previous = evi;
1557 }
1558 else if (newdist == evi2->distance &&
1559 evi2->previous != NULL &&
1560 strcmp(evi->name, evi2->previous->name) < 0)
1561 {
1562 /*
1563 * Break ties in favor of the version name that comes first
1564 * according to strcmp(). This behavior is undocumented and
1565 * users shouldn't rely on it. We do it just to ensure that
1566 * if there is a tie, the update path that is chosen does not
1567 * depend on random factors like the order in which directory
1568 * entries get visited.
1569 */
1570 evi2->previous = evi;
1571 }
1572 }
1573 }
1574
1575 /* Return NIL if target is not reachable from start */
1576 if (!evi_target->distance_known)
1577 return NIL;
1578
1579 /* Build and return list of version names representing the update path */
1580 result = NIL;
1581 for (evi = evi_target; evi != evi_start; evi = evi->previous)
1582 result = lcons(evi->name, result);
1583
1584 return result;
1585}
static ExtensionVersionInfo * get_nearest_unprocessed_vertex(List *evi_list)
Definition: extension.c:1375
List * lcons(void *datum, List *list)
Definition: list.c:495
struct ExtensionVersionInfo * previous
Definition: extension.c:115

References Assert(), ExtensionVersionInfo::distance, ExtensionVersionInfo::distance_known, get_nearest_unprocessed_vertex(), ExtensionVersionInfo::installable, lcons(), lfirst, ExtensionVersionInfo::name, NIL, ExtensionVersionInfo::previous, and ExtensionVersionInfo::reachable.

Referenced by find_install_path(), identify_update_path(), and pg_extension_update_paths().

◆ get_available_versions_for_extension()

static void get_available_versions_for_extension ( ExtensionControlFile pcontrol,
Tuplestorestate tupstore,
TupleDesc  tupdesc 
)
static

Definition at line 2357 of file extension.c.

2360{
2361 List *evi_list;
2362 ListCell *lc;
2363
2364 /* Extract the version update graph from the script directory */
2365 evi_list = get_ext_ver_list(pcontrol);
2366
2367 /* For each installable version ... */
2368 foreach(lc, evi_list)
2369 {
2371 ExtensionControlFile *control;
2372 Datum values[8];
2373 bool nulls[8];
2374 ListCell *lc2;
2375
2376 if (!evi->installable)
2377 continue;
2378
2379 /*
2380 * Fetch parameters for specific version (pcontrol is not changed)
2381 */
2382 control = read_extension_aux_control_file(pcontrol, evi->name);
2383
2384 memset(values, 0, sizeof(values));
2385 memset(nulls, 0, sizeof(nulls));
2386
2387 /* name */
2389 CStringGetDatum(control->name));
2390 /* version */
2391 values[1] = CStringGetTextDatum(evi->name);
2392 /* superuser */
2393 values[2] = BoolGetDatum(control->superuser);
2394 /* trusted */
2395 values[3] = BoolGetDatum(control->trusted);
2396 /* relocatable */
2397 values[4] = BoolGetDatum(control->relocatable);
2398 /* schema */
2399 if (control->schema == NULL)
2400 nulls[5] = true;
2401 else
2403 CStringGetDatum(control->schema));
2404 /* requires */
2405 if (control->requires == NIL)
2406 nulls[6] = true;
2407 else
2408 values[6] = convert_requires_to_datum(control->requires);
2409 /* comment */
2410 if (control->comment == NULL)
2411 nulls[7] = true;
2412 else
2413 values[7] = CStringGetTextDatum(control->comment);
2414
2415 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2416
2417 /*
2418 * Find all non-directly-installable versions that would be installed
2419 * starting from this version, and report them, inheriting the
2420 * parameters that aren't changed in updates from this version.
2421 */
2422 foreach(lc2, evi_list)
2423 {
2425 List *best_path;
2426
2427 if (evi2->installable)
2428 continue;
2429 if (find_install_path(evi_list, evi2, &best_path) == evi)
2430 {
2431 /*
2432 * Fetch parameters for this version (pcontrol is not changed)
2433 */
2434 control = read_extension_aux_control_file(pcontrol, evi2->name);
2435
2436 /* name stays the same */
2437 /* version */
2438 values[1] = CStringGetTextDatum(evi2->name);
2439 /* superuser */
2440 values[2] = BoolGetDatum(control->superuser);
2441 /* trusted */
2442 values[3] = BoolGetDatum(control->trusted);
2443 /* relocatable */
2444 values[4] = BoolGetDatum(control->relocatable);
2445 /* schema stays the same */
2446 /* requires */
2447 if (control->requires == NIL)
2448 nulls[6] = true;
2449 else
2450 {
2451 values[6] = convert_requires_to_datum(control->requires);
2452 nulls[6] = false;
2453 }
2454 /* comment stays the same */
2455
2456 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2457 }
2458 }
2459 }
2460}
static Datum convert_requires_to_datum(List *requires)
Definition: extension.c:2530
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:784

References BoolGetDatum(), ExtensionControlFile::comment, convert_requires_to_datum(), CStringGetDatum(), CStringGetTextDatum, DirectFunctionCall1, find_install_path(), get_ext_ver_list(), ExtensionVersionInfo::installable, lfirst, ExtensionControlFile::name, ExtensionVersionInfo::name, namein(), NIL, read_extension_aux_control_file(), ExtensionControlFile::relocatable, ExtensionControlFile::schema, ExtensionControlFile::superuser, ExtensionControlFile::trusted, tuplestore_putvalues(), and values.

Referenced by pg_available_extension_versions().

◆ get_ext_ver_info()

static ExtensionVersionInfo * get_ext_ver_info ( const char *  versionname,
List **  evi_list 
)
static

Definition at line 1342 of file extension.c.

1343{
1345 ListCell *lc;
1346
1347 foreach(lc, *evi_list)
1348 {
1349 evi = (ExtensionVersionInfo *) lfirst(lc);
1350 if (strcmp(evi->name, versionname) == 0)
1351 return evi;
1352 }
1353
1355 evi->name = pstrdup(versionname);
1356 evi->reachable = NIL;
1357 evi->installable = false;
1358 /* initialize for later application of Dijkstra's algorithm */
1359 evi->distance_known = false;
1360 evi->distance = INT_MAX;
1361 evi->previous = NULL;
1362
1363 *evi_list = lappend(*evi_list, evi);
1364
1365 return evi;
1366}
List * lappend(List *list, void *datum)
Definition: list.c:339

References ExtensionVersionInfo::distance, ExtensionVersionInfo::distance_known, ExtensionVersionInfo::installable, lappend(), lfirst, ExtensionVersionInfo::name, NIL, palloc(), ExtensionVersionInfo::previous, pstrdup(), and ExtensionVersionInfo::reachable.

Referenced by CreateExtensionInternal(), get_ext_ver_list(), and identify_update_path().

◆ get_ext_ver_list()

static List * get_ext_ver_list ( ExtensionControlFile control)
static

Definition at line 1403 of file extension.c.

1404{
1405 List *evi_list = NIL;
1406 int extnamelen = strlen(control->name);
1407 char *location;
1408 DIR *dir;
1409 struct dirent *de;
1410
1411 location = get_extension_script_directory(control);
1412 dir = AllocateDir(location);
1413 while ((de = ReadDir(dir, location)) != NULL)
1414 {
1415 char *vername;
1416 char *vername2;
1419
1420 /* must be a .sql file ... */
1422 continue;
1423
1424 /* ... matching extension name followed by separator */
1425 if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
1426 de->d_name[extnamelen] != '-' ||
1427 de->d_name[extnamelen + 1] != '-')
1428 continue;
1429
1430 /* extract version name(s) from 'extname--something.sql' filename */
1431 vername = pstrdup(de->d_name + extnamelen + 2);
1432 *strrchr(vername, '.') = '\0';
1433 vername2 = strstr(vername, "--");
1434 if (!vername2)
1435 {
1436 /* It's an install, not update, script; record its version name */
1437 evi = get_ext_ver_info(vername, &evi_list);
1438 evi->installable = true;
1439 continue;
1440 }
1441 *vername2 = '\0'; /* terminate first version */
1442 vername2 += 2; /* and point to second */
1443
1444 /* if there's a third --, it's bogus, ignore it */
1445 if (strstr(vername2, "--"))
1446 continue;
1447
1448 /* Create ExtensionVersionInfos and link them together */
1449 evi = get_ext_ver_info(vername, &evi_list);
1450 evi2 = get_ext_ver_info(vername2, &evi_list);
1451 evi->reachable = lappend(evi->reachable, evi2);
1452 }
1453 FreeDir(dir);
1454
1455 return evi_list;
1456}
static bool is_extension_script_filename(const char *filename)
Definition: extension.c:333
static char * get_extension_script_directory(ExtensionControlFile *control)
Definition: extension.c:438

References AllocateDir(), dirent::d_name, FreeDir(), get_ext_ver_info(), get_extension_script_directory(), ExtensionVersionInfo::installable, is_extension_script_filename(), lappend(), ExtensionControlFile::name, NIL, pstrdup(), ExtensionVersionInfo::reachable, and ReadDir().

Referenced by CreateExtensionInternal(), get_available_versions_for_extension(), identify_update_path(), and pg_extension_update_paths().

◆ get_extension_aux_control_filename()

static char * get_extension_aux_control_filename ( ExtensionControlFile control,
const char *  version 
)
static

Definition at line 457 of file extension.c.

459{
460 char *result;
461 char *scriptdir;
462
463 scriptdir = get_extension_script_directory(control);
464
465 result = (char *) palloc(MAXPGPATH);
466 snprintf(result, MAXPGPATH, "%s/%s--%s.control",
467 scriptdir, control->name, version);
468
469 pfree(scriptdir);
470
471 return result;
472}
#define MAXPGPATH
#define snprintf
Definition: port.h:239

References get_extension_script_directory(), MAXPGPATH, ExtensionControlFile::name, palloc(), pfree(), and snprintf.

Referenced by parse_extension_control_file().

◆ get_extension_control_directories()

static List * get_extension_control_directories ( void  )
static

Definition at line 344 of file extension.c.

345{
346 char sharepath[MAXPGPATH];
347 char *system_dir;
348 char *ecp;
349 List *paths = NIL;
350
351 get_share_path(my_exec_path, sharepath);
352
353 system_dir = psprintf("%s/extension", sharepath);
354
355 if (strlen(Extension_control_path) == 0)
356 {
357 paths = lappend(paths, system_dir);
358 }
359 else
360 {
361 /* Duplicate the string so we can modify it */
363
364 for (;;)
365 {
366 int len;
367 char *mangled;
368 char *piece = first_path_var_separator(ecp);
369
370 /* Get the length of the next path on ecp */
371 if (piece == NULL)
372 len = strlen(ecp);
373 else
374 len = piece - ecp;
375
376 /* Copy the next path found on ecp */
377 piece = palloc(len + 1);
378 strlcpy(piece, ecp, len + 1);
379
380 /*
381 * Substitute the path macro if needed or append "extension"
382 * suffix if it is a custom extension control path.
383 */
384 if (strcmp(piece, "$system") == 0)
385 mangled = substitute_path_macro(piece, "$system", system_dir);
386 else
387 mangled = psprintf("%s/extension", piece);
388
389 pfree(piece);
390
391 /* Canonicalize the path based on the OS and add to the list */
392 canonicalize_path(mangled);
393 paths = lappend(paths, mangled);
394
395 /* Break if ecp is empty or move to the next path on ecp */
396 if (ecp[len] == '\0')
397 break;
398 else
399 ecp += len + 1;
400 }
401 }
402
403 return paths;
404}
char * substitute_path_macro(const char *str, const char *macro, const char *value)
Definition: dfmgr.c:529
char * Extension_control_path
Definition: extension.c:74
char my_exec_path[MAXPGPATH]
Definition: globals.c:82
const void size_t len
void get_share_path(const char *my_exec_path, char *ret_path)
Definition: path.c:902
char * first_path_var_separator(const char *pathlist)
Definition: path.c:127
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45

References canonicalize_path(), Extension_control_path, first_path_var_separator(), get_share_path(), lappend(), len, MAXPGPATH, my_exec_path, NIL, palloc(), pfree(), psprintf(), pstrdup(), strlcpy(), and substitute_path_macro().

Referenced by extension_file_exists(), find_extension_control_filename(), pg_available_extension_versions(), and pg_available_extensions().

◆ get_extension_name()

char * get_extension_name ( Oid  ext_oid)

Definition at line 189 of file extension.c.

190{
191 char *result;
192 HeapTuple tuple;
193
194 tuple = SearchSysCache1(EXTENSIONOID, ObjectIdGetDatum(ext_oid));
195
196 if (!HeapTupleIsValid(tuple))
197 return NULL;
198
199 result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
200 ReleaseSysCache(tuple);
201
202 return result;
203}
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221

References GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), pstrdup(), ReleaseSysCache(), and SearchSysCache1().

Referenced by AlterExtensionNamespace(), checkMembershipInCurrentExtension(), ExecAlterExtensionContentsRecurse(), getObjectDescription(), getObjectIdentityParts(), recordDependencyOnCurrentExtension(), and RemoveExtensionById().

◆ get_extension_oid()

Oid get_extension_oid ( const char *  extname,
bool  missing_ok 
)

Definition at line 167 of file extension.c.

168{
169 Oid result;
170
171 result = GetSysCacheOid1(EXTENSIONNAME, Anum_pg_extension_oid,
172 CStringGetDatum(extname));
173
174 if (!OidIsValid(result) && !missing_ok)
176 (errcode(ERRCODE_UNDEFINED_OBJECT),
177 errmsg("extension \"%s\" does not exist",
178 extname)));
179
180 return result;
181}
#define GetSysCacheOid1(cacheId, oidcol, key1)
Definition: syscache.h:109

References CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, GetSysCacheOid1, and OidIsValid.

Referenced by AlterExtensionNamespace(), binary_upgrade_create_empty_extension(), CreateExtension(), ExtractExtensionList(), get_object_address_unqualified(), and get_required_extension().

◆ get_extension_schema()

Oid get_extension_schema ( Oid  ext_oid)

Definition at line 211 of file extension.c.

212{
213 Oid result;
214 HeapTuple tuple;
215
216 tuple = SearchSysCache1(EXTENSIONOID, ObjectIdGetDatum(ext_oid));
217
218 if (!HeapTupleIsValid(tuple))
219 return InvalidOid;
220
221 result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
222 ReleaseSysCache(tuple);
223
224 return result;
225}

References GETSTRUCT(), HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), ReleaseSysCache(), and SearchSysCache1().

Referenced by ApplyExtensionUpdates(), CreateExtensionInternal(), and ExecAlterExtensionContentsRecurse().

◆ get_extension_script_directory()

static char * get_extension_script_directory ( ExtensionControlFile control)
static

Definition at line 438 of file extension.c.

439{
440 /*
441 * The directory parameter can be omitted, absolute, or relative to the
442 * installation's base directory, which can be the sharedir or a custom
443 * path that it was set extension_control_path. It depends where the
444 * .control file was found.
445 */
446 if (!control->directory)
447 return pstrdup(control->control_dir);
448
449 if (is_absolute_path(control->directory))
450 return pstrdup(control->directory);
451
452 Assert(control->basedir != NULL);
453 return psprintf("%s/%s", control->basedir, control->directory);
454}

References Assert(), ExtensionControlFile::basedir, ExtensionControlFile::control_dir, ExtensionControlFile::directory, is_absolute_path, psprintf(), and pstrdup().

Referenced by get_ext_ver_list(), get_extension_aux_control_filename(), and get_extension_script_filename().

◆ get_extension_script_filename()

static char * get_extension_script_filename ( ExtensionControlFile control,
const char *  from_version,
const char *  version 
)
static

Definition at line 475 of file extension.c.

477{
478 char *result;
479 char *scriptdir;
480
481 scriptdir = get_extension_script_directory(control);
482
483 result = (char *) palloc(MAXPGPATH);
484 if (from_version)
485 snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
486 scriptdir, control->name, from_version, version);
487 else
488 snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
489 scriptdir, control->name, version);
490
491 pfree(scriptdir);
492
493 return result;
494}

References get_extension_script_directory(), MAXPGPATH, ExtensionControlFile::name, palloc(), pfree(), and snprintf.

Referenced by CreateExtensionInternal(), and execute_extension_script().

◆ get_nearest_unprocessed_vertex()

static ExtensionVersionInfo * get_nearest_unprocessed_vertex ( List evi_list)
static

Definition at line 1375 of file extension.c.

1376{
1377 ExtensionVersionInfo *evi = NULL;
1378 ListCell *lc;
1379
1380 foreach(lc, evi_list)
1381 {
1383
1384 /* only vertices whose distance is still uncertain are candidates */
1385 if (evi2->distance_known)
1386 continue;
1387 /* remember the closest such vertex */
1388 if (evi == NULL ||
1389 evi->distance > evi2->distance)
1390 evi = evi2;
1391 }
1392
1393 return evi;
1394}

References ExtensionVersionInfo::distance, ExtensionVersionInfo::distance_known, and lfirst.

Referenced by find_update_path().

◆ get_required_extension()

static Oid get_required_extension ( char *  reqExtensionName,
char *  extensionName,
char *  origSchemaName,
bool  cascade,
List parents,
bool  is_create 
)
static

Definition at line 1896 of file extension.c.

1902{
1903 Oid reqExtensionOid;
1904
1905 reqExtensionOid = get_extension_oid(reqExtensionName, true);
1906 if (!OidIsValid(reqExtensionOid))
1907 {
1908 if (cascade)
1909 {
1910 /* Must install it. */
1911 ObjectAddress addr;
1912 List *cascade_parents;
1913 ListCell *lc;
1914
1915 /* Check extension name validity before trying to cascade. */
1916 check_valid_extension_name(reqExtensionName);
1917
1918 /* Check for cyclic dependency between extensions. */
1919 foreach(lc, parents)
1920 {
1921 char *pname = (char *) lfirst(lc);
1922
1923 if (strcmp(pname, reqExtensionName) == 0)
1924 ereport(ERROR,
1925 (errcode(ERRCODE_INVALID_RECURSION),
1926 errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
1927 reqExtensionName, extensionName)));
1928 }
1929
1931 (errmsg("installing required extension \"%s\"",
1932 reqExtensionName)));
1933
1934 /* Add current extension to list of parents to pass down. */
1935 cascade_parents = lappend(list_copy(parents), extensionName);
1936
1937 /*
1938 * Create the required extension. We propagate the SCHEMA option
1939 * if any, and CASCADE, but no other options.
1940 */
1941 addr = CreateExtensionInternal(reqExtensionName,
1942 origSchemaName,
1943 NULL,
1944 cascade,
1945 cascade_parents,
1946 is_create);
1947
1948 /* Get its newly-assigned OID. */
1949 reqExtensionOid = addr.objectId;
1950 }
1951 else
1952 ereport(ERROR,
1953 (errcode(ERRCODE_UNDEFINED_OBJECT),
1954 errmsg("required extension \"%s\" is not installed",
1955 reqExtensionName),
1956 is_create ?
1957 errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
1958 }
1959
1960 return reqExtensionOid;
1961}
List * list_copy(const List *oldlist)
Definition: list.c:1573

References check_valid_extension_name(), CreateExtensionInternal(), ereport, errcode(), errhint(), errmsg(), ERROR, get_extension_oid(), lappend(), lfirst, list_copy(), NOTICE, ObjectAddress::objectId, and OidIsValid.

Referenced by ApplyExtensionUpdates(), and CreateExtensionInternal().

◆ identify_update_path()

static List * identify_update_path ( ExtensionControlFile control,
const char *  oldVersion,
const char *  newVersion 
)
static

Definition at line 1466 of file extension.c.

1468{
1469 List *result;
1470 List *evi_list;
1471 ExtensionVersionInfo *evi_start;
1472 ExtensionVersionInfo *evi_target;
1473
1474 /* Extract the version update graph from the script directory */
1475 evi_list = get_ext_ver_list(control);
1476
1477 /* Initialize start and end vertices */
1478 evi_start = get_ext_ver_info(oldVersion, &evi_list);
1479 evi_target = get_ext_ver_info(newVersion, &evi_list);
1480
1481 /* Find shortest path */
1482 result = find_update_path(evi_list, evi_start, evi_target, false, false);
1483
1484 if (result == NIL)
1485 ereport(ERROR,
1486 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1487 errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
1488 control->name, oldVersion, newVersion)));
1489
1490 return result;
1491}

References ereport, errcode(), errmsg(), ERROR, find_update_path(), get_ext_ver_info(), get_ext_ver_list(), ExtensionControlFile::name, and NIL.

Referenced by ExecAlterExtensionStmt().

◆ InsertExtensionTuple()

ObjectAddress InsertExtensionTuple ( const char *  extName,
Oid  extOwner,
Oid  schemaOid,
bool  relocatable,
const char *  extVersion,
Datum  extConfig,
Datum  extCondition,
List requiredExtensions 
)

Definition at line 2065 of file extension.c.

2069{
2070 Oid extensionOid;
2071 Relation rel;
2072 Datum values[Natts_pg_extension];
2073 bool nulls[Natts_pg_extension];
2074 HeapTuple tuple;
2075 ObjectAddress myself;
2076 ObjectAddress nsp;
2077 ObjectAddresses *refobjs;
2078 ListCell *lc;
2079
2080 /*
2081 * Build and insert the pg_extension tuple
2082 */
2083 rel = table_open(ExtensionRelationId, RowExclusiveLock);
2084
2085 memset(values, 0, sizeof(values));
2086 memset(nulls, 0, sizeof(nulls));
2087
2088 extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
2089 Anum_pg_extension_oid);
2090 values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
2091 values[Anum_pg_extension_extname - 1] =
2093 values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
2094 values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
2095 values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
2096 values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
2097
2098 if (extConfig == PointerGetDatum(NULL))
2099 nulls[Anum_pg_extension_extconfig - 1] = true;
2100 else
2101 values[Anum_pg_extension_extconfig - 1] = extConfig;
2102
2103 if (extCondition == PointerGetDatum(NULL))
2104 nulls[Anum_pg_extension_extcondition - 1] = true;
2105 else
2106 values[Anum_pg_extension_extcondition - 1] = extCondition;
2107
2108 tuple = heap_form_tuple(rel->rd_att, values, nulls);
2109
2110 CatalogTupleInsert(rel, tuple);
2111
2112 heap_freetuple(tuple);
2114
2115 /*
2116 * Record dependencies on owner, schema, and prerequisite extensions
2117 */
2118 recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
2119
2120 refobjs = new_object_addresses();
2121
2122 ObjectAddressSet(myself, ExtensionRelationId, extensionOid);
2123
2124 ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
2125 add_exact_object_address(&nsp, refobjs);
2126
2127 foreach(lc, requiredExtensions)
2128 {
2129 Oid reqext = lfirst_oid(lc);
2130 ObjectAddress otherext;
2131
2132 ObjectAddressSet(otherext, ExtensionRelationId, reqext);
2133 add_exact_object_address(&otherext, refobjs);
2134 }
2135
2136 /* Record all of them (this includes duplicate elimination) */
2138 free_object_addresses(refobjs);
2139
2140 /* Post creation hook for new extension */
2141 InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
2142
2143 return myself;
2144}
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:450
void record_object_address_dependencies(const ObjectAddress *depender, ObjectAddresses *referenced, DependencyType behavior)
Definition: dependency.c:2757
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2548
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2788
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1117
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:168
TupleDesc rd_att
Definition: rel.h:112

References add_exact_object_address(), BoolGetDatum(), CatalogTupleInsert(), CStringGetDatum(), CStringGetTextDatum, DEPENDENCY_NORMAL, DirectFunctionCall1, free_object_addresses(), GetNewOidWithIndex(), heap_form_tuple(), heap_freetuple(), InvokeObjectPostCreateHook, lfirst_oid, namein(), new_object_addresses(), ObjectAddressSet, ObjectIdGetDatum(), PointerGetDatum(), RelationData::rd_att, record_object_address_dependencies(), recordDependencyOnOwner(), RowExclusiveLock, table_close(), table_open(), and values.

Referenced by binary_upgrade_create_empty_extension(), and CreateExtensionInternal().

◆ is_extension_control_filename()

static bool is_extension_control_filename ( const char *  filename)
static

Definition at line 325 of file extension.c.

326{
327 const char *extension = strrchr(filename, '.');
328
329 return (extension != NULL) && (strcmp(extension, ".control") == 0);
330}

References filename.

Referenced by extension_file_exists(), pg_available_extension_versions(), and pg_available_extensions().

◆ is_extension_script_filename()

static bool is_extension_script_filename ( const char *  filename)
static

Definition at line 333 of file extension.c.

334{
335 const char *extension = strrchr(filename, '.');
336
337 return (extension != NULL) && (strcmp(extension, ".sql") == 0);
338}

References filename.

Referenced by get_ext_ver_list().

◆ new_ExtensionControlFile()

static ExtensionControlFile * new_ExtensionControlFile ( const char *  extname)
static

Definition at line 3853 of file extension.c.

3854{
3855 /*
3856 * Set up default values. Pointer fields are initially null.
3857 */
3859
3860 control->name = pstrdup(extname);
3861 control->relocatable = false;
3862 control->superuser = true;
3863 control->trusted = false;
3864 control->encoding = -1;
3865
3866 return control;
3867}
#define palloc0_object(type)
Definition: fe_memutils.h:75

References ExtensionControlFile::encoding, ExtensionControlFile::name, palloc0_object, pstrdup(), ExtensionControlFile::relocatable, ExtensionControlFile::superuser, and ExtensionControlFile::trusted.

Referenced by pg_available_extension_versions(), pg_available_extensions(), and read_extension_control_file().

◆ parse_extension_control_file()

static void parse_extension_control_file ( ExtensionControlFile control,
const char *  version 
)
static

Definition at line 512 of file extension.c.

514{
515 char *filename;
516 FILE *file;
517 ConfigVariable *item,
518 *head = NULL,
519 *tail = NULL;
520
521 /*
522 * Locate the file to read. Auxiliary files are optional.
523 */
524 if (version)
526 else
527 {
528 /*
529 * If control_dir is already set, use it, else do a path search.
530 */
531 if (control->control_dir)
532 {
533 filename = psprintf("%s/%s.control", control->control_dir, control->name);
534 }
535 else
537 }
538
539 if (!filename)
540 {
542 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
543 errmsg("extension \"%s\" is not available", control->name),
544 errhint("The extension must first be installed on the system where PostgreSQL is running.")));
545 }
546
547 /* Assert that the control_dir ends with /extension */
548 Assert(control->control_dir != NULL);
549 Assert(strcmp(control->control_dir + strlen(control->control_dir) - strlen("/extension"), "/extension") == 0);
550
551 control->basedir = pnstrdup(
552 control->control_dir,
553 strlen(control->control_dir) - strlen("/extension"));
554
555 if ((file = AllocateFile(filename, "r")) == NULL)
556 {
557 /* no complaint for missing auxiliary file */
558 if (errno == ENOENT && version)
559 {
561 return;
562 }
563
566 errmsg("could not open extension control file \"%s\": %m",
567 filename)));
568 }
569
570 /*
571 * Parse the file content, using GUC's file parsing code. We need not
572 * check the return value since any errors will be thrown at ERROR level.
573 */
575 &head, &tail);
576
577 FreeFile(file);
578
579 /*
580 * Convert the ConfigVariable list into ExtensionControlFile entries.
581 */
582 for (item = head; item != NULL; item = item->next)
583 {
584 if (strcmp(item->name, "directory") == 0)
585 {
586 if (version)
588 (errcode(ERRCODE_SYNTAX_ERROR),
589 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
590 item->name)));
591
592 control->directory = pstrdup(item->value);
593 }
594 else if (strcmp(item->name, "default_version") == 0)
595 {
596 if (version)
598 (errcode(ERRCODE_SYNTAX_ERROR),
599 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
600 item->name)));
601
602 control->default_version = pstrdup(item->value);
603 }
604 else if (strcmp(item->name, "module_pathname") == 0)
605 {
606 control->module_pathname = pstrdup(item->value);
607 }
608 else if (strcmp(item->name, "comment") == 0)
609 {
610 control->comment = pstrdup(item->value);
611 }
612 else if (strcmp(item->name, "schema") == 0)
613 {
614 control->schema = pstrdup(item->value);
615 }
616 else if (strcmp(item->name, "relocatable") == 0)
617 {
618 if (!parse_bool(item->value, &control->relocatable))
620 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
621 errmsg("parameter \"%s\" requires a Boolean value",
622 item->name)));
623 }
624 else if (strcmp(item->name, "superuser") == 0)
625 {
626 if (!parse_bool(item->value, &control->superuser))
628 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
629 errmsg("parameter \"%s\" requires a Boolean value",
630 item->name)));
631 }
632 else if (strcmp(item->name, "trusted") == 0)
633 {
634 if (!parse_bool(item->value, &control->trusted))
636 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
637 errmsg("parameter \"%s\" requires a Boolean value",
638 item->name)));
639 }
640 else if (strcmp(item->name, "encoding") == 0)
641 {
642 control->encoding = pg_valid_server_encoding(item->value);
643 if (control->encoding < 0)
645 (errcode(ERRCODE_UNDEFINED_OBJECT),
646 errmsg("\"%s\" is not a valid encoding name",
647 item->value)));
648 }
649 else if (strcmp(item->name, "requires") == 0)
650 {
651 /* Need a modifiable copy of string */
652 char *rawnames = pstrdup(item->value);
653
654 /* Parse string into list of identifiers */
655 if (!SplitIdentifierString(rawnames, ',', &control->requires))
656 {
657 /* syntax error in name list */
659 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
660 errmsg("parameter \"%s\" must be a list of extension names",
661 item->name)));
662 }
663 }
664 else if (strcmp(item->name, "no_relocate") == 0)
665 {
666 /* Need a modifiable copy of string */
667 char *rawnames = pstrdup(item->value);
668
669 /* Parse string into list of identifiers */
670 if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
671 {
672 /* syntax error in name list */
674 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
675 errmsg("parameter \"%s\" must be a list of extension names",
676 item->name)));
677 }
678 }
679 else
681 (errcode(ERRCODE_SYNTAX_ERROR),
682 errmsg("unrecognized parameter \"%s\" in file \"%s\"",
683 item->name, filename)));
684 }
685
687
688 if (control->relocatable && control->schema != NULL)
690 (errcode(ERRCODE_SYNTAX_ERROR),
691 errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
692
694}
bool parse_bool(const char *value, bool *result)
Definition: bool.c:31
#define CONF_FILE_START_DEPTH
Definition: conffiles.h:17
int errcode_for_file_access(void)
Definition: elog.c:877
static char * get_extension_aux_control_filename(ExtensionControlFile *control, const char *version)
Definition: extension.c:457
static char * find_extension_control_filename(ExtensionControlFile *control)
Definition: extension.c:412
int FreeFile(FILE *file)
Definition: fd.c:2843
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2644
void FreeConfigVariables(ConfigVariable *list)
Definition: guc-file.l:617
bool ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, ConfigVariable **head_p, ConfigVariable **tail_p)
Definition: guc-file.l:350
#define pg_valid_server_encoding
Definition: pg_wchar.h:631
char * name
Definition: guc.h:141
struct ConfigVariable * next
Definition: guc.h:148
char * value
Definition: guc.h:142
bool SplitIdentifierString(char *rawstring, char separator, List **namelist)
Definition: varlena.c:3525

References AllocateFile(), Assert(), ExtensionControlFile::basedir, ExtensionControlFile::comment, CONF_FILE_START_DEPTH, ExtensionControlFile::control_dir, ExtensionControlFile::default_version, ExtensionControlFile::directory, ExtensionControlFile::encoding, ereport, errcode(), errcode_for_file_access(), errhint(), errmsg(), ERROR, filename, find_extension_control_filename(), FreeConfigVariables(), FreeFile(), get_extension_aux_control_filename(), ExtensionControlFile::module_pathname, ExtensionControlFile::name, ConfigVariable::name, ConfigVariable::next, ExtensionControlFile::no_relocate, parse_bool(), ParseConfigFp(), pfree(), pg_valid_server_encoding, pnstrdup(), psprintf(), pstrdup(), ExtensionControlFile::relocatable, ExtensionControlFile::schema, SplitIdentifierString(), ExtensionControlFile::superuser, ExtensionControlFile::trusted, and ConfigVariable::value.

Referenced by pg_available_extension_versions(), pg_available_extensions(), read_extension_aux_control_file(), and read_extension_control_file().

◆ pg_available_extension_versions()

Datum pg_available_extension_versions ( PG_FUNCTION_ARGS  )

Definition at line 2293 of file extension.c.

2294{
2295 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2296 List *locations;
2297 DIR *dir;
2298 struct dirent *de;
2299
2300 /* Build tuplestore to hold the result rows */
2301 InitMaterializedSRF(fcinfo, 0);
2302
2304
2305 foreach_ptr(char, location, locations)
2306 {
2307 dir = AllocateDir(location);
2308
2309 /*
2310 * If the control directory doesn't exist, we want to silently return
2311 * an empty set. Any other error will be reported by ReadDir.
2312 */
2313 if (dir == NULL && errno == ENOENT)
2314 {
2315 /* do nothing */
2316 }
2317 else
2318 {
2319 while ((de = ReadDir(dir, location)) != NULL)
2320 {
2321 ExtensionControlFile *control;
2322 char *extname;
2323
2324 if (!is_extension_control_filename(de->d_name))
2325 continue;
2326
2327 /* extract extension name from 'name.control' filename */
2328 extname = pstrdup(de->d_name);
2329 *strrchr(extname, '.') = '\0';
2330
2331 /* ignore it if it's an auxiliary control file */
2332 if (strstr(extname, "--"))
2333 continue;
2334
2335 /* read the control file */
2336 control = new_ExtensionControlFile(extname);
2337 control->control_dir = pstrdup(location);
2338 parse_extension_control_file(control, NULL);
2339
2340 /* scan extension's script directory for install scripts */
2342 rsinfo->setDesc);
2343 }
2344
2345 FreeDir(dir);
2346 }
2347 }
2348
2349 return (Datum) 0;
2350}
static void parse_extension_control_file(ExtensionControlFile *control, const char *version)
Definition: extension.c:512
static ExtensionControlFile * new_ExtensionControlFile(const char *extname)
Definition: extension.c:3853
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol, Tuplestorestate *tupstore, TupleDesc tupdesc)
Definition: extension.c:2357
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
TupleDesc setDesc
Definition: execnodes.h:359
Tuplestorestate * setResult
Definition: execnodes.h:358

References AllocateDir(), ExtensionControlFile::control_dir, foreach_ptr, FreeDir(), get_available_versions_for_extension(), get_extension_control_directories(), InitMaterializedSRF(), is_extension_control_filename(), new_ExtensionControlFile(), parse_extension_control_file(), pstrdup(), ReadDir(), ReturnSetInfo::setDesc, and ReturnSetInfo::setResult.

◆ pg_available_extensions()

Datum pg_available_extensions ( PG_FUNCTION_ARGS  )

Definition at line 2207 of file extension.c.

2208{
2209 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2210 List *locations;
2211 DIR *dir;
2212 struct dirent *de;
2213
2214 /* Build tuplestore to hold the result rows */
2215 InitMaterializedSRF(fcinfo, 0);
2216
2218
2219 foreach_ptr(char, location, locations)
2220 {
2221 dir = AllocateDir(location);
2222
2223 /*
2224 * If the control directory doesn't exist, we want to silently return
2225 * an empty set. Any other error will be reported by ReadDir.
2226 */
2227 if (dir == NULL && errno == ENOENT)
2228 {
2229 /* do nothing */
2230 }
2231 else
2232 {
2233 while ((de = ReadDir(dir, location)) != NULL)
2234 {
2235 ExtensionControlFile *control;
2236 char *extname;
2237 Datum values[3];
2238 bool nulls[3];
2239
2240 if (!is_extension_control_filename(de->d_name))
2241 continue;
2242
2243 /* extract extension name from 'name.control' filename */
2244 extname = pstrdup(de->d_name);
2245 *strrchr(extname, '.') = '\0';
2246
2247 /* ignore it if it's an auxiliary control file */
2248 if (strstr(extname, "--"))
2249 continue;
2250
2251 control = new_ExtensionControlFile(extname);
2252 control->control_dir = pstrdup(location);
2253 parse_extension_control_file(control, NULL);
2254
2255 memset(values, 0, sizeof(values));
2256 memset(nulls, 0, sizeof(nulls));
2257
2258 /* name */
2260 CStringGetDatum(control->name));
2261 /* default_version */
2262 if (control->default_version == NULL)
2263 nulls[1] = true;
2264 else
2266 /* comment */
2267 if (control->comment == NULL)
2268 nulls[2] = true;
2269 else
2270 values[2] = CStringGetTextDatum(control->comment);
2271
2272 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2273 values, nulls);
2274 }
2275
2276 FreeDir(dir);
2277 }
2278 }
2279
2280 return (Datum) 0;
2281}

References AllocateDir(), ExtensionControlFile::comment, ExtensionControlFile::control_dir, CStringGetDatum(), CStringGetTextDatum, ExtensionControlFile::default_version, DirectFunctionCall1, foreach_ptr, FreeDir(), get_extension_control_directories(), InitMaterializedSRF(), is_extension_control_filename(), ExtensionControlFile::name, namein(), new_ExtensionControlFile(), parse_extension_control_file(), pstrdup(), ReadDir(), ReturnSetInfo::setDesc, ReturnSetInfo::setResult, tuplestore_putvalues(), and values.

◆ pg_extension_config_dump()

Datum pg_extension_config_dump ( PG_FUNCTION_ARGS  )

Definition at line 2641 of file extension.c.

2642{
2643 Oid tableoid = PG_GETARG_OID(0);
2644 text *wherecond = PG_GETARG_TEXT_PP(1);
2645 char *tablename;
2646 Relation extRel;
2647 ScanKeyData key[1];
2648 SysScanDesc extScan;
2649 HeapTuple extTup;
2650 Datum arrayDatum;
2651 Datum elementDatum;
2652 int arrayLength;
2653 int arrayIndex;
2654 bool isnull;
2655 Datum repl_val[Natts_pg_extension];
2656 bool repl_null[Natts_pg_extension];
2657 bool repl_repl[Natts_pg_extension];
2658 ArrayType *a;
2659
2660 /*
2661 * We only allow this to be called from an extension's SQL script. We
2662 * shouldn't need any permissions check beyond that.
2663 */
2664 if (!creating_extension)
2665 ereport(ERROR,
2666 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2667 errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
2668 "pg_extension_config_dump()")));
2669
2670 /*
2671 * Check that the table exists and is a member of the extension being
2672 * created. This ensures that we don't need to register an additional
2673 * dependency to protect the extconfig entry.
2674 */
2675 tablename = get_rel_name(tableoid);
2676 if (tablename == NULL)
2677 ereport(ERROR,
2679 errmsg("OID %u does not refer to a table", tableoid)));
2680 if (getExtensionOfObject(RelationRelationId, tableoid) !=
2682 ereport(ERROR,
2683 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2684 errmsg("table \"%s\" is not a member of the extension being created",
2685 tablename)));
2686
2687 /*
2688 * Add the table OID and WHERE condition to the extension's extconfig and
2689 * extcondition arrays.
2690 *
2691 * If the table is already in extconfig, treat this as an update of the
2692 * WHERE condition.
2693 */
2694
2695 /* Find the pg_extension tuple */
2696 extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2697
2698 ScanKeyInit(&key[0],
2699 Anum_pg_extension_oid,
2700 BTEqualStrategyNumber, F_OIDEQ,
2702
2703 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2704 NULL, 1, key);
2705
2706 extTup = systable_getnext(extScan);
2707
2708 if (!HeapTupleIsValid(extTup)) /* should not happen */
2709 elog(ERROR, "could not find tuple for extension %u",
2711
2712 memset(repl_val, 0, sizeof(repl_val));
2713 memset(repl_null, false, sizeof(repl_null));
2714 memset(repl_repl, false, sizeof(repl_repl));
2715
2716 /* Build or modify the extconfig value */
2717 elementDatum = ObjectIdGetDatum(tableoid);
2718
2719 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2720 RelationGetDescr(extRel), &isnull);
2721 if (isnull)
2722 {
2723 /* Previously empty extconfig, so build 1-element array */
2724 arrayLength = 0;
2725 arrayIndex = 1;
2726
2727 a = construct_array_builtin(&elementDatum, 1, OIDOID);
2728 }
2729 else
2730 {
2731 /* Modify or extend existing extconfig array */
2732 Oid *arrayData;
2733 int i;
2734
2735 a = DatumGetArrayTypeP(arrayDatum);
2736
2737 arrayLength = ARR_DIMS(a)[0];
2738 if (ARR_NDIM(a) != 1 ||
2739 ARR_LBOUND(a)[0] != 1 ||
2740 arrayLength < 0 ||
2741 ARR_HASNULL(a) ||
2742 ARR_ELEMTYPE(a) != OIDOID)
2743 elog(ERROR, "extconfig is not a 1-D Oid array");
2744 arrayData = (Oid *) ARR_DATA_PTR(a);
2745
2746 arrayIndex = arrayLength + 1; /* set up to add after end */
2747
2748 for (i = 0; i < arrayLength; i++)
2749 {
2750 if (arrayData[i] == tableoid)
2751 {
2752 arrayIndex = i + 1; /* replace this element instead */
2753 break;
2754 }
2755 }
2756
2757 a = array_set(a, 1, &arrayIndex,
2758 elementDatum,
2759 false,
2760 -1 /* varlena array */ ,
2761 sizeof(Oid) /* OID's typlen */ ,
2762 true /* OID's typbyval */ ,
2763 TYPALIGN_INT /* OID's typalign */ );
2764 }
2765 repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2766 repl_repl[Anum_pg_extension_extconfig - 1] = true;
2767
2768 /* Build or modify the extcondition value */
2769 elementDatum = PointerGetDatum(wherecond);
2770
2771 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2772 RelationGetDescr(extRel), &isnull);
2773 if (isnull)
2774 {
2775 if (arrayLength != 0)
2776 elog(ERROR, "extconfig and extcondition arrays do not match");
2777
2778 a = construct_array_builtin(&elementDatum, 1, TEXTOID);
2779 }
2780 else
2781 {
2782 a = DatumGetArrayTypeP(arrayDatum);
2783
2784 if (ARR_NDIM(a) != 1 ||
2785 ARR_LBOUND(a)[0] != 1 ||
2786 ARR_HASNULL(a) ||
2787 ARR_ELEMTYPE(a) != TEXTOID)
2788 elog(ERROR, "extcondition is not a 1-D text array");
2789 if (ARR_DIMS(a)[0] != arrayLength)
2790 elog(ERROR, "extconfig and extcondition arrays do not match");
2791
2792 /* Add or replace at same index as in extconfig */
2793 a = array_set(a, 1, &arrayIndex,
2794 elementDatum,
2795 false,
2796 -1 /* varlena array */ ,
2797 -1 /* TEXT's typlen */ ,
2798 false /* TEXT's typbyval */ ,
2799 TYPALIGN_INT /* TEXT's typalign */ );
2800 }
2801 repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2802 repl_repl[Anum_pg_extension_extcondition - 1] = true;
2803
2804 extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2805 repl_val, repl_null, repl_repl);
2806
2807 CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2808
2809 systable_endscan(extScan);
2810
2812
2814}
ArrayType * array_set(ArrayType *array, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3163
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2068
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:79
Definition: c.h:658

References a, ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_LBOUND, ARR_NDIM, array_set(), BTEqualStrategyNumber, CatalogTupleUpdate(), construct_array_builtin(), creating_extension, CurrentExtensionObject, DatumGetArrayTypeP, elog, ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errmsg(), ERROR, get_rel_name(), getExtensionOfObject(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, i, sort-test::key, ObjectIdGetDatum(), PG_GETARG_OID, PG_GETARG_TEXT_PP, PG_RETURN_VOID, PointerGetDatum(), RelationGetDescr, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

◆ pg_extension_update_paths()

Datum pg_extension_update_paths ( PG_FUNCTION_ARGS  )

Definition at line 2556 of file extension.c.

2557{
2558 Name extname = PG_GETARG_NAME(0);
2559 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2560 List *evi_list;
2561 ExtensionControlFile *control;
2562 ListCell *lc1;
2563
2564 /* Check extension name validity before any filesystem access */
2566
2567 /* Build tuplestore to hold the result rows */
2568 InitMaterializedSRF(fcinfo, 0);
2569
2570 /* Read the extension's control file */
2571 control = read_extension_control_file(NameStr(*extname));
2572
2573 /* Extract the version update graph from the script directory */
2574 evi_list = get_ext_ver_list(control);
2575
2576 /* Iterate over all pairs of versions */
2577 foreach(lc1, evi_list)
2578 {
2580 ListCell *lc2;
2581
2582 foreach(lc2, evi_list)
2583 {
2585 List *path;
2586 Datum values[3];
2587 bool nulls[3];
2588
2589 if (evi1 == evi2)
2590 continue;
2591
2592 /* Find shortest path from evi1 to evi2 */
2593 path = find_update_path(evi_list, evi1, evi2, false, true);
2594
2595 /* Emit result row */
2596 memset(values, 0, sizeof(values));
2597 memset(nulls, 0, sizeof(nulls));
2598
2599 /* source */
2600 values[0] = CStringGetTextDatum(evi1->name);
2601 /* target */
2602 values[1] = CStringGetTextDatum(evi2->name);
2603 /* path */
2604 if (path == NIL)
2605 nulls[2] = true;
2606 else
2607 {
2608 StringInfoData pathbuf;
2609 ListCell *lcv;
2610
2611 initStringInfo(&pathbuf);
2612 /* The path doesn't include start vertex, but show it */
2613 appendStringInfoString(&pathbuf, evi1->name);
2614 foreach(lcv, path)
2615 {
2616 char *versionName = (char *) lfirst(lcv);
2617
2618 appendStringInfoString(&pathbuf, "--");
2619 appendStringInfoString(&pathbuf, versionName);
2620 }
2621 values[2] = CStringGetTextDatum(pathbuf.data);
2622 pfree(pathbuf.data);
2623 }
2624
2625 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2626 values, nulls);
2627 }
2628 }
2629
2630 return (Datum) 0;
2631}
#define PG_GETARG_NAME(n)
Definition: fmgr.h:278
Definition: c.h:712

References appendStringInfoString(), check_valid_extension_name(), CStringGetTextDatum, StringInfoData::data, find_update_path(), get_ext_ver_list(), InitMaterializedSRF(), initStringInfo(), lfirst, ExtensionVersionInfo::name, NameStr, NIL, pfree(), PG_GETARG_NAME, read_extension_control_file(), ReturnSetInfo::setDesc, ReturnSetInfo::setResult, tuplestore_putvalues(), and values.

◆ pg_get_loaded_modules()

Datum pg_get_loaded_modules ( PG_FUNCTION_ARGS  )

Definition at line 2824 of file extension.c.

2825{
2826 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2827 DynamicFileList *file_scanner;
2828
2829 /* Build tuplestore to hold the result rows */
2830 InitMaterializedSRF(fcinfo, 0);
2831
2832 for (file_scanner = get_first_loaded_module(); file_scanner != NULL;
2833 file_scanner = get_next_loaded_module(file_scanner))
2834 {
2835 const char *library_path,
2836 *module_name,
2837 *module_version;
2838 const char *sep;
2839 Datum values[3] = {0};
2840 bool nulls[3] = {0};
2841
2842 get_loaded_module_details(file_scanner,
2843 &library_path,
2844 &module_name,
2845 &module_version);
2846
2847 if (module_name == NULL)
2848 nulls[0] = true;
2849 else
2850 values[0] = CStringGetTextDatum(module_name);
2851 if (module_version == NULL)
2852 nulls[1] = true;
2853 else
2854 values[1] = CStringGetTextDatum(module_version);
2855
2856 /* For security reasons, we don't show the directory path */
2857 sep = last_dir_separator(library_path);
2858 if (sep)
2859 library_path = sep + 1;
2860 values[2] = CStringGetTextDatum(library_path);
2861
2862 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2863 values, nulls);
2864 }
2865
2866 return (Datum) 0;
2867}
DynamicFileList * get_next_loaded_module(DynamicFileList *dfptr)
Definition: dfmgr.c:416
DynamicFileList * get_first_loaded_module(void)
Definition: dfmgr.c:410
void get_loaded_module_details(DynamicFileList *dfptr, const char **library_path, const char **module_name, const char **module_version)
Definition: dfmgr.c:430
char * last_dir_separator(const char *filename)
Definition: path.c:145

References CStringGetTextDatum, get_first_loaded_module(), get_loaded_module_details(), get_next_loaded_module(), InitMaterializedSRF(), last_dir_separator(), ReturnSetInfo::setDesc, ReturnSetInfo::setResult, tuplestore_putvalues(), and values.

◆ read_extension_aux_control_file()

static ExtensionControlFile * read_extension_aux_control_file ( const ExtensionControlFile pcontrol,
const char *  version 
)
static

Definition at line 719 of file extension.c.

721{
722 ExtensionControlFile *acontrol;
723
724 /*
725 * Flat-copy the struct. Pointer fields share values with original.
726 */
727 acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
728 memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
729
730 /*
731 * Parse the auxiliary control file, overwriting struct fields
732 */
733 parse_extension_control_file(acontrol, version);
734
735 return acontrol;
736}

References palloc(), and parse_extension_control_file().

Referenced by ApplyExtensionUpdates(), CreateExtensionInternal(), and get_available_versions_for_extension().

◆ read_extension_control_file()

static ExtensionControlFile * read_extension_control_file ( const char *  extname)
static

Definition at line 700 of file extension.c.

701{
703
704 /*
705 * Parse the primary control file.
706 */
707 parse_extension_control_file(control, NULL);
708
709 return control;
710}

References new_ExtensionControlFile(), and parse_extension_control_file().

Referenced by AlterExtensionNamespace(), CreateExtensionInternal(), ExecAlterExtensionStmt(), and pg_extension_update_paths().

◆ read_extension_script_file()

static char * read_extension_script_file ( const ExtensionControlFile control,
const char *  filename 
)
static

Definition at line 742 of file extension.c.

744{
745 int src_encoding;
746 char *src_str;
747 char *dest_str;
748 int len;
749
750 src_str = read_whole_file(filename, &len);
751
752 /* use database encoding if not given */
753 if (control->encoding < 0)
754 src_encoding = GetDatabaseEncoding();
755 else
756 src_encoding = control->encoding;
757
758 /* make sure that source string is valid in the expected encoding */
759 (void) pg_verify_mbstr(src_encoding, src_str, len, false);
760
761 /*
762 * Convert the encoding to the database encoding. read_whole_file
763 * null-terminated the string, so if no conversion happens the string is
764 * valid as is.
765 */
766 dest_str = pg_any_to_server(src_str, len, src_encoding);
767
768 return dest_str;
769}
static char * read_whole_file(const char *filename, int *length)
Definition: extension.c:3788
int GetDatabaseEncoding(void)
Definition: mbutils.c:1261
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676
bool pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError)
Definition: mbutils.c:1566

References ExtensionControlFile::encoding, filename, GetDatabaseEncoding(), len, pg_any_to_server(), pg_verify_mbstr(), and read_whole_file().

Referenced by execute_extension_script().

◆ read_whole_file()

static char * read_whole_file ( const char *  filename,
int *  length 
)
static

Definition at line 3788 of file extension.c.

3789{
3790 char *buf;
3791 FILE *file;
3792 size_t bytes_to_read;
3793 struct stat fst;
3794
3795 if (stat(filename, &fst) < 0)
3796 ereport(ERROR,
3798 errmsg("could not stat file \"%s\": %m", filename)));
3799
3800 if (fst.st_size > (MaxAllocSize - 1))
3801 ereport(ERROR,
3802 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3803 errmsg("file \"%s\" is too large", filename)));
3804 bytes_to_read = (size_t) fst.st_size;
3805
3806 if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
3807 ereport(ERROR,
3809 errmsg("could not open file \"%s\" for reading: %m",
3810 filename)));
3811
3812 buf = (char *) palloc(bytes_to_read + 1);
3813
3814 bytes_to_read = fread(buf, 1, bytes_to_read, file);
3815
3816 if (ferror(file))
3817 ereport(ERROR,
3819 errmsg("could not read file \"%s\": %m", filename)));
3820
3821 FreeFile(file);
3822
3823 buf[bytes_to_read] = '\0';
3824
3825 /*
3826 * On Windows, manually convert Windows-style newlines (\r\n) to the Unix
3827 * convention of \n only. This avoids gotchas due to script files
3828 * possibly getting converted when being transferred between platforms.
3829 * Ideally we'd do this by using text mode to read the file, but that also
3830 * causes control-Z to be treated as end-of-file. Historically we've
3831 * allowed control-Z in script files, so breaking that seems unwise.
3832 */
3833#ifdef WIN32
3834 {
3835 char *s,
3836 *d;
3837
3838 for (s = d = buf; *s; s++)
3839 {
3840 if (!(*s == '\r' && s[1] == '\n'))
3841 *d++ = *s;
3842 }
3843 *d = '\0';
3844 bytes_to_read = d - buf;
3845 }
3846#endif
3847
3848 *length = bytes_to_read;
3849 return buf;
3850}
#define PG_BINARY_R
Definition: c.h:1246
#define MaxAllocSize
Definition: fe_memutils.h:22
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
static char * buf
Definition: pg_test_fsync.c:72

References AllocateFile(), buf, ereport, errcode(), errcode_for_file_access(), errmsg(), ERROR, filename, FreeFile(), if(), MaxAllocSize, palloc(), PG_BINARY_R, stat::st_size, and stat.

Referenced by read_extension_script_file().

◆ RemoveExtensionById()

void RemoveExtensionById ( Oid  extId)

Definition at line 2153 of file extension.c.

2154{
2155 Relation rel;
2156 SysScanDesc scandesc;
2157 HeapTuple tuple;
2158 ScanKeyData entry[1];
2159
2160 /*
2161 * Disallow deletion of any extension that's currently open for insertion;
2162 * else subsequent executions of recordDependencyOnCurrentExtension()
2163 * could create dangling pg_depend records that refer to a no-longer-valid
2164 * pg_extension OID. This is needed not so much because we think people
2165 * might write "DROP EXTENSION foo" in foo's own script files, as because
2166 * errors in dependency management in extension script files could give
2167 * rise to cases where an extension is dropped as a result of recursing
2168 * from some contained object. Because of that, we must test for the case
2169 * here, not at some higher level of the DROP EXTENSION command.
2170 */
2171 if (extId == CurrentExtensionObject)
2172 ereport(ERROR,
2173 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2174 errmsg("cannot drop extension \"%s\" because it is being modified",
2175 get_extension_name(extId))));
2176
2177 rel = table_open(ExtensionRelationId, RowExclusiveLock);
2178
2179 ScanKeyInit(&entry[0],
2180 Anum_pg_extension_oid,
2181 BTEqualStrategyNumber, F_OIDEQ,
2182 ObjectIdGetDatum(extId));
2183 scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
2184 NULL, 1, entry);
2185
2186 tuple = systable_getnext(scandesc);
2187
2188 /* We assume that there can be at most one matching tuple */
2189 if (HeapTupleIsValid(tuple))
2190 CatalogTupleDelete(rel, &tuple->t_self);
2191
2192 systable_endscan(scandesc);
2193
2195}
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365

References BTEqualStrategyNumber, CatalogTupleDelete(), CurrentExtensionObject, ereport, errcode(), errmsg(), ERROR, get_extension_name(), HeapTupleIsValid, ObjectIdGetDatum(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by doDeletion().

◆ script_error_callback()

static void script_error_callback ( void *  arg)
static

Definition at line 775 of file extension.c.

776{
778 const char *query = callback_arg->sql;
779 int location = callback_arg->stmt_location;
780 int len = callback_arg->stmt_len;
781 int syntaxerrposition;
782 const char *lastslash;
783
784 /*
785 * If there is a syntax error position, convert to internal syntax error;
786 * otherwise report the current query as an item of context stack.
787 *
788 * Note: we'll provide no context except the filename if there's neither
789 * an error position nor any known current query. That shouldn't happen
790 * though: all errors reported during raw parsing should come with an
791 * error position.
792 */
793 syntaxerrposition = geterrposition();
794 if (syntaxerrposition > 0)
795 {
796 /*
797 * If we do not know the bounds of the current statement (as would
798 * happen for an error occurring during initial raw parsing), we have
799 * to use a heuristic to decide how much of the script to show. We'll
800 * also use the heuristic in the unlikely case that syntaxerrposition
801 * is outside what we think the statement bounds are.
802 */
803 if (location < 0 || syntaxerrposition < location ||
804 (len > 0 && syntaxerrposition > location + len))
805 {
806 /*
807 * Our heuristic is pretty simple: look for semicolon-newline
808 * sequences, and break at the last one strictly before
809 * syntaxerrposition and the first one strictly after. It's
810 * certainly possible to fool this with semicolon-newline embedded
811 * in a string literal, but it seems better to do this than to
812 * show the entire extension script.
813 *
814 * Notice we cope with Windows-style newlines (\r\n) regardless of
815 * platform. This is because there might be such newlines in
816 * script files on other platforms.
817 */
818 int slen = strlen(query);
819
820 location = len = 0;
821 for (int loc = 0; loc < slen; loc++)
822 {
823 if (query[loc] != ';')
824 continue;
825 if (query[loc + 1] == '\r')
826 loc++;
827 if (query[loc + 1] == '\n')
828 {
829 int bkpt = loc + 2;
830
831 if (bkpt < syntaxerrposition)
832 location = bkpt;
833 else if (bkpt > syntaxerrposition)
834 {
835 len = bkpt - location;
836 break; /* no need to keep searching */
837 }
838 }
839 }
840 }
841
842 /* Trim leading/trailing whitespace, for consistency */
843 query = CleanQuerytext(query, &location, &len);
844
845 /*
846 * Adjust syntaxerrposition. It shouldn't be pointing into the
847 * whitespace we just trimmed, but cope if it is.
848 */
849 syntaxerrposition -= location;
850 if (syntaxerrposition < 0)
851 syntaxerrposition = 0;
852 else if (syntaxerrposition > len)
853 syntaxerrposition = len;
854
855 /* And report. */
856 errposition(0);
857 internalerrposition(syntaxerrposition);
859 }
860 else if (location >= 0)
861 {
862 /*
863 * Since no syntax cursor will be shown, it's okay and helpful to trim
864 * the reported query string to just the current statement.
865 */
866 query = CleanQuerytext(query, &location, &len);
867 errcontext("SQL statement \"%.*s\"", len, query);
868 }
869
870 /*
871 * Trim the reported file name to remove the path. We know that
872 * get_extension_script_filename() inserted a '/', regardless of whether
873 * we're on Windows.
874 */
875 lastslash = strrchr(callback_arg->filename, '/');
876 if (lastslash)
877 lastslash++;
878 else
879 lastslash = callback_arg->filename; /* shouldn't happen, but cope */
880
881 /*
882 * If we have a location (which, as said above, we really always should)
883 * then report a line number to aid in localizing problems in big scripts.
884 */
885 if (location >= 0)
886 {
887 int linenumber = 1;
888
889 for (query = callback_arg->sql; *query; query++)
890 {
891 if (--location < 0)
892 break;
893 if (*query == '\n')
894 linenumber++;
895 }
896 errcontext("extension script file \"%s\", near line %d",
897 lastslash, linenumber);
898 }
899 else
900 errcontext("extension script file \"%s\"", lastslash);
901}
int internalerrquery(const char *query)
Definition: elog.c:1504
int internalerrposition(int cursorpos)
Definition: elog.c:1484
int geterrposition(void)
Definition: elog.c:1600
int errposition(int cursorpos)
Definition: elog.c:1468
#define errcontext
Definition: elog.h:197
void * arg
const char * CleanQuerytext(const char *query, int *location, int *len)

References arg, CleanQuerytext(), errcontext, errposition(), script_error_callback_arg::filename, geterrposition(), internalerrposition(), internalerrquery(), len, pnstrdup(), script_error_callback_arg::sql, script_error_callback_arg::stmt_len, and script_error_callback_arg::stmt_location.

Referenced by execute_sql_string().

Variable Documentation

◆ creating_extension

◆ CurrentExtensionObject

◆ Extension_control_path

char* Extension_control_path

Definition at line 74 of file extension.c.

Referenced by get_extension_control_directories().