PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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_proc.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/inval.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
 
struct  ExtensionLocation
 
struct  ExtensionSiblingCache
 

Typedefs

typedef struct ExtensionControlFile ExtensionControlFile
 
typedef struct ExtensionVersionInfo ExtensionVersionInfo
 
typedef struct ExtensionSiblingCache ExtensionSiblingCache
 

Functions

static void ext_sibling_callback (Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
 
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, ExtensionLocation *location)
 
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 charread_whole_file (const char *filename, int *length)
 
static ExtensionControlFilenew_ExtensionControlFile (const char *extname)
 
charfind_in_paths (const char *basename, List *paths)
 
static charget_extension_location (ExtensionLocation *loc)
 
Oid get_extension_oid (const char *extname, bool missing_ok)
 
charget_extension_name (Oid ext_oid)
 
Oid get_extension_schema (Oid ext_oid)
 
Oid get_function_sibling_type (Oid funcoid, const char *typname)
 
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 charfind_extension_control_filename (ExtensionControlFile *control)
 
static charget_extension_script_directory (ExtensionControlFile *control)
 
static charget_extension_aux_control_filename (ExtensionControlFile *control, const char *version)
 
static charget_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 charread_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

charExtension_control_path
 
bool creating_extension = false
 
Oid CurrentExtensionObject = InvalidOid
 
static ExtensionSiblingCacheext_sibling_list = NULL
 

Typedef Documentation

◆ ExtensionControlFile

◆ ExtensionSiblingCache

◆ ExtensionVersionInfo

Function Documentation

◆ AlterExtensionNamespace()

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

Definition at line 3252 of file extension.c.

3253{
3255 Oid nspOid;
3256 Oid oldNspOid;
3259 ScanKeyData key[2];
3268
3270
3271 nspOid = LookupCreationNamespace(newschema);
3272
3273 /*
3274 * Permission check: must own extension. Note that we don't bother to
3275 * check ownership of the individual member objects ...
3276 */
3280
3281 /* Permission check: must have creation rights in target namespace */
3283 if (aclresult != ACLCHECK_OK)
3285
3286 /*
3287 * If the schema is currently a member of the extension, disallow moving
3288 * the extension into the schema. That would create a dependency loop.
3289 */
3291 ereport(ERROR,
3293 errmsg("cannot move extension \"%s\" into schema \"%s\" "
3294 "because the extension contains the schema",
3295 extensionName, newschema)));
3296
3297 /* Locate the pg_extension tuple */
3299
3300 ScanKeyInit(&key[0],
3304
3306 NULL, 1, key);
3307
3309
3310 if (!HeapTupleIsValid(extTup)) /* should not happen */
3311 elog(ERROR, "could not find tuple for extension %u",
3312 extensionOid);
3313
3314 /* Copy tuple so we can modify it below */
3317
3319
3320 /*
3321 * If the extension is already in the target schema, just silently do
3322 * nothing.
3323 */
3324 if (extForm->extnamespace == nspOid)
3325 {
3327 return InvalidObjectAddress;
3328 }
3329
3330 /* Check extension is supposed to be relocatable */
3331 if (!extForm->extrelocatable)
3332 ereport(ERROR,
3334 errmsg("extension \"%s\" does not support SET SCHEMA",
3335 NameStr(extForm->extname))));
3336
3338
3339 /* store the OID of the namespace to-be-changed */
3340 oldNspOid = extForm->extnamespace;
3341
3342 /*
3343 * Scan pg_depend to find objects that depend directly on the extension,
3344 * and alter each one's schema.
3345 */
3347
3348 ScanKeyInit(&key[0],
3352 ScanKeyInit(&key[1],
3356
3358 NULL, 2, key);
3359
3361 {
3365
3366 /*
3367 * If a dependent extension has a no_relocate request for this
3368 * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
3369 * the same loop that's actually executing the renames: we may detect
3370 * the error condition only after having expended a fair amount of
3371 * work. However, the alternative is to do two scans of pg_depend,
3372 * which seems like optimizing for failure cases. The rename work
3373 * will all roll back cleanly enough if we do fail here.)
3374 */
3375 if (pg_depend->deptype == DEPENDENCY_NORMAL &&
3376 pg_depend->classid == ExtensionRelationId)
3377 {
3378 char *depextname = get_extension_name(pg_depend->objid);
3380 ListCell *lc;
3381
3383 foreach(lc, dcontrol->no_relocate)
3384 {
3385 char *nrextname = (char *) lfirst(lc);
3386
3387 if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
3388 {
3389 ereport(ERROR,
3391 errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
3392 NameStr(extForm->extname)),
3393 errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
3394 depextname,
3395 NameStr(extForm->extname))));
3396 }
3397 }
3398 }
3399
3400 /*
3401 * Otherwise, ignore non-membership dependencies. (Currently, the
3402 * only other case we could see here is a normal dependency from
3403 * another extension.)
3404 */
3405 if (pg_depend->deptype != DEPENDENCY_EXTENSION)
3406 continue;
3407
3408 dep.classId = pg_depend->classid;
3409 dep.objectId = pg_depend->objid;
3410 dep.objectSubId = pg_depend->objsubid;
3411
3412 if (dep.objectSubId != 0) /* should not happen */
3413 elog(ERROR, "extension should not have a sub-object dependency");
3414
3415 /* Relocate the object */
3417 dep.objectId,
3418 nspOid,
3419 objsMoved);
3420
3421 /*
3422 * If not all the objects had the same old namespace (ignoring any
3423 * that are not in namespaces or are dependent types), complain.
3424 */
3426 ereport(ERROR,
3428 errmsg("extension \"%s\" does not support SET SCHEMA",
3429 NameStr(extForm->extname)),
3430 errdetail("%s is not in the extension's schema \"%s\"",
3431 getObjectDescription(&dep, false),
3433 }
3434
3435 /* report old schema, if caller wants it */
3436 if (oldschema)
3438
3440
3442
3443 /* Now adjust pg_extension.extnamespace */
3444 extForm->extnamespace = nspOid;
3445
3447
3449
3450 /* update dependency to point to the new schema */
3453 elog(ERROR, "could not change schema dependency for extension %s",
3454 NameStr(extForm->extname));
3455
3457
3459
3460 return extAddr;
3461}
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:2654
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3836
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4090
Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, ObjectAddresses *objsMoved)
Definition alter.c:619
#define NameStr(name)
Definition c.h:777
ObjectAddresses * new_object_addresses(void)
@ DEPENDENCY_EXTENSION
Definition dependency.h:38
@ DEPENDENCY_NORMAL
Definition dependency.h:33
int errdetail(const char *fmt,...)
Definition elog.c:1217
int errcode(int sqlerrcode)
Definition elog.c:864
int errmsg(const char *fmt,...)
Definition elog.c:1081
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
static ExtensionControlFile * read_extension_control_file(const char *extname)
Definition extension.c:878
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition extension.c:228
char * get_extension_name(Oid ext_oid)
Definition extension.c:250
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)
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *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:3518
Oid GetUserId(void)
Definition miscinit.c:469
Oid LookupCreationNamespace(const char *nspname)
Definition namespace.c:3499
#define InvokeObjectPostAlterHook(classId, objectId, subId)
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
@ OBJECT_SCHEMA
@ OBJECT_EXTENSION
#define ACL_CREATE
Definition parsenodes.h:85
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:459
Oid getExtensionOfObject(Oid classId, Oid objectId)
Definition pg_depend.c:734
FormData_pg_depend * Form_pg_depend
Definition pg_depend.h:72
FormData_pg_extension * Form_pg_extension
#define lfirst(lc)
Definition pg_list.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:262
#define InvalidOid
unsigned int Oid
static int fb(int x)
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
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(), DEPENDENCY_EXTENSION, DEPENDENCY_NORMAL, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), get_extension_name(), get_extension_oid(), get_namespace_name(), getExtensionOfObject(), getObjectDescription(), GETSTRUCT(), GetUserId(), heap_copytuple(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, InvokeObjectPostAlterHook, lfirst, LookupCreationNamespace(), NameStr, new_object_addresses(), object_aclcheck(), OBJECT_EXTENSION, object_ownercheck(), OBJECT_SCHEMA, ObjectAddressSet, ObjectIdGetDatum(), read_extension_control_file(), relation_close(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), 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 3614 of file extension.c.

3621{
3622 const char *oldVersionName = initialVersion;
3623 ListCell *lcv;
3624
3625 foreach(lcv, updateVersions)
3626 {
3627 char *versionName = (char *) lfirst(lcv);
3628 ExtensionControlFile *control;
3629 char *schemaName;
3630 Oid schemaOid;
3634 ScanKeyData key[1];
3639 bool nulls[Natts_pg_extension];
3640 bool repl[Natts_pg_extension];
3642 ListCell *lc;
3643
3644 /*
3645 * Fetch parameters for specific version (pcontrol is not changed)
3646 */
3648
3649 /* Find the pg_extension tuple */
3651
3652 ScanKeyInit(&key[0],
3656
3658 NULL, 1, key);
3659
3661
3662 if (!HeapTupleIsValid(extTup)) /* should not happen */
3663 elog(ERROR, "could not find tuple for extension %u",
3664 extensionOid);
3665
3667
3668 /*
3669 * Determine the target schema (set by original install)
3670 */
3671 schemaOid = extForm->extnamespace;
3673
3674 /*
3675 * Modify extrelocatable and extversion in the pg_extension tuple
3676 */
3677 memset(values, 0, sizeof(values));
3678 memset(nulls, 0, sizeof(nulls));
3679 memset(repl, 0, sizeof(repl));
3680
3682 BoolGetDatum(control->relocatable);
3683 repl[Anum_pg_extension_extrelocatable - 1] = true;
3686 repl[Anum_pg_extension_extversion - 1] = true;
3687
3689 values, nulls, repl);
3690
3692
3694
3696
3697 /*
3698 * Look up the prerequisite extensions for this version, install them
3699 * if necessary, and build lists of their OIDs and the OIDs of their
3700 * target schemas.
3701 */
3704 foreach(lc, control->requires)
3705 {
3706 char *curreq = (char *) lfirst(lc);
3707 Oid reqext;
3708 Oid reqschema;
3709
3711 control->name,
3713 cascade,
3714 NIL,
3715 is_create);
3719 }
3720
3721 /*
3722 * Remove and recreate dependencies on prerequisite extensions
3723 */
3727
3728 myself.classId = ExtensionRelationId;
3729 myself.objectId = extensionOid;
3730 myself.objectSubId = 0;
3731
3732 foreach(lc, requiredExtensions)
3733 {
3736
3738 otherext.objectId = reqext;
3739 otherext.objectSubId = 0;
3740
3742 }
3743
3745
3746 /*
3747 * Finally, execute the update script file
3748 */
3752 schemaName);
3753
3754 /*
3755 * Update prior-version name and loop around. Since
3756 * execute_sql_string did a final CommandCounterIncrement, we can
3757 * update the pg_extension row again.
3758 */
3760 }
3761}
static Datum values[MAXATTR]
Definition bootstrap.c:147
#define CStringGetTextDatum(s)
Definition builtins.h:98
Oid get_extension_schema(Oid ext_oid)
Definition extension.c:272
static Oid get_required_extension(char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade, List *parents, bool is_create)
Definition extension.c:2072
static ExtensionControlFile * read_extension_aux_control_file(const ExtensionControlFile *pcontrol, const char *version)
Definition extension.c:897
static void execute_extension_script(Oid extensionOid, ExtensionControlFile *control, const char *from_version, const char *version, List *requiredSchemas, const char *schemaName)
Definition extension.c:1245
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:47
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:353
#define NIL
Definition pg_list.h:68
#define lfirst_oid(lc)
Definition pg_list.h:174
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
uint64_t Datum
Definition postgres.h:70
#define RelationGetDescr(relation)
Definition rel.h:540
Definition pg_list.h:54

References BoolGetDatum(), BTEqualStrategyNumber, CatalogTupleUpdate(), ObjectAddress::classId, CStringGetTextDatum, deleteDependencyRecordsForClass(), DEPENDENCY_NORMAL, elog, ERROR, execute_extension_script(), fb(), get_extension_schema(), get_namespace_name(), get_required_extension(), GETSTRUCT(), heap_modify_tuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, lappend_oid(), lfirst, lfirst_oid, ExtensionControlFile::name, NIL, ObjectIdGetDatum(), read_extension_aux_control_file(), recordDependencyOn(), RelationGetDescr, ExtensionControlFile::relocatable, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), 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 400 of file extension.c.

401{
402 int namelen = strlen(extensionname);
403
404 /*
405 * Disallow empty names (the parser rejects empty identifiers anyway, but
406 * let's check).
407 */
408 if (namelen == 0)
411 errmsg("invalid extension name: \"%s\"", extensionname),
412 errdetail("Extension names must not be empty.")));
413
414 /*
415 * No double dashes, since that would make script filenames ambiguous.
416 */
417 if (strstr(extensionname, "--"))
420 errmsg("invalid extension name: \"%s\"", extensionname),
421 errdetail("Extension names must not contain \"--\".")));
422
423 /*
424 * No leading or trailing dash either. (We could probably allow this, but
425 * it would require much care in filename parsing and would make filenames
426 * visually if not formally ambiguous. Since there's no real-world use
427 * case, let's just forbid it.)
428 */
429 if (extensionname[0] == '-' || extensionname[namelen - 1] == '-')
432 errmsg("invalid extension name: \"%s\"", extensionname),
433 errdetail("Extension names must not begin or end with \"-\".")));
434
435 /*
436 * No directory separators either (this is sufficient to prevent ".."
437 * style attacks).
438 */
442 errmsg("invalid extension name: \"%s\"", extensionname),
443 errdetail("Extension names must not contain directory separator characters.")));
444}
char * first_dir_separator(const char *filename)
Definition path.c:110

References ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), 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 447 of file extension.c.

448{
449 int namelen = strlen(versionname);
450
451 /*
452 * Disallow empty names (we could possibly allow this, but there seems
453 * little point).
454 */
455 if (namelen == 0)
458 errmsg("invalid extension version name: \"%s\"", versionname),
459 errdetail("Version names must not be empty.")));
460
461 /*
462 * No double dashes, since that would make script filenames ambiguous.
463 */
464 if (strstr(versionname, "--"))
467 errmsg("invalid extension version name: \"%s\"", versionname),
468 errdetail("Version names must not contain \"--\".")));
469
470 /*
471 * No leading or trailing dash either.
472 */
473 if (versionname[0] == '-' || versionname[namelen - 1] == '-')
476 errmsg("invalid extension version name: \"%s\"", versionname),
477 errdetail("Version names must not begin or end with \"-\".")));
478
479 /*
480 * No directory separators either (this is sufficient to prevent ".."
481 * style attacks).
482 */
486 errmsg("invalid extension version name: \"%s\"", versionname),
487 errdetail("Version names must not contain directory separator characters.")));
488}

References ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), 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 2740 of file extension.c.

2741{
2742 Datum *datums;
2743 int ndatums;
2744 ArrayType *a;
2745 ListCell *lc;
2746
2747 ndatums = list_length(requires);
2748 datums = (Datum *) palloc(ndatums * sizeof(Datum));
2749 ndatums = 0;
2750 foreach(lc, requires)
2751 {
2752 char *curreq = (char *) lfirst(lc);
2753
2754 datums[ndatums++] =
2756 }
2757 a = construct_array_builtin(datums, ndatums, NAMEOID);
2758 return PointerGetDatum(a);
2759}
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:684
int a
Definition isn.c:73
void * palloc(Size size)
Definition mcxt.c:1387
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:352
static Datum CStringGetDatum(const char *X)
Definition postgres.h:380

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

Referenced by get_available_versions_for_extension().

◆ CreateExtension()

ObjectAddress CreateExtension ( ParseState pstate,
CreateExtensionStmt stmt 
)

Definition at line 2143 of file extension.c.

2144{
2148 char *schemaName = NULL;
2149 char *versionName = NULL;
2150 bool cascade = false;
2151 ListCell *lc;
2152
2153 /* Check extension name validity before any filesystem access */
2155
2156 /*
2157 * Check for duplicate extension name. The unique index on
2158 * pg_extension.extname would catch this anyway, and serves as a backstop
2159 * in case of race conditions; but this is a friendlier error message, and
2160 * besides we need a check to support IF NOT EXISTS.
2161 */
2162 if (get_extension_oid(stmt->extname, true) != InvalidOid)
2163 {
2164 if (stmt->if_not_exists)
2165 {
2168 errmsg("extension \"%s\" already exists, skipping",
2169 stmt->extname)));
2170 return InvalidObjectAddress;
2171 }
2172 else
2173 ereport(ERROR,
2175 errmsg("extension \"%s\" already exists",
2176 stmt->extname)));
2177 }
2178
2179 /*
2180 * We use global variables to track the extension being created, so we can
2181 * create only one extension at the same time.
2182 */
2184 ereport(ERROR,
2186 errmsg("nested CREATE EXTENSION is not supported")));
2187
2188 /* Deconstruct the statement option list */
2189 foreach(lc, stmt->options)
2190 {
2191 DefElem *defel = (DefElem *) lfirst(lc);
2192
2193 if (strcmp(defel->defname, "schema") == 0)
2194 {
2195 if (d_schema)
2197 d_schema = defel;
2199 }
2200 else if (strcmp(defel->defname, "new_version") == 0)
2201 {
2202 if (d_new_version)
2206 }
2207 else if (strcmp(defel->defname, "cascade") == 0)
2208 {
2209 if (d_cascade)
2211 d_cascade = defel;
2212 cascade = defGetBoolean(d_cascade);
2213 }
2214 else
2215 elog(ERROR, "unrecognized option: %s", defel->defname);
2216 }
2217
2218 /* Call CreateExtensionInternal to do the real work. */
2219 return CreateExtensionInternal(stmt->extname,
2220 schemaName,
2222 cascade,
2223 NIL,
2224 true);
2225}
char * defGetString(DefElem *def)
Definition define.c:34
bool defGetBoolean(DefElem *def)
Definition define.c:93
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
Definition define.c:370
#define NOTICE
Definition elog.h:35
static void check_valid_extension_name(const char *extensionname)
Definition extension.c:400
bool creating_extension
Definition extension.c:79
static ObjectAddress CreateExtensionInternal(char *extensionName, char *schemaName, const char *versionName, bool cascade, List *parents, bool is_create)
Definition extension.c:1833
#define stmt
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30

References check_valid_extension_name(), CreateExtensionInternal(), creating_extension, defGetBoolean(), defGetString(), elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, errorConflictingDefElem(), fb(), 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 1833 of file extension.c.

1839{
1840 char *origSchemaName = schemaName;
1844 ExtensionControlFile *control;
1845 char *filename;
1846 struct stat fst;
1851 ObjectAddress address;
1852 ListCell *lc;
1853
1854 /*
1855 * Read the primary control file. Note we assume that it does not contain
1856 * any non-ASCII data, so there is no need to worry about encoding at this
1857 * point.
1858 */
1860
1861 /*
1862 * Determine the version to install
1863 */
1864 if (versionName == NULL)
1865 {
1866 if (pcontrol->default_version)
1867 versionName = pcontrol->default_version;
1868 else
1869 ereport(ERROR,
1871 errmsg("version to install must be specified")));
1872 }
1874
1875 /*
1876 * Figure out which script(s) we need to run to install the desired
1877 * version of the extension. If we do not have a script that directly
1878 * does what is needed, we try to find a sequence of update scripts that
1879 * will get us there.
1880 */
1882 if (stat(filename, &fst) == 0)
1883 {
1884 /* Easy, no extra scripts */
1886 }
1887 else
1888 {
1889 /* Look for best way to install this version */
1890 List *evi_list;
1893
1894 /* Extract the version update graph from the script directory */
1896
1897 /* Identify the target version */
1899
1900 /* Identify best path to reach target */
1903
1904 /* Fail if no path ... */
1905 if (evi_start == NULL)
1906 ereport(ERROR,
1908 errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
1909 pcontrol->name, versionName)));
1910
1911 /* Otherwise, install best starting point and then upgrade */
1912 versionName = evi_start->name;
1913 }
1914
1915 /*
1916 * Fetch control parameters for installation target version
1917 */
1919
1920 /*
1921 * Determine the target schema to install the extension into
1922 */
1923 if (schemaName)
1924 {
1925 /* If the user is giving us the schema name, it must exist already. */
1927 }
1928
1929 if (control->schema != NULL)
1930 {
1931 /*
1932 * The extension is not relocatable and the author gave us a schema
1933 * for it.
1934 *
1935 * Unless CASCADE parameter was given, it's an error to give a schema
1936 * different from control->schema if control->schema is specified.
1937 */
1938 if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1939 !cascade)
1940 ereport(ERROR,
1942 errmsg("extension \"%s\" must be installed in schema \"%s\"",
1943 control->name,
1944 control->schema)));
1945
1946 /* Always use the schema from control file for current extension. */
1947 schemaName = control->schema;
1948
1949 /* Find or create the schema in case it does not exist. */
1951
1952 if (!OidIsValid(schemaOid))
1953 {
1955
1956 csstmt->schemaname = schemaName;
1957 csstmt->authrole = NULL; /* will be created by current user */
1958 csstmt->schemaElts = NIL;
1959 csstmt->if_not_exists = false;
1960 CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
1961 -1, -1);
1962
1963 /*
1964 * CreateSchemaCommand includes CommandCounterIncrement, so new
1965 * schema is now visible.
1966 */
1968 }
1969 }
1970 else if (!OidIsValid(schemaOid))
1971 {
1972 /*
1973 * Neither user nor author of the extension specified schema; use the
1974 * current default creation namespace, which is the first explicit
1975 * entry in the search_path.
1976 */
1977 List *search_path = fetch_search_path(false);
1978
1979 if (search_path == NIL) /* nothing valid in search_path? */
1980 ereport(ERROR,
1982 errmsg("no schema has been selected to create in")));
1983 schemaOid = linitial_oid(search_path);
1985 if (schemaName == NULL) /* recently-deleted namespace? */
1986 ereport(ERROR,
1988 errmsg("no schema has been selected to create in")));
1989
1990 list_free(search_path);
1991 }
1992
1993 /*
1994 * Make note if a temporary namespace has been accessed in this
1995 * transaction.
1996 */
1999
2000 /*
2001 * We don't check creation rights on the target namespace here. If the
2002 * extension script actually creates any objects there, it will fail if
2003 * the user doesn't have such permissions. But there are cases such as
2004 * procedural languages where it's convenient to set schema = pg_catalog
2005 * yet we don't want to restrict the command to users with ACL_CREATE for
2006 * pg_catalog.
2007 */
2008
2009 /*
2010 * Look up the prerequisite extensions, install them if necessary, and
2011 * build lists of their OIDs and the OIDs of their target schemas.
2012 */
2015 foreach(lc, control->requires)
2016 {
2017 char *curreq = (char *) lfirst(lc);
2018 Oid reqext;
2019 Oid reqschema;
2020
2024 cascade,
2025 parents,
2026 is_create);
2030 }
2031
2032 /*
2033 * Insert new tuple into pg_extension, and create dependency entries.
2034 */
2035 address = InsertExtensionTuple(control->name, extowner,
2036 schemaOid, control->relocatable,
2041 extensionOid = address.objectId;
2042
2043 /*
2044 * Apply any control-file comment on extension
2045 */
2046 if (control->comment != NULL)
2048
2049 /*
2050 * Execute the installation script file
2051 */
2055 schemaName);
2056
2057 /*
2058 * If additional update scripts have to be executed, apply the updates as
2059 * though a series of ALTER EXTENSION UPDATE commands were given
2060 */
2063 origSchemaName, cascade, is_create);
2064
2065 return address;
2066}
#define OidIsValid(objectId)
Definition c.h:800
void CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
Definition comment.c:153
ObjectAddress InsertExtensionTuple(const char *extName, Oid extOwner, Oid schemaOid, bool relocatable, const char *extVersion, Datum extConfig, Datum extCondition, List *requiredExtensions)
Definition extension.c:2241
static void check_valid_version_name(const char *versionname)
Definition extension.c:447
static ExtensionVersionInfo * find_install_path(List *evi_list, ExtensionVersionInfo *evi_target, List **best_path)
Definition extension.c:1778
static ExtensionVersionInfo * get_ext_ver_info(const char *versionname, List **evi_list)
Definition extension.c:1518
static List * get_ext_ver_list(ExtensionControlFile *control)
Definition extension.c:1579
static char * get_extension_script_filename(ExtensionControlFile *control, const char *from_version, const char *version)
Definition extension.c:655
static void ApplyExtensionUpdates(Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
Definition extension.c:3614
void list_free(List *list)
Definition list.c:1546
bool isTempNamespace(Oid namespaceId)
Definition namespace.c:3720
List * fetch_search_path(bool includeImplicit)
Definition namespace.c:4890
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition namespace.c:3606
#define makeNode(_type_)
Definition nodes.h:161
static char * filename
Definition pg_dumpall.c:120
#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
#define stat
Definition win32_port.h:74
int MyXactFlags
Definition xact.c:137
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition xact.h:103

References ApplyExtensionUpdates(), check_valid_version_name(), ExtensionControlFile::comment, CreateComments(), CreateSchemaCommand(), ereport, errcode(), errmsg(), ERROR, execute_extension_script(), fb(), 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(), InsertExtensionTuple(), InvalidOid, isTempNamespace(), lappend_oid(), lfirst, linitial_oid, list_free(), makeNode, MyXactFlags, ExtensionControlFile::name, NIL, ObjectAddress::objectId, OidIsValid, PointerGetDatum(), read_extension_aux_control_file(), read_extension_control_file(), ExtensionControlFile::relocatable, ExtensionControlFile::schema, 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 3858 of file extension.c.

3861{
3863
3864 /*
3865 * Check existing extension membership.
3866 */
3867 oldExtension = getExtensionOfObject(object.classId, object.objectId);
3868
3869 if (stmt->action > 0)
3870 {
3871 /*
3872 * ADD, so complain if object is already attached to some extension.
3873 */
3875 ereport(ERROR,
3877 errmsg("%s is already a member of extension \"%s\"",
3878 getObjectDescription(&object, false),
3880
3881 /*
3882 * Prevent a schema from being added to an extension if the schema
3883 * contains the extension. That would create a dependency loop.
3884 */
3885 if (object.classId == NamespaceRelationId &&
3886 object.objectId == get_extension_schema(extension.objectId))
3887 ereport(ERROR,
3889 errmsg("cannot add schema \"%s\" to extension \"%s\" "
3890 "because the schema contains the extension",
3891 get_namespace_name(object.objectId),
3892 stmt->extname)));
3893
3894 /*
3895 * OK, add the dependency.
3896 */
3898
3899 /*
3900 * Also record the initial ACL on the object, if any.
3901 *
3902 * Note that this will handle the object's ACLs, as well as any ACLs
3903 * on object subIds. (In other words, when the object is a table,
3904 * this will record the table's ACL and the ACLs for the columns on
3905 * the table, if any).
3906 */
3907 recordExtObjInitPriv(object.objectId, object.classId);
3908 }
3909 else
3910 {
3911 /*
3912 * DROP, so complain if it's not a member.
3913 */
3914 if (oldExtension != extension.objectId)
3915 ereport(ERROR,
3917 errmsg("%s is not a member of extension \"%s\"",
3918 getObjectDescription(&object, false),
3919 stmt->extname)));
3920
3921 /*
3922 * OK, drop the dependency.
3923 */
3924 if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3927 elog(ERROR, "unexpected number of extension dependency records");
3928
3929 /*
3930 * If it's a relation, it might have an entry in the extension's
3931 * extconfig array, which we must remove.
3932 */
3933 if (object.classId == RelationRelationId)
3934 extension_config_remove(extension.objectId, object.objectId);
3935
3936 /*
3937 * Remove all the initial ACLs, if any.
3938 *
3939 * Note that this will remove the object's ACLs, as well as any ACLs
3940 * on object subIds. (In other words, when the object is a table,
3941 * this will remove the table's ACL and the ACLs for the columns on
3942 * the table, if any).
3943 */
3944 removeExtObjInitPriv(object.objectId, object.classId);
3945 }
3946
3947 /*
3948 * Recurse to any dependent objects; currently, this includes the array
3949 * type of a base type, the multirange type associated with a range type,
3950 * and the rowtype of a table.
3951 */
3952 if (object.classId == TypeRelationId)
3953 {
3955
3957 depobject.objectSubId = 0;
3958
3959 /* If it has an array type, update that too */
3960 depobject.objectId = get_array_type(object.objectId);
3961 if (OidIsValid(depobject.objectId))
3963
3964 /* If it is a range type, update the associated multirange too */
3965 if (type_is_range(object.objectId))
3966 {
3967 depobject.objectId = get_range_multirange(object.objectId);
3968 if (!OidIsValid(depobject.objectId))
3969 ereport(ERROR,
3971 errmsg("could not find multirange type for data type %s",
3972 format_type_be(object.objectId))));
3974 }
3975 }
3976 if (object.classId == RelationRelationId)
3977 {
3979
3981 depobject.objectSubId = 0;
3982
3983 /* It might not have a rowtype, but if it does, update that */
3984 depobject.objectId = get_rel_type_id(object.objectId);
3985 if (OidIsValid(depobject.objectId))
3987 }
3988}
void recordExtObjInitPriv(Oid objoid, Oid classoid)
Definition aclchk.c:4354
void removeExtObjInitPriv(Oid objoid, Oid classoid)
Definition aclchk.c:4518
static void extension_config_remove(Oid extensionoid, Oid tableoid)
Definition extension.c:3087
static void ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt, ObjectAddress extension, ObjectAddress object)
Definition extension.c:3858
char * format_type_be(Oid type_oid)
bool type_is_range(Oid typid)
Definition lsyscache.c:2840
Oid get_rel_type_id(Oid relid)
Definition lsyscache.c:2129
Oid get_range_multirange(Oid rangeOid)
Definition lsyscache.c:3610
Oid get_array_type(Oid typid)
Definition lsyscache.c:2939

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

Referenced by ExecAlterExtensionContentsRecurse(), and ExecAlterExtensionContentsStmt().

◆ ExecAlterExtensionContentsStmt()

ObjectAddress ExecAlterExtensionContentsStmt ( AlterExtensionContentsStmt stmt,
ObjectAddress objAddr 
)

Definition at line 3772 of file extension.c.

3774{
3776 ObjectAddress object;
3777 Relation relation;
3778
3779 switch (stmt->objtype)
3780 {
3781 case OBJECT_DATABASE:
3782 case OBJECT_EXTENSION:
3783 case OBJECT_INDEX:
3784 case OBJECT_PUBLICATION:
3785 case OBJECT_ROLE:
3788 case OBJECT_TABLESPACE:
3789 ereport(ERROR,
3791 errmsg("cannot add an object of this type to an extension")));
3792 break;
3793 default:
3794 /* OK */
3795 break;
3796 }
3797
3798 /*
3799 * Find the extension and acquire a lock on it, to ensure it doesn't get
3800 * dropped concurrently. A sharable lock seems sufficient: there's no
3801 * reason not to allow other sorts of manipulations, such as add/drop of
3802 * other objects, to occur concurrently. Concurrently adding/dropping the
3803 * *same* object would be bad, but we prevent that by using a non-sharable
3804 * lock on the individual object, below.
3805 */
3807 (Node *) makeString(stmt->extname),
3808 &relation, AccessShareLock, false);
3809
3810 /* Permission check: must own extension */
3813 stmt->extname);
3814
3815 /*
3816 * Translate the parser representation that identifies the object into an
3817 * ObjectAddress. get_object_address() will throw an error if the object
3818 * does not exist, and will also acquire a lock on the object to guard
3819 * against concurrent DROP and ALTER EXTENSION ADD/DROP operations.
3820 */
3821 object = get_object_address(stmt->objtype, stmt->object,
3822 &relation, ShareUpdateExclusiveLock, false);
3823
3824 Assert(object.objectSubId == 0);
3825 if (objAddr)
3826 *objAddr = object;
3827
3828 /* Permission check: must own target object, too */
3829 check_object_ownership(GetUserId(), stmt->objtype, object,
3830 stmt->object, relation);
3831
3832 /* Do the update, recursing to any dependent objects */
3834
3835 /* Finish up */
3837
3838 /*
3839 * If get_object_address() opened the relation for us, we close it to keep
3840 * the reference count correct - but we retain any locks acquired by
3841 * get_object_address() until commit time, to guard against concurrent
3842 * activity.
3843 */
3844 if (relation != NULL)
3845 relation_close(relation, NoLock);
3846
3847 return extension;
3848}
#define Assert(condition)
Definition c.h:885
#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
@ OBJECT_ROLE
@ OBJECT_INDEX
@ OBJECT_DATABASE
@ OBJECT_PUBLICATION
@ OBJECT_SUBSCRIPTION
@ OBJECT_STATISTIC_EXT
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(), fb(), 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, relation_close(), ShareUpdateExclusiveLock, and stmt.

Referenced by ProcessUtilitySlow().

◆ ExecAlterExtensionStmt()

ObjectAddress ExecAlterExtensionStmt ( ParseState pstate,
AlterExtensionStmt stmt 
)

Definition at line 3467 of file extension.c.

3468{
3470 char *versionName;
3471 char *oldVersionName;
3472 ExtensionControlFile *control;
3475 ScanKeyData key[1];
3479 Datum datum;
3480 bool isnull;
3481 ListCell *lc;
3482 ObjectAddress address;
3483
3484 /*
3485 * We use global variables to track the extension being created, so we can
3486 * create/update only one extension at the same time.
3487 */
3489 ereport(ERROR,
3491 errmsg("nested ALTER EXTENSION is not supported")));
3492
3493 /*
3494 * Look up the extension --- it must already exist in pg_extension
3495 */
3497
3498 ScanKeyInit(&key[0],
3501 CStringGetDatum(stmt->extname));
3502
3504 NULL, 1, key);
3505
3507
3509 ereport(ERROR,
3511 errmsg("extension \"%s\" does not exist",
3512 stmt->extname)));
3513
3515
3516 /*
3517 * Determine the existing version we are updating from
3518 */
3520 RelationGetDescr(extRel), &isnull);
3521 if (isnull)
3522 elog(ERROR, "extversion is null");
3524
3526
3528
3529 /* Permission check: must own extension */
3532 stmt->extname);
3533
3534 /*
3535 * Read the primary control file. Note we assume that it does not contain
3536 * any non-ASCII data, so there is no need to worry about encoding at this
3537 * point.
3538 */
3539 control = read_extension_control_file(stmt->extname);
3540
3541 /*
3542 * Read the statement option list
3543 */
3544 foreach(lc, stmt->options)
3545 {
3546 DefElem *defel = (DefElem *) lfirst(lc);
3547
3548 if (strcmp(defel->defname, "new_version") == 0)
3549 {
3550 if (d_new_version)
3553 }
3554 else
3555 elog(ERROR, "unrecognized option: %s", defel->defname);
3556 }
3557
3558 /*
3559 * Determine the version to update to
3560 */
3561 if (d_new_version && d_new_version->arg)
3563 else if (control->default_version)
3564 versionName = control->default_version;
3565 else
3566 {
3567 ereport(ERROR,
3569 errmsg("version to install must be specified")));
3570 versionName = NULL; /* keep compiler quiet */
3571 }
3573
3574 /*
3575 * If we're already at that version, just say so
3576 */
3578 {
3580 (errmsg("version \"%s\" of extension \"%s\" is already installed",
3581 versionName, stmt->extname)));
3582 return InvalidObjectAddress;
3583 }
3584
3585 /*
3586 * Identify the series of update script files we need to execute
3587 */
3590 versionName);
3591
3592 /*
3593 * Update the pg_extension row and execute the update scripts, one at a
3594 * time
3595 */
3598 NULL, false, false);
3599
3601
3602 return address;
3603}
static List * identify_update_path(ExtensionControlFile *control, const char *oldVersion, const char *newVersion)
Definition extension.c:1642
#define DatumGetTextPP(X)
Definition fmgr.h:293
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
#define strVal(v)
Definition value.h:82
char * text_to_cstring(const text *t)
Definition varlena.c:215

References AccessShareLock, aclcheck_error(), ACLCHECK_NOT_OWNER, ApplyExtensionUpdates(), BTEqualStrategyNumber, check_valid_version_name(), creating_extension, CStringGetDatum(), DatumGetTextPP, ExtensionControlFile::default_version, elog, ereport, errcode(), errmsg(), ERROR, errorConflictingDefElem(), fb(), GETSTRUCT(), GetUserId(), heap_getattr(), HeapTupleIsValid, identify_update_path(), InvalidObjectAddress, 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 1245 of file extension.c.

1250{
1251 bool switch_to_superuser = false;
1252 char *filename;
1253 Oid save_userid = 0;
1254 int save_sec_context = 0;
1255 int save_nestlevel;
1257 ListCell *lc;
1258 ListCell *lc2;
1259
1260 /*
1261 * Enforce superuser-ness if appropriate. We postpone these checks until
1262 * here so that the control flags are correctly associated with the right
1263 * script(s) if they happen to be set in secondary control files.
1264 */
1265 if (control->superuser && !superuser())
1266 {
1267 if (extension_is_trusted(control))
1268 switch_to_superuser = true;
1269 else if (from_version == NULL)
1270 ereport(ERROR,
1272 errmsg("permission denied to create extension \"%s\"",
1273 control->name),
1274 control->trusted
1275 ? errhint("Must have CREATE privilege on current database to create this extension.")
1276 : errhint("Must be superuser to create this extension.")));
1277 else
1278 ereport(ERROR,
1280 errmsg("permission denied to update extension \"%s\"",
1281 control->name),
1282 control->trusted
1283 ? errhint("Must have CREATE privilege on current database to update this extension.")
1284 : errhint("Must be superuser to update this extension.")));
1285 }
1286
1288
1289 if (from_version == NULL)
1290 elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
1291 else
1292 elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
1293
1294 /*
1295 * If installing a trusted extension on behalf of a non-superuser, become
1296 * the bootstrap superuser. (This switch will be cleaned up automatically
1297 * if the transaction aborts, as will the GUC changes below.)
1298 */
1300 {
1301 GetUserIdAndSecContext(&save_userid, &save_sec_context);
1303 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
1304 }
1305
1306 /*
1307 * Force client_min_messages and log_min_messages to be at least WARNING,
1308 * so that we won't spam the user with useless NOTICE messages from common
1309 * script actions like creating shell types.
1310 *
1311 * We use the equivalent of a function SET option to allow the setting to
1312 * persist for exactly the duration of the script execution. guc.c also
1313 * takes care of undoing the setting on error.
1314 *
1315 * log_min_messages can't be set by ordinary users, so for that one we
1316 * pretend to be superuser.
1317 */
1318 save_nestlevel = NewGUCNestLevel();
1319
1321 (void) set_config_option("client_min_messages", "warning",
1323 GUC_ACTION_SAVE, true, 0, false);
1325 (void) set_config_option_ext("log_min_messages", "warning",
1328 GUC_ACTION_SAVE, true, 0, false);
1329
1330 /*
1331 * Similarly disable check_function_bodies, to ensure that SQL functions
1332 * won't be parsed during creation.
1333 */
1335 (void) set_config_option("check_function_bodies", "off",
1337 GUC_ACTION_SAVE, true, 0, false);
1338
1339 /*
1340 * Set up the search path to have the target schema first, making it be
1341 * the default creation target namespace. Then add the schemas of any
1342 * prerequisite extensions, unless they are in pg_catalog which would be
1343 * searched anyway. (Listing pg_catalog explicitly in a non-first
1344 * position would be bad for security.) Finally add pg_temp to ensure
1345 * that temp objects can't take precedence over others.
1346 */
1349 foreach(lc, requiredSchemas)
1350 {
1353
1354 if (reqname && strcmp(reqname, "pg_catalog") != 0)
1356 }
1357 appendStringInfoString(&pathbuf, ", pg_temp");
1358
1359 (void) set_config_option("search_path", pathbuf.data,
1361 GUC_ACTION_SAVE, true, 0, false);
1362
1363 /*
1364 * Set creating_extension and related variables so that
1365 * recordDependencyOnCurrentExtension and other functions do the right
1366 * things. On failure, ensure we reset these variables.
1367 */
1368 creating_extension = true;
1370 PG_TRY();
1371 {
1372 char *c_sql = read_extension_script_file(control, filename);
1373 Datum t_sql;
1374
1375 /*
1376 * We filter each substitution through quote_identifier(). When the
1377 * arg contains one of the following characters, no one collection of
1378 * quoting can work inside $$dollar-quoted string literals$$,
1379 * 'single-quoted string literals', and outside of any literal. To
1380 * avoid a security snare for extension authors, error on substitution
1381 * for arguments containing these.
1382 */
1383 const char *quoting_relevant_chars = "\"$'\\";
1384
1385 /* We use various functions that want to operate on text datums */
1387
1388 /*
1389 * Reduce any lines beginning with "\echo" to empty. This allows
1390 * scripts to contain messages telling people not to run them via
1391 * psql, which has been found to be necessary due to old habits.
1392 */
1395 t_sql,
1396 CStringGetTextDatum("^\\\\echo.*$"),
1398 CStringGetTextDatum("ng"));
1399
1400 /*
1401 * If the script uses @extowner@, substitute the calling username.
1402 */
1403 if (strstr(c_sql, "@extowner@"))
1404 {
1405 Oid uid = switch_to_superuser ? save_userid : GetUserId();
1406 const char *userName = GetUserNameFromId(uid, false);
1407 const char *qUserName = quote_identifier(userName);
1408
1411 t_sql,
1412 CStringGetTextDatum("@extowner@"),
1415 ereport(ERROR,
1417 errmsg("invalid character in extension owner: must not contain any of \"%s\"",
1419 }
1420
1421 /*
1422 * If it's not relocatable, substitute the target schema name for
1423 * occurrences of @extschema@.
1424 *
1425 * For a relocatable extension, we needn't do this. There cannot be
1426 * any need for @extschema@, else it wouldn't be relocatable.
1427 */
1428 if (!control->relocatable)
1429 {
1430 Datum old = t_sql;
1432
1435 t_sql,
1436 CStringGetTextDatum("@extschema@"),
1439 ereport(ERROR,
1441 errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1442 control->name, quoting_relevant_chars)));
1443 }
1444
1445 /*
1446 * Likewise, substitute required extensions' schema names for
1447 * occurrences of @extschema:extension_name@.
1448 */
1449 Assert(list_length(control->requires) == list_length(requiredSchemas));
1450 forboth(lc, control->requires, lc2, requiredSchemas)
1451 {
1452 Datum old = t_sql;
1453 char *reqextname = (char *) lfirst(lc);
1457 char *repltoken;
1458
1459 repltoken = psprintf("@extschema:%s@", reqextname);
1462 t_sql,
1466 ereport(ERROR,
1468 errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1470 }
1471
1472 /*
1473 * If module_pathname was set in the control file, substitute its
1474 * value for occurrences of MODULE_PATHNAME.
1475 */
1476 if (control->module_pathname)
1477 {
1480 t_sql,
1481 CStringGetTextDatum("MODULE_PATHNAME"),
1483 }
1484
1485 /* And now back to C string */
1487
1489 }
1490 PG_FINALLY();
1491 {
1492 creating_extension = false;
1494 }
1495 PG_END_TRY();
1496
1497 /*
1498 * Restore the GUC variables we set above.
1499 */
1500 AtEOXact_GUC(true, save_nestlevel);
1501
1502 /*
1503 * Restore authentication state if needed.
1504 */
1506 SetUserIdAndSecContext(save_userid, save_sec_context);
1507}
int errhint(const char *fmt,...)
Definition elog.c:1331
#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:1095
Oid CurrentExtensionObject
Definition extension.c:80
static char * read_extension_script_file(const ExtensionControlFile *control, const char *filename)
Definition extension.c:920
static bool extension_is_trusted(ExtensionControlFile *control)
Definition extension.c:1223
Datum DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4)
Definition fmgr.c:860
Datum DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition fmgr.c:835
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:3256
int NewGUCNestLevel(void)
Definition guc.c:2110
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2137
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:3216
@ 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:538
int client_min_messages
Definition guc_tables.c:549
int log_min_messages[]
Definition guc_tables.c:662
#define SECURITY_LOCAL_USERID_CHANGE
Definition miscadmin.h:318
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:612
BackendType MyBackendType
Definition miscinit.c:64
char * GetUserNameFromId(Oid roleid, bool noerr)
Definition miscinit.c:988
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:619
#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)
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
bool superuser(void)
Definition superuser.c:47
Datum replace_text(PG_FUNCTION_ARGS)
Definition varlena.c:3123

References appendStringInfo(), appendStringInfoString(), Assert, AtEOXact_GUC(), check_function_bodies, client_min_messages, creating_extension, CStringGetTextDatum, CurrentExtensionObject, DatumGetTextPP, DEBUG1, DirectFunctionCall3Coll(), DirectFunctionCall4Coll(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, execute_sql_string(), extension_is_trusted(), fb(), 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, MyBackendType, 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 1095 of file extension.c.

1096{
1097 script_error_callback_arg callback_arg;
1101 ListCell *lc1;
1102
1103 /*
1104 * Setup error traceback support for ereport().
1105 */
1106 callback_arg.sql = sql;
1107 callback_arg.filename = filename;
1108 callback_arg.stmt_location = -1;
1109 callback_arg.stmt_len = -1;
1110
1112 scripterrcontext.arg = &callback_arg;
1115
1116 /*
1117 * Parse the SQL string into a list of raw parse trees.
1118 */
1120
1121 /* All output from SELECTs goes to the bit bucket */
1123
1124 /*
1125 * Do parse analysis, rule rewrite, planning, and execution for each raw
1126 * parsetree. We must fully execute each query before beginning parse
1127 * analysis on the next one, since there may be interdependencies.
1128 */
1129 foreach(lc1, raw_parsetree_list)
1130 {
1131 RawStmt *parsetree = lfirst_node(RawStmt, lc1);
1133 oldcontext;
1134 List *stmt_list;
1135 ListCell *lc2;
1136
1137 /* Report location of this query for error context callback */
1138 callback_arg.stmt_location = parsetree->stmt_location;
1139 callback_arg.stmt_len = parsetree->stmt_len;
1140
1141 /*
1142 * We do the work for each parsetree in a short-lived context, to
1143 * limit the memory used when there are many commands in the string.
1144 */
1147 "execute_sql_string per-statement context",
1150
1151 /* Be sure parser can see any DDL done so far */
1153
1154 stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
1155 sql,
1156 NULL,
1157 0,
1158 NULL);
1159 stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
1160
1161 foreach(lc2, stmt_list)
1162 {
1164
1166
1168
1169 if (stmt->utilityStmt == NULL)
1170 {
1172
1174 sql,
1176 dest, NULL, NULL, 0);
1177
1178 ExecutorStart(qdesc, 0);
1182
1184 }
1185 else
1186 {
1187 if (IsA(stmt->utilityStmt, TransactionStmt))
1188 ereport(ERROR,
1190 errmsg("transaction control statements are not allowed within an extension script")));
1191
1193 sql,
1194 false,
1196 NULL,
1197 NULL,
1198 dest,
1199 NULL);
1200 }
1201
1203 }
1204
1205 /* Clean up per-parsetree context. */
1206 MemoryContextSwitchTo(oldcontext);
1208 }
1209
1211
1212 /* Be sure to advance the command counter after the last script command */
1214}
DestReceiver * CreateDestReceiver(CommandDest dest)
Definition dest.c:113
@ DestNone
Definition dest.h:87
ErrorContextCallback * error_context_stack
Definition elog.c:95
void ExecutorEnd(QueryDesc *queryDesc)
Definition execMain.c:466
void ExecutorFinish(QueryDesc *queryDesc)
Definition execMain.c:406
void ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition execMain.c:122
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Definition execMain.c:297
static void script_error_callback(void *arg)
Definition extension.c:953
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define IsA(nodeptr, _type_)
Definition nodes.h:164
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
#define CURSOR_OPT_PARALLEL_OK
#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:974
List * pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char *query_string, const Oid *paramTypes, int numParams, QueryEnvironment *queryEnv)
Definition postgres.c:669
void FreeQueryDesc(QueryDesc *qdesc)
Definition pquery.c:106
QueryDesc * CreateQueryDesc(PlannedStmt *plannedstmt, const char *sourceText, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, ParamListInfo params, QueryEnvironment *queryEnv, int instrument_options)
Definition pquery.c:68
@ ForwardScanDirection
Definition sdir.h:28
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition snapmgr.c:682
void PopActiveSnapshot(void)
Definition snapmgr.c:775
Snapshot GetActiveSnapshot(void)
Definition snapmgr.c:800
struct ErrorContextCallback * previous
Definition elog.h:297
ParseLoc stmt_location
ParseLoc stmt_len
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition utility.c:501
@ PROCESS_UTILITY_QUERY
Definition utility.h:23
void CommandCounterIncrement(void)
Definition xact.c:1101

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, CommandCounterIncrement(), CreateDestReceiver(), CreateQueryDesc(), CurrentMemoryContext, CURSOR_OPT_PARALLEL_OK, DestNone, ereport, errcode(), errmsg(), ERROR, error_context_stack, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), fb(), 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().

◆ ext_sibling_callback()

static void ext_sibling_callback ( Datum  arg,
SysCacheIdentifier  cacheid,
uint32  hashvalue 
)
static

Definition at line 383 of file extension.c.

384{
386
389 {
390 if (hashvalue == 0 ||
391 cache_entry->exthash == hashvalue)
392 cache_entry->valid = false;
393 }
394}
static ExtensionSiblingCache * ext_sibling_list
Definition extension.c:162
struct ExtensionSiblingCache * next
Definition extension.c:152

References ext_sibling_list, fb(), ExtensionSiblingCache::next, and ExtensionSiblingCache::valid.

Referenced by get_function_sibling_type().

◆ extension_config_remove()

static void extension_config_remove ( Oid  extensionoid,
Oid  tableoid 
)
static

Definition at line 3087 of file extension.c.

3088{
3090 ScanKeyData key[1];
3094 int arrayLength;
3095 int arrayIndex;
3096 bool isnull;
3100 ArrayType *a;
3101
3102 /* Find the pg_extension tuple */
3104
3105 ScanKeyInit(&key[0],
3109
3111 NULL, 1, key);
3112
3114
3115 if (!HeapTupleIsValid(extTup)) /* should not happen */
3116 elog(ERROR, "could not find tuple for extension %u",
3117 extensionoid);
3118
3119 /* Search extconfig for the tableoid */
3121 RelationGetDescr(extRel), &isnull);
3122 if (isnull)
3123 {
3124 /* nothing to do */
3125 a = NULL;
3126 arrayLength = 0;
3127 arrayIndex = -1;
3128 }
3129 else
3130 {
3131 Oid *arrayData;
3132 int i;
3133
3135
3136 arrayLength = ARR_DIMS(a)[0];
3137 if (ARR_NDIM(a) != 1 ||
3138 ARR_LBOUND(a)[0] != 1 ||
3139 arrayLength < 0 ||
3140 ARR_HASNULL(a) ||
3141 ARR_ELEMTYPE(a) != OIDOID)
3142 elog(ERROR, "extconfig is not a 1-D Oid array");
3143 arrayData = (Oid *) ARR_DATA_PTR(a);
3144
3145 arrayIndex = -1; /* flag for no deletion needed */
3146
3147 for (i = 0; i < arrayLength; i++)
3148 {
3149 if (arrayData[i] == tableoid)
3150 {
3151 arrayIndex = i; /* index to remove */
3152 break;
3153 }
3154 }
3155 }
3156
3157 /* If tableoid is not in extconfig, nothing to do */
3158 if (arrayIndex < 0)
3159 {
3162 return;
3163 }
3164
3165 /* Modify or delete the extconfig value */
3166 memset(repl_val, 0, sizeof(repl_val));
3167 memset(repl_null, false, sizeof(repl_null));
3168 memset(repl_repl, false, sizeof(repl_repl));
3169
3170 if (arrayLength <= 1)
3171 {
3172 /* removing only element, just set array to null */
3174 }
3175 else
3176 {
3177 /* squeeze out the target element */
3178 Datum *dvalues;
3179 int nelems;
3180 int i;
3181
3182 /* We already checked there are no nulls */
3183 deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
3184
3185 for (i = arrayIndex; i < arrayLength - 1; i++)
3186 dvalues[i] = dvalues[i + 1];
3187
3189
3191 }
3193
3194 /* Modify or delete the extcondition value */
3196 RelationGetDescr(extRel), &isnull);
3197 if (isnull)
3198 {
3199 elog(ERROR, "extconfig and extcondition arrays do not match");
3200 }
3201 else
3202 {
3204
3205 if (ARR_NDIM(a) != 1 ||
3206 ARR_LBOUND(a)[0] != 1 ||
3207 ARR_HASNULL(a) ||
3209 elog(ERROR, "extcondition is not a 1-D text array");
3210 if (ARR_DIMS(a)[0] != arrayLength)
3211 elog(ERROR, "extconfig and extcondition arrays do not match");
3212 }
3213
3214 if (arrayLength <= 1)
3215 {
3216 /* removing only element, just set array to null */
3218 }
3219 else
3220 {
3221 /* squeeze out the target element */
3222 Datum *dvalues;
3223 int nelems;
3224 int i;
3225
3226 /* We already checked there are no nulls */
3227 deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
3228
3229 for (i = arrayIndex; i < arrayLength - 1; i++)
3230 dvalues[i] = dvalues[i + 1];
3231
3233
3235 }
3237
3240
3242
3244
3246}
#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(const ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
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, fb(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, i, ObjectIdGetDatum(), PointerGetDatum(), RelationGetDescr, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ExecAlterExtensionContentsRecurse().

◆ extension_file_exists()

bool extension_file_exists ( const char extensionName)

Definition at line 2681 of file extension.c.

2682{
2683 bool result = false;
2684 List *locations;
2685 DIR *dir;
2686 struct dirent *de;
2687
2689
2691 {
2692 dir = AllocateDir(location->loc);
2693
2694 /*
2695 * If the control directory doesn't exist, we want to silently return
2696 * false. Any other error will be reported by ReadDir.
2697 */
2698 if (dir == NULL && errno == ENOENT)
2699 {
2700 /* do nothing */
2701 }
2702 else
2703 {
2704 while ((de = ReadDir(dir, location->loc)) != NULL)
2705 {
2706 char *extname;
2707
2708 if (!is_extension_control_filename(de->d_name))
2709 continue;
2710
2711 /* extract extension name from 'name.control' filename */
2712 extname = pstrdup(de->d_name);
2713 *strrchr(extname, '.') = '\0';
2714
2715 /* ignore it if it's an auxiliary control file */
2716 if (strstr(extname, "--"))
2717 continue;
2718
2719 /* done if it matches request */
2720 if (strcmp(extname, extensionName) == 0)
2721 {
2722 result = true;
2723 break;
2724 }
2725 }
2726
2727 FreeDir(dir);
2728 }
2729 if (result)
2730 break;
2731 }
2732
2733 return result;
2734}
static bool is_extension_control_filename(const char *filename)
Definition extension.c:494
static List * get_extension_control_directories(void)
Definition extension.c:513
int FreeDir(DIR *dir)
Definition fd.c:3008
DIR * AllocateDir(const char *dirname)
Definition fd.c:2890
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition fd.c:2956
char * pstrdup(const char *in)
Definition mcxt.c:1781
#define foreach_ptr(type, var, lst)
Definition pg_list.h:469
Definition dirent.c:26

References AllocateDir(), fb(), 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 1223 of file extension.c.

1224{
1226
1227 /* Never trust unless extension's control file says it's okay */
1228 if (!control->trusted)
1229 return false;
1230 /* Allow if user has CREATE privilege on current database */
1232 if (aclresult == ACLCHECK_OK)
1233 return true;
1234 return false;
1235}
Oid MyDatabaseId
Definition globals.c:94

References ACL_CREATE, ACLCHECK_OK, fb(), 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 592 of file extension.c.

593{
594 char *basename;
595 char *result;
596 List *paths;
597
598 Assert(control->name);
599
600 basename = psprintf("%s.control", control->name);
601
603 result = find_in_paths(basename, paths);
604
605 if (result)
606 {
607 const char *p;
608
609 p = strrchr(result, '/');
610 Assert(p);
611 control->control_dir = pnstrdup(result, p - result);
612 }
613
614 return result;
615}
char * find_in_paths(const char *basename, List *paths)
Definition extension.c:4086
char * pnstrdup(const char *in, Size len)
Definition mcxt.c:1792

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

Referenced by parse_extension_control_file().

◆ find_in_paths()

char * find_in_paths ( const char basename,
List paths 
)

Definition at line 4086 of file extension.c.

4087{
4088 ListCell *cell;
4089
4090 foreach(cell, paths)
4091 {
4092 ExtensionLocation *location = lfirst(cell);
4093 char *path = location->loc;
4094 char *full;
4095
4096 Assert(path != NULL);
4097
4098 path = pstrdup(path);
4099 canonicalize_path(path);
4100
4101 /* only absolute paths */
4102 if (!is_absolute_path(path))
4103 ereport(ERROR,
4105 errmsg("component in parameter \"%s\" is not an absolute path", "extension_control_path"));
4106
4107 full = psprintf("%s/%s", path, basename);
4108
4109 if (pg_file_exists(full))
4110 return full;
4111
4112 pfree(path);
4113 pfree(full);
4114 }
4115
4116 return NULL;
4117}
bool pg_file_exists(const char *name)
Definition fd.c:503
void pfree(void *pointer)
Definition mcxt.c:1616
#define is_absolute_path(filename)
Definition port.h:104
void canonicalize_path(char *path)
Definition path.c:337

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

Referenced by find_extension_control_filename().

◆ find_install_path()

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

Definition at line 1778 of file extension.c.

1780{
1782 ListCell *lc;
1783
1784 *best_path = NIL;
1785
1786 /*
1787 * We don't expect to be called for an installable target, but if we are,
1788 * the answer is easy: just start from there, with an empty update path.
1789 */
1790 if (evi_target->installable)
1791 return evi_target;
1792
1793 /* Consider all installable versions as start points */
1794 foreach(lc, evi_list)
1795 {
1797 List *path;
1798
1799 if (!evi1->installable)
1800 continue;
1801
1802 /*
1803 * Find shortest path from evi1 to evi_target; but no need to consider
1804 * paths going through other installable versions.
1805 */
1806 path = find_update_path(evi_list, evi1, evi_target, true, true);
1807 if (path == NIL)
1808 continue;
1809
1810 /* Remember best path */
1811 if (evi_start == NULL ||
1812 list_length(path) < list_length(*best_path) ||
1813 (list_length(path) == list_length(*best_path) &&
1814 strcmp(evi_start->name, evi1->name) < 0))
1815 {
1816 evi_start = evi1;
1817 *best_path = path;
1818 }
1819 }
1820
1821 return evi_start;
1822}
static List * find_update_path(List *evi_list, ExtensionVersionInfo *evi_start, ExtensionVersionInfo *evi_target, bool reject_indirect, bool reinitialize)
Definition extension.c:1685

References fb(), find_update_path(), lfirst, list_length(), 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 1685 of file extension.c.

1690{
1691 List *result;
1693 ListCell *lc;
1694
1695 /* Caller error if start == target */
1697 /* Caller error if reject_indirect and target is installable */
1698 Assert(!(reject_indirect && evi_target->installable));
1699
1700 if (reinitialize)
1701 {
1702 foreach(lc, evi_list)
1703 {
1705 evi->distance_known = false;
1706 evi->distance = INT_MAX;
1707 evi->previous = NULL;
1708 }
1709 }
1710
1711 evi_start->distance = 0;
1712
1714 {
1715 if (evi->distance == INT_MAX)
1716 break; /* all remaining vertices are unreachable */
1717 evi->distance_known = true;
1718 if (evi == evi_target)
1719 break; /* found shortest path to target */
1720 foreach(lc, evi->reachable)
1721 {
1723 int newdist;
1724
1725 /* if reject_indirect, treat installable versions as unreachable */
1726 if (reject_indirect && evi2->installable)
1727 continue;
1728 newdist = evi->distance + 1;
1729 if (newdist < evi2->distance)
1730 {
1731 evi2->distance = newdist;
1732 evi2->previous = evi;
1733 }
1734 else if (newdist == evi2->distance &&
1735 evi2->previous != NULL &&
1736 strcmp(evi->name, evi2->previous->name) < 0)
1737 {
1738 /*
1739 * Break ties in favor of the version name that comes first
1740 * according to strcmp(). This behavior is undocumented and
1741 * users shouldn't rely on it. We do it just to ensure that
1742 * if there is a tie, the update path that is chosen does not
1743 * depend on random factors like the order in which directory
1744 * entries get visited.
1745 */
1746 evi2->previous = evi;
1747 }
1748 }
1749 }
1750
1751 /* Return NIL if target is not reachable from start */
1752 if (!evi_target->distance_known)
1753 return NIL;
1754
1755 /* Build and return list of version names representing the update path */
1756 result = NIL;
1757 for (evi = evi_target; evi != evi_start; evi = evi->previous)
1758 result = lcons(evi->name, result);
1759
1760 return result;
1761}
static ExtensionVersionInfo * get_nearest_unprocessed_vertex(List *evi_list)
Definition extension.c:1551
List * lcons(void *datum, List *list)
Definition list.c:495

References Assert, ExtensionVersionInfo::distance, fb(), get_nearest_unprocessed_vertex(), lcons(), lfirst, and NIL.

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,
ExtensionLocation location 
)
static

Definition at line 2562 of file extension.c.

2566{
2567 List *evi_list;
2568 ListCell *lc;
2569
2570 /* Extract the version update graph from the script directory */
2572
2573 /* For each installable version ... */
2574 foreach(lc, evi_list)
2575 {
2577 ExtensionControlFile *control;
2578 Datum values[9];
2579 bool nulls[9];
2580 ListCell *lc2;
2581
2582 if (!evi->installable)
2583 continue;
2584
2585 /*
2586 * Fetch parameters for specific version (pcontrol is not changed)
2587 */
2589
2590 memset(values, 0, sizeof(values));
2591 memset(nulls, 0, sizeof(nulls));
2592
2593 /* name */
2595 CStringGetDatum(control->name));
2596 /* version */
2597 values[1] = CStringGetTextDatum(evi->name);
2598 /* superuser */
2599 values[2] = BoolGetDatum(control->superuser);
2600 /* trusted */
2601 values[3] = BoolGetDatum(control->trusted);
2602 /* relocatable */
2603 values[4] = BoolGetDatum(control->relocatable);
2604 /* schema */
2605 if (control->schema == NULL)
2606 nulls[5] = true;
2607 else
2609 CStringGetDatum(control->schema));
2610 /* requires */
2611 if (control->requires == NIL)
2612 nulls[6] = true;
2613 else
2614 values[6] = convert_requires_to_datum(control->requires);
2615
2616 /* location */
2618
2619 /* comment */
2620 if (control->comment == NULL)
2621 nulls[8] = true;
2622 else
2623 values[8] = CStringGetTextDatum(control->comment);
2624
2625 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2626
2627 /*
2628 * Find all non-directly-installable versions that would be installed
2629 * starting from this version, and report them, inheriting the
2630 * parameters that aren't changed in updates from this version.
2631 */
2632 foreach(lc2, evi_list)
2633 {
2635 List *best_path;
2636
2637 if (evi2->installable)
2638 continue;
2640 {
2641 /*
2642 * Fetch parameters for this version (pcontrol is not changed)
2643 */
2645
2646 /* name stays the same */
2647 /* version */
2648 values[1] = CStringGetTextDatum(evi2->name);
2649 /* superuser */
2650 values[2] = BoolGetDatum(control->superuser);
2651 /* trusted */
2652 values[3] = BoolGetDatum(control->trusted);
2653 /* relocatable */
2654 values[4] = BoolGetDatum(control->relocatable);
2655 /* schema stays the same */
2656 /* requires */
2657 if (control->requires == NIL)
2658 nulls[6] = true;
2659 else
2660 {
2661 values[6] = convert_requires_to_datum(control->requires);
2662 nulls[6] = false;
2663 }
2664 /* comment and location stay the same */
2665
2666 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2667 }
2668 }
2669 }
2670}
static char * get_extension_location(ExtensionLocation *loc)
Definition extension.c:203
static Datum convert_requires_to_datum(List *requires)
Definition extension.c:2740
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, fb(), find_install_path(), get_ext_ver_list(), get_extension_location(), lfirst, ExtensionControlFile::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 1518 of file extension.c.

1519{
1521 ListCell *lc;
1522
1523 foreach(lc, *evi_list)
1524 {
1526 if (strcmp(evi->name, versionname) == 0)
1527 return evi;
1528 }
1529
1531 evi->name = pstrdup(versionname);
1532 evi->reachable = NIL;
1533 evi->installable = false;
1534 /* initialize for later application of Dijkstra's algorithm */
1535 evi->distance_known = false;
1536 evi->distance = INT_MAX;
1537 evi->previous = NULL;
1538
1540
1541 return evi;
1542}
#define palloc_object(type)
Definition fe_memutils.h:74
List * lappend(List *list, void *datum)
Definition list.c:339

References fb(), lappend(), lfirst, NIL, palloc_object, and pstrdup().

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

1580{
1581 List *evi_list = NIL;
1582 int extnamelen = strlen(control->name);
1583 char *location;
1584 DIR *dir;
1585 struct dirent *de;
1586
1587 location = get_extension_script_directory(control);
1588 dir = AllocateDir(location);
1589 while ((de = ReadDir(dir, location)) != NULL)
1590 {
1591 char *vername;
1592 char *vername2;
1595
1596 /* must be a .sql file ... */
1597 if (!is_extension_script_filename(de->d_name))
1598 continue;
1599
1600 /* ... matching extension name followed by separator */
1601 if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
1602 de->d_name[extnamelen] != '-' ||
1603 de->d_name[extnamelen + 1] != '-')
1604 continue;
1605
1606 /* extract version name(s) from 'extname--something.sql' filename */
1607 vername = pstrdup(de->d_name + extnamelen + 2);
1608 *strrchr(vername, '.') = '\0';
1609 vername2 = strstr(vername, "--");
1610 if (!vername2)
1611 {
1612 /* It's an install, not update, script; record its version name */
1614 evi->installable = true;
1615 continue;
1616 }
1617 *vername2 = '\0'; /* terminate first version */
1618 vername2 += 2; /* and point to second */
1619
1620 /* if there's a third --, it's bogus, ignore it */
1621 if (strstr(vername2, "--"))
1622 continue;
1623
1624 /* Create ExtensionVersionInfos and link them together */
1627 evi->reachable = lappend(evi->reachable, evi2);
1628 }
1629 FreeDir(dir);
1630
1631 return evi_list;
1632}
static bool is_extension_script_filename(const char *filename)
Definition extension.c:502
static char * get_extension_script_directory(ExtensionControlFile *control)
Definition extension.c:618

References AllocateDir(), fb(), FreeDir(), get_ext_ver_info(), get_extension_script_directory(), is_extension_script_filename(), lappend(), ExtensionControlFile::name, NIL, pstrdup(), 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 637 of file extension.c.

639{
640 char *result;
641 char *scriptdir;
642
644
645 result = (char *) palloc(MAXPGPATH);
646 snprintf(result, MAXPGPATH, "%s/%s--%s.control",
647 scriptdir, control->name, version);
648
650
651 return result;
652}
#define MAXPGPATH
#define snprintf
Definition port.h:260

References fb(), 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 513 of file extension.c.

514{
515 char sharepath[MAXPGPATH];
516 char *system_dir;
517 char *ecp;
518 List *paths = NIL;
519
521
522 system_dir = psprintf("%s/extension", sharepath);
523
525 {
527
528 location->macro = NULL;
529 location->loc = system_dir;
530 paths = lappend(paths, location);
531 }
532 else
533 {
534 /* Duplicate the string so we can modify it */
536
537 for (;;)
538 {
539 int len;
540 char *mangled;
543
544 /* Get the length of the next path on ecp */
545 if (piece == NULL)
546 len = strlen(ecp);
547 else
548 len = piece - ecp;
549
550 /* Copy the next path found on ecp */
551 piece = palloc(len + 1);
552 strlcpy(piece, ecp, len + 1);
553
554 /*
555 * Substitute the path macro if needed or append "extension"
556 * suffix if it is a custom extension control path.
557 */
558 if (strcmp(piece, "$system") == 0)
559 {
560 location->macro = pstrdup(piece);
562 }
563 else
564 {
565 location->macro = NULL;
566 mangled = psprintf("%s/extension", piece);
567 }
568 pfree(piece);
569
570 /* Canonicalize the path based on the OS and add to the list */
572 location->loc = mangled;
573 paths = lappend(paths, location);
574
575 /* Break if ecp is empty or move to the next path on ecp */
576 if (ecp[len] == '\0')
577 break;
578 else
579 ecp += len + 1;
580 }
581 }
582
583 return paths;
584}
char * substitute_path_macro(const char *str, const char *macro, const char *value)
Definition dfmgr.c:536
char * Extension_control_path
Definition extension.c:76
char my_exec_path[MAXPGPATH]
Definition globals.c:81
const void size_t len
void get_share_path(const char *my_exec_path, char *ret_path)
Definition path.c:902
char * first_path_var_separator(const char *pathlist)
Definition path.c:127
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition strlcpy.c:45

References canonicalize_path(), Extension_control_path, fb(), first_path_var_separator(), get_share_path(), lappend(), len, ExtensionLocation::loc, ExtensionLocation::macro, MAXPGPATH, my_exec_path, NIL, palloc(), palloc_object, pfree(), psprintf(), pstrdup(), strlcpy(), and substitute_path_macro().

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

◆ get_extension_location()

static char * get_extension_location ( ExtensionLocation loc)
static

Definition at line 203 of file extension.c.

204{
205 /* We only want to show extension paths for superusers. */
206 if (superuser())
207 {
208 /* Return the macro value if present to avoid showing system paths. */
209 if (loc->macro != NULL)
210 return loc->macro;
211 else
212 return loc->loc;
213 }
214 else
215 {
216 /* Similar to pg_stat_activity for unprivileged users */
217 return "<insufficient privilege>";
218 }
219}

References fb(), ExtensionLocation::loc, ExtensionLocation::macro, and superuser().

Referenced by get_available_versions_for_extension(), and pg_available_extensions().

◆ get_extension_name()

char * get_extension_name ( Oid  ext_oid)

Definition at line 250 of file extension.c.

251{
252 char *result;
253 HeapTuple tuple;
254
256
257 if (!HeapTupleIsValid(tuple))
258 return NULL;
259
260 result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
261 ReleaseSysCache(tuple);
262
263 return result;
264}
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220

References fb(), 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 228 of file extension.c.

229{
230 Oid result;
231
233 CStringGetDatum(extname));
234
235 if (!OidIsValid(result) && !missing_ok)
238 errmsg("extension \"%s\" does not exist",
239 extname)));
240
241 return result;
242}
#define GetSysCacheOid1(cacheId, oidcol, key1)
Definition syscache.h:109

References CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, fb(), 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 272 of file extension.c.

273{
274 Oid result;
275 HeapTuple tuple;
276
278
279 if (!HeapTupleIsValid(tuple))
280 return InvalidOid;
281
282 result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
283 ReleaseSysCache(tuple);
284
285 return result;
286}

References fb(), 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 618 of file extension.c.

619{
620 /*
621 * The directory parameter can be omitted, absolute, or relative to the
622 * installation's base directory, which can be the sharedir or a custom
623 * path that was set via extension_control_path. It depends on where the
624 * .control file was found.
625 */
626 if (!control->directory)
627 return pstrdup(control->control_dir);
628
629 if (is_absolute_path(control->directory))
630 return pstrdup(control->directory);
631
632 Assert(control->basedir != NULL);
633 return psprintf("%s/%s", control->basedir, control->directory);
634}

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

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

◆ get_extension_script_filename()

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

Definition at line 655 of file extension.c.

657{
658 char *result;
659 char *scriptdir;
660
662
663 result = (char *) palloc(MAXPGPATH);
664 if (from_version)
665 snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
666 scriptdir, control->name, from_version, version);
667 else
668 snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
669 scriptdir, control->name, version);
670
672
673 return result;
674}

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

Referenced by CreateExtensionInternal(), and execute_extension_script().

◆ get_function_sibling_type()

Oid get_function_sibling_type ( Oid  funcoid,
const char typname 
)

Definition at line 312 of file extension.c.

313{
315 Oid extoid;
316 Oid typeoid;
317
318 /*
319 * See if we have the answer cached. Someday there may be enough callers
320 * to justify a hash table, but for now, a simple linked list is fine.
321 */
324 {
325 if (funcoid == cache_entry->reqfuncoid &&
326 strcmp(typname, cache_entry->typname) == 0)
327 break;
328 }
329 if (cache_entry && cache_entry->valid)
330 return cache_entry->typeoid;
331
332 /*
333 * Nope, so do the expensive lookups. We do not expect failures, so we do
334 * not cache negative results.
335 */
337 if (!OidIsValid(extoid))
338 return InvalidOid;
339 typeoid = getExtensionType(extoid, typname);
340 if (!OidIsValid(typeoid))
341 return InvalidOid;
342
343 /*
344 * Build, or revalidate, cache entry.
345 */
346 if (cache_entry == NULL)
347 {
348 /* Register invalidation hook if this is first entry */
349 if (ext_sibling_list == NULL)
352 (Datum) 0);
353
354 /* Momentarily zero the space to ensure valid flag is false */
357 sizeof(ExtensionSiblingCache));
360 }
361
363 cache_entry->typname = typname;
366 cache_entry->typeoid = typeoid;
367 /* Mark it valid only once it's fully populated */
368 cache_entry->valid = true;
369
370 return typeoid;
371}
static void ext_sibling_callback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition extension.c:383
void CacheRegisterSyscacheCallback(SysCacheIdentifier cacheid, SyscacheCallbackFunction func, Datum arg)
Definition inval.c:1816
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1266
MemoryContext CacheMemoryContext
Definition mcxt.c:169
Oid getExtensionType(Oid extensionOid, const char *typname)
Definition pg_depend.c:832
NameData typname
Definition pg_type.h:41
#define GetSysCacheHashValue1(cacheId, key1)
Definition syscache.h:118

References CacheMemoryContext, CacheRegisterSyscacheCallback(), ext_sibling_callback(), ext_sibling_list, fb(), getExtensionOfObject(), getExtensionType(), GetSysCacheHashValue1, InvalidOid, MemoryContextAllocZero(), ExtensionSiblingCache::next, ObjectIdGetDatum(), OidIsValid, ExtensionSiblingCache::reqfuncoid, ExtensionSiblingCache::typeoid, and typname.

Referenced by _int_matchsel().

◆ get_nearest_unprocessed_vertex()

static ExtensionVersionInfo * get_nearest_unprocessed_vertex ( List evi_list)
static

Definition at line 1551 of file extension.c.

1552{
1554 ListCell *lc;
1555
1556 foreach(lc, evi_list)
1557 {
1559
1560 /* only vertices whose distance is still uncertain are candidates */
1561 if (evi2->distance_known)
1562 continue;
1563 /* remember the closest such vertex */
1564 if (evi == NULL ||
1565 evi->distance > evi2->distance)
1566 evi = evi2;
1567 }
1568
1569 return evi;
1570}

References fb(), 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 2072 of file extension.c.

2078{
2080
2083 {
2084 if (cascade)
2085 {
2086 /* Must install it. */
2087 ObjectAddress addr;
2089 ListCell *lc;
2090
2091 /* Check extension name validity before trying to cascade. */
2093
2094 /* Check for cyclic dependency between extensions. */
2095 foreach(lc, parents)
2096 {
2097 char *pname = (char *) lfirst(lc);
2098
2099 if (strcmp(pname, reqExtensionName) == 0)
2100 ereport(ERROR,
2102 errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
2104 }
2105
2107 (errmsg("installing required extension \"%s\"",
2109
2110 /* Add current extension to list of parents to pass down. */
2112
2113 /*
2114 * Create the required extension. We propagate the SCHEMA option
2115 * if any, and CASCADE, but no other options.
2116 */
2119 NULL,
2120 cascade,
2122 is_create);
2123
2124 /* Get its newly-assigned OID. */
2126 }
2127 else
2128 ereport(ERROR,
2130 errmsg("required extension \"%s\" is not installed",
2132 is_create ?
2133 errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
2134 }
2135
2136 return reqExtensionOid;
2137}
List * list_copy(const List *oldlist)
Definition list.c:1573

References check_valid_extension_name(), CreateExtensionInternal(), ereport, errcode(), errhint(), errmsg(), ERROR, fb(), 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 1642 of file extension.c.

1644{
1645 List *result;
1646 List *evi_list;
1649
1650 /* Extract the version update graph from the script directory */
1651 evi_list = get_ext_ver_list(control);
1652
1653 /* Initialize start and end vertices */
1656
1657 /* Find shortest path */
1658 result = find_update_path(evi_list, evi_start, evi_target, false, false);
1659
1660 if (result == NIL)
1661 ereport(ERROR,
1663 errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
1664 control->name, oldVersion, newVersion)));
1665
1666 return result;
1667}

References ereport, errcode(), errmsg(), ERROR, fb(), 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 2241 of file extension.c.

2245{
2247 Relation rel;
2249 bool nulls[Natts_pg_extension];
2250 HeapTuple tuple;
2254 ListCell *lc;
2255
2256 /*
2257 * Build and insert the pg_extension tuple
2258 */
2260
2261 memset(values, 0, sizeof(values));
2262 memset(nulls, 0, sizeof(nulls));
2263
2273
2275 nulls[Anum_pg_extension_extconfig - 1] = true;
2276 else
2278
2280 nulls[Anum_pg_extension_extcondition - 1] = true;
2281 else
2283
2284 tuple = heap_form_tuple(rel->rd_att, values, nulls);
2285
2286 CatalogTupleInsert(rel, tuple);
2287
2288 heap_freetuple(tuple);
2290
2291 /*
2292 * Record dependencies on owner, schema, and prerequisite extensions
2293 */
2295
2297
2299
2302
2303 foreach(lc, requiredExtensions)
2304 {
2307
2310 }
2311
2312 /* Record all of them (this includes duplicate elimination) */
2315
2316 /* Post creation hook for new extension */
2318
2319 return myself;
2320}
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:448
void record_object_address_dependencies(const ObjectAddress *depender, ObjectAddresses *referenced, DependencyType behavior)
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
void free_object_addresses(ObjectAddresses *addrs)
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)
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
TupleDesc rd_att
Definition rel.h:112

References add_exact_object_address(), BoolGetDatum(), CatalogTupleInsert(), CStringGetDatum(), CStringGetTextDatum, DEPENDENCY_NORMAL, DirectFunctionCall1, fb(), 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 494 of file extension.c.

495{
496 const char *extension = strrchr(filename, '.');
497
498 return (extension != NULL) && (strcmp(extension, ".control") == 0);
499}

References fb(), and 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 502 of file extension.c.

503{
504 const char *extension = strrchr(filename, '.');
505
506 return (extension != NULL) && (strcmp(extension, ".sql") == 0);
507}

References fb(), and filename.

Referenced by get_ext_ver_list().

◆ new_ExtensionControlFile()

static ExtensionControlFile * new_ExtensionControlFile ( const char extname)
static

Definition at line 4063 of file extension.c.

4064{
4065 /*
4066 * Set up default values. Pointer fields are initially null.
4067 */
4069
4070 control->name = pstrdup(extname);
4071 control->relocatable = false;
4072 control->superuser = true;
4073 control->trusted = false;
4074 control->encoding = -1;
4075
4076 return control;
4077}
#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 690 of file extension.c.

692{
693 char *filename;
694 FILE *file;
695 ConfigVariable *item,
696 *head = NULL,
697 *tail = NULL;
698
699 /*
700 * Locate the file to read. Auxiliary files are optional.
701 */
702 if (version)
704 else
705 {
706 /*
707 * If control_dir is already set, use it, else do a path search.
708 */
709 if (control->control_dir)
710 {
711 filename = psprintf("%s/%s.control", control->control_dir, control->name);
712 }
713 else
715 }
716
717 if (!filename)
718 {
721 errmsg("extension \"%s\" is not available", control->name),
722 errhint("The extension must first be installed on the system where PostgreSQL is running.")));
723 }
724
725 /* Assert that the control_dir ends with /extension */
726 Assert(control->control_dir != NULL);
727 Assert(strcmp(control->control_dir + strlen(control->control_dir) - strlen("/extension"), "/extension") == 0);
728
729 control->basedir = pnstrdup(
730 control->control_dir,
731 strlen(control->control_dir) - strlen("/extension"));
732
733 if ((file = AllocateFile(filename, "r")) == NULL)
734 {
735 /* no complaint for missing auxiliary file */
736 if (errno == ENOENT && version)
737 {
739 return;
740 }
741
744 errmsg("could not open extension control file \"%s\": %m",
745 filename)));
746 }
747
748 /*
749 * Parse the file content, using GUC's file parsing code. We need not
750 * check the return value since any errors will be thrown at ERROR level.
751 */
753 &head, &tail);
754
755 FreeFile(file);
756
757 /*
758 * Convert the ConfigVariable list into ExtensionControlFile entries.
759 */
760 for (item = head; item != NULL; item = item->next)
761 {
762 if (strcmp(item->name, "directory") == 0)
763 {
764 if (version)
767 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
768 item->name)));
769
770 control->directory = pstrdup(item->value);
771 }
772 else if (strcmp(item->name, "default_version") == 0)
773 {
774 if (version)
777 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
778 item->name)));
779
780 control->default_version = pstrdup(item->value);
781 }
782 else if (strcmp(item->name, "module_pathname") == 0)
783 {
784 control->module_pathname = pstrdup(item->value);
785 }
786 else if (strcmp(item->name, "comment") == 0)
787 {
788 control->comment = pstrdup(item->value);
789 }
790 else if (strcmp(item->name, "schema") == 0)
791 {
792 control->schema = pstrdup(item->value);
793 }
794 else if (strcmp(item->name, "relocatable") == 0)
795 {
796 if (!parse_bool(item->value, &control->relocatable))
799 errmsg("parameter \"%s\" requires a Boolean value",
800 item->name)));
801 }
802 else if (strcmp(item->name, "superuser") == 0)
803 {
804 if (!parse_bool(item->value, &control->superuser))
807 errmsg("parameter \"%s\" requires a Boolean value",
808 item->name)));
809 }
810 else if (strcmp(item->name, "trusted") == 0)
811 {
812 if (!parse_bool(item->value, &control->trusted))
815 errmsg("parameter \"%s\" requires a Boolean value",
816 item->name)));
817 }
818 else if (strcmp(item->name, "encoding") == 0)
819 {
820 control->encoding = pg_valid_server_encoding(item->value);
821 if (control->encoding < 0)
824 errmsg("\"%s\" is not a valid encoding name",
825 item->value)));
826 }
827 else if (strcmp(item->name, "requires") == 0)
828 {
829 /* Need a modifiable copy of string */
830 char *rawnames = pstrdup(item->value);
831
832 /* Parse string into list of identifiers */
833 if (!SplitIdentifierString(rawnames, ',', &control->requires))
834 {
835 /* syntax error in name list */
838 errmsg("parameter \"%s\" must be a list of extension names",
839 item->name)));
840 }
841 }
842 else if (strcmp(item->name, "no_relocate") == 0)
843 {
844 /* Need a modifiable copy of string */
845 char *rawnames = pstrdup(item->value);
846
847 /* Parse string into list of identifiers */
848 if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
849 {
850 /* syntax error in name list */
853 errmsg("parameter \"%s\" must be a list of extension names",
854 item->name)));
855 }
856 }
857 else
860 errmsg("unrecognized parameter \"%s\" in file \"%s\"",
861 item->name, filename)));
862 }
863
865
866 if (control->relocatable && control->schema != NULL)
869 errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
870
872}
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:887
static char * get_extension_aux_control_filename(ExtensionControlFile *control, const char *version)
Definition extension.c:637
static char * find_extension_control_filename(ExtensionControlFile *control)
Definition extension.c:592
int FreeFile(FILE *file)
Definition fd.c:2826
FILE * AllocateFile(const char *name, const char *mode)
Definition fd.c:2627
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
List *List * no_relocate
Definition extension.c:102
bool SplitIdentifierString(char *rawstring, char separator, List **namelist)
Definition varlena.c:2775

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

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

◆ pg_available_extension_versions()

Datum pg_available_extension_versions ( PG_FUNCTION_ARGS  )

Definition at line 2485 of file extension.c.

2486{
2487 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2488 List *locations;
2489 DIR *dir;
2490 struct dirent *de;
2491 List *found_ext = NIL;
2492
2493 /* Build tuplestore to hold the result rows */
2494 InitMaterializedSRF(fcinfo, 0);
2495
2497
2499 {
2500 dir = AllocateDir(location->loc);
2501
2502 /*
2503 * If the control directory doesn't exist, we want to silently return
2504 * an empty set. Any other error will be reported by ReadDir.
2505 */
2506 if (dir == NULL && errno == ENOENT)
2507 {
2508 /* do nothing */
2509 }
2510 else
2511 {
2512 while ((de = ReadDir(dir, location->loc)) != NULL)
2513 {
2514 ExtensionControlFile *control;
2515 char *extname;
2517
2518 if (!is_extension_control_filename(de->d_name))
2519 continue;
2520
2521 /* extract extension name from 'name.control' filename */
2522 extname = pstrdup(de->d_name);
2523 *strrchr(extname, '.') = '\0';
2524
2525 /* ignore it if it's an auxiliary control file */
2526 if (strstr(extname, "--"))
2527 continue;
2528
2529 /*
2530 * Ignore already-found names. They are not reachable by the
2531 * path search, so don't shown them.
2532 */
2533 extname_str = makeString(extname);
2535 continue;
2536 else
2538
2539 /* read the control file */
2540 control = new_ExtensionControlFile(extname);
2541 control->control_dir = pstrdup(location->loc);
2543
2544 /* scan extension's script directory for install scripts */
2545 get_available_versions_for_extension(control, rsinfo->setResult,
2546 rsinfo->setDesc,
2547 location);
2548 }
2549
2550 FreeDir(dir);
2551 }
2552 }
2553
2554 return (Datum) 0;
2555}
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol, Tuplestorestate *tupstore, TupleDesc tupdesc, ExtensionLocation *location)
Definition extension.c:2562
static void parse_extension_control_file(ExtensionControlFile *control, const char *version)
Definition extension.c:690
static ExtensionControlFile * new_ExtensionControlFile(const char *extname)
Definition extension.c:4063
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition funcapi.c:76
bool list_member(const List *list, const void *datum)
Definition list.c:661
Definition value.h:64

References AllocateDir(), ExtensionControlFile::control_dir, fb(), foreach_ptr, FreeDir(), get_available_versions_for_extension(), get_extension_control_directories(), InitMaterializedSRF(), is_extension_control_filename(), lappend(), list_member(), makeString(), new_ExtensionControlFile(), NIL, parse_extension_control_file(), pstrdup(), and ReadDir().

◆ pg_available_extensions()

Datum pg_available_extensions ( PG_FUNCTION_ARGS  )

Definition at line 2383 of file extension.c.

2384{
2385 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2386 List *locations;
2387 DIR *dir;
2388 struct dirent *de;
2389 List *found_ext = NIL;
2390
2391 /* Build tuplestore to hold the result rows */
2392 InitMaterializedSRF(fcinfo, 0);
2393
2395
2397 {
2398 dir = AllocateDir(location->loc);
2399
2400 /*
2401 * If the control directory doesn't exist, we want to silently return
2402 * an empty set. Any other error will be reported by ReadDir.
2403 */
2404 if (dir == NULL && errno == ENOENT)
2405 {
2406 /* do nothing */
2407 }
2408 else
2409 {
2410 while ((de = ReadDir(dir, location->loc)) != NULL)
2411 {
2412 ExtensionControlFile *control;
2413 char *extname;
2415 Datum values[4];
2416 bool nulls[4];
2417
2418 if (!is_extension_control_filename(de->d_name))
2419 continue;
2420
2421 /* extract extension name from 'name.control' filename */
2422 extname = pstrdup(de->d_name);
2423 *strrchr(extname, '.') = '\0';
2424
2425 /* ignore it if it's an auxiliary control file */
2426 if (strstr(extname, "--"))
2427 continue;
2428
2429 /*
2430 * Ignore already-found names. They are not reachable by the
2431 * path search, so don't show them.
2432 */
2433 extname_str = makeString(extname);
2435 continue;
2436 else
2438
2439 control = new_ExtensionControlFile(extname);
2440 control->control_dir = pstrdup(location->loc);
2442
2443 memset(values, 0, sizeof(values));
2444 memset(nulls, 0, sizeof(nulls));
2445
2446 /* name */
2448 CStringGetDatum(control->name));
2449 /* default_version */
2450 if (control->default_version == NULL)
2451 nulls[1] = true;
2452 else
2454
2455 /* location */
2457
2458 /* comment */
2459 if (control->comment == NULL)
2460 nulls[3] = true;
2461 else
2462 values[3] = CStringGetTextDatum(control->comment);
2463
2464 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2465 values, nulls);
2466 }
2467
2468 FreeDir(dir);
2469 }
2470 }
2471
2472 return (Datum) 0;
2473}

References AllocateDir(), ExtensionControlFile::comment, ExtensionControlFile::control_dir, CStringGetDatum(), CStringGetTextDatum, ExtensionControlFile::default_version, DirectFunctionCall1, fb(), foreach_ptr, FreeDir(), get_extension_control_directories(), get_extension_location(), InitMaterializedSRF(), is_extension_control_filename(), lappend(), list_member(), makeString(), ExtensionControlFile::name, namein(), new_ExtensionControlFile(), NIL, parse_extension_control_file(), pstrdup(), ReadDir(), tuplestore_putvalues(), and values.

◆ pg_extension_config_dump()

Datum pg_extension_config_dump ( PG_FUNCTION_ARGS  )

Definition at line 2851 of file extension.c.

2852{
2853 Oid tableoid = PG_GETARG_OID(0);
2855 char *tablename;
2857 ScanKeyData key[1];
2862 int arrayLength;
2863 int arrayIndex;
2864 bool isnull;
2868 ArrayType *a;
2869
2870 /*
2871 * We only allow this to be called from an extension's SQL script. We
2872 * shouldn't need any permissions check beyond that.
2873 */
2874 if (!creating_extension)
2875 ereport(ERROR,
2877 errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
2878 "pg_extension_config_dump()")));
2879
2880 /*
2881 * Check that the table exists and is a member of the extension being
2882 * created. This ensures that we don't need to register an additional
2883 * dependency to protect the extconfig entry.
2884 */
2885 tablename = get_rel_name(tableoid);
2886 if (tablename == NULL)
2887 ereport(ERROR,
2889 errmsg("OID %u does not refer to a table", tableoid)));
2892 ereport(ERROR,
2894 errmsg("table \"%s\" is not a member of the extension being created",
2895 tablename)));
2896
2897 /*
2898 * Add the table OID and WHERE condition to the extension's extconfig and
2899 * extcondition arrays.
2900 *
2901 * If the table is already in extconfig, treat this as an update of the
2902 * WHERE condition.
2903 */
2904
2905 /* Find the pg_extension tuple */
2907
2908 ScanKeyInit(&key[0],
2912
2914 NULL, 1, key);
2915
2917
2918 if (!HeapTupleIsValid(extTup)) /* should not happen */
2919 elog(ERROR, "could not find tuple for extension %u",
2921
2922 memset(repl_val, 0, sizeof(repl_val));
2923 memset(repl_null, false, sizeof(repl_null));
2924 memset(repl_repl, false, sizeof(repl_repl));
2925
2926 /* Build or modify the extconfig value */
2927 elementDatum = ObjectIdGetDatum(tableoid);
2928
2930 RelationGetDescr(extRel), &isnull);
2931 if (isnull)
2932 {
2933 /* Previously empty extconfig, so build 1-element array */
2934 arrayLength = 0;
2935 arrayIndex = 1;
2936
2938 }
2939 else
2940 {
2941 /* Modify or extend existing extconfig array */
2942 Oid *arrayData;
2943 int i;
2944
2946
2947 arrayLength = ARR_DIMS(a)[0];
2948 if (ARR_NDIM(a) != 1 ||
2949 ARR_LBOUND(a)[0] != 1 ||
2950 arrayLength < 0 ||
2951 ARR_HASNULL(a) ||
2952 ARR_ELEMTYPE(a) != OIDOID)
2953 elog(ERROR, "extconfig is not a 1-D Oid array");
2954 arrayData = (Oid *) ARR_DATA_PTR(a);
2955
2956 arrayIndex = arrayLength + 1; /* set up to add after end */
2957
2958 for (i = 0; i < arrayLength; i++)
2959 {
2960 if (arrayData[i] == tableoid)
2961 {
2962 arrayIndex = i + 1; /* replace this element instead */
2963 break;
2964 }
2965 }
2966
2967 a = array_set(a, 1, &arrayIndex,
2969 false,
2970 -1 /* varlena array */ ,
2971 sizeof(Oid) /* OID's typlen */ ,
2972 true /* OID's typbyval */ ,
2973 TYPALIGN_INT /* OID's typalign */ );
2974 }
2977
2978 /* Build or modify the extcondition value */
2980
2982 RelationGetDescr(extRel), &isnull);
2983 if (isnull)
2984 {
2985 if (arrayLength != 0)
2986 elog(ERROR, "extconfig and extcondition arrays do not match");
2987
2989 }
2990 else
2991 {
2993
2994 if (ARR_NDIM(a) != 1 ||
2995 ARR_LBOUND(a)[0] != 1 ||
2996 ARR_HASNULL(a) ||
2998 elog(ERROR, "extcondition is not a 1-D text array");
2999 if (ARR_DIMS(a)[0] != arrayLength)
3000 elog(ERROR, "extconfig and extcondition arrays do not match");
3001
3002 /* Add or replace at same index as in extconfig */
3003 a = array_set(a, 1, &arrayIndex,
3005 false,
3006 -1 /* varlena array */ ,
3007 -1 /* TEXT's typlen */ ,
3008 false /* TEXT's typbyval */ ,
3009 TYPALIGN_INT /* TEXT's typalign */ );
3010 }
3013
3016
3018
3020
3022
3024}
ArrayType * array_set(ArrayType *array, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
#define PG_RETURN_VOID()
Definition fmgr.h:350
#define PG_GETARG_OID(n)
Definition fmgr.h:275
#define PG_GETARG_TEXT_PP(n)
Definition fmgr.h:310
char * get_rel_name(Oid relid)
Definition lsyscache.c:2078
#define ERRCODE_UNDEFINED_TABLE
Definition pgbench.c:79
Definition c.h:718

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, fb(), get_rel_name(), getExtensionOfObject(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, i, ObjectIdGetDatum(), PG_GETARG_OID, PG_GETARG_TEXT_PP, PG_RETURN_VOID, PointerGetDatum(), RelationGetDescr, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

◆ pg_extension_update_paths()

Datum pg_extension_update_paths ( PG_FUNCTION_ARGS  )

Definition at line 2766 of file extension.c.

2767{
2768 Name extname = PG_GETARG_NAME(0);
2769 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2770 List *evi_list;
2771 ExtensionControlFile *control;
2772 ListCell *lc1;
2773
2774 /* Check extension name validity before any filesystem access */
2776
2777 /* Build tuplestore to hold the result rows */
2778 InitMaterializedSRF(fcinfo, 0);
2779
2780 /* Read the extension's control file */
2781 control = read_extension_control_file(NameStr(*extname));
2782
2783 /* Extract the version update graph from the script directory */
2784 evi_list = get_ext_ver_list(control);
2785
2786 /* Iterate over all pairs of versions */
2787 foreach(lc1, evi_list)
2788 {
2790 ListCell *lc2;
2791
2792 foreach(lc2, evi_list)
2793 {
2795 List *path;
2796 Datum values[3];
2797 bool nulls[3];
2798
2799 if (evi1 == evi2)
2800 continue;
2801
2802 /* Find shortest path from evi1 to evi2 */
2803 path = find_update_path(evi_list, evi1, evi2, false, true);
2804
2805 /* Emit result row */
2806 memset(values, 0, sizeof(values));
2807 memset(nulls, 0, sizeof(nulls));
2808
2809 /* source */
2810 values[0] = CStringGetTextDatum(evi1->name);
2811 /* target */
2812 values[1] = CStringGetTextDatum(evi2->name);
2813 /* path */
2814 if (path == NIL)
2815 nulls[2] = true;
2816 else
2817 {
2819 ListCell *lcv;
2820
2822 /* The path doesn't include start vertex, but show it */
2824 foreach(lcv, path)
2825 {
2826 char *versionName = (char *) lfirst(lcv);
2827
2830 }
2832 pfree(pathbuf.data);
2833 }
2834
2835 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2836 values, nulls);
2837 }
2838 }
2839
2840 return (Datum) 0;
2841}
#define PG_GETARG_NAME(n)
Definition fmgr.h:279
Definition c.h:772

References appendStringInfoString(), check_valid_extension_name(), CStringGetTextDatum, fb(), find_update_path(), get_ext_ver_list(), InitMaterializedSRF(), initStringInfo(), lfirst, NameStr, NIL, pfree(), PG_GETARG_NAME, read_extension_control_file(), tuplestore_putvalues(), and values.

◆ pg_get_loaded_modules()

Datum pg_get_loaded_modules ( PG_FUNCTION_ARGS  )

Definition at line 3034 of file extension.c.

3035{
3036 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
3038
3039 /* Build tuplestore to hold the result rows */
3040 InitMaterializedSRF(fcinfo, 0);
3041
3044 {
3045 const char *library_path,
3046 *module_name,
3048 const char *sep;
3049 Datum values[3] = {0};
3050 bool nulls[3] = {0};
3051
3053 &library_path,
3054 &module_name,
3056
3057 if (module_name == NULL)
3058 nulls[0] = true;
3059 else
3061 if (module_version == NULL)
3062 nulls[1] = true;
3063 else
3065
3066 /* For security reasons, we don't show the directory path */
3068 if (sep)
3069 library_path = sep + 1;
3071
3072 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
3073 values, nulls);
3074 }
3075
3076 return (Datum) 0;
3077}
DynamicFileList * get_next_loaded_module(DynamicFileList *dfptr)
Definition dfmgr.c:431
DynamicFileList * get_first_loaded_module(void)
Definition dfmgr.c:425
void get_loaded_module_details(DynamicFileList *dfptr, const char **library_path, const char **module_name, const char **module_version)
Definition dfmgr.c:445
char * last_dir_separator(const char *filename)
Definition path.c:145

References CStringGetTextDatum, fb(), get_first_loaded_module(), get_loaded_module_details(), get_next_loaded_module(), InitMaterializedSRF(), last_dir_separator(), 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 897 of file extension.c.

899{
901
902 /*
903 * Flat-copy the struct. Pointer fields share values with original.
904 */
907
908 /*
909 * Parse the auxiliary control file, overwriting struct fields
910 */
912
913 return acontrol;
914}

References fb(), palloc_object, 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 878 of file extension.c.

879{
881
882 /*
883 * Parse the primary control file.
884 */
886
887 return control;
888}

References fb(), 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 920 of file extension.c.

922{
923 int src_encoding;
924 char *src_str;
925 char *dest_str;
926 int len;
927
929
930 /* use database encoding if not given */
931 if (control->encoding < 0)
933 else
934 src_encoding = control->encoding;
935
936 /* make sure that source string is valid in the expected encoding */
938
939 /*
940 * Convert the encoding to the database encoding. read_whole_file
941 * null-terminated the string, so if no conversion happens the string is
942 * valid as is.
943 */
945
946 return dest_str;
947}
static char * read_whole_file(const char *filename, int *length)
Definition extension.c:3998
int GetDatabaseEncoding(void)
Definition mbutils.c:1389
char * pg_any_to_server(const char *s, int len, int encoding)
Definition mbutils.c:687
bool pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError)
Definition mbutils.c:1694

References ExtensionControlFile::encoding, fb(), 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 3998 of file extension.c.

3999{
4000 char *buf;
4001 FILE *file;
4002 size_t bytes_to_read;
4003 struct stat fst;
4004
4005 if (stat(filename, &fst) < 0)
4006 ereport(ERROR,
4008 errmsg("could not stat file \"%s\": %m", filename)));
4009
4010 if (fst.st_size > (MaxAllocSize - 1))
4011 ereport(ERROR,
4013 errmsg("file \"%s\" is too large", filename)));
4014 bytes_to_read = (size_t) fst.st_size;
4015
4016 if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
4017 ereport(ERROR,
4019 errmsg("could not open file \"%s\" for reading: %m",
4020 filename)));
4021
4022 buf = (char *) palloc(bytes_to_read + 1);
4023
4024 bytes_to_read = fread(buf, 1, bytes_to_read, file);
4025
4026 if (ferror(file))
4027 ereport(ERROR,
4029 errmsg("could not read file \"%s\": %m", filename)));
4030
4031 FreeFile(file);
4032
4033 buf[bytes_to_read] = '\0';
4034
4035 /*
4036 * On Windows, manually convert Windows-style newlines (\r\n) to the Unix
4037 * convention of \n only. This avoids gotchas due to script files
4038 * possibly getting converted when being transferred between platforms.
4039 * Ideally we'd do this by using text mode to read the file, but that also
4040 * causes control-Z to be treated as end-of-file. Historically we've
4041 * allowed control-Z in script files, so breaking that seems unwise.
4042 */
4043#ifdef WIN32
4044 {
4045 char *s,
4046 *d;
4047
4048 for (s = d = buf; *s; s++)
4049 {
4050 if (!(*s == '\r' && s[1] == '\n'))
4051 *d++ = *s;
4052 }
4053 *d = '\0';
4054 bytes_to_read = d - buf;
4055 }
4056#endif
4057
4058 *length = bytes_to_read;
4059 return buf;
4060}
#define PG_BINARY_R
Definition c.h:1311
#define MaxAllocSize
Definition fe_memutils.h:22
static char buf[DEFAULT_XLOG_SEG_SIZE]

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

Referenced by read_extension_script_file().

◆ RemoveExtensionById()

void RemoveExtensionById ( Oid  extId)

Definition at line 2329 of file extension.c.

2330{
2331 Relation rel;
2332 SysScanDesc scandesc;
2333 HeapTuple tuple;
2334 ScanKeyData entry[1];
2335
2336 /*
2337 * Disallow deletion of any extension that's currently open for insertion;
2338 * else subsequent executions of recordDependencyOnCurrentExtension()
2339 * could create dangling pg_depend records that refer to a no-longer-valid
2340 * pg_extension OID. This is needed not so much because we think people
2341 * might write "DROP EXTENSION foo" in foo's own script files, as because
2342 * errors in dependency management in extension script files could give
2343 * rise to cases where an extension is dropped as a result of recursing
2344 * from some contained object. Because of that, we must test for the case
2345 * here, not at some higher level of the DROP EXTENSION command.
2346 */
2348 ereport(ERROR,
2350 errmsg("cannot drop extension \"%s\" because it is being modified",
2352
2354
2355 ScanKeyInit(&entry[0],
2359 scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
2360 NULL, 1, entry);
2361
2362 tuple = systable_getnext(scandesc);
2363
2364 /* We assume that there can be at most one matching tuple */
2365 if (HeapTupleIsValid(tuple))
2366 CatalogTupleDelete(rel, &tuple->t_self);
2367
2368 systable_endscan(scandesc);
2369
2371}
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition indexing.c:365
ItemPointerData t_self
Definition htup.h:65

References BTEqualStrategyNumber, CatalogTupleDelete(), CurrentExtensionObject, ereport, errcode(), errmsg(), ERROR, fb(), 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 953 of file extension.c.

954{
956 const char *query = callback_arg->sql;
957 int location = callback_arg->stmt_location;
958 int len = callback_arg->stmt_len;
960 const char *lastslash;
961
962 /*
963 * If there is a syntax error position, convert to internal syntax error;
964 * otherwise report the current query as an item of context stack.
965 *
966 * Note: we'll provide no context except the filename if there's neither
967 * an error position nor any known current query. That shouldn't happen
968 * though: all errors reported during raw parsing should come with an
969 * error position.
970 */
972 if (syntaxerrposition > 0)
973 {
974 /*
975 * If we do not know the bounds of the current statement (as would
976 * happen for an error occurring during initial raw parsing), we have
977 * to use a heuristic to decide how much of the script to show. We'll
978 * also use the heuristic in the unlikely case that syntaxerrposition
979 * is outside what we think the statement bounds are.
980 */
981 if (location < 0 || syntaxerrposition < location ||
982 (len > 0 && syntaxerrposition > location + len))
983 {
984 /*
985 * Our heuristic is pretty simple: look for semicolon-newline
986 * sequences, and break at the last one strictly before
987 * syntaxerrposition and the first one strictly after. It's
988 * certainly possible to fool this with semicolon-newline embedded
989 * in a string literal, but it seems better to do this than to
990 * show the entire extension script.
991 *
992 * Notice we cope with Windows-style newlines (\r\n) regardless of
993 * platform. This is because there might be such newlines in
994 * script files on other platforms.
995 */
996 int slen = strlen(query);
997
998 location = len = 0;
999 for (int loc = 0; loc < slen; loc++)
1000 {
1001 if (query[loc] != ';')
1002 continue;
1003 if (query[loc + 1] == '\r')
1004 loc++;
1005 if (query[loc + 1] == '\n')
1006 {
1007 int bkpt = loc + 2;
1008
1009 if (bkpt < syntaxerrposition)
1010 location = bkpt;
1011 else if (bkpt > syntaxerrposition)
1012 {
1013 len = bkpt - location;
1014 break; /* no need to keep searching */
1015 }
1016 }
1017 }
1018 }
1019
1020 /* Trim leading/trailing whitespace, for consistency */
1021 query = CleanQuerytext(query, &location, &len);
1022
1023 /*
1024 * Adjust syntaxerrposition. It shouldn't be pointing into the
1025 * whitespace we just trimmed, but cope if it is.
1026 */
1027 syntaxerrposition -= location;
1028 if (syntaxerrposition < 0)
1030 else if (syntaxerrposition > len)
1032
1033 /* And report. */
1034 errposition(0);
1036 internalerrquery(pnstrdup(query, len));
1037 }
1038 else if (location >= 0)
1039 {
1040 /*
1041 * Since no syntax cursor will be shown, it's okay and helpful to trim
1042 * the reported query string to just the current statement.
1043 */
1044 query = CleanQuerytext(query, &location, &len);
1045 errcontext("SQL statement \"%.*s\"", len, query);
1046 }
1047
1048 /*
1049 * Trim the reported file name to remove the path. We know that
1050 * get_extension_script_filename() inserted a '/', regardless of whether
1051 * we're on Windows.
1052 */
1053 lastslash = strrchr(callback_arg->filename, '/');
1054 if (lastslash)
1055 lastslash++;
1056 else
1057 lastslash = callback_arg->filename; /* shouldn't happen, but cope */
1058
1059 /*
1060 * If we have a location (which, as said above, we really always should)
1061 * then report a line number to aid in localizing problems in big scripts.
1062 */
1063 if (location >= 0)
1064 {
1065 int linenumber = 1;
1066
1067 for (query = callback_arg->sql; *query; query++)
1068 {
1069 if (--location < 0)
1070 break;
1071 if (*query == '\n')
1072 linenumber++;
1073 }
1074 errcontext("extension script file \"%s\", near line %d",
1075 lastslash, linenumber);
1076 }
1077 else
1078 errcontext("extension script file \"%s\"", lastslash);
1079}
int internalerrquery(const char *query)
Definition elog.c:1517
int internalerrposition(int cursorpos)
Definition elog.c:1497
int geterrposition(void)
Definition elog.c:1613
int errposition(int cursorpos)
Definition elog.c:1481
#define errcontext
Definition elog.h:198
void * arg
const char * CleanQuerytext(const char *query, int *location, int *len)

References arg, CleanQuerytext(), errcontext, errposition(), fb(), 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

◆ ext_sibling_list

ExtensionSiblingCache* ext_sibling_list = NULL
static

Definition at line 162 of file extension.c.

Referenced by ext_sibling_callback(), and get_function_sibling_type().

◆ Extension_control_path

char* Extension_control_path

Definition at line 76 of file extension.c.

Referenced by get_extension_control_directories().