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/tuplestore.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
 

Macros

#define EXTENSION_SYSTEM_MACRO   "$system"
 

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
 

Macro Definition Documentation

◆ EXTENSION_SYSTEM_MACRO

#define EXTENSION_SYSTEM_MACRO   "$system"

Typedef Documentation

◆ ExtensionControlFile

◆ ExtensionSiblingCache

◆ ExtensionVersionInfo

Function Documentation

◆ AlterExtensionNamespace()

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

Definition at line 3258 of file extension.c.

3259{
3261 Oid nspOid;
3262 Oid oldNspOid;
3265 ScanKeyData key[2];
3274
3276
3277 nspOid = LookupCreationNamespace(newschema);
3278
3279 /*
3280 * Permission check: must own extension. Note that we don't bother to
3281 * check ownership of the individual member objects ...
3282 */
3286
3287 /* Permission check: must have creation rights in target namespace */
3289 if (aclresult != ACLCHECK_OK)
3291
3292 /*
3293 * If the schema is currently a member of the extension, disallow moving
3294 * the extension into the schema. That would create a dependency loop.
3295 */
3297 ereport(ERROR,
3299 errmsg("cannot move extension \"%s\" into schema \"%s\" "
3300 "because the extension contains the schema",
3301 extensionName, newschema)));
3302
3303 /* Locate the pg_extension tuple */
3305
3306 ScanKeyInit(&key[0],
3310
3312 NULL, 1, key);
3313
3315
3316 if (!HeapTupleIsValid(extTup)) /* should not happen */
3317 elog(ERROR, "could not find tuple for extension %u",
3318 extensionOid);
3319
3320 /* Copy tuple so we can modify it below */
3323
3325
3326 /*
3327 * If the extension is already in the target schema, just silently do
3328 * nothing.
3329 */
3330 if (extForm->extnamespace == nspOid)
3331 {
3333 return InvalidObjectAddress;
3334 }
3335
3336 /* Check extension is supposed to be relocatable */
3337 if (!extForm->extrelocatable)
3338 ereport(ERROR,
3340 errmsg("extension \"%s\" does not support SET SCHEMA",
3341 NameStr(extForm->extname))));
3342
3344
3345 /* store the OID of the namespace to-be-changed */
3346 oldNspOid = extForm->extnamespace;
3347
3348 /*
3349 * Scan pg_depend to find objects that depend directly on the extension,
3350 * and alter each one's schema.
3351 */
3353
3354 ScanKeyInit(&key[0],
3358 ScanKeyInit(&key[1],
3362
3364 NULL, 2, key);
3365
3367 {
3371
3372 /*
3373 * If a dependent extension has a no_relocate request for this
3374 * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
3375 * the same loop that's actually executing the renames: we may detect
3376 * the error condition only after having expended a fair amount of
3377 * work. However, the alternative is to do two scans of pg_depend,
3378 * which seems like optimizing for failure cases. The rename work
3379 * will all roll back cleanly enough if we do fail here.)
3380 */
3381 if (pg_depend->deptype == DEPENDENCY_NORMAL &&
3382 pg_depend->classid == ExtensionRelationId)
3383 {
3384 char *depextname = get_extension_name(pg_depend->objid);
3386 ListCell *lc;
3387
3389 foreach(lc, dcontrol->no_relocate)
3390 {
3391 char *nrextname = (char *) lfirst(lc);
3392
3393 if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
3394 {
3395 ereport(ERROR,
3397 errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
3398 NameStr(extForm->extname)),
3399 errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
3400 depextname,
3401 NameStr(extForm->extname))));
3402 }
3403 }
3404 }
3405
3406 /*
3407 * Otherwise, ignore non-membership dependencies. (Currently, the
3408 * only other case we could see here is a normal dependency from
3409 * another extension.)
3410 */
3411 if (pg_depend->deptype != DEPENDENCY_EXTENSION)
3412 continue;
3413
3414 dep.classId = pg_depend->classid;
3415 dep.objectId = pg_depend->objid;
3416 dep.objectSubId = pg_depend->objsubid;
3417
3418 if (dep.objectSubId != 0) /* should not happen */
3419 elog(ERROR, "extension should not have a sub-object dependency");
3420
3421 /* Relocate the object */
3423 dep.objectId,
3424 nspOid,
3425 objsMoved);
3426
3427 /*
3428 * If not all the objects had the same old namespace (ignoring any
3429 * that are not in namespaces or are dependent types), complain.
3430 */
3432 ereport(ERROR,
3434 errmsg("extension \"%s\" does not support SET SCHEMA",
3435 NameStr(extForm->extname)),
3436 errdetail("%s is not in the extension's schema \"%s\"",
3437 getObjectDescription(&dep, false),
3439 }
3440
3441 /* report old schema, if caller wants it */
3442 if (oldschema)
3444
3446
3448
3449 /* Now adjust pg_extension.extnamespace */
3450 extForm->extnamespace = nspOid;
3451
3453
3455
3456 /* update dependency to point to the new schema */
3459 elog(ERROR, "could not change schema dependency for extension %s",
3460 NameStr(extForm->extname));
3461
3463
3465
3466 return extAddr;
3467}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
@ ACLCHECK_NOT_OWNER
Definition acl.h:186
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3880
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4134
Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, ObjectAddresses *objsMoved)
Definition alter.c:621
#define NameStr(name)
Definition c.h:835
ObjectAddresses * new_object_addresses(void)
@ DEPENDENCY_EXTENSION
Definition dependency.h:38
@ DEPENDENCY_NORMAL
Definition dependency.h:33
int errcode(int sqlerrcode)
Definition elog.c:875
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
static ExtensionControlFile * read_extension_control_file(const char *extname)
Definition extension.c:881
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition extension.c:229
char * get_extension_name(Oid ext_oid)
Definition extension.c:251
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
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:686
#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:3599
Oid GetUserId(void)
Definition miscinit.c:470
Oid LookupCreationNamespace(const char *nspname)
Definition namespace.c:3500
static char * errmsg
#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:470
Oid getExtensionOfObject(Oid classId, Oid objectId)
Definition pg_depend.c:865
END_CATALOG_STRUCT typedef FormData_pg_depend * Form_pg_depend
Definition pg_depend.h:76
END_CATALOG_STRUCT typedef FormData_pg_extension * Form_pg_extension
#define lfirst(lc)
Definition pg_list.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
#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:206
#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(), Form_pg_depend, Form_pg_extension, 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 3620 of file extension.c.

3627{
3628 const char *oldVersionName = initialVersion;
3629 ListCell *lcv;
3630
3631 foreach(lcv, updateVersions)
3632 {
3633 char *versionName = (char *) lfirst(lcv);
3634 ExtensionControlFile *control;
3635 char *schemaName;
3636 Oid schemaOid;
3640 ScanKeyData key[1];
3645 bool nulls[Natts_pg_extension];
3646 bool repl[Natts_pg_extension];
3648 ListCell *lc;
3649
3650 /*
3651 * Fetch parameters for specific version (pcontrol is not changed)
3652 */
3654
3655 /* Find the pg_extension tuple */
3657
3658 ScanKeyInit(&key[0],
3662
3664 NULL, 1, key);
3665
3667
3668 if (!HeapTupleIsValid(extTup)) /* should not happen */
3669 elog(ERROR, "could not find tuple for extension %u",
3670 extensionOid);
3671
3673
3674 /*
3675 * Determine the target schema (set by original install)
3676 */
3677 schemaOid = extForm->extnamespace;
3679
3680 /*
3681 * Modify extrelocatable and extversion in the pg_extension tuple
3682 */
3683 memset(values, 0, sizeof(values));
3684 memset(nulls, 0, sizeof(nulls));
3685 memset(repl, 0, sizeof(repl));
3686
3688 BoolGetDatum(control->relocatable);
3689 repl[Anum_pg_extension_extrelocatable - 1] = true;
3692 repl[Anum_pg_extension_extversion - 1] = true;
3693
3695 values, nulls, repl);
3696
3698
3700
3702
3703 /*
3704 * Look up the prerequisite extensions for this version, install them
3705 * if necessary, and build lists of their OIDs and the OIDs of their
3706 * target schemas.
3707 */
3710 foreach(lc, control->requires)
3711 {
3712 char *curreq = (char *) lfirst(lc);
3713 Oid reqext;
3714 Oid reqschema;
3715
3717 control->name,
3719 cascade,
3720 NIL,
3721 is_create);
3725 }
3726
3727 /*
3728 * Remove and recreate dependencies on prerequisite extensions
3729 */
3733
3734 myself.classId = ExtensionRelationId;
3735 myself.objectId = extensionOid;
3736 myself.objectSubId = 0;
3737
3738 foreach(lc, requiredExtensions)
3739 {
3742
3744 otherext.objectId = reqext;
3745 otherext.objectSubId = 0;
3746
3748 }
3749
3751
3752 /*
3753 * Finally, execute the update script file
3754 */
3758 schemaName);
3759
3760 /*
3761 * Update prior-version name and loop around. Since
3762 * execute_sql_string did a final CommandCounterIncrement, we can
3763 * update the pg_extension row again.
3764 */
3766 }
3767}
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define CStringGetTextDatum(s)
Definition builtins.h:98
Oid get_extension_schema(Oid ext_oid)
Definition extension.c:273
static Oid get_required_extension(char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade, List *parents, bool is_create)
Definition extension.c:2078
static ExtensionControlFile * read_extension_aux_control_file(const ExtensionControlFile *pcontrol, const char *version)
Definition extension.c:900
static void execute_extension_script(Oid extensionOid, ExtensionControlFile *control, const char *from_version, const char *version, List *requiredSchemas, const char *schemaName)
Definition extension.c:1248
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1118
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:51
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:364
#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:542
Definition pg_list.h:54

References BoolGetDatum(), BTEqualStrategyNumber, CatalogTupleUpdate(), ObjectAddress::classId, CStringGetTextDatum, deleteDependencyRecordsForClass(), DEPENDENCY_NORMAL, elog, ERROR, execute_extension_script(), fb(), Form_pg_extension, 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 401 of file extension.c.

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

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

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

2747{
2748 Datum *datums;
2749 int ndatums;
2750 ArrayType *a;
2751 ListCell *lc;
2752
2753 ndatums = list_length(requires);
2754 datums = (Datum *) palloc(ndatums * sizeof(Datum));
2755 ndatums = 0;
2756 foreach(lc, requires)
2757 {
2758 char *curreq = (char *) lfirst(lc);
2759
2760 datums[ndatums++] =
2762 }
2763 a = construct_array_builtin(datums, ndatums, NAMEOID);
2764 return PointerGetDatum(a);
2765}
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:688
int a
Definition isn.c:73
void * palloc(Size size)
Definition mcxt.c:1390
Datum namein(PG_FUNCTION_ARGS)
Definition name.c:48
static int list_length(const List *l)
Definition pg_list.h:152
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
#define PointerGetDatum(X)
Definition postgres.h:354

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

2150{
2154 char *schemaName = NULL;
2155 char *versionName = NULL;
2156 bool cascade = false;
2157 ListCell *lc;
2158
2159 /* Check extension name validity before any filesystem access */
2161
2162 /*
2163 * Check for duplicate extension name. The unique index on
2164 * pg_extension.extname would catch this anyway, and serves as a backstop
2165 * in case of race conditions; but this is a friendlier error message, and
2166 * besides we need a check to support IF NOT EXISTS.
2167 */
2168 if (get_extension_oid(stmt->extname, true) != InvalidOid)
2169 {
2170 if (stmt->if_not_exists)
2171 {
2174 errmsg("extension \"%s\" already exists, skipping",
2175 stmt->extname)));
2176 return InvalidObjectAddress;
2177 }
2178 else
2179 ereport(ERROR,
2181 errmsg("extension \"%s\" already exists",
2182 stmt->extname)));
2183 }
2184
2185 /*
2186 * We use global variables to track the extension being created, so we can
2187 * create only one extension at the same time.
2188 */
2190 ereport(ERROR,
2192 errmsg("nested CREATE EXTENSION is not supported")));
2193
2194 /* Deconstruct the statement option list */
2195 foreach(lc, stmt->options)
2196 {
2197 DefElem *defel = (DefElem *) lfirst(lc);
2198
2199 if (strcmp(defel->defname, "schema") == 0)
2200 {
2201 if (d_schema)
2203 d_schema = defel;
2205 }
2206 else if (strcmp(defel->defname, "new_version") == 0)
2207 {
2208 if (d_new_version)
2212 }
2213 else if (strcmp(defel->defname, "cascade") == 0)
2214 {
2215 if (d_cascade)
2217 d_cascade = defel;
2218 cascade = defGetBoolean(d_cascade);
2219 }
2220 else
2221 elog(ERROR, "unrecognized option: %s", defel->defname);
2222 }
2223
2224 /* Call CreateExtensionInternal to do the real work. */
2225 return CreateExtensionInternal(stmt->extname,
2226 schemaName,
2228 cascade,
2229 NIL,
2230 true);
2231}
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:36
static void check_valid_extension_name(const char *extensionname)
Definition extension.c:401
bool creating_extension
Definition extension.c:80
static ObjectAddress CreateExtensionInternal(char *extensionName, char *schemaName, const char *versionName, bool cascade, List *parents, bool is_create)
Definition extension.c:1836
#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 1836 of file extension.c.

1842{
1843 char *origSchemaName = schemaName;
1847 ExtensionControlFile *control;
1848 char *filename;
1849 struct stat fst;
1854 ObjectAddress address;
1855 ListCell *lc;
1856
1857 /*
1858 * Read the primary control file. Note we assume that it does not contain
1859 * any non-ASCII data, so there is no need to worry about encoding at this
1860 * point.
1861 */
1863
1864 /*
1865 * Determine the version to install
1866 */
1867 if (versionName == NULL)
1868 {
1869 if (pcontrol->default_version)
1870 versionName = pcontrol->default_version;
1871 else
1872 ereport(ERROR,
1874 errmsg("version to install must be specified")));
1875 }
1877
1878 /*
1879 * Figure out which script(s) we need to run to install the desired
1880 * version of the extension. If we do not have a script that directly
1881 * does what is needed, we try to find a sequence of update scripts that
1882 * will get us there.
1883 */
1885 if (stat(filename, &fst) == 0)
1886 {
1887 /* Easy, no extra scripts */
1889 }
1890 else
1891 {
1892 /* Look for best way to install this version */
1893 List *evi_list;
1896
1897 /* Extract the version update graph from the script directory */
1899
1900 /* Identify the target version */
1902
1903 /* Identify best path to reach target */
1906
1907 /* Fail if no path ... */
1908 if (evi_start == NULL)
1909 ereport(ERROR,
1911 errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
1912 pcontrol->name, versionName)));
1913
1914 /* Otherwise, install best starting point and then upgrade */
1915 versionName = evi_start->name;
1916 }
1917
1918 /*
1919 * Fetch control parameters for installation target version
1920 */
1922
1923 /*
1924 * Determine the target schema to install the extension into
1925 */
1926 if (schemaName)
1927 {
1928 /* If the user is giving us the schema name, it must exist already. */
1930 }
1931
1932 if (control->schema != NULL)
1933 {
1934 /*
1935 * The extension is not relocatable and the author gave us a schema
1936 * for it.
1937 *
1938 * Unless CASCADE parameter was given, it's an error to give a schema
1939 * different from control->schema if control->schema is specified.
1940 */
1941 if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1942 !cascade)
1943 ereport(ERROR,
1945 errmsg("extension \"%s\" must be installed in schema \"%s\"",
1946 control->name,
1947 control->schema)));
1948
1949 /* Always use the schema from control file for current extension. */
1950 schemaName = control->schema;
1951
1952 /* Find or create the schema in case it does not exist. */
1954
1955 if (!OidIsValid(schemaOid))
1956 {
1957 ParseState *pstate = make_parsestate(NULL);
1959
1960 pstate->p_sourcetext = "(generated CREATE SCHEMA command)";
1961
1962 csstmt->schemaname = schemaName;
1963 csstmt->authrole = NULL; /* will be created by current user */
1964 csstmt->schemaElts = NIL;
1965 csstmt->if_not_exists = false;
1966
1967 CreateSchemaCommand(pstate, csstmt, -1, -1);
1968
1969 /*
1970 * CreateSchemaCommand includes CommandCounterIncrement, so new
1971 * schema is now visible.
1972 */
1974 }
1975 }
1976 else if (!OidIsValid(schemaOid))
1977 {
1978 /*
1979 * Neither user nor author of the extension specified schema; use the
1980 * current default creation namespace, which is the first explicit
1981 * entry in the search_path.
1982 */
1983 List *search_path = fetch_search_path(false);
1984
1985 if (search_path == NIL) /* nothing valid in search_path? */
1986 ereport(ERROR,
1988 errmsg("no schema has been selected to create in")));
1989 schemaOid = linitial_oid(search_path);
1991 if (schemaName == NULL) /* recently-deleted namespace? */
1992 ereport(ERROR,
1994 errmsg("no schema has been selected to create in")));
1995
1996 list_free(search_path);
1997 }
1998
1999 /*
2000 * Make note if a temporary namespace has been accessed in this
2001 * transaction.
2002 */
2005
2006 /*
2007 * We don't check creation rights on the target namespace here. If the
2008 * extension script actually creates any objects there, it will fail if
2009 * the user doesn't have such permissions. But there are cases such as
2010 * procedural languages where it's convenient to set schema = pg_catalog
2011 * yet we don't want to restrict the command to users with ACL_CREATE for
2012 * pg_catalog.
2013 */
2014
2015 /*
2016 * Look up the prerequisite extensions, install them if necessary, and
2017 * build lists of their OIDs and the OIDs of their target schemas.
2018 */
2021 foreach(lc, control->requires)
2022 {
2023 char *curreq = (char *) lfirst(lc);
2024 Oid reqext;
2025 Oid reqschema;
2026
2030 cascade,
2031 parents,
2032 is_create);
2036 }
2037
2038 /*
2039 * Insert new tuple into pg_extension, and create dependency entries.
2040 */
2041 address = InsertExtensionTuple(control->name, extowner,
2042 schemaOid, control->relocatable,
2047 extensionOid = address.objectId;
2048
2049 /*
2050 * Apply any control-file comment on extension
2051 */
2052 if (control->comment != NULL)
2054
2055 /*
2056 * Execute the installation script file
2057 */
2061 schemaName);
2062
2063 /*
2064 * If additional update scripts have to be executed, apply the updates as
2065 * though a series of ALTER EXTENSION UPDATE commands were given
2066 */
2069 origSchemaName, cascade, is_create);
2070
2071 return address;
2072}
#define OidIsValid(objectId)
Definition c.h:858
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:2247
static void check_valid_version_name(const char *versionname)
Definition extension.c:448
static ExtensionVersionInfo * find_install_path(List *evi_list, ExtensionVersionInfo *evi_target, List **best_path)
Definition extension.c:1781
static ExtensionVersionInfo * get_ext_ver_info(const char *versionname, List **evi_list)
Definition extension.c:1521
static List * get_ext_ver_list(ExtensionControlFile *control)
Definition extension.c:1582
static char * get_extension_script_filename(ExtensionControlFile *control, const char *from_version, const char *version)
Definition extension.c:658
static void ApplyExtensionUpdates(Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
Definition extension.c:3620
void list_free(List *list)
Definition list.c:1546
bool isTempNamespace(Oid namespaceId)
Definition namespace.c:3721
List * fetch_search_path(bool includeImplicit)
Definition namespace.c:4891
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition namespace.c:3607
#define makeNode(_type_)
Definition nodes.h:161
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
static char * filename
Definition pg_dumpall.c:133
#define linitial_oid(l)
Definition pg_list.h:180
Oid CreateSchemaCommand(ParseState *pstate, CreateSchemaStmt *stmt, int stmt_location, int stmt_len)
Definition schemacmds.c:52
const char * p_sourcetext
Definition parse_node.h:214
#define stat
Definition win32_port.h:74
int MyXactFlags
Definition xact.c:138
#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(), make_parsestate(), makeNode, MyXactFlags, ExtensionControlFile::name, NIL, ObjectAddress::objectId, OidIsValid, ParseState::p_sourcetext, 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 3864 of file extension.c.

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

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

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

3474{
3476 char *versionName;
3477 char *oldVersionName;
3478 ExtensionControlFile *control;
3481 ScanKeyData key[1];
3485 Datum datum;
3486 bool isnull;
3487 ListCell *lc;
3488 ObjectAddress address;
3489
3490 /*
3491 * We use global variables to track the extension being created, so we can
3492 * create/update only one extension at the same time.
3493 */
3495 ereport(ERROR,
3497 errmsg("nested ALTER EXTENSION is not supported")));
3498
3499 /*
3500 * Look up the extension --- it must already exist in pg_extension
3501 */
3503
3504 ScanKeyInit(&key[0],
3507 CStringGetDatum(stmt->extname));
3508
3510 NULL, 1, key);
3511
3513
3515 ereport(ERROR,
3517 errmsg("extension \"%s\" does not exist",
3518 stmt->extname)));
3519
3521
3522 /*
3523 * Determine the existing version we are updating from
3524 */
3526 RelationGetDescr(extRel), &isnull);
3527 if (isnull)
3528 elog(ERROR, "extversion is null");
3530
3532
3534
3535 /* Permission check: must own extension */
3538 stmt->extname);
3539
3540 /*
3541 * Read the primary control file. Note we assume that it does not contain
3542 * any non-ASCII data, so there is no need to worry about encoding at this
3543 * point.
3544 */
3545 control = read_extension_control_file(stmt->extname);
3546
3547 /*
3548 * Read the statement option list
3549 */
3550 foreach(lc, stmt->options)
3551 {
3552 DefElem *defel = (DefElem *) lfirst(lc);
3553
3554 if (strcmp(defel->defname, "new_version") == 0)
3555 {
3556 if (d_new_version)
3559 }
3560 else
3561 elog(ERROR, "unrecognized option: %s", defel->defname);
3562 }
3563
3564 /*
3565 * Determine the version to update to
3566 */
3567 if (d_new_version && d_new_version->arg)
3569 else if (control->default_version)
3570 versionName = control->default_version;
3571 else
3572 {
3573 ereport(ERROR,
3575 errmsg("version to install must be specified")));
3576 versionName = NULL; /* keep compiler quiet */
3577 }
3579
3580 /*
3581 * If we're already at that version, just say so
3582 */
3584 {
3586 (errmsg("version \"%s\" of extension \"%s\" is already installed",
3587 versionName, stmt->extname)));
3588 return InvalidObjectAddress;
3589 }
3590
3591 /*
3592 * Identify the series of update script files we need to execute
3593 */
3596 versionName);
3597
3598 /*
3599 * Update the pg_extension row and execute the update scripts, one at a
3600 * time
3601 */
3604 NULL, false, false);
3605
3607
3608 return address;
3609}
static List * identify_update_path(ExtensionControlFile *control, const char *oldVersion, const char *newVersion)
Definition extension.c:1645
#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:217

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(), Form_pg_extension, 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 1248 of file extension.c.

1253{
1254 bool switch_to_superuser = false;
1255 char *filename;
1256 Oid save_userid = 0;
1257 int save_sec_context = 0;
1258 int save_nestlevel;
1260 ListCell *lc;
1261 ListCell *lc2;
1262
1263 /*
1264 * Enforce superuser-ness if appropriate. We postpone these checks until
1265 * here so that the control flags are correctly associated with the right
1266 * script(s) if they happen to be set in secondary control files.
1267 */
1268 if (control->superuser && !superuser())
1269 {
1270 if (extension_is_trusted(control))
1271 switch_to_superuser = true;
1272 else if (from_version == NULL)
1273 ereport(ERROR,
1275 errmsg("permission denied to create extension \"%s\"",
1276 control->name),
1277 control->trusted
1278 ? errhint("Must have CREATE privilege on current database to create this extension.")
1279 : errhint("Must be superuser to create this extension.")));
1280 else
1281 ereport(ERROR,
1283 errmsg("permission denied to update extension \"%s\"",
1284 control->name),
1285 control->trusted
1286 ? errhint("Must have CREATE privilege on current database to update this extension.")
1287 : errhint("Must be superuser to update this extension.")));
1288 }
1289
1291
1292 if (from_version == NULL)
1293 elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
1294 else
1295 elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
1296
1297 /*
1298 * If installing a trusted extension on behalf of a non-superuser, become
1299 * the bootstrap superuser. (This switch will be cleaned up automatically
1300 * if the transaction aborts, as will the GUC changes below.)
1301 */
1303 {
1304 GetUserIdAndSecContext(&save_userid, &save_sec_context);
1306 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
1307 }
1308
1309 /*
1310 * Force client_min_messages and log_min_messages to be at least WARNING,
1311 * so that we won't spam the user with useless NOTICE messages from common
1312 * script actions like creating shell types.
1313 *
1314 * We use the equivalent of a function SET option to allow the setting to
1315 * persist for exactly the duration of the script execution. guc.c also
1316 * takes care of undoing the setting on error.
1317 *
1318 * log_min_messages can't be set by ordinary users, so for that one we
1319 * pretend to be superuser.
1320 */
1321 save_nestlevel = NewGUCNestLevel();
1322
1324 (void) set_config_option("client_min_messages", "warning",
1326 GUC_ACTION_SAVE, true, 0, false);
1328 (void) set_config_option_ext("log_min_messages", "warning",
1331 GUC_ACTION_SAVE, true, 0, false);
1332
1333 /*
1334 * Similarly disable check_function_bodies, to ensure that SQL functions
1335 * won't be parsed during creation.
1336 */
1338 (void) set_config_option("check_function_bodies", "off",
1340 GUC_ACTION_SAVE, true, 0, false);
1341
1342 /*
1343 * Set up the search path to have the target schema first, making it be
1344 * the default creation target namespace. Then add the schemas of any
1345 * prerequisite extensions, unless they are in pg_catalog which would be
1346 * searched anyway. (Listing pg_catalog explicitly in a non-first
1347 * position would be bad for security.) Finally add pg_temp to ensure
1348 * that temp objects can't take precedence over others.
1349 */
1352 foreach(lc, requiredSchemas)
1353 {
1356
1357 if (reqname && strcmp(reqname, "pg_catalog") != 0)
1359 }
1360 appendStringInfoString(&pathbuf, ", pg_temp");
1361
1362 (void) set_config_option("search_path", pathbuf.data,
1364 GUC_ACTION_SAVE, true, 0, false);
1365
1366 /*
1367 * Set creating_extension and related variables so that
1368 * recordDependencyOnCurrentExtension and other functions do the right
1369 * things. On failure, ensure we reset these variables.
1370 */
1371 creating_extension = true;
1373 PG_TRY();
1374 {
1375 char *c_sql = read_extension_script_file(control, filename);
1376 Datum t_sql;
1377
1378 /*
1379 * We filter each substitution through quote_identifier(). When the
1380 * arg contains one of the following characters, no one collection of
1381 * quoting can work inside $$dollar-quoted string literals$$,
1382 * 'single-quoted string literals', and outside of any literal. To
1383 * avoid a security snare for extension authors, error on substitution
1384 * for arguments containing these.
1385 */
1386 const char *quoting_relevant_chars = "\"$'\\";
1387
1388 /* We use various functions that want to operate on text datums */
1390
1391 /*
1392 * Reduce any lines beginning with "\echo" to empty. This allows
1393 * scripts to contain messages telling people not to run them via
1394 * psql, which has been found to be necessary due to old habits.
1395 */
1398 t_sql,
1399 CStringGetTextDatum("^\\\\echo.*$"),
1401 CStringGetTextDatum("ng"));
1402
1403 /*
1404 * If the script uses @extowner@, substitute the calling username.
1405 */
1406 if (strstr(c_sql, "@extowner@"))
1407 {
1408 Oid uid = switch_to_superuser ? save_userid : GetUserId();
1409 const char *userName = GetUserNameFromId(uid, false);
1410 const char *qUserName = quote_identifier(userName);
1411
1414 t_sql,
1415 CStringGetTextDatum("@extowner@"),
1418 ereport(ERROR,
1420 errmsg("invalid character in extension owner: must not contain any of \"%s\"",
1422 }
1423
1424 /*
1425 * If it's not relocatable, substitute the target schema name for
1426 * occurrences of @extschema@.
1427 *
1428 * For a relocatable extension, we needn't do this. There cannot be
1429 * any need for @extschema@, else it wouldn't be relocatable.
1430 */
1431 if (!control->relocatable)
1432 {
1433 Datum old = t_sql;
1435
1438 t_sql,
1439 CStringGetTextDatum("@extschema@"),
1442 ereport(ERROR,
1444 errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1445 control->name, quoting_relevant_chars)));
1446 }
1447
1448 /*
1449 * Likewise, substitute required extensions' schema names for
1450 * occurrences of @extschema:extension_name@.
1451 */
1452 Assert(list_length(control->requires) == list_length(requiredSchemas));
1453 forboth(lc, control->requires, lc2, requiredSchemas)
1454 {
1455 Datum old = t_sql;
1456 char *reqextname = (char *) lfirst(lc);
1460 char *repltoken;
1461
1462 repltoken = psprintf("@extschema:%s@", reqextname);
1465 t_sql,
1469 ereport(ERROR,
1471 errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1473 }
1474
1475 /*
1476 * If module_pathname was set in the control file, substitute its
1477 * value for occurrences of MODULE_PATHNAME.
1478 */
1479 if (control->module_pathname)
1480 {
1483 t_sql,
1484 CStringGetTextDatum("MODULE_PATHNAME"),
1486 }
1487
1488 /* And now back to C string */
1490
1492 }
1493 PG_FINALLY();
1494 {
1495 creating_extension = false;
1497 }
1498 PG_END_TRY();
1499
1500 /*
1501 * Restore the GUC variables we set above.
1502 */
1503 AtEOXact_GUC(true, save_nestlevel);
1504
1505 /*
1506 * Restore authentication state if needed.
1507 */
1509 SetUserIdAndSecContext(save_userid, save_sec_context);
1510}
int errhint(const char *fmt,...) pg_attribute_printf(1
#define PG_TRY(...)
Definition elog.h:374
#define WARNING
Definition elog.h:37
#define PG_END_TRY(...)
Definition elog.h:399
#define DEBUG1
Definition elog.h:31
#define PG_FINALLY(...)
Definition elog.h:391
static void execute_sql_string(const char *sql, const char *filename)
Definition extension.c:1098
Oid CurrentExtensionObject
Definition extension.c:81
static char * read_extension_script_file(const ExtensionControlFile *control, const char *filename)
Definition extension.c:923
static bool extension_is_trusted(ExtensionControlFile *control)
Definition extension.c:1226
Datum DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4)
Definition fmgr.c:861
Datum DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition fmgr.c:836
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:3288
int NewGUCNestLevel(void)
Definition guc.c:2142
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
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:3248
@ 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:557
int client_min_messages
Definition guc_tables.c:568
int log_min_messages[]
Definition guc_tables.c:681
#define SECURITY_LOCAL_USERID_CHANGE
Definition miscadmin.h:321
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
BackendType MyBackendType
Definition miscinit.c:65
char * GetUserNameFromId(Oid roleid, bool noerr)
Definition miscinit.c:990
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
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:3137

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

1099{
1100 script_error_callback_arg callback_arg;
1104 ListCell *lc1;
1105
1106 /*
1107 * Setup error traceback support for ereport().
1108 */
1109 callback_arg.sql = sql;
1110 callback_arg.filename = filename;
1111 callback_arg.stmt_location = -1;
1112 callback_arg.stmt_len = -1;
1113
1115 scripterrcontext.arg = &callback_arg;
1118
1119 /*
1120 * Parse the SQL string into a list of raw parse trees.
1121 */
1123
1124 /* All output from SELECTs goes to the bit bucket */
1126
1127 /*
1128 * Do parse analysis, rule rewrite, planning, and execution for each raw
1129 * parsetree. We must fully execute each query before beginning parse
1130 * analysis on the next one, since there may be interdependencies.
1131 */
1132 foreach(lc1, raw_parsetree_list)
1133 {
1134 RawStmt *parsetree = lfirst_node(RawStmt, lc1);
1136 oldcontext;
1137 List *stmt_list;
1138 ListCell *lc2;
1139
1140 /* Report location of this query for error context callback */
1141 callback_arg.stmt_location = parsetree->stmt_location;
1142 callback_arg.stmt_len = parsetree->stmt_len;
1143
1144 /*
1145 * We do the work for each parsetree in a short-lived context, to
1146 * limit the memory used when there are many commands in the string.
1147 */
1150 "execute_sql_string per-statement context",
1153
1154 /* Be sure parser can see any DDL done so far */
1156
1157 stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
1158 sql,
1159 NULL,
1160 0,
1161 NULL);
1162 stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
1163
1164 foreach(lc2, stmt_list)
1165 {
1167
1169
1171
1172 if (stmt->utilityStmt == NULL)
1173 {
1175
1177 sql,
1179 dest, NULL, NULL, 0);
1180
1181 ExecutorStart(qdesc, 0);
1185
1187 }
1188 else
1189 {
1190 if (IsA(stmt->utilityStmt, TransactionStmt))
1191 ereport(ERROR,
1193 errmsg("transaction control statements are not allowed within an extension script")));
1194
1196 sql,
1197 false,
1199 NULL,
1200 NULL,
1201 dest,
1202 NULL);
1203 }
1204
1206 }
1207
1208 /* Clean up per-parsetree context. */
1209 MemoryContextSwitchTo(oldcontext);
1211 }
1212
1214
1215 /* Be sure to advance the command counter after the last script command */
1217}
DestReceiver * CreateDestReceiver(CommandDest dest)
Definition dest.c:113
@ DestNone
Definition dest.h:87
ErrorContextCallback * error_context_stack
Definition elog.c:100
void ExecutorEnd(QueryDesc *queryDesc)
Definition execMain.c:477
void ExecutorFinish(QueryDesc *queryDesc)
Definition execMain.c:417
void ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition execMain.c:124
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Definition execMain.c:308
static void script_error_callback(void *arg)
Definition extension.c:956
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#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:138
#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:616
List * pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition postgres.c:987
List * pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char *query_string, const Oid *paramTypes, int numParams, QueryEnvironment *queryEnv)
Definition postgres.c:682
void FreeQueryDesc(QueryDesc *qdesc)
Definition pquery.c:107
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:299
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:504
@ PROCESS_UTILITY_QUERY
Definition utility.h:23
void CommandCounterIncrement(void)
Definition xact.c:1130

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

385{
387
390 {
391 if (hashvalue == 0 ||
392 cache_entry->exthash == hashvalue)
393 cache_entry->valid = false;
394 }
395}
static ExtensionSiblingCache * ext_sibling_list
Definition extension.c:163
struct ExtensionSiblingCache * next
Definition extension.c:153

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

3094{
3096 ScanKeyData key[1];
3100 int arrayLength;
3101 int arrayIndex;
3102 bool isnull;
3106 ArrayType *a;
3107
3108 /* Find the pg_extension tuple */
3110
3111 ScanKeyInit(&key[0],
3115
3117 NULL, 1, key);
3118
3120
3121 if (!HeapTupleIsValid(extTup)) /* should not happen */
3122 elog(ERROR, "could not find tuple for extension %u",
3123 extensionoid);
3124
3125 /* Search extconfig for the tableoid */
3127 RelationGetDescr(extRel), &isnull);
3128 if (isnull)
3129 {
3130 /* nothing to do */
3131 a = NULL;
3132 arrayLength = 0;
3133 arrayIndex = -1;
3134 }
3135 else
3136 {
3137 Oid *arrayData;
3138 int i;
3139
3141
3142 arrayLength = ARR_DIMS(a)[0];
3143 if (ARR_NDIM(a) != 1 ||
3144 ARR_LBOUND(a)[0] != 1 ||
3145 arrayLength < 0 ||
3146 ARR_HASNULL(a) ||
3147 ARR_ELEMTYPE(a) != OIDOID)
3148 elog(ERROR, "extconfig is not a 1-D Oid array");
3149 arrayData = (Oid *) ARR_DATA_PTR(a);
3150
3151 arrayIndex = -1; /* flag for no deletion needed */
3152
3153 for (i = 0; i < arrayLength; i++)
3154 {
3155 if (arrayData[i] == tableoid)
3156 {
3157 arrayIndex = i; /* index to remove */
3158 break;
3159 }
3160 }
3161 }
3162
3163 /* If tableoid is not in extconfig, nothing to do */
3164 if (arrayIndex < 0)
3165 {
3168 return;
3169 }
3170
3171 /* Modify or delete the extconfig value */
3172 memset(repl_val, 0, sizeof(repl_val));
3173 memset(repl_null, false, sizeof(repl_null));
3174 memset(repl_repl, false, sizeof(repl_repl));
3175
3176 if (arrayLength <= 1)
3177 {
3178 /* removing only element, just set array to null */
3180 }
3181 else
3182 {
3183 /* squeeze out the target element */
3184 Datum *dvalues;
3185 int nelems;
3186 int i;
3187
3188 /* We already checked there are no nulls */
3189 deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
3190
3191 for (i = arrayIndex; i < arrayLength - 1; i++)
3192 dvalues[i] = dvalues[i + 1];
3193
3195
3197 }
3199
3200 /* Modify or delete the extcondition value */
3202 RelationGetDescr(extRel), &isnull);
3203 if (isnull)
3204 {
3205 elog(ERROR, "extconfig and extcondition arrays do not match");
3206 }
3207 else
3208 {
3210
3211 if (ARR_NDIM(a) != 1 ||
3212 ARR_LBOUND(a)[0] != 1 ||
3213 ARR_HASNULL(a) ||
3215 elog(ERROR, "extcondition is not a 1-D text array");
3216 if (ARR_DIMS(a)[0] != arrayLength)
3217 elog(ERROR, "extconfig and extcondition arrays do not match");
3218 }
3219
3220 if (arrayLength <= 1)
3221 {
3222 /* removing only element, just set array to null */
3224 }
3225 else
3226 {
3227 /* squeeze out the target element */
3228 Datum *dvalues;
3229 int nelems;
3230 int i;
3231
3232 /* We already checked there are no nulls */
3233 deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
3234
3235 for (i = arrayIndex; i < arrayLength - 1; i++)
3236 dvalues[i] = dvalues[i + 1];
3237
3239
3241 }
3243
3246
3248
3250
3252}
#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 2687 of file extension.c.

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

References AllocateDir(), fb(), foreach_ptr, FreeDir(), get_extension_control_directories(), is_extension_control_filename(), pstrdup(), ReadDir(), and result.

Referenced by CreateFunction(), and ExecuteDoStmt().

◆ extension_is_trusted()

static bool extension_is_trusted ( ExtensionControlFile control)
static

Definition at line 1226 of file extension.c.

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

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

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

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

Referenced by parse_extension_control_file().

◆ find_in_paths()

char * find_in_paths ( const char basename,
List paths 
)

Definition at line 4092 of file extension.c.

4093{
4094 ListCell *cell;
4095
4096 foreach(cell, paths)
4097 {
4098 ExtensionLocation *location = lfirst(cell);
4099 char *path = location->loc;
4100 char *full;
4101
4102 Assert(path != NULL);
4103
4104 path = pstrdup(path);
4105 canonicalize_path(path);
4106
4107 /* only absolute paths */
4108 if (!is_absolute_path(path))
4109 ereport(ERROR,
4111 errmsg("component in parameter \"%s\" is not an absolute path", "extension_control_path"));
4112
4113 full = psprintf("%s/%s", path, basename);
4114
4115 if (pg_file_exists(full))
4116 return full;
4117
4118 pfree(path);
4119 pfree(full);
4120 }
4121
4122 return NULL;
4123}
bool pg_file_exists(const char *name)
Definition fd.c:504
void pfree(void *pointer)
Definition mcxt.c:1619
#define is_absolute_path(filename)
Definition port.h:105
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 1781 of file extension.c.

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

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

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

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

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

2572{
2573 List *evi_list;
2574 ListCell *lc;
2575
2576 /* Extract the version update graph from the script directory */
2578
2579 /* For each installable version ... */
2580 foreach(lc, evi_list)
2581 {
2583 ExtensionControlFile *control;
2584 Datum values[9];
2585 bool nulls[9];
2586 ListCell *lc2;
2587
2588 if (!evi->installable)
2589 continue;
2590
2591 /*
2592 * Fetch parameters for specific version (pcontrol is not changed)
2593 */
2595
2596 memset(values, 0, sizeof(values));
2597 memset(nulls, 0, sizeof(nulls));
2598
2599 /* name */
2601 CStringGetDatum(control->name));
2602 /* version */
2603 values[1] = CStringGetTextDatum(evi->name);
2604 /* superuser */
2605 values[2] = BoolGetDatum(control->superuser);
2606 /* trusted */
2607 values[3] = BoolGetDatum(control->trusted);
2608 /* relocatable */
2609 values[4] = BoolGetDatum(control->relocatable);
2610 /* schema */
2611 if (control->schema == NULL)
2612 nulls[5] = true;
2613 else
2615 CStringGetDatum(control->schema));
2616 /* requires */
2617 if (control->requires == NIL)
2618 nulls[6] = true;
2619 else
2620 values[6] = convert_requires_to_datum(control->requires);
2621
2622 /* location */
2624
2625 /* comment */
2626 if (control->comment == NULL)
2627 nulls[8] = true;
2628 else
2629 values[8] = CStringGetTextDatum(control->comment);
2630
2631 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2632
2633 /*
2634 * Find all non-directly-installable versions that would be installed
2635 * starting from this version, and report them, inheriting the
2636 * parameters that aren't changed in updates from this version.
2637 */
2638 foreach(lc2, evi_list)
2639 {
2641 List *best_path;
2642
2643 if (evi2->installable)
2644 continue;
2646 {
2647 /*
2648 * Fetch parameters for this version (pcontrol is not changed)
2649 */
2651
2652 /* name stays the same */
2653 /* version */
2654 values[1] = CStringGetTextDatum(evi2->name);
2655 /* superuser */
2656 values[2] = BoolGetDatum(control->superuser);
2657 /* trusted */
2658 values[3] = BoolGetDatum(control->trusted);
2659 /* relocatable */
2660 values[4] = BoolGetDatum(control->relocatable);
2661 /* schema stays the same */
2662 /* requires */
2663 if (control->requires == NIL)
2664 nulls[6] = true;
2665 else
2666 {
2667 values[6] = convert_requires_to_datum(control->requires);
2668 nulls[6] = false;
2669 }
2670 /* comment and location stay the same */
2671
2672 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2673 }
2674 }
2675 }
2676}
static char * get_extension_location(ExtensionLocation *loc)
Definition extension.c:204
static Datum convert_requires_to_datum(List *requires)
Definition extension.c:2746
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition tuplestore.c:785

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

1522{
1524 ListCell *lc;
1525
1526 foreach(lc, *evi_list)
1527 {
1529 if (strcmp(evi->name, versionname) == 0)
1530 return evi;
1531 }
1532
1534 evi->name = pstrdup(versionname);
1535 evi->reachable = NIL;
1536 evi->installable = false;
1537 /* initialize for later application of Dijkstra's algorithm */
1538 evi->distance_known = false;
1539 evi->distance = INT_MAX;
1540 evi->previous = NULL;
1541
1543
1544 return evi;
1545}
#define palloc_object(type)
Definition fe_memutils.h:89
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 1582 of file extension.c.

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

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

642{
643 char *result;
644 char *scriptdir;
645
647
648 result = (char *) palloc(MAXPGPATH);
649 snprintf(result, MAXPGPATH, "%s/%s--%s.control",
650 scriptdir, control->name, version);
651
653
654 return result;
655}
#define MAXPGPATH
#define snprintf
Definition port.h:261

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

Referenced by parse_extension_control_file().

◆ get_extension_control_directories()

static List * get_extension_control_directories ( void  )
static

Definition at line 514 of file extension.c.

515{
516#define EXTENSION_SYSTEM_MACRO "$system"
517 char sharepath[MAXPGPATH];
518 char *system_dir;
519 char *ecp;
520 List *paths = NIL;
521
523
524 system_dir = psprintf("%s/extension", sharepath);
525
527 {
529
531 location->loc = system_dir;
532 paths = lappend(paths, location);
533 }
534 else
535 {
536 /* Duplicate the string so we can modify it */
538
539 for (;;)
540 {
541 int len;
542 char *mangled;
545
546 /* Get the length of the next path on ecp */
547 if (piece == NULL)
548 len = strlen(ecp);
549 else
550 len = piece - ecp;
551
552 /* Copy the next path found on ecp */
553 piece = palloc(len + 1);
554 strlcpy(piece, ecp, len + 1);
555
556 /*
557 * Substitute the path macro if needed or append "extension"
558 * suffix if it is a custom extension control path.
559 */
561 {
562 location->macro = pstrdup(piece);
564 }
565 else
566 {
567 location->macro = NULL;
568 mangled = psprintf("%s/extension", piece);
569 }
570 pfree(piece);
571
572 /* Canonicalize the path based on the OS and add to the list */
574 location->loc = mangled;
575 paths = lappend(paths, location);
576
577 /* Break if ecp is empty or move to the next path on ecp */
578 if (ecp[len] == '\0')
579 break;
580 else
581 ecp += len + 1;
582 }
583 }
584
585 return paths;
586#undef EXTENSION_SYSTEM_MACRO
587}
char * substitute_path_macro(const char *str, const char *macro, const char *value)
Definition dfmgr.c:536
#define EXTENSION_SYSTEM_MACRO
char * Extension_control_path
Definition extension.c:77
char my_exec_path[MAXPGPATH]
Definition globals.c:83
const void size_t len
void get_share_path(const char *my_exec_path, char *ret_path)
Definition path.c:919
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, EXTENSION_SYSTEM_MACRO, 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 204 of file extension.c.

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

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

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

References fb(), Form_pg_extension, GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), pstrdup(), ReleaseSysCache(), result, 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 229 of file extension.c.

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

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

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)

◆ get_extension_script_directory()

static char * get_extension_script_directory ( ExtensionControlFile control)
static

Definition at line 621 of file extension.c.

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

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

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

References fb(), get_extension_script_directory(), MAXPGPATH, ExtensionControlFile::name, palloc(), pfree(), result, 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 313 of file extension.c.

314{
316 Oid extoid;
317 Oid typeoid;
318
319 /*
320 * See if we have the answer cached. Someday there may be enough callers
321 * to justify a hash table, but for now, a simple linked list is fine.
322 */
325 {
326 if (funcoid == cache_entry->reqfuncoid &&
327 strcmp(typname, cache_entry->typname) == 0)
328 break;
329 }
330 if (cache_entry && cache_entry->valid)
331 return cache_entry->typeoid;
332
333 /*
334 * Nope, so do the expensive lookups. We do not expect failures, so we do
335 * not cache negative results.
336 */
338 if (!OidIsValid(extoid))
339 return InvalidOid;
340 typeoid = getExtensionType(extoid, typname);
341 if (!OidIsValid(typeoid))
342 return InvalidOid;
343
344 /*
345 * Build, or revalidate, cache entry.
346 */
347 if (cache_entry == NULL)
348 {
349 /* Register invalidation hook if this is first entry */
350 if (ext_sibling_list == NULL)
353 (Datum) 0);
354
355 /* Momentarily zero the space to ensure valid flag is false */
358 sizeof(ExtensionSiblingCache));
361 }
362
364 cache_entry->typname = typname;
367 cache_entry->typeoid = typeoid;
368 /* Mark it valid only once it's fully populated */
369 cache_entry->valid = true;
370
371 return typeoid;
372}
static void ext_sibling_callback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
Definition extension.c:384
void CacheRegisterSyscacheCallback(SysCacheIdentifier cacheid, SyscacheCallbackFunction func, Datum arg)
Definition inval.c:1813
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269
MemoryContext CacheMemoryContext
Definition mcxt.c:170
Oid getExtensionType(Oid extensionOid, const char *typname)
Definition pg_depend.c:963
NameData typname
Definition pg_type.h:43
#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 1554 of file extension.c.

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

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

2084{
2086
2089 {
2090 if (cascade)
2091 {
2092 /* Must install it. */
2093 ObjectAddress addr;
2095 ListCell *lc;
2096
2097 /* Check extension name validity before trying to cascade. */
2099
2100 /* Check for cyclic dependency between extensions. */
2101 foreach(lc, parents)
2102 {
2103 char *pname = (char *) lfirst(lc);
2104
2105 if (strcmp(pname, reqExtensionName) == 0)
2106 ereport(ERROR,
2108 errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
2110 }
2111
2113 (errmsg("installing required extension \"%s\"",
2115
2116 /* Add current extension to list of parents to pass down. */
2118
2119 /*
2120 * Create the required extension. We propagate the SCHEMA option
2121 * if any, and CASCADE, but no other options.
2122 */
2125 NULL,
2126 cascade,
2128 is_create);
2129
2130 /* Get its newly-assigned OID. */
2132 }
2133 else
2134 ereport(ERROR,
2136 errmsg("required extension \"%s\" is not installed",
2138 is_create ?
2139 errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
2140 }
2141
2142 return reqExtensionOid;
2143}
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 1645 of file extension.c.

1647{
1648 List *result;
1649 List *evi_list;
1652
1653 /* Extract the version update graph from the script directory */
1654 evi_list = get_ext_ver_list(control);
1655
1656 /* Initialize start and end vertices */
1659
1660 /* Find shortest path */
1662
1663 if (result == NIL)
1664 ereport(ERROR,
1666 errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
1667 control->name, oldVersion, newVersion)));
1668
1669 return result;
1670}

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

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

2251{
2253 Relation rel;
2255 bool nulls[Natts_pg_extension];
2256 HeapTuple tuple;
2260 ListCell *lc;
2261
2262 /*
2263 * Build and insert the pg_extension tuple
2264 */
2266
2267 memset(values, 0, sizeof(values));
2268 memset(nulls, 0, sizeof(nulls));
2269
2279
2281 nulls[Anum_pg_extension_extconfig - 1] = true;
2282 else
2284
2286 nulls[Anum_pg_extension_extcondition - 1] = true;
2287 else
2289
2290 tuple = heap_form_tuple(rel->rd_att, values, nulls);
2291
2292 CatalogTupleInsert(rel, tuple);
2293
2294 heap_freetuple(tuple);
2296
2297 /*
2298 * Record dependencies on owner, schema, and prerequisite extensions
2299 */
2301
2303
2305
2308
2309 foreach(lc, requiredExtensions)
2310 {
2313
2316 }
2317
2318 /* Record all of them (this includes duplicate elimination) */
2321
2322 /* Post creation hook for new extension */
2324
2325 return myself;
2326}
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:1025
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
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 495 of file extension.c.

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

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

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

References fb(), and filename.

Referenced by get_ext_ver_list().

◆ new_ExtensionControlFile()

static ExtensionControlFile * new_ExtensionControlFile ( const char extname)
static

Definition at line 4069 of file extension.c.

4070{
4071 /*
4072 * Set up default values. Pointer fields are initially null.
4073 */
4075
4076 control->name = pstrdup(extname);
4077 control->relocatable = false;
4078 control->superuser = true;
4079 control->trusted = false;
4080 control->encoding = -1;
4081
4082 return control;
4083}
#define palloc0_object(type)
Definition fe_memutils.h:90

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

695{
696 char *filename;
697 FILE *file;
698 ConfigVariable *item,
699 *head = NULL,
700 *tail = NULL;
701
702 /*
703 * Locate the file to read. Auxiliary files are optional.
704 */
705 if (version)
707 else
708 {
709 /*
710 * If control_dir is already set, use it, else do a path search.
711 */
712 if (control->control_dir)
713 {
714 filename = psprintf("%s/%s.control", control->control_dir, control->name);
715 }
716 else
718 }
719
720 if (!filename)
721 {
724 errmsg("extension \"%s\" is not available", control->name),
725 errhint("The extension must first be installed on the system where PostgreSQL is running.")));
726 }
727
728 /* Assert that the control_dir ends with /extension */
729 Assert(control->control_dir != NULL);
730 Assert(strcmp(control->control_dir + strlen(control->control_dir) - strlen("/extension"), "/extension") == 0);
731
732 control->basedir = pnstrdup(
733 control->control_dir,
734 strlen(control->control_dir) - strlen("/extension"));
735
736 if ((file = AllocateFile(filename, "r")) == NULL)
737 {
738 /* no complaint for missing auxiliary file */
739 if (errno == ENOENT && version)
740 {
742 return;
743 }
744
747 errmsg("could not open extension control file \"%s\": %m",
748 filename)));
749 }
750
751 /*
752 * Parse the file content, using GUC's file parsing code. We need not
753 * check the return value since any errors will be thrown at ERROR level.
754 */
756 &head, &tail);
757
758 FreeFile(file);
759
760 /*
761 * Convert the ConfigVariable list into ExtensionControlFile entries.
762 */
763 for (item = head; item != NULL; item = item->next)
764 {
765 if (strcmp(item->name, "directory") == 0)
766 {
767 if (version)
770 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
771 item->name)));
772
773 control->directory = pstrdup(item->value);
774 }
775 else if (strcmp(item->name, "default_version") == 0)
776 {
777 if (version)
780 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
781 item->name)));
782
783 control->default_version = pstrdup(item->value);
784 }
785 else if (strcmp(item->name, "module_pathname") == 0)
786 {
787 control->module_pathname = pstrdup(item->value);
788 }
789 else if (strcmp(item->name, "comment") == 0)
790 {
791 control->comment = pstrdup(item->value);
792 }
793 else if (strcmp(item->name, "schema") == 0)
794 {
795 control->schema = pstrdup(item->value);
796 }
797 else if (strcmp(item->name, "relocatable") == 0)
798 {
799 if (!parse_bool(item->value, &control->relocatable))
802 errmsg("parameter \"%s\" requires a Boolean value",
803 item->name)));
804 }
805 else if (strcmp(item->name, "superuser") == 0)
806 {
807 if (!parse_bool(item->value, &control->superuser))
810 errmsg("parameter \"%s\" requires a Boolean value",
811 item->name)));
812 }
813 else if (strcmp(item->name, "trusted") == 0)
814 {
815 if (!parse_bool(item->value, &control->trusted))
818 errmsg("parameter \"%s\" requires a Boolean value",
819 item->name)));
820 }
821 else if (strcmp(item->name, "encoding") == 0)
822 {
823 control->encoding = pg_valid_server_encoding(item->value);
824 if (control->encoding < 0)
827 errmsg("\"%s\" is not a valid encoding name",
828 item->value)));
829 }
830 else if (strcmp(item->name, "requires") == 0)
831 {
832 /* Need a modifiable copy of string */
833 char *rawnames = pstrdup(item->value);
834
835 /* Parse string into list of identifiers */
836 if (!SplitIdentifierString(rawnames, ',', &control->requires))
837 {
838 /* syntax error in name list */
841 errmsg("parameter \"%s\" must be a list of extension names",
842 item->name)));
843 }
844 }
845 else if (strcmp(item->name, "no_relocate") == 0)
846 {
847 /* Need a modifiable copy of string */
848 char *rawnames = pstrdup(item->value);
849
850 /* Parse string into list of identifiers */
851 if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
852 {
853 /* syntax error in name list */
856 errmsg("parameter \"%s\" must be a list of extension names",
857 item->name)));
858 }
859 }
860 else
863 errmsg("unrecognized parameter \"%s\" in file \"%s\"",
864 item->name, filename)));
865 }
866
868
869 if (control->relocatable && control->schema != NULL)
872 errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
873
875}
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:898
static char * get_extension_aux_control_filename(ExtensionControlFile *control, const char *version)
Definition extension.c:640
static char * find_extension_control_filename(ExtensionControlFile *control)
Definition extension.c:595
int FreeFile(FILE *file)
Definition fd.c:2827
FILE * AllocateFile(const char *name, const char *mode)
Definition fd.c:2628
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:484
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:103
bool SplitIdentifierString(char *rawstring, char separator, List **namelist)
Definition varlena.c:2870

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

2492{
2493 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2494 List *locations;
2495 DIR *dir;
2496 struct dirent *de;
2497 List *found_ext = NIL;
2498
2499 /* Build tuplestore to hold the result rows */
2500 InitMaterializedSRF(fcinfo, 0);
2501
2503
2505 {
2506 dir = AllocateDir(location->loc);
2507
2508 /*
2509 * If the control directory doesn't exist, we want to silently return
2510 * an empty set. Any other error will be reported by ReadDir.
2511 */
2512 if (dir == NULL && errno == ENOENT)
2513 {
2514 /* do nothing */
2515 }
2516 else
2517 {
2518 while ((de = ReadDir(dir, location->loc)) != NULL)
2519 {
2520 ExtensionControlFile *control;
2521 char *extname;
2523
2524 if (!is_extension_control_filename(de->d_name))
2525 continue;
2526
2527 /* extract extension name from 'name.control' filename */
2528 extname = pstrdup(de->d_name);
2529 *strrchr(extname, '.') = '\0';
2530
2531 /* ignore it if it's an auxiliary control file */
2532 if (strstr(extname, "--"))
2533 continue;
2534
2535 /*
2536 * Ignore already-found names. They are not reachable by the
2537 * path search, so don't show them.
2538 */
2539 extname_str = makeString(extname);
2541 continue;
2542 else
2544
2545 /* read the control file */
2546 control = new_ExtensionControlFile(extname);
2547 control->control_dir = pstrdup(location->loc);
2549
2550 /* scan extension's script directory for install scripts */
2551 get_available_versions_for_extension(control, rsinfo->setResult,
2552 rsinfo->setDesc,
2553 location);
2554 }
2555
2556 FreeDir(dir);
2557 }
2558 }
2559
2560 return (Datum) 0;
2561}
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol, Tuplestorestate *tupstore, TupleDesc tupdesc, ExtensionLocation *location)
Definition extension.c:2568
static void parse_extension_control_file(ExtensionControlFile *control, const char *version)
Definition extension.c:693
static ExtensionControlFile * new_ExtensionControlFile(const char *extname)
Definition extension.c:4069
void InitMaterializedSRF(FunctionCallInfo fcinfo, uint32 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 2389 of file extension.c.

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

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

2858{
2859 Oid tableoid = PG_GETARG_OID(0);
2861 char *tablename;
2863 ScanKeyData key[1];
2868 int arrayLength;
2869 int arrayIndex;
2870 bool isnull;
2874 ArrayType *a;
2875
2876 /*
2877 * We only allow this to be called from an extension's SQL script. We
2878 * shouldn't need any permissions check beyond that.
2879 */
2880 if (!creating_extension)
2881 ereport(ERROR,
2883 errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
2884 "pg_extension_config_dump()")));
2885
2886 /*
2887 * Check that the table exists and is a member of the extension being
2888 * created. This ensures that we don't need to register an additional
2889 * dependency to protect the extconfig entry.
2890 */
2891 tablename = get_rel_name(tableoid);
2892 if (tablename == NULL)
2893 ereport(ERROR,
2895 errmsg("OID %u does not refer to a table", tableoid)));
2898 ereport(ERROR,
2900 errmsg("table \"%s\" is not a member of the extension being created",
2901 tablename)));
2902
2903 /*
2904 * Add the table OID and WHERE condition to the extension's extconfig and
2905 * extcondition arrays.
2906 *
2907 * If the table is already in extconfig, treat this as an update of the
2908 * WHERE condition.
2909 */
2910
2911 /* Find the pg_extension tuple */
2913
2914 ScanKeyInit(&key[0],
2918
2920 NULL, 1, key);
2921
2923
2924 if (!HeapTupleIsValid(extTup)) /* should not happen */
2925 elog(ERROR, "could not find tuple for extension %u",
2927
2928 memset(repl_val, 0, sizeof(repl_val));
2929 memset(repl_null, false, sizeof(repl_null));
2930 memset(repl_repl, false, sizeof(repl_repl));
2931
2932 /* Build or modify the extconfig value */
2933 elementDatum = ObjectIdGetDatum(tableoid);
2934
2936 RelationGetDescr(extRel), &isnull);
2937 if (isnull)
2938 {
2939 /* Previously empty extconfig, so build 1-element array */
2940 arrayLength = 0;
2941 arrayIndex = 1;
2942
2944 }
2945 else
2946 {
2947 /* Modify or extend existing extconfig array */
2948 Oid *arrayData;
2949 int i;
2950
2952
2953 arrayLength = ARR_DIMS(a)[0];
2954 if (ARR_NDIM(a) != 1 ||
2955 ARR_LBOUND(a)[0] != 1 ||
2956 arrayLength < 0 ||
2957 ARR_HASNULL(a) ||
2958 ARR_ELEMTYPE(a) != OIDOID)
2959 elog(ERROR, "extconfig is not a 1-D Oid array");
2960 arrayData = (Oid *) ARR_DATA_PTR(a);
2961
2962 arrayIndex = arrayLength + 1; /* set up to add after end */
2963
2964 for (i = 0; i < arrayLength; i++)
2965 {
2966 if (arrayData[i] == tableoid)
2967 {
2968 arrayIndex = i + 1; /* replace this element instead */
2969 break;
2970 }
2971 }
2972
2973 a = array_set(a, 1, &arrayIndex,
2975 false,
2976 -1 /* varlena array */ ,
2977 sizeof(Oid) /* OID's typlen */ ,
2978 true /* OID's typbyval */ ,
2979 TYPALIGN_INT /* OID's typalign */ );
2980 }
2983
2984 /* Build or modify the extcondition value */
2986
2988 RelationGetDescr(extRel), &isnull);
2989 if (isnull)
2990 {
2991 if (arrayLength != 0)
2992 elog(ERROR, "extconfig and extcondition arrays do not match");
2993
2995 }
2996 else
2997 {
2999
3000 if (ARR_NDIM(a) != 1 ||
3001 ARR_LBOUND(a)[0] != 1 ||
3002 ARR_HASNULL(a) ||
3004 elog(ERROR, "extcondition is not a 1-D text array");
3005 if (ARR_DIMS(a)[0] != arrayLength)
3006 elog(ERROR, "extconfig and extcondition arrays do not match");
3007
3008 /* Add or replace at same index as in extconfig */
3009 a = array_set(a, 1, &arrayIndex,
3011 false,
3012 -1 /* varlena array */ ,
3013 -1 /* TEXT's typlen */ ,
3014 false /* TEXT's typbyval */ ,
3015 TYPALIGN_INT /* TEXT's typalign */ );
3016 }
3019
3022
3024
3026
3028
3030}
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:2159
#define ERRCODE_UNDEFINED_TABLE
Definition pgbench.c:79
Definition c.h:776

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

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

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

3041{
3042 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
3044
3045 /* Build tuplestore to hold the result rows */
3046 InitMaterializedSRF(fcinfo, 0);
3047
3050 {
3051 const char *library_path,
3052 *module_name,
3054 const char *sep;
3055 Datum values[3] = {0};
3056 bool nulls[3] = {0};
3057
3059 &library_path,
3060 &module_name,
3062
3063 if (module_name == NULL)
3064 nulls[0] = true;
3065 else
3067 if (module_version == NULL)
3068 nulls[1] = true;
3069 else
3071
3072 /* For security reasons, we don't show the directory path */
3074 if (sep)
3075 library_path = sep + 1;
3077
3078 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
3079 values, nulls);
3080 }
3081
3082 return (Datum) 0;
3083}
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 900 of file extension.c.

902{
904
905 /*
906 * Flat-copy the struct. Pointer fields share values with original.
907 */
910
911 /*
912 * Parse the auxiliary control file, overwriting struct fields
913 */
915
916 return acontrol;
917}
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))

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

882{
884
885 /*
886 * Parse the primary control file.
887 */
889
890 return control;
891}

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

925{
926 int src_encoding;
927 char *src_str;
928 char *dest_str;
929 int len;
930
932
933 /* use database encoding if not given */
934 if (control->encoding < 0)
936 else
937 src_encoding = control->encoding;
938
939 /* make sure that source string is valid in the expected encoding */
941
942 /*
943 * Convert the encoding to the database encoding. read_whole_file
944 * null-terminated the string, so if no conversion happens the string is
945 * valid as is.
946 */
948
949 return dest_str;
950}
static char * read_whole_file(const char *filename, int *length)
Definition extension.c:4004
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:1693

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

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

2336{
2337 Relation rel;
2338 SysScanDesc scandesc;
2339 HeapTuple tuple;
2340 ScanKeyData entry[1];
2341
2342 /*
2343 * Disallow deletion of any extension that's currently open for insertion;
2344 * else subsequent executions of recordDependencyOnCurrentExtension()
2345 * could create dangling pg_depend records that refer to a no-longer-valid
2346 * pg_extension OID. This is needed not so much because we think people
2347 * might write "DROP EXTENSION foo" in foo's own script files, as because
2348 * errors in dependency management in extension script files could give
2349 * rise to cases where an extension is dropped as a result of recursing
2350 * from some contained object. Because of that, we must test for the case
2351 * here, not at some higher level of the DROP EXTENSION command.
2352 */
2354 ereport(ERROR,
2356 errmsg("cannot drop extension \"%s\" because it is being modified",
2358
2360
2361 ScanKeyInit(&entry[0],
2365 scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
2366 NULL, 1, entry);
2367
2368 tuple = systable_getnext(scandesc);
2369
2370 /* We assume that there can be at most one matching tuple */
2371 if (HeapTupleIsValid(tuple))
2372 CatalogTupleDelete(rel, &tuple->t_self);
2373
2374 systable_endscan(scandesc);
2375
2377}
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 956 of file extension.c.

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

Referenced by ext_sibling_callback(), and get_function_sibling_type().

◆ Extension_control_path

char* Extension_control_path

Definition at line 77 of file extension.c.

Referenced by get_extension_control_directories().