PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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)
 
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 3040 of file extension.c.

3041{
3042 Oid extensionOid;
3043 Oid nspOid;
3044 Oid oldNspOid;
3045 AclResult aclresult;
3046 Relation extRel;
3047 ScanKeyData key[2];
3048 SysScanDesc extScan;
3049 HeapTuple extTup;
3050 Form_pg_extension extForm;
3051 Relation depRel;
3052 SysScanDesc depScan;
3053 HeapTuple depTup;
3054 ObjectAddresses *objsMoved;
3055 ObjectAddress extAddr;
3056
3057 extensionOid = get_extension_oid(extensionName, false);
3058
3059 nspOid = LookupCreationNamespace(newschema);
3060
3061 /*
3062 * Permission check: must own extension. Note that we don't bother to
3063 * check ownership of the individual member objects ...
3064 */
3065 if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3067 extensionName);
3068
3069 /* Permission check: must have creation rights in target namespace */
3070 aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
3071 if (aclresult != ACLCHECK_OK)
3072 aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
3073
3074 /*
3075 * If the schema is currently a member of the extension, disallow moving
3076 * the extension into the schema. That would create a dependency loop.
3077 */
3078 if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
3079 ereport(ERROR,
3080 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3081 errmsg("cannot move extension \"%s\" into schema \"%s\" "
3082 "because the extension contains the schema",
3083 extensionName, newschema)));
3084
3085 /* Locate the pg_extension tuple */
3086 extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3087
3088 ScanKeyInit(&key[0],
3089 Anum_pg_extension_oid,
3090 BTEqualStrategyNumber, F_OIDEQ,
3091 ObjectIdGetDatum(extensionOid));
3092
3093 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3094 NULL, 1, key);
3095
3096 extTup = systable_getnext(extScan);
3097
3098 if (!HeapTupleIsValid(extTup)) /* should not happen */
3099 elog(ERROR, "could not find tuple for extension %u",
3100 extensionOid);
3101
3102 /* Copy tuple so we can modify it below */
3103 extTup = heap_copytuple(extTup);
3104 extForm = (Form_pg_extension) GETSTRUCT(extTup);
3105
3106 systable_endscan(extScan);
3107
3108 /*
3109 * If the extension is already in the target schema, just silently do
3110 * nothing.
3111 */
3112 if (extForm->extnamespace == nspOid)
3113 {
3115 return InvalidObjectAddress;
3116 }
3117
3118 /* Check extension is supposed to be relocatable */
3119 if (!extForm->extrelocatable)
3120 ereport(ERROR,
3121 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3122 errmsg("extension \"%s\" does not support SET SCHEMA",
3123 NameStr(extForm->extname))));
3124
3125 objsMoved = new_object_addresses();
3126
3127 /* store the OID of the namespace to-be-changed */
3128 oldNspOid = extForm->extnamespace;
3129
3130 /*
3131 * Scan pg_depend to find objects that depend directly on the extension,
3132 * and alter each one's schema.
3133 */
3134 depRel = table_open(DependRelationId, AccessShareLock);
3135
3136 ScanKeyInit(&key[0],
3137 Anum_pg_depend_refclassid,
3138 BTEqualStrategyNumber, F_OIDEQ,
3139 ObjectIdGetDatum(ExtensionRelationId));
3140 ScanKeyInit(&key[1],
3141 Anum_pg_depend_refobjid,
3142 BTEqualStrategyNumber, F_OIDEQ,
3143 ObjectIdGetDatum(extensionOid));
3144
3145 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
3146 NULL, 2, key);
3147
3148 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
3149 {
3150 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
3151 ObjectAddress dep;
3152 Oid dep_oldNspOid;
3153
3154 /*
3155 * If a dependent extension has a no_relocate request for this
3156 * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
3157 * the same loop that's actually executing the renames: we may detect
3158 * the error condition only after having expended a fair amount of
3159 * work. However, the alternative is to do two scans of pg_depend,
3160 * which seems like optimizing for failure cases. The rename work
3161 * will all roll back cleanly enough if we do fail here.)
3162 */
3163 if (pg_depend->deptype == DEPENDENCY_NORMAL &&
3164 pg_depend->classid == ExtensionRelationId)
3165 {
3166 char *depextname = get_extension_name(pg_depend->objid);
3167 ExtensionControlFile *dcontrol;
3168 ListCell *lc;
3169
3170 dcontrol = read_extension_control_file(depextname);
3171 foreach(lc, dcontrol->no_relocate)
3172 {
3173 char *nrextname = (char *) lfirst(lc);
3174
3175 if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
3176 {
3177 ereport(ERROR,
3178 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3179 errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
3180 NameStr(extForm->extname)),
3181 errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
3182 depextname,
3183 NameStr(extForm->extname))));
3184 }
3185 }
3186 }
3187
3188 /*
3189 * Otherwise, ignore non-membership dependencies. (Currently, the
3190 * only other case we could see here is a normal dependency from
3191 * another extension.)
3192 */
3193 if (pg_depend->deptype != DEPENDENCY_EXTENSION)
3194 continue;
3195
3196 dep.classId = pg_depend->classid;
3197 dep.objectId = pg_depend->objid;
3198 dep.objectSubId = pg_depend->objsubid;
3199
3200 if (dep.objectSubId != 0) /* should not happen */
3201 elog(ERROR, "extension should not have a sub-object dependency");
3202
3203 /* Relocate the object */
3204 dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
3205 dep.objectId,
3206 nspOid,
3207 objsMoved);
3208
3209 /*
3210 * If not all the objects had the same old namespace (ignoring any
3211 * that are not in namespaces or are dependent types), complain.
3212 */
3213 if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
3214 ereport(ERROR,
3215 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3216 errmsg("extension \"%s\" does not support SET SCHEMA",
3217 NameStr(extForm->extname)),
3218 errdetail("%s is not in the extension's schema \"%s\"",
3219 getObjectDescription(&dep, false),
3220 get_namespace_name(oldNspOid))));
3221 }
3222
3223 /* report old schema, if caller wants it */
3224 if (oldschema)
3225 *oldschema = oldNspOid;
3226
3227 systable_endscan(depScan);
3228
3230
3231 /* Now adjust pg_extension.extnamespace */
3232 extForm->extnamespace = nspOid;
3233
3234 CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3235
3237
3238 /* update dependency to point to the new schema */
3239 if (changeDependencyFor(ExtensionRelationId, extensionOid,
3240 NamespaceRelationId, oldNspOid, nspOid) != 1)
3241 elog(ERROR, "could not change schema dependency for extension %s",
3242 NameStr(extForm->extname));
3243
3244 InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3245
3246 ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
3247
3248 return extAddr;
3249}
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:226
#define ereport(elevel,...)
Definition: elog.h:149
static ExtensionControlFile * read_extension_control_file(const char *extname)
Definition: extension.c:698
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition: extension.c:164
char * get_extension_name(Oid ext_oid)
Definition: extension.c:186
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:98
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 3402 of file extension.c.

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

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

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

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 2528 of file extension.c.

2529{
2530 Datum *datums;
2531 int ndatums;
2532 ArrayType *a;
2533 ListCell *lc;
2534
2535 ndatums = list_length(requires);
2536 datums = (Datum *) palloc(ndatums * sizeof(Datum));
2537 ndatums = 0;
2538 foreach(lc, requires)
2539 {
2540 char *curreq = (char *) lfirst(lc);
2541
2542 datums[ndatums++] =
2544 }
2545 a = construct_array_builtin(datums, ndatums, NAMEOID);
2546 return PointerGetDatum(a);
2547}
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:1939
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 1965 of file extension.c.

1966{
1967 DefElem *d_schema = NULL;
1968 DefElem *d_new_version = NULL;
1969 DefElem *d_cascade = NULL;
1970 char *schemaName = NULL;
1971 char *versionName = NULL;
1972 bool cascade = false;
1973 ListCell *lc;
1974
1975 /* Check extension name validity before any filesystem access */
1977
1978 /*
1979 * Check for duplicate extension name. The unique index on
1980 * pg_extension.extname would catch this anyway, and serves as a backstop
1981 * in case of race conditions; but this is a friendlier error message, and
1982 * besides we need a check to support IF NOT EXISTS.
1983 */
1984 if (get_extension_oid(stmt->extname, true) != InvalidOid)
1985 {
1986 if (stmt->if_not_exists)
1987 {
1990 errmsg("extension \"%s\" already exists, skipping",
1991 stmt->extname)));
1992 return InvalidObjectAddress;
1993 }
1994 else
1995 ereport(ERROR,
1997 errmsg("extension \"%s\" already exists",
1998 stmt->extname)));
1999 }
2000
2001 /*
2002 * We use global variables to track the extension being created, so we can
2003 * create only one extension at the same time.
2004 */
2006 ereport(ERROR,
2007 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2008 errmsg("nested CREATE EXTENSION is not supported")));
2009
2010 /* Deconstruct the statement option list */
2011 foreach(lc, stmt->options)
2012 {
2013 DefElem *defel = (DefElem *) lfirst(lc);
2014
2015 if (strcmp(defel->defname, "schema") == 0)
2016 {
2017 if (d_schema)
2018 errorConflictingDefElem(defel, pstate);
2019 d_schema = defel;
2020 schemaName = defGetString(d_schema);
2021 }
2022 else if (strcmp(defel->defname, "new_version") == 0)
2023 {
2024 if (d_new_version)
2025 errorConflictingDefElem(defel, pstate);
2026 d_new_version = defel;
2027 versionName = defGetString(d_new_version);
2028 }
2029 else if (strcmp(defel->defname, "cascade") == 0)
2030 {
2031 if (d_cascade)
2032 errorConflictingDefElem(defel, pstate);
2033 d_cascade = defel;
2034 cascade = defGetBoolean(d_cascade);
2035 }
2036 else
2037 elog(ERROR, "unrecognized option: %s", defel->defname);
2038 }
2039
2040 /* Call CreateExtensionInternal to do the real work. */
2041 return CreateExtensionInternal(stmt->extname,
2042 schemaName,
2043 versionName,
2044 cascade,
2045 NIL,
2046 true);
2047}
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:228
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:1655
#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 1655 of file extension.c.

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

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

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

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

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

916{
917 script_error_callback_arg callback_arg;
918 ErrorContextCallback scripterrcontext;
919 List *raw_parsetree_list;
921 ListCell *lc1;
922
923 /*
924 * Setup error traceback support for ereport().
925 */
926 callback_arg.sql = sql;
927 callback_arg.filename = filename;
928 callback_arg.stmt_location = -1;
929 callback_arg.stmt_len = -1;
930
931 scripterrcontext.callback = script_error_callback;
932 scripterrcontext.arg = (void *) &callback_arg;
933 scripterrcontext.previous = error_context_stack;
934 error_context_stack = &scripterrcontext;
935
936 /*
937 * Parse the SQL string into a list of raw parse trees.
938 */
939 raw_parsetree_list = pg_parse_query(sql);
940
941 /* All output from SELECTs goes to the bit bucket */
943
944 /*
945 * Do parse analysis, rule rewrite, planning, and execution for each raw
946 * parsetree. We must fully execute each query before beginning parse
947 * analysis on the next one, since there may be interdependencies.
948 */
949 foreach(lc1, raw_parsetree_list)
950 {
951 RawStmt *parsetree = lfirst_node(RawStmt, lc1);
952 MemoryContext per_parsetree_context,
953 oldcontext;
954 List *stmt_list;
955 ListCell *lc2;
956
957 /* Report location of this query for error context callback */
958 callback_arg.stmt_location = parsetree->stmt_location;
959 callback_arg.stmt_len = parsetree->stmt_len;
960
961 /*
962 * We do the work for each parsetree in a short-lived context, to
963 * limit the memory used when there are many commands in the string.
964 */
965 per_parsetree_context =
967 "execute_sql_string per-statement context",
969 oldcontext = MemoryContextSwitchTo(per_parsetree_context);
970
971 /* Be sure parser can see any DDL done so far */
973
974 stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
975 sql,
976 NULL,
977 0,
978 NULL);
979 stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
980
981 foreach(lc2, stmt_list)
982 {
984
986
988
989 if (stmt->utilityStmt == NULL)
990 {
991 QueryDesc *qdesc;
992
993 qdesc = CreateQueryDesc(stmt,
994 NULL,
995 sql,
996 GetActiveSnapshot(), NULL,
997 dest, NULL, NULL, 0);
998
999 if (!ExecutorStart(qdesc, 0))
1000 elog(ERROR, "ExecutorStart() failed unexpectedly");
1002 ExecutorFinish(qdesc);
1003 ExecutorEnd(qdesc);
1004
1005 FreeQueryDesc(qdesc);
1006 }
1007 else
1008 {
1009 if (IsA(stmt->utilityStmt, TransactionStmt))
1010 ereport(ERROR,
1011 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1012 errmsg("transaction control statements are not allowed within an extension script")));
1013
1015 sql,
1016 false,
1018 NULL,
1019 NULL,
1020 dest,
1021 NULL);
1022 }
1023
1025 }
1026
1027 /* Clean up per-parsetree context. */
1028 MemoryContextSwitchTo(oldcontext);
1029 MemoryContextDelete(per_parsetree_context);
1030 }
1031
1032 error_context_stack = scripterrcontext.previous;
1033
1034 /* Be sure to advance the command counter after the last script command */
1036}
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:773
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:297
void(* callback)(void *arg)
Definition: elog.h:298
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 2875 of file extension.c.

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

2470{
2471 bool result = false;
2472 List *locations;
2473 DIR *dir;
2474 struct dirent *de;
2475
2477
2478 foreach_ptr(char, location, locations)
2479 {
2480 dir = AllocateDir(location);
2481
2482 /*
2483 * If the control directory doesn't exist, we want to silently return
2484 * false. Any other error will be reported by ReadDir.
2485 */
2486 if (dir == NULL && errno == ENOENT)
2487 {
2488 /* do nothing */
2489 }
2490 else
2491 {
2492 while ((de = ReadDir(dir, location)) != NULL)
2493 {
2494 char *extname;
2495
2497 continue;
2498
2499 /* extract extension name from 'name.control' filename */
2500 extname = pstrdup(de->d_name);
2501 *strrchr(extname, '.') = '\0';
2502
2503 /* ignore it if it's an auxiliary control file */
2504 if (strstr(extname, "--"))
2505 continue;
2506
2507 /* done if it matches request */
2508 if (strcmp(extname, extensionName) == 0)
2509 {
2510 result = true;
2511 break;
2512 }
2513 }
2514
2515 FreeDir(dir);
2516 }
2517 if (result)
2518 break;
2519 }
2520
2521 return result;
2522}
static bool is_extension_control_filename(const char *filename)
Definition: extension.c:322
static List * get_extension_control_directories(void)
Definition: extension.c:341
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:2321
#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 1045 of file extension.c.

1046{
1047 AclResult aclresult;
1048
1049 /* Never trust unless extension's control file says it's okay */
1050 if (!control->trusted)
1051 return false;
1052 /* Allow if user has CREATE privilege on current database */
1053 aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
1054 if (aclresult == ACLCHECK_OK)
1055 return true;
1056 return false;
1057}
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 402 of file extension.c.

403{
404 char sharepath[MAXPGPATH];
405 char *system_dir;
406 char *basename;
407 char *ecp;
408 char *result;
409
410 Assert(control->name);
411
412 get_share_path(my_exec_path, sharepath);
413 system_dir = psprintf("%s/extension", sharepath);
414
415 basename = psprintf("%s.control", control->name);
416
417 /*
418 * find_in_path() does nothing if the path value is empty. This is the
419 * historical behavior for dynamic_library_path, but it makes no sense for
420 * extensions. So in that case, substitute a default value.
421 */
423 if (strlen(ecp) == 0)
424 ecp = "$system";
425 result = find_in_path(basename, ecp, "extension_control_path", "$system", system_dir);
426
427 if (result)
428 {
429 const char *p;
430
431 p = strrchr(result, '/');
432 Assert(p);
433 control->control_dir = pnstrdup(result, p - result);
434 }
435
436 return result;
437}
char * find_in_path(const char *basename, const char *path, const char *path_param, const char *macro, const char *macro_val)
Definition: dfmgr.c:567
char * Extension_control_path
Definition: extension.c:74
char my_exec_path[MAXPGPATH]
Definition: globals.c:82
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:2332
#define MAXPGPATH
void get_share_path(const char *my_exec_path, char *ret_path)
Definition: path.c:902

References Assert(), ExtensionControlFile::control_dir, Extension_control_path, find_in_path(), get_share_path(), MAXPGPATH, my_exec_path, ExtensionControlFile::name, pnstrdup(), and psprintf().

Referenced by parse_extension_control_file().

◆ find_install_path()

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

Definition at line 1600 of file extension.c.

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

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 1507 of file extension.c.

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

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 2355 of file extension.c.

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

1341{
1343 ListCell *lc;
1344
1345 foreach(lc, *evi_list)
1346 {
1347 evi = (ExtensionVersionInfo *) lfirst(lc);
1348 if (strcmp(evi->name, versionname) == 0)
1349 return evi;
1350 }
1351
1353 evi->name = pstrdup(versionname);
1354 evi->reachable = NIL;
1355 evi->installable = false;
1356 /* initialize for later application of Dijkstra's algorithm */
1357 evi->distance_known = false;
1358 evi->distance = INT_MAX;
1359 evi->previous = NULL;
1360
1361 *evi_list = lappend(*evi_list, evi);
1362
1363 return evi;
1364}
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 1401 of file extension.c.

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

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 463 of file extension.c.

465{
466 char *result;
467 char *scriptdir;
468
469 scriptdir = get_extension_script_directory(control);
470
471 result = (char *) palloc(MAXPGPATH);
472 snprintf(result, MAXPGPATH, "%s/%s--%s.control",
473 scriptdir, control->name, version);
474
475 pfree(scriptdir);
476
477 return result;
478}
void pfree(void *pointer)
Definition: mcxt.c:2146
#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 341 of file extension.c.

342{
343 char sharepath[MAXPGPATH];
344 char *system_dir;
345 char *ecp;
346 List *paths = NIL;
347
348 get_share_path(my_exec_path, sharepath);
349
350 system_dir = psprintf("%s/extension", sharepath);
351
352 if (strlen(Extension_control_path) == 0)
353 {
354 paths = lappend(paths, system_dir);
355 }
356 else
357 {
358 /* Duplicate the string so we can modify it */
360
361 for (;;)
362 {
363 int len;
364 char *mangled;
365 char *piece = first_path_var_separator(ecp);
366
367 /* Get the length of the next path on ecp */
368 if (piece == NULL)
369 len = strlen(ecp);
370 else
371 len = piece - ecp;
372
373 /* Copy the next path found on ecp */
374 piece = palloc(len + 1);
375 strlcpy(piece, ecp, len + 1);
376
377 /* Substitute the path macro if needed */
378 mangled = substitute_path_macro(piece, "$system", system_dir);
379 pfree(piece);
380
381 /* Canonicalize the path based on the OS and add to the list */
382 canonicalize_path(mangled);
383 paths = lappend(paths, mangled);
384
385 /* Break if ecp is empty or move to the next path on ecp */
386 if (ecp[len] == '\0')
387 break;
388 else
389 ecp += len + 1;
390 }
391 }
392
393 return paths;
394}
char * substitute_path_macro(const char *str, const char *macro, const char *value)
Definition: dfmgr.c:529
const void size_t len
char * first_path_var_separator(const char *pathlist)
Definition: path.c:127
void canonicalize_path(char *path)
Definition: path.c:337
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(), pg_available_extension_versions(), and pg_available_extensions().

◆ get_extension_name()

char * get_extension_name ( Oid  ext_oid)

Definition at line 186 of file extension.c.

187{
188 char *result;
189 HeapTuple tuple;
190
191 tuple = SearchSysCache1(EXTENSIONOID, ObjectIdGetDatum(ext_oid));
192
193 if (!HeapTupleIsValid(tuple))
194 return NULL;
195
196 result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
197 ReleaseSysCache(tuple);
198
199 return result;
200}
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 164 of file extension.c.

165{
166 Oid result;
167
168 result = GetSysCacheOid1(EXTENSIONNAME, Anum_pg_extension_oid,
169 CStringGetDatum(extname));
170
171 if (!OidIsValid(result) && !missing_ok)
173 (errcode(ERRCODE_UNDEFINED_OBJECT),
174 errmsg("extension \"%s\" does not exist",
175 extname)));
176
177 return result;
178}
#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 208 of file extension.c.

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

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 440 of file extension.c.

441{
442 char sharepath[MAXPGPATH];
443 char *result;
444
445 /*
446 * The directory parameter can be omitted, absolute, or relative to the
447 * installation's share directory.
448 */
449 if (!control->directory)
450 return pstrdup(control->control_dir);
451
452 if (is_absolute_path(control->directory))
453 return pstrdup(control->directory);
454
455 get_share_path(my_exec_path, sharepath);
456 result = (char *) palloc(MAXPGPATH);
457 snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
458
459 return result;
460}
#define is_absolute_path(filename)
Definition: port.h:104

References ExtensionControlFile::control_dir, ExtensionControlFile::directory, get_share_path(), is_absolute_path, MAXPGPATH, my_exec_path, palloc(), pstrdup(), and snprintf.

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 481 of file extension.c.

483{
484 char *result;
485 char *scriptdir;
486
487 scriptdir = get_extension_script_directory(control);
488
489 result = (char *) palloc(MAXPGPATH);
490 if (from_version)
491 snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
492 scriptdir, control->name, from_version, version);
493 else
494 snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
495 scriptdir, control->name, version);
496
497 pfree(scriptdir);
498
499 return result;
500}

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 1373 of file extension.c.

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

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 1894 of file extension.c.

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

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

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 2063 of file extension.c.

2067{
2068 Oid extensionOid;
2069 Relation rel;
2070 Datum values[Natts_pg_extension];
2071 bool nulls[Natts_pg_extension];
2072 HeapTuple tuple;
2073 ObjectAddress myself;
2074 ObjectAddress nsp;
2075 ObjectAddresses *refobjs;
2076 ListCell *lc;
2077
2078 /*
2079 * Build and insert the pg_extension tuple
2080 */
2081 rel = table_open(ExtensionRelationId, RowExclusiveLock);
2082
2083 memset(values, 0, sizeof(values));
2084 memset(nulls, 0, sizeof(nulls));
2085
2086 extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
2087 Anum_pg_extension_oid);
2088 values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
2089 values[Anum_pg_extension_extname - 1] =
2091 values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
2092 values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
2093 values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
2094 values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
2095
2096 if (extConfig == PointerGetDatum(NULL))
2097 nulls[Anum_pg_extension_extconfig - 1] = true;
2098 else
2099 values[Anum_pg_extension_extconfig - 1] = extConfig;
2100
2101 if (extCondition == PointerGetDatum(NULL))
2102 nulls[Anum_pg_extension_extcondition - 1] = true;
2103 else
2104 values[Anum_pg_extension_extcondition - 1] = extCondition;
2105
2106 tuple = heap_form_tuple(rel->rd_att, values, nulls);
2107
2108 CatalogTupleInsert(rel, tuple);
2109
2110 heap_freetuple(tuple);
2112
2113 /*
2114 * Record dependencies on owner, schema, and prerequisite extensions
2115 */
2116 recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
2117
2118 refobjs = new_object_addresses();
2119
2120 ObjectAddressSet(myself, ExtensionRelationId, extensionOid);
2121
2122 ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
2123 add_exact_object_address(&nsp, refobjs);
2124
2125 foreach(lc, requiredExtensions)
2126 {
2127 Oid reqext = lfirst_oid(lc);
2128 ObjectAddress otherext;
2129
2130 ObjectAddressSet(otherext, ExtensionRelationId, reqext);
2131 add_exact_object_address(&otherext, refobjs);
2132 }
2133
2134 /* Record all of them (this includes duplicate elimination) */
2136 free_object_addresses(refobjs);
2137
2138 /* Post creation hook for new extension */
2139 InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
2140
2141 return myself;
2142}
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 322 of file extension.c.

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

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 330 of file extension.c.

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

References filename.

Referenced by get_ext_ver_list().

◆ new_ExtensionControlFile()

static ExtensionControlFile * new_ExtensionControlFile ( const char *  extname)
static

Definition at line 3851 of file extension.c.

3852{
3853 /*
3854 * Set up default values. Pointer fields are initially null.
3855 */
3857
3858 control->name = pstrdup(extname);
3859 control->relocatable = false;
3860 control->superuser = true;
3861 control->trusted = false;
3862 control->encoding = -1;
3863
3864 return control;
3865}
#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 518 of file extension.c.

520{
521 char *filename;
522 FILE *file;
523 ConfigVariable *item,
524 *head = NULL,
525 *tail = NULL;
526
527 /*
528 * Locate the file to read. Auxiliary files are optional.
529 */
530 if (version)
532 else
533 {
534 /*
535 * If control_dir is already set, use it, else do a path search.
536 */
537 if (control->control_dir)
538 {
539 filename = psprintf("%s/%s.control", control->control_dir, control->name);
540 }
541 else
543 }
544
545 if (!filename)
546 {
548 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
549 errmsg("extension \"%s\" is not available", control->name),
550 errhint("The extension must first be installed on the system where PostgreSQL is running.")));
551 }
552
553 if ((file = AllocateFile(filename, "r")) == NULL)
554 {
555 /* no complaint for missing auxiliary file */
556 if (errno == ENOENT && version)
557 {
559 return;
560 }
561
564 errmsg("could not open extension control file \"%s\": %m",
565 filename)));
566 }
567
568 /*
569 * Parse the file content, using GUC's file parsing code. We need not
570 * check the return value since any errors will be thrown at ERROR level.
571 */
573 &head, &tail);
574
575 FreeFile(file);
576
577 /*
578 * Convert the ConfigVariable list into ExtensionControlFile entries.
579 */
580 for (item = head; item != NULL; item = item->next)
581 {
582 if (strcmp(item->name, "directory") == 0)
583 {
584 if (version)
586 (errcode(ERRCODE_SYNTAX_ERROR),
587 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
588 item->name)));
589
590 control->directory = pstrdup(item->value);
591 }
592 else if (strcmp(item->name, "default_version") == 0)
593 {
594 if (version)
596 (errcode(ERRCODE_SYNTAX_ERROR),
597 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
598 item->name)));
599
600 control->default_version = pstrdup(item->value);
601 }
602 else if (strcmp(item->name, "module_pathname") == 0)
603 {
604 control->module_pathname = pstrdup(item->value);
605 }
606 else if (strcmp(item->name, "comment") == 0)
607 {
608 control->comment = pstrdup(item->value);
609 }
610 else if (strcmp(item->name, "schema") == 0)
611 {
612 control->schema = pstrdup(item->value);
613 }
614 else if (strcmp(item->name, "relocatable") == 0)
615 {
616 if (!parse_bool(item->value, &control->relocatable))
618 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
619 errmsg("parameter \"%s\" requires a Boolean value",
620 item->name)));
621 }
622 else if (strcmp(item->name, "superuser") == 0)
623 {
624 if (!parse_bool(item->value, &control->superuser))
626 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
627 errmsg("parameter \"%s\" requires a Boolean value",
628 item->name)));
629 }
630 else if (strcmp(item->name, "trusted") == 0)
631 {
632 if (!parse_bool(item->value, &control->trusted))
634 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
635 errmsg("parameter \"%s\" requires a Boolean value",
636 item->name)));
637 }
638 else if (strcmp(item->name, "encoding") == 0)
639 {
640 control->encoding = pg_valid_server_encoding(item->value);
641 if (control->encoding < 0)
643 (errcode(ERRCODE_UNDEFINED_OBJECT),
644 errmsg("\"%s\" is not a valid encoding name",
645 item->value)));
646 }
647 else if (strcmp(item->name, "requires") == 0)
648 {
649 /* Need a modifiable copy of string */
650 char *rawnames = pstrdup(item->value);
651
652 /* Parse string into list of identifiers */
653 if (!SplitIdentifierString(rawnames, ',', &control->requires))
654 {
655 /* syntax error in name list */
657 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
658 errmsg("parameter \"%s\" must be a list of extension names",
659 item->name)));
660 }
661 }
662 else if (strcmp(item->name, "no_relocate") == 0)
663 {
664 /* Need a modifiable copy of string */
665 char *rawnames = pstrdup(item->value);
666
667 /* Parse string into list of identifiers */
668 if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
669 {
670 /* syntax error in name list */
672 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
673 errmsg("parameter \"%s\" must be a list of extension names",
674 item->name)));
675 }
676 }
677 else
679 (errcode(ERRCODE_SYNTAX_ERROR),
680 errmsg("unrecognized parameter \"%s\" in file \"%s\"",
681 item->name, filename)));
682 }
683
685
686 if (control->relocatable && control->schema != NULL)
688 (errcode(ERRCODE_SYNTAX_ERROR),
689 errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
690
692}
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:463
static char * find_extension_control_filename(ExtensionControlFile *control)
Definition: extension.c:402
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(), 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, 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 2291 of file extension.c.

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

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

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 2639 of file extension.c.

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

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

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

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

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 698 of file extension.c.

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

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 740 of file extension.c.

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

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

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

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