PostgreSQL Source Code  git master
extension.h File Reference
Include dependency graph for extension.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

ObjectAddress CreateExtension (ParseState *pstate, CreateExtensionStmt *stmt)
 
void RemoveExtensionById (Oid extId)
 
ObjectAddress InsertExtensionTuple (const char *extName, Oid extOwner, Oid schemaOid, bool relocatable, const char *extVersion, Datum extConfig, Datum extCondition, List *requiredExtensions)
 
ObjectAddress ExecAlterExtensionStmt (ParseState *pstate, AlterExtensionStmt *stmt)
 
ObjectAddress ExecAlterExtensionContentsStmt (AlterExtensionContentsStmt *stmt, ObjectAddress *objAddr)
 
Oid get_extension_oid (const char *extname, bool missing_ok)
 
char * get_extension_name (Oid ext_oid)
 
Oid get_extension_schema (Oid ext_oid)
 
bool extension_file_exists (const char *extensionName)
 
ObjectAddress AlterExtensionNamespace (const char *extensionName, const char *newschema, Oid *oldschema)
 

Variables

PGDLLIMPORT bool creating_extension
 
PGDLLIMPORT Oid CurrentExtensionObject
 

Function Documentation

◆ AlterExtensionNamespace()

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

Definition at line 2749 of file extension.c.

2750 {
2751  Oid extensionOid;
2752  Oid nspOid;
2753  Oid oldNspOid = InvalidOid;
2754  AclResult aclresult;
2755  Relation extRel;
2756  ScanKeyData key[2];
2757  SysScanDesc extScan;
2758  HeapTuple extTup;
2759  Form_pg_extension extForm;
2760  Relation depRel;
2761  SysScanDesc depScan;
2762  HeapTuple depTup;
2763  ObjectAddresses *objsMoved;
2764  ObjectAddress extAddr;
2765 
2766  extensionOid = get_extension_oid(extensionName, false);
2767 
2768  nspOid = LookupCreationNamespace(newschema);
2769 
2770  /*
2771  * Permission check: must own extension. Note that we don't bother to
2772  * check ownership of the individual member objects ...
2773  */
2774  if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
2776  extensionName);
2777 
2778  /* Permission check: must have creation rights in target namespace */
2779  aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
2780  if (aclresult != ACLCHECK_OK)
2781  aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
2782 
2783  /*
2784  * If the schema is currently a member of the extension, disallow moving
2785  * the extension into the schema. That would create a dependency loop.
2786  */
2787  if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
2788  ereport(ERROR,
2789  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2790  errmsg("cannot move extension \"%s\" into schema \"%s\" "
2791  "because the extension contains the schema",
2792  extensionName, newschema)));
2793 
2794  /* Locate the pg_extension tuple */
2795  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2796 
2797  ScanKeyInit(&key[0],
2798  Anum_pg_extension_oid,
2799  BTEqualStrategyNumber, F_OIDEQ,
2800  ObjectIdGetDatum(extensionOid));
2801 
2802  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2803  NULL, 1, key);
2804 
2805  extTup = systable_getnext(extScan);
2806 
2807  if (!HeapTupleIsValid(extTup)) /* should not happen */
2808  elog(ERROR, "could not find tuple for extension %u",
2809  extensionOid);
2810 
2811  /* Copy tuple so we can modify it below */
2812  extTup = heap_copytuple(extTup);
2813  extForm = (Form_pg_extension) GETSTRUCT(extTup);
2814 
2815  systable_endscan(extScan);
2816 
2817  /*
2818  * If the extension is already in the target schema, just silently do
2819  * nothing.
2820  */
2821  if (extForm->extnamespace == nspOid)
2822  {
2823  table_close(extRel, RowExclusiveLock);
2824  return InvalidObjectAddress;
2825  }
2826 
2827  /* Check extension is supposed to be relocatable */
2828  if (!extForm->extrelocatable)
2829  ereport(ERROR,
2830  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2831  errmsg("extension \"%s\" does not support SET SCHEMA",
2832  NameStr(extForm->extname))));
2833 
2834  objsMoved = new_object_addresses();
2835 
2836  /*
2837  * Scan pg_depend to find objects that depend directly on the extension,
2838  * and alter each one's schema.
2839  */
2840  depRel = table_open(DependRelationId, AccessShareLock);
2841 
2842  ScanKeyInit(&key[0],
2843  Anum_pg_depend_refclassid,
2844  BTEqualStrategyNumber, F_OIDEQ,
2845  ObjectIdGetDatum(ExtensionRelationId));
2846  ScanKeyInit(&key[1],
2847  Anum_pg_depend_refobjid,
2848  BTEqualStrategyNumber, F_OIDEQ,
2849  ObjectIdGetDatum(extensionOid));
2850 
2851  depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
2852  NULL, 2, key);
2853 
2854  while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2855  {
2856  Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2857  ObjectAddress dep;
2858  Oid dep_oldNspOid;
2859 
2860  /*
2861  * If a dependent extension has a no_relocate request for this
2862  * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
2863  * the same loop that's actually executing the renames: we may detect
2864  * the error condition only after having expended a fair amount of
2865  * work. However, the alternative is to do two scans of pg_depend,
2866  * which seems like optimizing for failure cases. The rename work
2867  * will all roll back cleanly enough if we do fail here.)
2868  */
2869  if (pg_depend->deptype == DEPENDENCY_NORMAL &&
2870  pg_depend->classid == ExtensionRelationId)
2871  {
2872  char *depextname = get_extension_name(pg_depend->objid);
2873  ExtensionControlFile *dcontrol;
2874  ListCell *lc;
2875 
2876  dcontrol = read_extension_control_file(depextname);
2877  foreach(lc, dcontrol->no_relocate)
2878  {
2879  char *nrextname = (char *) lfirst(lc);
2880 
2881  if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
2882  {
2883  ereport(ERROR,
2884  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2885  errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
2886  NameStr(extForm->extname)),
2887  errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
2888  depextname,
2889  NameStr(extForm->extname))));
2890  }
2891  }
2892  }
2893 
2894  /*
2895  * Otherwise, ignore non-membership dependencies. (Currently, the
2896  * only other case we could see here is a normal dependency from
2897  * another extension.)
2898  */
2899  if (pg_depend->deptype != DEPENDENCY_EXTENSION)
2900  continue;
2901 
2902  dep.classId = pg_depend->classid;
2903  dep.objectId = pg_depend->objid;
2904  dep.objectSubId = pg_depend->objsubid;
2905 
2906  if (dep.objectSubId != 0) /* should not happen */
2907  elog(ERROR, "extension should not have a sub-object dependency");
2908 
2909  /* Relocate the object */
2910  dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
2911  dep.objectId,
2912  nspOid,
2913  objsMoved);
2914 
2915  /*
2916  * Remember previous namespace of first object that has one
2917  */
2918  if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid)
2919  oldNspOid = dep_oldNspOid;
2920 
2921  /*
2922  * If not all the objects had the same old namespace (ignoring any
2923  * that are not in namespaces), complain.
2924  */
2925  if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
2926  ereport(ERROR,
2927  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2928  errmsg("extension \"%s\" does not support SET SCHEMA",
2929  NameStr(extForm->extname)),
2930  errdetail("%s is not in the extension's schema \"%s\"",
2931  getObjectDescription(&dep, false),
2932  get_namespace_name(oldNspOid))));
2933  }
2934 
2935  /* report old schema, if caller wants it */
2936  if (oldschema)
2937  *oldschema = oldNspOid;
2938 
2939  systable_endscan(depScan);
2940 
2942 
2943  /* Now adjust pg_extension.extnamespace */
2944  extForm->extnamespace = nspOid;
2945 
2946  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2947 
2948  table_close(extRel, RowExclusiveLock);
2949 
2950  /* update dependencies to point to the new schema */
2951  changeDependencyFor(ExtensionRelationId, extensionOid,
2952  NamespaceRelationId, oldNspOid, nspOid);
2953 
2954  InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
2955 
2956  ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
2957 
2958  return extAddr;
2959 }
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2673
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3775
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:3976
Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: alter.c:617
#define NameStr(name)
Definition: c.h:730
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2532
@ DEPENDENCY_EXTENSION
Definition: dependency.h:38
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
static ExtensionControlFile * read_extension_control_file(const char *extname)
Definition: extension.c:647
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition: extension.c:144
char * get_extension_name(Oid ext_oid)
Definition: extension.c:189
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:599
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:506
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:387
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3324
Oid GetUserId(void)
Definition: miscinit.c:510
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2979
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
@ OBJECT_SCHEMA
Definition: parsenodes.h:2118
@ OBJECT_EXTENSION
Definition: parsenodes.h:2097
#define ACL_CREATE
Definition: parsenodes.h:92
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:456
Oid getExtensionOfObject(Oid classId, Oid objectId)
Definition: pg_depend.c:731
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
FormData_pg_extension * Form_pg_extension
Definition: pg_extension.h:52
#define lfirst(lc)
Definition: pg_list.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
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
ItemPointerData t_self
Definition: htup.h:65
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

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

Referenced by ExecAlterObjectSchemaStmt().

◆ CreateExtension()

ObjectAddress CreateExtension ( ParseState pstate,
CreateExtensionStmt stmt 
)

Definition at line 1745 of file extension.c.

1746 {
1747  DefElem *d_schema = NULL;
1748  DefElem *d_new_version = NULL;
1749  DefElem *d_cascade = NULL;
1750  char *schemaName = NULL;
1751  char *versionName = NULL;
1752  bool cascade = false;
1753  ListCell *lc;
1754 
1755  /* Check extension name validity before any filesystem access */
1756  check_valid_extension_name(stmt->extname);
1757 
1758  /*
1759  * Check for duplicate extension name. The unique index on
1760  * pg_extension.extname would catch this anyway, and serves as a backstop
1761  * in case of race conditions; but this is a friendlier error message, and
1762  * besides we need a check to support IF NOT EXISTS.
1763  */
1764  if (get_extension_oid(stmt->extname, true) != InvalidOid)
1765  {
1766  if (stmt->if_not_exists)
1767  {
1768  ereport(NOTICE,
1770  errmsg("extension \"%s\" already exists, skipping",
1771  stmt->extname)));
1772  return InvalidObjectAddress;
1773  }
1774  else
1775  ereport(ERROR,
1777  errmsg("extension \"%s\" already exists",
1778  stmt->extname)));
1779  }
1780 
1781  /*
1782  * We use global variables to track the extension being created, so we can
1783  * create only one extension at the same time.
1784  */
1785  if (creating_extension)
1786  ereport(ERROR,
1787  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1788  errmsg("nested CREATE EXTENSION is not supported")));
1789 
1790  /* Deconstruct the statement option list */
1791  foreach(lc, stmt->options)
1792  {
1793  DefElem *defel = (DefElem *) lfirst(lc);
1794 
1795  if (strcmp(defel->defname, "schema") == 0)
1796  {
1797  if (d_schema)
1798  errorConflictingDefElem(defel, pstate);
1799  d_schema = defel;
1800  schemaName = defGetString(d_schema);
1801  }
1802  else if (strcmp(defel->defname, "new_version") == 0)
1803  {
1804  if (d_new_version)
1805  errorConflictingDefElem(defel, pstate);
1806  d_new_version = defel;
1807  versionName = defGetString(d_new_version);
1808  }
1809  else if (strcmp(defel->defname, "cascade") == 0)
1810  {
1811  if (d_cascade)
1812  errorConflictingDefElem(defel, pstate);
1813  d_cascade = defel;
1814  cascade = defGetBoolean(d_cascade);
1815  }
1816  else
1817  elog(ERROR, "unrecognized option: %s", defel->defname);
1818  }
1819 
1820  /* Call CreateExtensionInternal to do the real work. */
1821  return CreateExtensionInternal(stmt->extname,
1822  schemaName,
1823  versionName,
1824  cascade,
1825  NIL,
1826  true);
1827 }
bool defGetBoolean(DefElem *def)
Definition: define.c:108
char * defGetString(DefElem *def)
Definition: define.c:49
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
Definition: define.c:385
#define NOTICE
Definition: elog.h:35
static void check_valid_extension_name(const char *extensionname)
Definition: extension.c:265
bool creating_extension
Definition: extension.c:73
static ObjectAddress CreateExtensionInternal(char *extensionName, char *schemaName, const char *versionName, bool cascade, List *parents, bool is_create)
Definition: extension.c:1435
#define stmt
Definition: indent_codes.h:59
#define NIL
Definition: pg_list.h:68
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
char * defname
Definition: parsenodes.h:810

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

Referenced by ProcessUtilitySlow().

◆ ExecAlterExtensionContentsStmt()

ObjectAddress ExecAlterExtensionContentsStmt ( AlterExtensionContentsStmt stmt,
ObjectAddress objAddr 
)

Definition at line 3270 of file extension.c.

3272 {
3273  ObjectAddress extension;
3274  ObjectAddress object;
3275  Relation relation;
3276  Oid oldExtension;
3277 
3278  switch (stmt->objtype)
3279  {
3280  case OBJECT_DATABASE:
3281  case OBJECT_EXTENSION:
3282  case OBJECT_INDEX:
3283  case OBJECT_PUBLICATION:
3284  case OBJECT_ROLE:
3285  case OBJECT_STATISTIC_EXT:
3286  case OBJECT_SUBSCRIPTION:
3287  case OBJECT_TABLESPACE:
3288  ereport(ERROR,
3289  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3290  errmsg("cannot add an object of this type to an extension")));
3291  break;
3292  default:
3293  /* OK */
3294  break;
3295  }
3296 
3297  /*
3298  * Find the extension and acquire a lock on it, to ensure it doesn't get
3299  * dropped concurrently. A sharable lock seems sufficient: there's no
3300  * reason not to allow other sorts of manipulations, such as add/drop of
3301  * other objects, to occur concurrently. Concurrently adding/dropping the
3302  * *same* object would be bad, but we prevent that by using a non-sharable
3303  * lock on the individual object, below.
3304  */
3306  (Node *) makeString(stmt->extname),
3307  &relation, AccessShareLock, false);
3308 
3309  /* Permission check: must own extension */
3310  if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
3312  stmt->extname);
3313 
3314  /*
3315  * Translate the parser representation that identifies the object into an
3316  * ObjectAddress. get_object_address() will throw an error if the object
3317  * does not exist, and will also acquire a lock on the object to guard
3318  * against concurrent DROP and ALTER EXTENSION ADD/DROP operations.
3319  */
3320  object = get_object_address(stmt->objtype, stmt->object,
3321  &relation, ShareUpdateExclusiveLock, false);
3322 
3323  Assert(object.objectSubId == 0);
3324  if (objAddr)
3325  *objAddr = object;
3326 
3327  /* Permission check: must own target object, too */
3328  check_object_ownership(GetUserId(), stmt->objtype, object,
3329  stmt->object, relation);
3330 
3331  /*
3332  * Check existing extension membership.
3333  */
3334  oldExtension = getExtensionOfObject(object.classId, object.objectId);
3335 
3336  if (stmt->action > 0)
3337  {
3338  /*
3339  * ADD, so complain if object is already attached to some extension.
3340  */
3341  if (OidIsValid(oldExtension))
3342  ereport(ERROR,
3343  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3344  errmsg("%s is already a member of extension \"%s\"",
3345  getObjectDescription(&object, false),
3346  get_extension_name(oldExtension))));
3347 
3348  /*
3349  * Prevent a schema from being added to an extension if the schema
3350  * contains the extension. That would create a dependency loop.
3351  */
3352  if (object.classId == NamespaceRelationId &&
3353  object.objectId == get_extension_schema(extension.objectId))
3354  ereport(ERROR,
3355  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3356  errmsg("cannot add schema \"%s\" to extension \"%s\" "
3357  "because the schema contains the extension",
3358  get_namespace_name(object.objectId),
3359  stmt->extname)));
3360 
3361  /*
3362  * OK, add the dependency.
3363  */
3364  recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
3365 
3366  /*
3367  * Also record the initial ACL on the object, if any.
3368  *
3369  * Note that this will handle the object's ACLs, as well as any ACLs
3370  * on object subIds. (In other words, when the object is a table,
3371  * this will record the table's ACL and the ACLs for the columns on
3372  * the table, if any).
3373  */
3374  recordExtObjInitPriv(object.objectId, object.classId);
3375  }
3376  else
3377  {
3378  /*
3379  * DROP, so complain if it's not a member.
3380  */
3381  if (oldExtension != extension.objectId)
3382  ereport(ERROR,
3383  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3384  errmsg("%s is not a member of extension \"%s\"",
3385  getObjectDescription(&object, false),
3386  stmt->extname)));
3387 
3388  /*
3389  * OK, drop the dependency.
3390  */
3391  if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3392  ExtensionRelationId,
3393  DEPENDENCY_EXTENSION) != 1)
3394  elog(ERROR, "unexpected number of extension dependency records");
3395 
3396  /*
3397  * If it's a relation, it might have an entry in the extension's
3398  * extconfig array, which we must remove.
3399  */
3400  if (object.classId == RelationRelationId)
3401  extension_config_remove(extension.objectId, object.objectId);
3402 
3403  /*
3404  * Remove all the initial ACLs, if any.
3405  *
3406  * Note that this will remove the object's ACLs, as well as any ACLs
3407  * on object subIds. (In other words, when the object is a table,
3408  * this will remove the table's ACL and the ACLs for the columns on
3409  * the table, if any).
3410  */
3411  removeExtObjInitPriv(object.objectId, object.classId);
3412  }
3413 
3414  InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
3415 
3416  /*
3417  * If get_object_address() opened the relation for us, we close it to keep
3418  * the reference count correct - but we retain any locks acquired by
3419  * get_object_address() until commit time, to guard against concurrent
3420  * activity.
3421  */
3422  if (relation != NULL)
3423  relation_close(relation, NoLock);
3424 
3425  return extension;
3426 }
void recordExtObjInitPriv(Oid objoid, Oid classoid)
Definition: aclchk.c:4234
void removeExtObjInitPriv(Oid objoid, Oid classoid)
Definition: aclchk.c:4397
#define OidIsValid(objectId)
Definition: c.h:759
Oid get_extension_schema(Oid ext_oid)
Definition: extension.c:228
static void extension_config_remove(Oid extensionoid, Oid tableoid)
Definition: extension.c:2584
Assert(fmt[strlen(fmt) - 1] !='\n')
#define NoLock
Definition: lockdefs.h:34
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
void check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, Node *object, Relation relation)
ObjectAddress get_object_address(ObjectType objtype, Node *object, Relation *relp, LOCKMODE lockmode, bool missing_ok)
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2124
@ OBJECT_ROLE
Definition: parsenodes.h:2115
@ OBJECT_INDEX
Definition: parsenodes.h:2102
@ OBJECT_DATABASE
Definition: parsenodes.h:2091
@ OBJECT_PUBLICATION
Definition: parsenodes.h:2112
@ OBJECT_SUBSCRIPTION
Definition: parsenodes.h:2120
@ OBJECT_STATISTIC_EXT
Definition: parsenodes.h:2121
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:350
Definition: nodes.h:129
String * makeString(char *str)
Definition: value.c:63

References AccessShareLock, aclcheck_error(), ACLCHECK_NOT_OWNER, Assert(), check_object_ownership(), deleteDependencyRecordsForClass(), DEPENDENCY_EXTENSION, elog(), ereport, errcode(), errmsg(), ERROR, extension_config_remove(), get_extension_name(), get_extension_schema(), get_namespace_name(), get_object_address(), getExtensionOfObject(), getObjectDescription(), GetUserId(), InvokeObjectPostAlterHook, makeString(), NoLock, OBJECT_DATABASE, OBJECT_EXTENSION, OBJECT_INDEX, object_ownercheck(), OBJECT_PUBLICATION, OBJECT_ROLE, OBJECT_STATISTIC_EXT, OBJECT_SUBSCRIPTION, OBJECT_TABLESPACE, ObjectAddress::objectId, OidIsValid, recordDependencyOn(), recordExtObjInitPriv(), relation_close(), removeExtObjInitPriv(), ShareUpdateExclusiveLock, and stmt.

Referenced by ProcessUtilitySlow().

◆ ExecAlterExtensionStmt()

ObjectAddress ExecAlterExtensionStmt ( ParseState pstate,
AlterExtensionStmt stmt 
)

Definition at line 2965 of file extension.c.

2966 {
2967  DefElem *d_new_version = NULL;
2968  char *versionName;
2969  char *oldVersionName;
2970  ExtensionControlFile *control;
2971  Oid extensionOid;
2972  Relation extRel;
2973  ScanKeyData key[1];
2974  SysScanDesc extScan;
2975  HeapTuple extTup;
2976  List *updateVersions;
2977  Datum datum;
2978  bool isnull;
2979  ListCell *lc;
2980  ObjectAddress address;
2981 
2982  /*
2983  * We use global variables to track the extension being created, so we can
2984  * create/update only one extension at the same time.
2985  */
2986  if (creating_extension)
2987  ereport(ERROR,
2988  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2989  errmsg("nested ALTER EXTENSION is not supported")));
2990 
2991  /*
2992  * Look up the extension --- it must already exist in pg_extension
2993  */
2994  extRel = table_open(ExtensionRelationId, AccessShareLock);
2995 
2996  ScanKeyInit(&key[0],
2997  Anum_pg_extension_extname,
2998  BTEqualStrategyNumber, F_NAMEEQ,
2999  CStringGetDatum(stmt->extname));
3000 
3001  extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
3002  NULL, 1, key);
3003 
3004  extTup = systable_getnext(extScan);
3005 
3006  if (!HeapTupleIsValid(extTup))
3007  ereport(ERROR,
3008  (errcode(ERRCODE_UNDEFINED_OBJECT),
3009  errmsg("extension \"%s\" does not exist",
3010  stmt->extname)));
3011 
3012  extensionOid = ((Form_pg_extension) GETSTRUCT(extTup))->oid;
3013 
3014  /*
3015  * Determine the existing version we are updating from
3016  */
3017  datum = heap_getattr(extTup, Anum_pg_extension_extversion,
3018  RelationGetDescr(extRel), &isnull);
3019  if (isnull)
3020  elog(ERROR, "extversion is null");
3021  oldVersionName = text_to_cstring(DatumGetTextPP(datum));
3022 
3023  systable_endscan(extScan);
3024 
3025  table_close(extRel, AccessShareLock);
3026 
3027  /* Permission check: must own extension */
3028  if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3030  stmt->extname);
3031 
3032  /*
3033  * Read the primary control file. Note we assume that it does not contain
3034  * any non-ASCII data, so there is no need to worry about encoding at this
3035  * point.
3036  */
3037  control = read_extension_control_file(stmt->extname);
3038 
3039  /*
3040  * Read the statement option list
3041  */
3042  foreach(lc, stmt->options)
3043  {
3044  DefElem *defel = (DefElem *) lfirst(lc);
3045 
3046  if (strcmp(defel->defname, "new_version") == 0)
3047  {
3048  if (d_new_version)
3049  errorConflictingDefElem(defel, pstate);
3050  d_new_version = defel;
3051  }
3052  else
3053  elog(ERROR, "unrecognized option: %s", defel->defname);
3054  }
3055 
3056  /*
3057  * Determine the version to update to
3058  */
3059  if (d_new_version && d_new_version->arg)
3060  versionName = strVal(d_new_version->arg);
3061  else if (control->default_version)
3062  versionName = control->default_version;
3063  else
3064  {
3065  ereport(ERROR,
3066  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3067  errmsg("version to install must be specified")));
3068  versionName = NULL; /* keep compiler quiet */
3069  }
3070  check_valid_version_name(versionName);
3071 
3072  /*
3073  * If we're already at that version, just say so
3074  */
3075  if (strcmp(oldVersionName, versionName) == 0)
3076  {
3077  ereport(NOTICE,
3078  (errmsg("version \"%s\" of extension \"%s\" is already installed",
3079  versionName, stmt->extname)));
3080  return InvalidObjectAddress;
3081  }
3082 
3083  /*
3084  * Identify the series of update script files we need to execute
3085  */
3086  updateVersions = identify_update_path(control,
3087  oldVersionName,
3088  versionName);
3089 
3090  /*
3091  * Update the pg_extension row and execute the update scripts, one at a
3092  * time
3093  */
3094  ApplyExtensionUpdates(extensionOid, control,
3095  oldVersionName, updateVersions,
3096  NULL, false, false);
3097 
3098  ObjectAddressSet(address, ExtensionRelationId, extensionOid);
3099 
3100  return address;
3101 }
static List * identify_update_path(ExtensionControlFile *control, const char *oldVersion, const char *newVersion)
Definition: extension.c:1244
static void check_valid_version_name(const char *versionname)
Definition: extension.c:312
static void ApplyExtensionUpdates(Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
Definition: extension.c:3112
#define DatumGetTextPP(X)
Definition: fmgr.h:292
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:792
uintptr_t Datum
Definition: postgres.h:64
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
#define RelationGetDescr(relation)
Definition: rel.h:530
Node * arg
Definition: parsenodes.h:811
char * default_version
Definition: extension.c:83
Definition: pg_list.h:54
#define strVal(v)
Definition: value.h:82
char * text_to_cstring(const text *t)
Definition: varlena.c:215

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

Referenced by ProcessUtilitySlow().

◆ extension_file_exists()

bool extension_file_exists ( const char *  extensionName)

Definition at line 2237 of file extension.c.

2238 {
2239  bool result = false;
2240  char *location;
2241  DIR *dir;
2242  struct dirent *de;
2243 
2244  location = get_extension_control_directory();
2245  dir = AllocateDir(location);
2246 
2247  /*
2248  * If the control directory doesn't exist, we want to silently return
2249  * false. Any other error will be reported by ReadDir.
2250  */
2251  if (dir == NULL && errno == ENOENT)
2252  {
2253  /* do nothing */
2254  }
2255  else
2256  {
2257  while ((de = ReadDir(dir, location)) != NULL)
2258  {
2259  char *extname;
2260 
2262  continue;
2263 
2264  /* extract extension name from 'name.control' filename */
2265  extname = pstrdup(de->d_name);
2266  *strrchr(extname, '.') = '\0';
2267 
2268  /* ignore it if it's an auxiliary control file */
2269  if (strstr(extname, "--"))
2270  continue;
2271 
2272  /* done if it matches request */
2273  if (strcmp(extname, extensionName) == 0)
2274  {
2275  result = true;
2276  break;
2277  }
2278  }
2279 
2280  FreeDir(dir);
2281  }
2282 
2283  return result;
2284 }
static bool is_extension_control_filename(const char *filename)
Definition: extension.c:359
static char * get_extension_control_directory(void)
Definition: extension.c:375
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2806
int FreeDir(DIR *dir)
Definition: fd.c:2858
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2740
char * pstrdup(const char *in)
Definition: mcxt.c:1644
Definition: dirent.c:26
Definition: dirent.h:10
char d_name[MAX_PATH]
Definition: dirent.h:15

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

Referenced by CreateFunction(), and ExecuteDoStmt().

◆ get_extension_name()

char* get_extension_name ( Oid  ext_oid)

Definition at line 189 of file extension.c.

190 {
191  char *result;
192  Relation rel;
193  SysScanDesc scandesc;
194  HeapTuple tuple;
195  ScanKeyData entry[1];
196 
197  rel = table_open(ExtensionRelationId, AccessShareLock);
198 
199  ScanKeyInit(&entry[0],
200  Anum_pg_extension_oid,
201  BTEqualStrategyNumber, F_OIDEQ,
202  ObjectIdGetDatum(ext_oid));
203 
204  scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
205  NULL, 1, entry);
206 
207  tuple = systable_getnext(scandesc);
208 
209  /* We assume that there can be at most one matching tuple */
210  if (HeapTupleIsValid(tuple))
211  result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
212  else
213  result = NULL;
214 
215  systable_endscan(scandesc);
216 
218 
219  return result;
220 }

References AccessShareLock, BTEqualStrategyNumber, GETSTRUCT, HeapTupleIsValid, NameStr, ObjectIdGetDatum(), pstrdup(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

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

◆ get_extension_oid()

Oid get_extension_oid ( const char *  extname,
bool  missing_ok 
)

Definition at line 144 of file extension.c.

145 {
146  Oid result;
147  Relation rel;
148  SysScanDesc scandesc;
149  HeapTuple tuple;
150  ScanKeyData entry[1];
151 
152  rel = table_open(ExtensionRelationId, AccessShareLock);
153 
154  ScanKeyInit(&entry[0],
155  Anum_pg_extension_extname,
156  BTEqualStrategyNumber, F_NAMEEQ,
157  CStringGetDatum(extname));
158 
159  scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
160  NULL, 1, entry);
161 
162  tuple = systable_getnext(scandesc);
163 
164  /* We assume that there can be at most one matching tuple */
165  if (HeapTupleIsValid(tuple))
166  result = ((Form_pg_extension) GETSTRUCT(tuple))->oid;
167  else
168  result = InvalidOid;
169 
170  systable_endscan(scandesc);
171 
173 
174  if (!OidIsValid(result) && !missing_ok)
175  ereport(ERROR,
176  (errcode(ERRCODE_UNDEFINED_OBJECT),
177  errmsg("extension \"%s\" does not exist",
178  extname)));
179 
180  return result;
181 }

References AccessShareLock, BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, InvalidOid, OidIsValid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

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

◆ get_extension_schema()

Oid get_extension_schema ( Oid  ext_oid)

Definition at line 228 of file extension.c.

229 {
230  Oid result;
231  Relation rel;
232  SysScanDesc scandesc;
233  HeapTuple tuple;
234  ScanKeyData entry[1];
235 
236  rel = table_open(ExtensionRelationId, AccessShareLock);
237 
238  ScanKeyInit(&entry[0],
239  Anum_pg_extension_oid,
240  BTEqualStrategyNumber, F_OIDEQ,
241  ObjectIdGetDatum(ext_oid));
242 
243  scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
244  NULL, 1, entry);
245 
246  tuple = systable_getnext(scandesc);
247 
248  /* We assume that there can be at most one matching tuple */
249  if (HeapTupleIsValid(tuple))
250  result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
251  else
252  result = InvalidOid;
253 
254  systable_endscan(scandesc);
255 
257 
258  return result;
259 }

References AccessShareLock, BTEqualStrategyNumber, GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

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

◆ InsertExtensionTuple()

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

Definition at line 1843 of file extension.c.

1847 {
1848  Oid extensionOid;
1849  Relation rel;
1850  Datum values[Natts_pg_extension];
1851  bool nulls[Natts_pg_extension];
1852  HeapTuple tuple;
1853  ObjectAddress myself;
1854  ObjectAddress nsp;
1855  ObjectAddresses *refobjs;
1856  ListCell *lc;
1857 
1858  /*
1859  * Build and insert the pg_extension tuple
1860  */
1861  rel = table_open(ExtensionRelationId, RowExclusiveLock);
1862 
1863  memset(values, 0, sizeof(values));
1864  memset(nulls, 0, sizeof(nulls));
1865 
1866  extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
1867  Anum_pg_extension_oid);
1868  values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
1869  values[Anum_pg_extension_extname - 1] =
1871  values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
1872  values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
1873  values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
1874  values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
1875 
1876  if (extConfig == PointerGetDatum(NULL))
1877  nulls[Anum_pg_extension_extconfig - 1] = true;
1878  else
1879  values[Anum_pg_extension_extconfig - 1] = extConfig;
1880 
1881  if (extCondition == PointerGetDatum(NULL))
1882  nulls[Anum_pg_extension_extcondition - 1] = true;
1883  else
1884  values[Anum_pg_extension_extcondition - 1] = extCondition;
1885 
1886  tuple = heap_form_tuple(rel->rd_att, values, nulls);
1887 
1888  CatalogTupleInsert(rel, tuple);
1889 
1890  heap_freetuple(tuple);
1892 
1893  /*
1894  * Record dependencies on owner, schema, and prerequisite extensions
1895  */
1896  recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
1897 
1898  refobjs = new_object_addresses();
1899 
1900  ObjectAddressSet(myself, ExtensionRelationId, extensionOid);
1901 
1902  ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
1903  add_exact_object_address(&nsp, refobjs);
1904 
1905  foreach(lc, requiredExtensions)
1906  {
1907  Oid reqext = lfirst_oid(lc);
1908  ObjectAddress otherext;
1909 
1910  ObjectAddressSet(otherext, ExtensionRelationId, reqext);
1911  add_exact_object_address(&otherext, refobjs);
1912  }
1913 
1914  /* Record all of them (this includes duplicate elimination) */
1916  free_object_addresses(refobjs);
1917 
1918  /* Post creation hook for new extension */
1919  InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
1920 
1921  return myself;
1922 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define CStringGetTextDatum(s)
Definition: builtins.h:94
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:393
void record_object_address_dependencies(const ObjectAddress *depender, ObjectAddresses *referenced, DependencyType behavior)
Definition: dependency.c:2790
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2581
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2821
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define lfirst_oid(lc)
Definition: pg_list.h:174
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:165
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
TupleDesc rd_att
Definition: rel.h:112

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

Referenced by binary_upgrade_create_empty_extension(), and CreateExtensionInternal().

◆ RemoveExtensionById()

void RemoveExtensionById ( Oid  extId)

Definition at line 1931 of file extension.c.

1932 {
1933  Relation rel;
1934  SysScanDesc scandesc;
1935  HeapTuple tuple;
1936  ScanKeyData entry[1];
1937 
1938  /*
1939  * Disallow deletion of any extension that's currently open for insertion;
1940  * else subsequent executions of recordDependencyOnCurrentExtension()
1941  * could create dangling pg_depend records that refer to a no-longer-valid
1942  * pg_extension OID. This is needed not so much because we think people
1943  * might write "DROP EXTENSION foo" in foo's own script files, as because
1944  * errors in dependency management in extension script files could give
1945  * rise to cases where an extension is dropped as a result of recursing
1946  * from some contained object. Because of that, we must test for the case
1947  * here, not at some higher level of the DROP EXTENSION command.
1948  */
1949  if (extId == CurrentExtensionObject)
1950  ereport(ERROR,
1951  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1952  errmsg("cannot drop extension \"%s\" because it is being modified",
1953  get_extension_name(extId))));
1954 
1955  rel = table_open(ExtensionRelationId, RowExclusiveLock);
1956 
1957  ScanKeyInit(&entry[0],
1958  Anum_pg_extension_oid,
1959  BTEqualStrategyNumber, F_OIDEQ,
1960  ObjectIdGetDatum(extId));
1961  scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
1962  NULL, 1, entry);
1963 
1964  tuple = systable_getnext(scandesc);
1965 
1966  /* We assume that there can be at most one matching tuple */
1967  if (HeapTupleIsValid(tuple))
1968  CatalogTupleDelete(rel, &tuple->t_self);
1969 
1970  systable_endscan(scandesc);
1971 
1973 }
Oid CurrentExtensionObject
Definition: extension.c:74
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365

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

Referenced by doDeletion().

Variable Documentation

◆ creating_extension

◆ CurrentExtensionObject