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

Go to the source code of this file.

Data Structures

struct  ExtensionControlFile
 
struct  ExtensionVersionInfo
 
struct  script_error_callback_arg
 

Typedefs

typedef struct ExtensionControlFile ExtensionControlFile
 
typedef struct ExtensionVersionInfo ExtensionVersionInfo
 

Functions

static Listfind_update_path (List *evi_list, ExtensionVersionInfo *evi_start, ExtensionVersionInfo *evi_target, bool reject_indirect, bool reinitialize)
 
static Oid get_required_extension (char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade, List *parents, bool is_create)
 
static void get_available_versions_for_extension (ExtensionControlFile *pcontrol, Tuplestorestate *tupstore, TupleDesc tupdesc)
 
static Datum convert_requires_to_datum (List *requires)
 
static void ApplyExtensionUpdates (Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
 
static void ExecAlterExtensionContentsRecurse (AlterExtensionContentsStmt *stmt, ObjectAddress extension, ObjectAddress object)
 
static char * read_whole_file (const char *filename, int *length)
 
Oid get_extension_oid (const char *extname, bool missing_ok)
 
char * get_extension_name (Oid ext_oid)
 
Oid get_extension_schema (Oid ext_oid)
 
static void check_valid_extension_name (const char *extensionname)
 
static void check_valid_version_name (const char *versionname)
 
static bool is_extension_control_filename (const char *filename)
 
static bool is_extension_script_filename (const char *filename)
 
static char * get_extension_control_directory (void)
 
static char * get_extension_control_filename (const char *extname)
 
static char * get_extension_script_directory (ExtensionControlFile *control)
 
static char * get_extension_aux_control_filename (ExtensionControlFile *control, const char *version)
 
static char * get_extension_script_filename (ExtensionControlFile *control, const char *from_version, const char *version)
 
static void parse_extension_control_file (ExtensionControlFile *control, const char *version)
 
static ExtensionControlFileread_extension_control_file (const char *extname)
 
static ExtensionControlFileread_extension_aux_control_file (const ExtensionControlFile *pcontrol, const char *version)
 
static char * read_extension_script_file (const ExtensionControlFile *control, const char *filename)
 
static void script_error_callback (void *arg)
 
static void execute_sql_string (const char *sql, const char *filename)
 
static bool extension_is_trusted (ExtensionControlFile *control)
 
static void execute_extension_script (Oid extensionOid, ExtensionControlFile *control, const char *from_version, const char *version, List *requiredSchemas, const char *schemaName, Oid schemaOid)
 
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)
 
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

bool creating_extension = false
 
Oid CurrentExtensionObject = InvalidOid
 

Typedef Documentation

◆ ExtensionControlFile

◆ ExtensionVersionInfo

Function Documentation

◆ AlterExtensionNamespace()

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

Definition at line 2883 of file extension.c.

2884 {
2885  Oid extensionOid;
2886  Oid nspOid;
2887  Oid oldNspOid;
2888  AclResult aclresult;
2889  Relation extRel;
2890  ScanKeyData key[2];
2891  SysScanDesc extScan;
2892  HeapTuple extTup;
2893  Form_pg_extension extForm;
2894  Relation depRel;
2895  SysScanDesc depScan;
2896  HeapTuple depTup;
2897  ObjectAddresses *objsMoved;
2898  ObjectAddress extAddr;
2899 
2900  extensionOid = get_extension_oid(extensionName, false);
2901 
2902  nspOid = LookupCreationNamespace(newschema);
2903 
2904  /*
2905  * Permission check: must own extension. Note that we don't bother to
2906  * check ownership of the individual member objects ...
2907  */
2908  if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
2910  extensionName);
2911 
2912  /* Permission check: must have creation rights in target namespace */
2913  aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
2914  if (aclresult != ACLCHECK_OK)
2915  aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
2916 
2917  /*
2918  * If the schema is currently a member of the extension, disallow moving
2919  * the extension into the schema. That would create a dependency loop.
2920  */
2921  if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
2922  ereport(ERROR,
2923  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2924  errmsg("cannot move extension \"%s\" into schema \"%s\" "
2925  "because the extension contains the schema",
2926  extensionName, newschema)));
2927 
2928  /* Locate the pg_extension tuple */
2929  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2930 
2931  ScanKeyInit(&key[0],
2932  Anum_pg_extension_oid,
2933  BTEqualStrategyNumber, F_OIDEQ,
2934  ObjectIdGetDatum(extensionOid));
2935 
2936  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2937  NULL, 1, key);
2938 
2939  extTup = systable_getnext(extScan);
2940 
2941  if (!HeapTupleIsValid(extTup)) /* should not happen */
2942  elog(ERROR, "could not find tuple for extension %u",
2943  extensionOid);
2944 
2945  /* Copy tuple so we can modify it below */
2946  extTup = heap_copytuple(extTup);
2947  extForm = (Form_pg_extension) GETSTRUCT(extTup);
2948 
2949  systable_endscan(extScan);
2950 
2951  /*
2952  * If the extension is already in the target schema, just silently do
2953  * nothing.
2954  */
2955  if (extForm->extnamespace == nspOid)
2956  {
2957  table_close(extRel, RowExclusiveLock);
2958  return InvalidObjectAddress;
2959  }
2960 
2961  /* Check extension is supposed to be relocatable */
2962  if (!extForm->extrelocatable)
2963  ereport(ERROR,
2964  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2965  errmsg("extension \"%s\" does not support SET SCHEMA",
2966  NameStr(extForm->extname))));
2967 
2968  objsMoved = new_object_addresses();
2969 
2970  /* store the OID of the namespace to-be-changed */
2971  oldNspOid = extForm->extnamespace;
2972 
2973  /*
2974  * Scan pg_depend to find objects that depend directly on the extension,
2975  * and alter each one's schema.
2976  */
2977  depRel = table_open(DependRelationId, AccessShareLock);
2978 
2979  ScanKeyInit(&key[0],
2980  Anum_pg_depend_refclassid,
2981  BTEqualStrategyNumber, F_OIDEQ,
2982  ObjectIdGetDatum(ExtensionRelationId));
2983  ScanKeyInit(&key[1],
2984  Anum_pg_depend_refobjid,
2985  BTEqualStrategyNumber, F_OIDEQ,
2986  ObjectIdGetDatum(extensionOid));
2987 
2988  depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
2989  NULL, 2, key);
2990 
2991  while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2992  {
2993  Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2994  ObjectAddress dep;
2995  Oid dep_oldNspOid;
2996 
2997  /*
2998  * If a dependent extension has a no_relocate request for this
2999  * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
3000  * the same loop that's actually executing the renames: we may detect
3001  * the error condition only after having expended a fair amount of
3002  * work. However, the alternative is to do two scans of pg_depend,
3003  * which seems like optimizing for failure cases. The rename work
3004  * will all roll back cleanly enough if we do fail here.)
3005  */
3006  if (pg_depend->deptype == DEPENDENCY_NORMAL &&
3007  pg_depend->classid == ExtensionRelationId)
3008  {
3009  char *depextname = get_extension_name(pg_depend->objid);
3010  ExtensionControlFile *dcontrol;
3011  ListCell *lc;
3012 
3013  dcontrol = read_extension_control_file(depextname);
3014  foreach(lc, dcontrol->no_relocate)
3015  {
3016  char *nrextname = (char *) lfirst(lc);
3017 
3018  if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
3019  {
3020  ereport(ERROR,
3021  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3022  errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
3023  NameStr(extForm->extname)),
3024  errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
3025  depextname,
3026  NameStr(extForm->extname))));
3027  }
3028  }
3029  }
3030 
3031  /*
3032  * Otherwise, ignore non-membership dependencies. (Currently, the
3033  * only other case we could see here is a normal dependency from
3034  * another extension.)
3035  */
3036  if (pg_depend->deptype != DEPENDENCY_EXTENSION)
3037  continue;
3038 
3039  dep.classId = pg_depend->classid;
3040  dep.objectId = pg_depend->objid;
3041  dep.objectSubId = pg_depend->objsubid;
3042 
3043  if (dep.objectSubId != 0) /* should not happen */
3044  elog(ERROR, "extension should not have a sub-object dependency");
3045 
3046  /* Relocate the object */
3047  dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
3048  dep.objectId,
3049  nspOid,
3050  objsMoved);
3051 
3052  /*
3053  * If not all the objects had the same old namespace (ignoring any
3054  * that are not in namespaces or are dependent types), complain.
3055  */
3056  if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
3057  ereport(ERROR,
3058  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3059  errmsg("extension \"%s\" does not support SET SCHEMA",
3060  NameStr(extForm->extname)),
3061  errdetail("%s is not in the extension's schema \"%s\"",
3062  getObjectDescription(&dep, false),
3063  get_namespace_name(oldNspOid))));
3064  }
3065 
3066  /* report old schema, if caller wants it */
3067  if (oldschema)
3068  *oldschema = oldNspOid;
3069 
3070  systable_endscan(depScan);
3071 
3073 
3074  /* Now adjust pg_extension.extnamespace */
3075  extForm->extnamespace = nspOid;
3076 
3077  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3078 
3079  table_close(extRel, RowExclusiveLock);
3080 
3081  /* update dependency to point to the new schema */
3082  if (changeDependencyFor(ExtensionRelationId, extensionOid,
3083  NamespaceRelationId, oldNspOid, nspOid) != 1)
3084  elog(ERROR, "could not change schema dependency for extension %s",
3085  NameStr(extForm->extname));
3086 
3087  InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3088 
3089  ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
3090 
3091  return extAddr;
3092 }
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:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3810
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4064
Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: alter.c:608
#define NameStr(name)
Definition: c.h:700
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2487
@ DEPENDENCY_EXTENSION
Definition: dependency.h:38
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
static ExtensionControlFile * read_extension_control_file(const char *extname)
Definition: extension.c:604
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition: extension.c:158
char * get_extension_name(Oid ext_oid)
Definition: extension.c:180
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:606
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:513
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:776
#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:3366
Oid GetUserId(void)
Definition: miscinit.c:524
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:3428
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
@ OBJECT_SCHEMA
Definition: parsenodes.h:2304
@ OBJECT_EXTENSION
Definition: parsenodes.h:2283
#define ACL_CREATE
Definition: parsenodes.h:85
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:457
Oid getExtensionOfObject(Oid classId, Oid objectId)
Definition: pg_depend.c:732
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
FormData_pg_extension * Form_pg_extension
Definition: pg_extension.h:52
#define lfirst(lc)
Definition: pg_list.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h: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:205
#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().

◆ ApplyExtensionUpdates()

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

Definition at line 3245 of file extension.c.

3252 {
3253  const char *oldVersionName = initialVersion;
3254  ListCell *lcv;
3255 
3256  foreach(lcv, updateVersions)
3257  {
3258  char *versionName = (char *) lfirst(lcv);
3259  ExtensionControlFile *control;
3260  char *schemaName;
3261  Oid schemaOid;
3262  List *requiredExtensions;
3263  List *requiredSchemas;
3264  Relation extRel;
3265  ScanKeyData key[1];
3266  SysScanDesc extScan;
3267  HeapTuple extTup;
3268  Form_pg_extension extForm;
3269  Datum values[Natts_pg_extension];
3270  bool nulls[Natts_pg_extension];
3271  bool repl[Natts_pg_extension];
3272  ObjectAddress myself;
3273  ListCell *lc;
3274 
3275  /*
3276  * Fetch parameters for specific version (pcontrol is not changed)
3277  */
3278  control = read_extension_aux_control_file(pcontrol, versionName);
3279 
3280  /* Find the pg_extension tuple */
3281  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3282 
3283  ScanKeyInit(&key[0],
3284  Anum_pg_extension_oid,
3285  BTEqualStrategyNumber, F_OIDEQ,
3286  ObjectIdGetDatum(extensionOid));
3287 
3288  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3289  NULL, 1, key);
3290 
3291  extTup = systable_getnext(extScan);
3292 
3293  if (!HeapTupleIsValid(extTup)) /* should not happen */
3294  elog(ERROR, "could not find tuple for extension %u",
3295  extensionOid);
3296 
3297  extForm = (Form_pg_extension) GETSTRUCT(extTup);
3298 
3299  /*
3300  * Determine the target schema (set by original install)
3301  */
3302  schemaOid = extForm->extnamespace;
3303  schemaName = get_namespace_name(schemaOid);
3304 
3305  /*
3306  * Modify extrelocatable and extversion in the pg_extension tuple
3307  */
3308  memset(values, 0, sizeof(values));
3309  memset(nulls, 0, sizeof(nulls));
3310  memset(repl, 0, sizeof(repl));
3311 
3312  values[Anum_pg_extension_extrelocatable - 1] =
3313  BoolGetDatum(control->relocatable);
3314  repl[Anum_pg_extension_extrelocatable - 1] = true;
3315  values[Anum_pg_extension_extversion - 1] =
3316  CStringGetTextDatum(versionName);
3317  repl[Anum_pg_extension_extversion - 1] = true;
3318 
3319  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3320  values, nulls, repl);
3321 
3322  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3323 
3324  systable_endscan(extScan);
3325 
3326  table_close(extRel, RowExclusiveLock);
3327 
3328  /*
3329  * Look up the prerequisite extensions for this version, install them
3330  * if necessary, and build lists of their OIDs and the OIDs of their
3331  * target schemas.
3332  */
3333  requiredExtensions = NIL;
3334  requiredSchemas = NIL;
3335  foreach(lc, control->requires)
3336  {
3337  char *curreq = (char *) lfirst(lc);
3338  Oid reqext;
3339  Oid reqschema;
3340 
3341  reqext = get_required_extension(curreq,
3342  control->name,
3343  origSchemaName,
3344  cascade,
3345  NIL,
3346  is_create);
3347  reqschema = get_extension_schema(reqext);
3348  requiredExtensions = lappend_oid(requiredExtensions, reqext);
3349  requiredSchemas = lappend_oid(requiredSchemas, reqschema);
3350  }
3351 
3352  /*
3353  * Remove and recreate dependencies on prerequisite extensions
3354  */
3355  deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
3356  ExtensionRelationId,
3358 
3359  myself.classId = ExtensionRelationId;
3360  myself.objectId = extensionOid;
3361  myself.objectSubId = 0;
3362 
3363  foreach(lc, requiredExtensions)
3364  {
3365  Oid reqext = lfirst_oid(lc);
3366  ObjectAddress otherext;
3367 
3368  otherext.classId = ExtensionRelationId;
3369  otherext.objectId = reqext;
3370  otherext.objectSubId = 0;
3371 
3372  recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
3373  }
3374 
3375  InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3376 
3377  /*
3378  * Finally, execute the update script file
3379  */
3380  execute_extension_script(extensionOid, control,
3381  oldVersionName, versionName,
3382  requiredSchemas,
3383  schemaName, schemaOid);
3384 
3385  /*
3386  * Update prior-version name and loop around. Since
3387  * execute_sql_string did a final CommandCounterIncrement, we can
3388  * update the pg_extension row again.
3389  */
3390  oldVersionName = versionName;
3391  }
3392 }
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define CStringGetTextDatum(s)
Definition: builtins.h:97
Oid get_extension_schema(Oid ext_oid)
Definition: extension.c:202
static Oid get_required_extension(char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade, List *parents, bool is_create)
Definition: extension.c:1808
static void execute_extension_script(Oid extensionOid, ExtensionControlFile *control, const char *from_version, const char *version, List *requiredSchemas, const char *schemaName, Oid schemaOid)
Definition: extension.c:981
static ExtensionControlFile * read_extension_aux_control_file(const ExtensionControlFile *pcontrol, const char *version)
Definition: extension.c:633
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1209
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
#define NIL
Definition: pg_list.h:68
#define lfirst_oid(lc)
Definition: pg_list.h:174
uintptr_t Datum
Definition: postgres.h:64
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
#define RelationGetDescr(relation)
Definition: rel.h:531
Definition: pg_list.h:54

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

Referenced by CreateExtensionInternal(), and ExecAlterExtensionStmt().

◆ check_valid_extension_name()

static void check_valid_extension_name ( const char *  extensionname)
static

Definition at line 222 of file extension.c.

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

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

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

◆ check_valid_version_name()

static void check_valid_version_name ( const char *  versionname)
static

Definition at line 269 of file extension.c.

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

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

Referenced by CreateExtensionInternal(), and ExecAlterExtensionStmt().

◆ convert_requires_to_datum()

static Datum convert_requires_to_datum ( List requires)
static

Definition at line 2424 of file extension.c.

2425 {
2426  Datum *datums;
2427  int ndatums;
2428  ArrayType *a;
2429  ListCell *lc;
2430 
2431  ndatums = list_length(requires);
2432  datums = (Datum *) palloc(ndatums * sizeof(Datum));
2433  ndatums = 0;
2434  foreach(lc, requires)
2435  {
2436  char *curreq = (char *) lfirst(lc);
2437 
2438  datums[ndatums++] =
2440  }
2441  a = construct_array_builtin(datums, ndatums, NAMEOID);
2442  return PointerGetDatum(a);
2443 }
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3381
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
int a
Definition: isn.c:68
void * palloc(Size size)
Definition: mcxt.c:1317
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
static int list_length(const List *l)
Definition: pg_list.h:152
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350

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

Referenced by get_available_versions_for_extension().

◆ CreateExtension()

ObjectAddress CreateExtension ( ParseState pstate,
CreateExtensionStmt stmt 
)

Definition at line 1879 of file extension.c.

1880 {
1881  DefElem *d_schema = NULL;
1882  DefElem *d_new_version = NULL;
1883  DefElem *d_cascade = NULL;
1884  char *schemaName = NULL;
1885  char *versionName = NULL;
1886  bool cascade = false;
1887  ListCell *lc;
1888 
1889  /* Check extension name validity before any filesystem access */
1890  check_valid_extension_name(stmt->extname);
1891 
1892  /*
1893  * Check for duplicate extension name. The unique index on
1894  * pg_extension.extname would catch this anyway, and serves as a backstop
1895  * in case of race conditions; but this is a friendlier error message, and
1896  * besides we need a check to support IF NOT EXISTS.
1897  */
1898  if (get_extension_oid(stmt->extname, true) != InvalidOid)
1899  {
1900  if (stmt->if_not_exists)
1901  {
1902  ereport(NOTICE,
1904  errmsg("extension \"%s\" already exists, skipping",
1905  stmt->extname)));
1906  return InvalidObjectAddress;
1907  }
1908  else
1909  ereport(ERROR,
1911  errmsg("extension \"%s\" already exists",
1912  stmt->extname)));
1913  }
1914 
1915  /*
1916  * We use global variables to track the extension being created, so we can
1917  * create only one extension at the same time.
1918  */
1919  if (creating_extension)
1920  ereport(ERROR,
1921  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1922  errmsg("nested CREATE EXTENSION is not supported")));
1923 
1924  /* Deconstruct the statement option list */
1925  foreach(lc, stmt->options)
1926  {
1927  DefElem *defel = (DefElem *) lfirst(lc);
1928 
1929  if (strcmp(defel->defname, "schema") == 0)
1930  {
1931  if (d_schema)
1932  errorConflictingDefElem(defel, pstate);
1933  d_schema = defel;
1934  schemaName = defGetString(d_schema);
1935  }
1936  else if (strcmp(defel->defname, "new_version") == 0)
1937  {
1938  if (d_new_version)
1939  errorConflictingDefElem(defel, pstate);
1940  d_new_version = defel;
1941  versionName = defGetString(d_new_version);
1942  }
1943  else if (strcmp(defel->defname, "cascade") == 0)
1944  {
1945  if (d_cascade)
1946  errorConflictingDefElem(defel, pstate);
1947  d_cascade = defel;
1948  cascade = defGetBoolean(d_cascade);
1949  }
1950  else
1951  elog(ERROR, "unrecognized option: %s", defel->defname);
1952  }
1953 
1954  /* Call CreateExtensionInternal to do the real work. */
1955  return CreateExtensionInternal(stmt->extname,
1956  schemaName,
1957  versionName,
1958  cascade,
1959  NIL,
1960  true);
1961 }
bool defGetBoolean(DefElem *def)
Definition: define.c:107
char * defGetString(DefElem *def)
Definition: define.c:48
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
Definition: define.c:384
#define NOTICE
Definition: elog.h:35
static void check_valid_extension_name(const char *extensionname)
Definition: extension.c:222
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:1569
#define stmt
Definition: indent_codes.h:59
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
char * defname
Definition: parsenodes.h:817

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

Referenced by ProcessUtilitySlow().

◆ CreateExtensionInternal()

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

Definition at line 1569 of file extension.c.

1575 {
1576  char *origSchemaName = schemaName;
1577  Oid schemaOid = InvalidOid;
1578  Oid extowner = GetUserId();
1579  ExtensionControlFile *pcontrol;
1580  ExtensionControlFile *control;
1581  char *filename;
1582  struct stat fst;
1583  List *updateVersions;
1584  List *requiredExtensions;
1585  List *requiredSchemas;
1586  Oid extensionOid;
1587  ObjectAddress address;
1588  ListCell *lc;
1589 
1590  /*
1591  * Read the primary control file. Note we assume that it does not contain
1592  * any non-ASCII data, so there is no need to worry about encoding at this
1593  * point.
1594  */
1595  pcontrol = read_extension_control_file(extensionName);
1596 
1597  /*
1598  * Determine the version to install
1599  */
1600  if (versionName == NULL)
1601  {
1602  if (pcontrol->default_version)
1603  versionName = pcontrol->default_version;
1604  else
1605  ereport(ERROR,
1606  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1607  errmsg("version to install must be specified")));
1608  }
1609  check_valid_version_name(versionName);
1610 
1611  /*
1612  * Figure out which script(s) we need to run to install the desired
1613  * version of the extension. If we do not have a script that directly
1614  * does what is needed, we try to find a sequence of update scripts that
1615  * will get us there.
1616  */
1617  filename = get_extension_script_filename(pcontrol, NULL, versionName);
1618  if (stat(filename, &fst) == 0)
1619  {
1620  /* Easy, no extra scripts */
1621  updateVersions = NIL;
1622  }
1623  else
1624  {
1625  /* Look for best way to install this version */
1626  List *evi_list;
1627  ExtensionVersionInfo *evi_start;
1628  ExtensionVersionInfo *evi_target;
1629 
1630  /* Extract the version update graph from the script directory */
1631  evi_list = get_ext_ver_list(pcontrol);
1632 
1633  /* Identify the target version */
1634  evi_target = get_ext_ver_info(versionName, &evi_list);
1635 
1636  /* Identify best path to reach target */
1637  evi_start = find_install_path(evi_list, evi_target,
1638  &updateVersions);
1639 
1640  /* Fail if no path ... */
1641  if (evi_start == NULL)
1642  ereport(ERROR,
1643  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1644  errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
1645  pcontrol->name, versionName)));
1646 
1647  /* Otherwise, install best starting point and then upgrade */
1648  versionName = evi_start->name;
1649  }
1650 
1651  /*
1652  * Fetch control parameters for installation target version
1653  */
1654  control = read_extension_aux_control_file(pcontrol, versionName);
1655 
1656  /*
1657  * Determine the target schema to install the extension into
1658  */
1659  if (schemaName)
1660  {
1661  /* If the user is giving us the schema name, it must exist already. */
1662  schemaOid = get_namespace_oid(schemaName, false);
1663  }
1664 
1665  if (control->schema != NULL)
1666  {
1667  /*
1668  * The extension is not relocatable and the author gave us a schema
1669  * for it.
1670  *
1671  * Unless CASCADE parameter was given, it's an error to give a schema
1672  * different from control->schema if control->schema is specified.
1673  */
1674  if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1675  !cascade)
1676  ereport(ERROR,
1677  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1678  errmsg("extension \"%s\" must be installed in schema \"%s\"",
1679  control->name,
1680  control->schema)));
1681 
1682  /* Always use the schema from control file for current extension. */
1683  schemaName = control->schema;
1684 
1685  /* Find or create the schema in case it does not exist. */
1686  schemaOid = get_namespace_oid(schemaName, true);
1687 
1688  if (!OidIsValid(schemaOid))
1689  {
1691 
1692  csstmt->schemaname = schemaName;
1693  csstmt->authrole = NULL; /* will be created by current user */
1694  csstmt->schemaElts = NIL;
1695  csstmt->if_not_exists = false;
1696  CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
1697  -1, -1);
1698 
1699  /*
1700  * CreateSchemaCommand includes CommandCounterIncrement, so new
1701  * schema is now visible.
1702  */
1703  schemaOid = get_namespace_oid(schemaName, false);
1704  }
1705  }
1706  else if (!OidIsValid(schemaOid))
1707  {
1708  /*
1709  * Neither user nor author of the extension specified schema; use the
1710  * current default creation namespace, which is the first explicit
1711  * entry in the search_path.
1712  */
1713  List *search_path = fetch_search_path(false);
1714 
1715  if (search_path == NIL) /* nothing valid in search_path? */
1716  ereport(ERROR,
1717  (errcode(ERRCODE_UNDEFINED_SCHEMA),
1718  errmsg("no schema has been selected to create in")));
1719  schemaOid = linitial_oid(search_path);
1720  schemaName = get_namespace_name(schemaOid);
1721  if (schemaName == NULL) /* recently-deleted namespace? */
1722  ereport(ERROR,
1723  (errcode(ERRCODE_UNDEFINED_SCHEMA),
1724  errmsg("no schema has been selected to create in")));
1725 
1726  list_free(search_path);
1727  }
1728 
1729  /*
1730  * Make note if a temporary namespace has been accessed in this
1731  * transaction.
1732  */
1733  if (isTempNamespace(schemaOid))
1735 
1736  /*
1737  * We don't check creation rights on the target namespace here. If the
1738  * extension script actually creates any objects there, it will fail if
1739  * the user doesn't have such permissions. But there are cases such as
1740  * procedural languages where it's convenient to set schema = pg_catalog
1741  * yet we don't want to restrict the command to users with ACL_CREATE for
1742  * pg_catalog.
1743  */
1744 
1745  /*
1746  * Look up the prerequisite extensions, install them if necessary, and
1747  * build lists of their OIDs and the OIDs of their target schemas.
1748  */
1749  requiredExtensions = NIL;
1750  requiredSchemas = NIL;
1751  foreach(lc, control->requires)
1752  {
1753  char *curreq = (char *) lfirst(lc);
1754  Oid reqext;
1755  Oid reqschema;
1756 
1757  reqext = get_required_extension(curreq,
1758  extensionName,
1759  origSchemaName,
1760  cascade,
1761  parents,
1762  is_create);
1763  reqschema = get_extension_schema(reqext);
1764  requiredExtensions = lappend_oid(requiredExtensions, reqext);
1765  requiredSchemas = lappend_oid(requiredSchemas, reqschema);
1766  }
1767 
1768  /*
1769  * Insert new tuple into pg_extension, and create dependency entries.
1770  */
1771  address = InsertExtensionTuple(control->name, extowner,
1772  schemaOid, control->relocatable,
1773  versionName,
1774  PointerGetDatum(NULL),
1775  PointerGetDatum(NULL),
1776  requiredExtensions);
1777  extensionOid = address.objectId;
1778 
1779  /*
1780  * Apply any control-file comment on extension
1781  */
1782  if (control->comment != NULL)
1783  CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
1784 
1785  /*
1786  * Execute the installation script file
1787  */
1788  execute_extension_script(extensionOid, control,
1789  NULL, versionName,
1790  requiredSchemas,
1791  schemaName, schemaOid);
1792 
1793  /*
1794  * If additional update scripts have to be executed, apply the updates as
1795  * though a series of ALTER EXTENSION UPDATE commands were given
1796  */
1797  ApplyExtensionUpdates(extensionOid, pcontrol,
1798  versionName, updateVersions,
1799  origSchemaName, cascade, is_create);
1800 
1801  return address;
1802 }
#define OidIsValid(objectId)
Definition: c.h:729
void CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
Definition: comment.c:143
static ExtensionVersionInfo * get_ext_ver_info(const char *versionname, List **evi_list)
Definition: extension.c:1254
ObjectAddress InsertExtensionTuple(const char *extName, Oid extOwner, Oid schemaOid, bool relocatable, const char *extVersion, Datum extConfig, Datum extCondition, List *requiredExtensions)
Definition: extension.c:1977
static void check_valid_version_name(const char *versionname)
Definition: extension.c:269
static char * get_extension_script_filename(ExtensionControlFile *control, const char *from_version, const char *version)
Definition: extension.c:400
static List * get_ext_ver_list(ExtensionControlFile *control)
Definition: extension.c:1315
static ExtensionVersionInfo * find_install_path(List *evi_list, ExtensionVersionInfo *evi_target, List **best_path)
Definition: extension.c:1514
static void ApplyExtensionUpdates(Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
Definition: extension.c:3245
void list_free(List *list)
Definition: list.c:1546
bool isTempNamespace(Oid namespaceId)
Definition: namespace.c:3649
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4819
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition: namespace.c:3535
#define makeNode(_type_)
Definition: nodes.h:155
static char * filename
Definition: pg_dumpall.c:119
#define linitial_oid(l)
Definition: pg_list.h:180
Oid CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString, int stmt_location, int stmt_len)
Definition: schemacmds.c:52
RoleSpec * authrole
Definition: parsenodes.h:2334
char * default_version
Definition: extension.c:83
#define stat
Definition: win32_port.h:284
int MyXactFlags
Definition: xact.c:135
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102

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

Referenced by CreateExtension(), and get_required_extension().

◆ ExecAlterExtensionContentsRecurse()

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

Definition at line 3489 of file extension.c.

3492 {
3493  Oid oldExtension;
3494 
3495  /*
3496  * Check existing extension membership.
3497  */
3498  oldExtension = getExtensionOfObject(object.classId, object.objectId);
3499 
3500  if (stmt->action > 0)
3501  {
3502  /*
3503  * ADD, so complain if object is already attached to some extension.
3504  */
3505  if (OidIsValid(oldExtension))
3506  ereport(ERROR,
3507  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3508  errmsg("%s is already a member of extension \"%s\"",
3509  getObjectDescription(&object, false),
3510  get_extension_name(oldExtension))));
3511 
3512  /*
3513  * Prevent a schema from being added to an extension if the schema
3514  * contains the extension. That would create a dependency loop.
3515  */
3516  if (object.classId == NamespaceRelationId &&
3517  object.objectId == get_extension_schema(extension.objectId))
3518  ereport(ERROR,
3519  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3520  errmsg("cannot add schema \"%s\" to extension \"%s\" "
3521  "because the schema contains the extension",
3522  get_namespace_name(object.objectId),
3523  stmt->extname)));
3524 
3525  /*
3526  * OK, add the dependency.
3527  */
3528  recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
3529 
3530  /*
3531  * Also record the initial ACL on the object, if any.
3532  *
3533  * Note that this will handle the object's ACLs, as well as any ACLs
3534  * on object subIds. (In other words, when the object is a table,
3535  * this will record the table's ACL and the ACLs for the columns on
3536  * the table, if any).
3537  */
3538  recordExtObjInitPriv(object.objectId, object.classId);
3539  }
3540  else
3541  {
3542  /*
3543  * DROP, so complain if it's not a member.
3544  */
3545  if (oldExtension != extension.objectId)
3546  ereport(ERROR,
3547  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3548  errmsg("%s is not a member of extension \"%s\"",
3549  getObjectDescription(&object, false),
3550  stmt->extname)));
3551 
3552  /*
3553  * OK, drop the dependency.
3554  */
3555  if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3556  ExtensionRelationId,
3557  DEPENDENCY_EXTENSION) != 1)
3558  elog(ERROR, "unexpected number of extension dependency records");
3559 
3560  /*
3561  * If it's a relation, it might have an entry in the extension's
3562  * extconfig array, which we must remove.
3563  */
3564  if (object.classId == RelationRelationId)
3565  extension_config_remove(extension.objectId, object.objectId);
3566 
3567  /*
3568  * Remove all the initial ACLs, if any.
3569  *
3570  * Note that this will remove the object's ACLs, as well as any ACLs
3571  * on object subIds. (In other words, when the object is a table,
3572  * this will remove the table's ACL and the ACLs for the columns on
3573  * the table, if any).
3574  */
3575  removeExtObjInitPriv(object.objectId, object.classId);
3576  }
3577 
3578  /*
3579  * Recurse to any dependent objects; currently, this includes the array
3580  * type of a base type, the multirange type associated with a range type,
3581  * and the rowtype of a table.
3582  */
3583  if (object.classId == TypeRelationId)
3584  {
3585  ObjectAddress depobject;
3586 
3587  depobject.classId = TypeRelationId;
3588  depobject.objectSubId = 0;
3589 
3590  /* If it has an array type, update that too */
3591  depobject.objectId = get_array_type(object.objectId);
3592  if (OidIsValid(depobject.objectId))
3593  ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3594 
3595  /* If it is a range type, update the associated multirange too */
3596  if (type_is_range(object.objectId))
3597  {
3598  depobject.objectId = get_range_multirange(object.objectId);
3599  if (!OidIsValid(depobject.objectId))
3600  ereport(ERROR,
3601  (errcode(ERRCODE_UNDEFINED_OBJECT),
3602  errmsg("could not find multirange type for data type %s",
3603  format_type_be(object.objectId))));
3604  ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3605  }
3606  }
3607  if (object.classId == RelationRelationId)
3608  {
3609  ObjectAddress depobject;
3610 
3611  depobject.classId = TypeRelationId;
3612  depobject.objectSubId = 0;
3613 
3614  /* It might not have a rowtype, but if it does, update that */
3615  depobject.objectId = get_rel_type_id(object.objectId);
3616  if (OidIsValid(depobject.objectId))
3617  ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3618  }
3619 }
void recordExtObjInitPriv(Oid objoid, Oid classoid)
Definition: aclchk.c:4326
void removeExtObjInitPriv(Oid objoid, Oid classoid)
Definition: aclchk.c:4490
static void extension_config_remove(Oid extensionoid, Oid tableoid)
Definition: extension.c:2718
static void ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt, ObjectAddress extension, ObjectAddress object)
Definition: extension.c:3489
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
bool type_is_range(Oid typid)
Definition: lsyscache.c:2688
Oid get_rel_type_id(Oid relid)
Definition: lsyscache.c:1979
Oid get_range_multirange(Oid rangeOid)
Definition: lsyscache.c:3458
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2787

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

Referenced by ExecAlterExtensionContentsStmt().

◆ ExecAlterExtensionContentsStmt()

ObjectAddress ExecAlterExtensionContentsStmt ( AlterExtensionContentsStmt stmt,
ObjectAddress objAddr 
)

Definition at line 3403 of file extension.c.

3405 {
3406  ObjectAddress extension;
3407  ObjectAddress object;
3408  Relation relation;
3409 
3410  switch (stmt->objtype)
3411  {
3412  case OBJECT_DATABASE:
3413  case OBJECT_EXTENSION:
3414  case OBJECT_INDEX:
3415  case OBJECT_PUBLICATION:
3416  case OBJECT_ROLE:
3417  case OBJECT_STATISTIC_EXT:
3418  case OBJECT_SUBSCRIPTION:
3419  case OBJECT_TABLESPACE:
3420  ereport(ERROR,
3421  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3422  errmsg("cannot add an object of this type to an extension")));
3423  break;
3424  default:
3425  /* OK */
3426  break;
3427  }
3428 
3429  /*
3430  * Find the extension and acquire a lock on it, to ensure it doesn't get
3431  * dropped concurrently. A sharable lock seems sufficient: there's no
3432  * reason not to allow other sorts of manipulations, such as add/drop of
3433  * other objects, to occur concurrently. Concurrently adding/dropping the
3434  * *same* object would be bad, but we prevent that by using a non-sharable
3435  * lock on the individual object, below.
3436  */
3438  (Node *) makeString(stmt->extname),
3439  &relation, AccessShareLock, false);
3440 
3441  /* Permission check: must own extension */
3442  if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
3444  stmt->extname);
3445 
3446  /*
3447  * Translate the parser representation that identifies the object into an
3448  * ObjectAddress. get_object_address() will throw an error if the object
3449  * does not exist, and will also acquire a lock on the object to guard
3450  * against concurrent DROP and ALTER EXTENSION ADD/DROP operations.
3451  */
3452  object = get_object_address(stmt->objtype, stmt->object,
3453  &relation, ShareUpdateExclusiveLock, false);
3454 
3455  Assert(object.objectSubId == 0);
3456  if (objAddr)
3457  *objAddr = object;
3458 
3459  /* Permission check: must own target object, too */
3460  check_object_ownership(GetUserId(), stmt->objtype, object,
3461  stmt->object, relation);
3462 
3463  /* Do the update, recursing to any dependent objects */
3464  ExecAlterExtensionContentsRecurse(stmt, extension, object);
3465 
3466  /* Finish up */
3467  InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
3468 
3469  /*
3470  * If get_object_address() opened the relation for us, we close it to keep
3471  * the reference count correct - but we retain any locks acquired by
3472  * get_object_address() until commit time, to guard against concurrent
3473  * activity.
3474  */
3475  if (relation != NULL)
3476  relation_close(relation, NoLock);
3477 
3478  return extension;
3479 }
#define Assert(condition)
Definition: c.h:812
#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:2310
@ OBJECT_ROLE
Definition: parsenodes.h:2301
@ OBJECT_INDEX
Definition: parsenodes.h:2288
@ OBJECT_DATABASE
Definition: parsenodes.h:2277
@ OBJECT_PUBLICATION
Definition: parsenodes.h:2298
@ OBJECT_SUBSCRIPTION
Definition: parsenodes.h:2306
@ OBJECT_STATISTIC_EXT
Definition: parsenodes.h:2307
Definition: nodes.h:129
String * makeString(char *str)
Definition: value.c:63

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

Referenced by ProcessUtilitySlow().

◆ ExecAlterExtensionStmt()

ObjectAddress ExecAlterExtensionStmt ( ParseState pstate,
AlterExtensionStmt stmt 
)

Definition at line 3098 of file extension.c.

3099 {
3100  DefElem *d_new_version = NULL;
3101  char *versionName;
3102  char *oldVersionName;
3103  ExtensionControlFile *control;
3104  Oid extensionOid;
3105  Relation extRel;
3106  ScanKeyData key[1];
3107  SysScanDesc extScan;
3108  HeapTuple extTup;
3109  List *updateVersions;
3110  Datum datum;
3111  bool isnull;
3112  ListCell *lc;
3113  ObjectAddress address;
3114 
3115  /*
3116  * We use global variables to track the extension being created, so we can
3117  * create/update only one extension at the same time.
3118  */
3119  if (creating_extension)
3120  ereport(ERROR,
3121  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3122  errmsg("nested ALTER EXTENSION is not supported")));
3123 
3124  /*
3125  * Look up the extension --- it must already exist in pg_extension
3126  */
3127  extRel = table_open(ExtensionRelationId, AccessShareLock);
3128 
3129  ScanKeyInit(&key[0],
3130  Anum_pg_extension_extname,
3131  BTEqualStrategyNumber, F_NAMEEQ,
3132  CStringGetDatum(stmt->extname));
3133 
3134  extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
3135  NULL, 1, key);
3136 
3137  extTup = systable_getnext(extScan);
3138 
3139  if (!HeapTupleIsValid(extTup))
3140  ereport(ERROR,
3141  (errcode(ERRCODE_UNDEFINED_OBJECT),
3142  errmsg("extension \"%s\" does not exist",
3143  stmt->extname)));
3144 
3145  extensionOid = ((Form_pg_extension) GETSTRUCT(extTup))->oid;
3146 
3147  /*
3148  * Determine the existing version we are updating from
3149  */
3150  datum = heap_getattr(extTup, Anum_pg_extension_extversion,
3151  RelationGetDescr(extRel), &isnull);
3152  if (isnull)
3153  elog(ERROR, "extversion is null");
3154  oldVersionName = text_to_cstring(DatumGetTextPP(datum));
3155 
3156  systable_endscan(extScan);
3157 
3158  table_close(extRel, AccessShareLock);
3159 
3160  /* Permission check: must own extension */
3161  if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3163  stmt->extname);
3164 
3165  /*
3166  * Read the primary control file. Note we assume that it does not contain
3167  * any non-ASCII data, so there is no need to worry about encoding at this
3168  * point.
3169  */
3170  control = read_extension_control_file(stmt->extname);
3171 
3172  /*
3173  * Read the statement option list
3174  */
3175  foreach(lc, stmt->options)
3176  {
3177  DefElem *defel = (DefElem *) lfirst(lc);
3178 
3179  if (strcmp(defel->defname, "new_version") == 0)
3180  {
3181  if (d_new_version)
3182  errorConflictingDefElem(defel, pstate);
3183  d_new_version = defel;
3184  }
3185  else
3186  elog(ERROR, "unrecognized option: %s", defel->defname);
3187  }
3188 
3189  /*
3190  * Determine the version to update to
3191  */
3192  if (d_new_version && d_new_version->arg)
3193  versionName = strVal(d_new_version->arg);
3194  else if (control->default_version)
3195  versionName = control->default_version;
3196  else
3197  {
3198  ereport(ERROR,
3199  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3200  errmsg("version to install must be specified")));
3201  versionName = NULL; /* keep compiler quiet */
3202  }
3203  check_valid_version_name(versionName);
3204 
3205  /*
3206  * If we're already at that version, just say so
3207  */
3208  if (strcmp(oldVersionName, versionName) == 0)
3209  {
3210  ereport(NOTICE,
3211  (errmsg("version \"%s\" of extension \"%s\" is already installed",
3212  versionName, stmt->extname)));
3213  return InvalidObjectAddress;
3214  }
3215 
3216  /*
3217  * Identify the series of update script files we need to execute
3218  */
3219  updateVersions = identify_update_path(control,
3220  oldVersionName,
3221  versionName);
3222 
3223  /*
3224  * Update the pg_extension row and execute the update scripts, one at a
3225  * time
3226  */
3227  ApplyExtensionUpdates(extensionOid, control,
3228  oldVersionName, updateVersions,
3229  NULL, false, false);
3230 
3231  ObjectAddressSet(address, ExtensionRelationId, extensionOid);
3232 
3233  return address;
3234 }
static List * identify_update_path(ExtensionControlFile *control, const char *oldVersion, const char *newVersion)
Definition: extension.c:1378
#define DatumGetTextPP(X)
Definition: fmgr.h:292
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:792
Node * arg
Definition: parsenodes.h:818
#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(), DefElem::arg, BTEqualStrategyNumber, check_valid_version_name(), creating_extension, CStringGetDatum(), DatumGetTextPP, ExtensionControlFile::default_version, DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, errorConflictingDefElem(), GETSTRUCT, GetUserId(), heap_getattr(), HeapTupleIsValid, identify_update_path(), InvalidObjectAddress, sort-test::key, lfirst, NOTICE, OBJECT_EXTENSION, object_ownercheck(), ObjectAddressSet, read_extension_control_file(), RelationGetDescr, ScanKeyInit(), stmt, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and text_to_cstring().

Referenced by ProcessUtilitySlow().

◆ execute_extension_script()

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

Definition at line 981 of file extension.c.

986 {
987  bool switch_to_superuser = false;
988  char *filename;
989  Oid save_userid = 0;
990  int save_sec_context = 0;
991  int save_nestlevel;
992  StringInfoData pathbuf;
993  ListCell *lc;
994  ListCell *lc2;
995 
996  /*
997  * Enforce superuser-ness if appropriate. We postpone these checks until
998  * here so that the control flags are correctly associated with the right
999  * script(s) if they happen to be set in secondary control files.
1000  */
1001  if (control->superuser && !superuser())
1002  {
1003  if (extension_is_trusted(control))
1004  switch_to_superuser = true;
1005  else if (from_version == NULL)
1006  ereport(ERROR,
1007  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1008  errmsg("permission denied to create extension \"%s\"",
1009  control->name),
1010  control->trusted
1011  ? errhint("Must have CREATE privilege on current database to create this extension.")
1012  : errhint("Must be superuser to create this extension.")));
1013  else
1014  ereport(ERROR,
1015  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1016  errmsg("permission denied to update extension \"%s\"",
1017  control->name),
1018  control->trusted
1019  ? errhint("Must have CREATE privilege on current database to update this extension.")
1020  : errhint("Must be superuser to update this extension.")));
1021  }
1022 
1023  filename = get_extension_script_filename(control, from_version, version);
1024 
1025  if (from_version == NULL)
1026  elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
1027  else
1028  elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
1029 
1030  /*
1031  * If installing a trusted extension on behalf of a non-superuser, become
1032  * the bootstrap superuser. (This switch will be cleaned up automatically
1033  * if the transaction aborts, as will the GUC changes below.)
1034  */
1035  if (switch_to_superuser)
1036  {
1037  GetUserIdAndSecContext(&save_userid, &save_sec_context);
1038  SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID,
1039  save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
1040  }
1041 
1042  /*
1043  * Force client_min_messages and log_min_messages to be at least WARNING,
1044  * so that we won't spam the user with useless NOTICE messages from common
1045  * script actions like creating shell types.
1046  *
1047  * We use the equivalent of a function SET option to allow the setting to
1048  * persist for exactly the duration of the script execution. guc.c also
1049  * takes care of undoing the setting on error.
1050  *
1051  * log_min_messages can't be set by ordinary users, so for that one we
1052  * pretend to be superuser.
1053  */
1054  save_nestlevel = NewGUCNestLevel();
1055 
1057  (void) set_config_option("client_min_messages", "warning",
1059  GUC_ACTION_SAVE, true, 0, false);
1060  if (log_min_messages < WARNING)
1061  (void) set_config_option_ext("log_min_messages", "warning",
1063  BOOTSTRAP_SUPERUSERID,
1064  GUC_ACTION_SAVE, true, 0, false);
1065 
1066  /*
1067  * Similarly disable check_function_bodies, to ensure that SQL functions
1068  * won't be parsed during creation.
1069  */
1071  (void) set_config_option("check_function_bodies", "off",
1073  GUC_ACTION_SAVE, true, 0, false);
1074 
1075  /*
1076  * Set up the search path to have the target schema first, making it be
1077  * the default creation target namespace. Then add the schemas of any
1078  * prerequisite extensions, unless they are in pg_catalog which would be
1079  * searched anyway. (Listing pg_catalog explicitly in a non-first
1080  * position would be bad for security.) Finally add pg_temp to ensure
1081  * that temp objects can't take precedence over others.
1082  */
1083  initStringInfo(&pathbuf);
1084  appendStringInfoString(&pathbuf, quote_identifier(schemaName));
1085  foreach(lc, requiredSchemas)
1086  {
1087  Oid reqschema = lfirst_oid(lc);
1088  char *reqname = get_namespace_name(reqschema);
1089 
1090  if (reqname && strcmp(reqname, "pg_catalog") != 0)
1091  appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
1092  }
1093  appendStringInfoString(&pathbuf, ", pg_temp");
1094 
1095  (void) set_config_option("search_path", pathbuf.data,
1097  GUC_ACTION_SAVE, true, 0, false);
1098 
1099  /*
1100  * Set creating_extension and related variables so that
1101  * recordDependencyOnCurrentExtension and other functions do the right
1102  * things. On failure, ensure we reset these variables.
1103  */
1104  creating_extension = true;
1105  CurrentExtensionObject = extensionOid;
1106  PG_TRY();
1107  {
1108  char *c_sql = read_extension_script_file(control, filename);
1109  Datum t_sql;
1110 
1111  /*
1112  * We filter each substitution through quote_identifier(). When the
1113  * arg contains one of the following characters, no one collection of
1114  * quoting can work inside $$dollar-quoted string literals$$,
1115  * 'single-quoted string literals', and outside of any literal. To
1116  * avoid a security snare for extension authors, error on substitution
1117  * for arguments containing these.
1118  */
1119  const char *quoting_relevant_chars = "\"$'\\";
1120 
1121  /* We use various functions that want to operate on text datums */
1122  t_sql = CStringGetTextDatum(c_sql);
1123 
1124  /*
1125  * Reduce any lines beginning with "\echo" to empty. This allows
1126  * scripts to contain messages telling people not to run them via
1127  * psql, which has been found to be necessary due to old habits.
1128  */
1130  C_COLLATION_OID,
1131  t_sql,
1132  CStringGetTextDatum("^\\\\echo.*$"),
1133  CStringGetTextDatum(""),
1134  CStringGetTextDatum("ng"));
1135 
1136  /*
1137  * If the script uses @extowner@, substitute the calling username.
1138  */
1139  if (strstr(c_sql, "@extowner@"))
1140  {
1141  Oid uid = switch_to_superuser ? save_userid : GetUserId();
1142  const char *userName = GetUserNameFromId(uid, false);
1143  const char *qUserName = quote_identifier(userName);
1144 
1146  C_COLLATION_OID,
1147  t_sql,
1148  CStringGetTextDatum("@extowner@"),
1149  CStringGetTextDatum(qUserName));
1150  if (strpbrk(userName, quoting_relevant_chars))
1151  ereport(ERROR,
1152  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1153  errmsg("invalid character in extension owner: must not contain any of \"%s\"",
1154  quoting_relevant_chars)));
1155  }
1156 
1157  /*
1158  * If it's not relocatable, substitute the target schema name for
1159  * occurrences of @extschema@.
1160  *
1161  * For a relocatable extension, we needn't do this. There cannot be
1162  * any need for @extschema@, else it wouldn't be relocatable.
1163  */
1164  if (!control->relocatable)
1165  {
1166  Datum old = t_sql;
1167  const char *qSchemaName = quote_identifier(schemaName);
1168 
1170  C_COLLATION_OID,
1171  t_sql,
1172  CStringGetTextDatum("@extschema@"),
1173  CStringGetTextDatum(qSchemaName));
1174  if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
1175  ereport(ERROR,
1176  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1177  errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1178  control->name, quoting_relevant_chars)));
1179  }
1180 
1181  /*
1182  * Likewise, substitute required extensions' schema names for
1183  * occurrences of @extschema:extension_name@.
1184  */
1185  Assert(list_length(control->requires) == list_length(requiredSchemas));
1186  forboth(lc, control->requires, lc2, requiredSchemas)
1187  {
1188  Datum old = t_sql;
1189  char *reqextname = (char *) lfirst(lc);
1190  Oid reqschema = lfirst_oid(lc2);
1191  char *schemaName = get_namespace_name(reqschema);
1192  const char *qSchemaName = quote_identifier(schemaName);
1193  char *repltoken;
1194 
1195  repltoken = psprintf("@extschema:%s@", reqextname);
1197  C_COLLATION_OID,
1198  t_sql,
1199  CStringGetTextDatum(repltoken),
1200  CStringGetTextDatum(qSchemaName));
1201  if (t_sql != old && strpbrk(schemaName, quoting_relevant_chars))
1202  ereport(ERROR,
1203  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1204  errmsg("invalid character in extension \"%s\" schema: must not contain any of \"%s\"",
1205  reqextname, quoting_relevant_chars)));
1206  }
1207 
1208  /*
1209  * If module_pathname was set in the control file, substitute its
1210  * value for occurrences of MODULE_PATHNAME.
1211  */
1212  if (control->module_pathname)
1213  {
1215  C_COLLATION_OID,
1216  t_sql,
1217  CStringGetTextDatum("MODULE_PATHNAME"),
1219  }
1220 
1221  /* And now back to C string */
1222  c_sql = text_to_cstring(DatumGetTextPP(t_sql));
1223 
1224  execute_sql_string(c_sql, filename);
1225  }
1226  PG_FINALLY();
1227  {
1228  creating_extension = false;
1230  }
1231  PG_END_TRY();
1232 
1233  /*
1234  * Restore the GUC variables we set above.
1235  */
1236  AtEOXact_GUC(true, save_nestlevel);
1237 
1238  /*
1239  * Restore authentication state if needed.
1240  */
1241  if (switch_to_superuser)
1242  SetUserIdAndSecContext(save_userid, save_sec_context);
1243 }
int errhint(const char *fmt,...)
Definition: elog.c:1317
#define PG_TRY(...)
Definition: elog.h:371
#define WARNING
Definition: elog.h:36
#define PG_END_TRY(...)
Definition: elog.h:396
#define DEBUG1
Definition: elog.h:30
#define PG_FINALLY(...)
Definition: elog.h:388
static char * read_extension_script_file(const ExtensionControlFile *control, const char *filename)
Definition: extension.c:656
static void execute_sql_string(const char *sql, const char *filename)
Definition: extension.c:831
Oid CurrentExtensionObject
Definition: extension.c:74
static bool extension_is_trusted(ExtensionControlFile *control)
Definition: extension.c:959
Datum DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4)
Definition: fmgr.c:859
Datum DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:834
int set_config_option_ext(const char *name, const char *value, GucContext context, GucSource source, Oid srole, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3382
int NewGUCNestLevel(void)
Definition: guc.c:2235
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2262
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3342
@ GUC_ACTION_SAVE
Definition: guc.h:201
@ PGC_S_SESSION
Definition: guc.h:122
@ PGC_SUSET
Definition: guc.h:74
@ PGC_USERSET
Definition: guc.h:75
bool check_function_bodies
Definition: guc_tables.c:511
int client_min_messages
Definition: guc_tables.c:523
int log_min_messages
Definition: guc_tables.c:522
#define SECURITY_LOCAL_USERID_CHANGE
Definition: miscadmin.h:311
char * GetUserNameFromId(Oid roleid, bool noerr)
Definition: miscinit.c:1041
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:667
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:674
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
Datum textregexreplace(PG_FUNCTION_ARGS)
Definition: regexp.c:658
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:12868
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:94
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:179
void initStringInfo(StringInfo str)
Definition: stringinfo.c:56
char * module_pathname
Definition: extension.c:84
bool superuser(void)
Definition: superuser.c:46
Datum replace_text(PG_FUNCTION_ARGS)
Definition: varlena.c:4009

References appendStringInfo(), appendStringInfoString(), Assert, AtEOXact_GUC(), check_function_bodies, client_min_messages, creating_extension, CStringGetTextDatum, CurrentExtensionObject, StringInfoData::data, DatumGetTextPP, DEBUG1, DirectFunctionCall3Coll(), DirectFunctionCall4Coll(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, execute_sql_string(), extension_is_trusted(), filename, forboth, get_extension_script_filename(), get_namespace_name(), GetUserId(), GetUserIdAndSecContext(), GetUserNameFromId(), GUC_ACTION_SAVE, initStringInfo(), InvalidOid, lfirst, lfirst_oid, list_length(), log_min_messages, ExtensionControlFile::module_pathname, ExtensionControlFile::name, NewGUCNestLevel(), PG_END_TRY, PG_FINALLY, PG_TRY, PGC_S_SESSION, PGC_SUSET, PGC_USERSET, psprintf(), quote_identifier(), read_extension_script_file(), ExtensionControlFile::relocatable, replace_text(), ExtensionControlFile::requires, 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 831 of file extension.c.

832 {
833  script_error_callback_arg callback_arg;
834  ErrorContextCallback scripterrcontext;
835  List *raw_parsetree_list;
837  ListCell *lc1;
838 
839  /*
840  * Setup error traceback support for ereport().
841  */
842  callback_arg.sql = sql;
843  callback_arg.filename = filename;
844  callback_arg.stmt_location = -1;
845  callback_arg.stmt_len = -1;
846 
847  scripterrcontext.callback = script_error_callback;
848  scripterrcontext.arg = (void *) &callback_arg;
849  scripterrcontext.previous = error_context_stack;
850  error_context_stack = &scripterrcontext;
851 
852  /*
853  * Parse the SQL string into a list of raw parse trees.
854  */
855  raw_parsetree_list = pg_parse_query(sql);
856 
857  /* All output from SELECTs goes to the bit bucket */
859 
860  /*
861  * Do parse analysis, rule rewrite, planning, and execution for each raw
862  * parsetree. We must fully execute each query before beginning parse
863  * analysis on the next one, since there may be interdependencies.
864  */
865  foreach(lc1, raw_parsetree_list)
866  {
867  RawStmt *parsetree = lfirst_node(RawStmt, lc1);
868  MemoryContext per_parsetree_context,
869  oldcontext;
870  List *stmt_list;
871  ListCell *lc2;
872 
873  /* Report location of this query for error context callback */
874  callback_arg.stmt_location = parsetree->stmt_location;
875  callback_arg.stmt_len = parsetree->stmt_len;
876 
877  /*
878  * We do the work for each parsetree in a short-lived context, to
879  * limit the memory used when there are many commands in the string.
880  */
881  per_parsetree_context =
883  "execute_sql_string per-statement context",
885  oldcontext = MemoryContextSwitchTo(per_parsetree_context);
886 
887  /* Be sure parser can see any DDL done so far */
889 
890  stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
891  sql,
892  NULL,
893  0,
894  NULL);
895  stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
896 
897  foreach(lc2, stmt_list)
898  {
900 
902 
904 
905  if (stmt->utilityStmt == NULL)
906  {
907  QueryDesc *qdesc;
908 
909  qdesc = CreateQueryDesc(stmt,
910  sql,
911  GetActiveSnapshot(), NULL,
912  dest, NULL, NULL, 0);
913 
914  ExecutorStart(qdesc, 0);
915  ExecutorRun(qdesc, ForwardScanDirection, 0, true);
916  ExecutorFinish(qdesc);
917  ExecutorEnd(qdesc);
918 
919  FreeQueryDesc(qdesc);
920  }
921  else
922  {
923  if (IsA(stmt->utilityStmt, TransactionStmt))
924  ereport(ERROR,
925  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
926  errmsg("transaction control statements are not allowed within an extension script")));
927 
929  sql,
930  false,
932  NULL,
933  NULL,
934  dest,
935  NULL);
936  }
937 
939  }
940 
941  /* Clean up per-parsetree context. */
942  MemoryContextSwitchTo(oldcontext);
943  MemoryContextDelete(per_parsetree_context);
944  }
945 
946  error_context_stack = scripterrcontext.previous;
947 
948  /* Be sure to advance the command counter after the last script command */
950 }
DestReceiver * CreateDestReceiver(CommandDest dest)
Definition: dest.c:113
@ DestNone
Definition: dest.h:87
ErrorContextCallback * error_context_stack
Definition: elog.c:94
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:466
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:406
void ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:121
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:296
static void script_error_callback(void *arg)
Definition: extension.c:689
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define CURSOR_OPT_PARALLEL_OK
Definition: parsenodes.h:3317
#define lfirst_node(type, lc)
Definition: pg_list.h:176
List * pg_parse_query(const char *query_string)
Definition: postgres.c:614
List * pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char *query_string, const Oid *paramTypes, int numParams, QueryEnvironment *queryEnv)
Definition: postgres.c:676
List * pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:981
void FreeQueryDesc(QueryDesc *qdesc)
Definition: pquery.c:105
QueryDesc * CreateQueryDesc(PlannedStmt *plannedstmt, const char *sourceText, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, ParamListInfo params, QueryEnvironment *queryEnv, int instrument_options)
Definition: pquery.c:67
MemoryContextSwitchTo(old_ctx)
@ ForwardScanDirection
Definition: sdir.h:28
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:216
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:648
void PopActiveSnapshot(void)
Definition: snapmgr.c:743
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:770
struct ErrorContextCallback * previous
Definition: elog.h:296
void(* callback)(void *arg)
Definition: elog.h:297
ParseLoc stmt_location
Definition: parsenodes.h:2023
ParseLoc stmt_len
Definition: parsenodes.h:2024
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:499
@ PROCESS_UTILITY_QUERY
Definition: utility.h:23
void CommandCounterIncrement(void)
Definition: xact.c:1099

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

Referenced by execute_extension_script().

◆ extension_config_remove()

static void extension_config_remove ( Oid  extensionoid,
Oid  tableoid 
)
static

Definition at line 2718 of file extension.c.

2719 {
2720  Relation extRel;
2721  ScanKeyData key[1];
2722  SysScanDesc extScan;
2723  HeapTuple extTup;
2724  Datum arrayDatum;
2725  int arrayLength;
2726  int arrayIndex;
2727  bool isnull;
2728  Datum repl_val[Natts_pg_extension];
2729  bool repl_null[Natts_pg_extension];
2730  bool repl_repl[Natts_pg_extension];
2731  ArrayType *a;
2732 
2733  /* Find the pg_extension tuple */
2734  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2735 
2736  ScanKeyInit(&key[0],
2737  Anum_pg_extension_oid,
2738  BTEqualStrategyNumber, F_OIDEQ,
2739  ObjectIdGetDatum(extensionoid));
2740 
2741  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2742  NULL, 1, key);
2743 
2744  extTup = systable_getnext(extScan);
2745 
2746  if (!HeapTupleIsValid(extTup)) /* should not happen */
2747  elog(ERROR, "could not find tuple for extension %u",
2748  extensionoid);
2749 
2750  /* Search extconfig for the tableoid */
2751  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2752  RelationGetDescr(extRel), &isnull);
2753  if (isnull)
2754  {
2755  /* nothing to do */
2756  a = NULL;
2757  arrayLength = 0;
2758  arrayIndex = -1;
2759  }
2760  else
2761  {
2762  Oid *arrayData;
2763  int i;
2764 
2765  a = DatumGetArrayTypeP(arrayDatum);
2766 
2767  arrayLength = ARR_DIMS(a)[0];
2768  if (ARR_NDIM(a) != 1 ||
2769  ARR_LBOUND(a)[0] != 1 ||
2770  arrayLength < 0 ||
2771  ARR_HASNULL(a) ||
2772  ARR_ELEMTYPE(a) != OIDOID)
2773  elog(ERROR, "extconfig is not a 1-D Oid array");
2774  arrayData = (Oid *) ARR_DATA_PTR(a);
2775 
2776  arrayIndex = -1; /* flag for no deletion needed */
2777 
2778  for (i = 0; i < arrayLength; i++)
2779  {
2780  if (arrayData[i] == tableoid)
2781  {
2782  arrayIndex = i; /* index to remove */
2783  break;
2784  }
2785  }
2786  }
2787 
2788  /* If tableoid is not in extconfig, nothing to do */
2789  if (arrayIndex < 0)
2790  {
2791  systable_endscan(extScan);
2792  table_close(extRel, RowExclusiveLock);
2793  return;
2794  }
2795 
2796  /* Modify or delete the extconfig value */
2797  memset(repl_val, 0, sizeof(repl_val));
2798  memset(repl_null, false, sizeof(repl_null));
2799  memset(repl_repl, false, sizeof(repl_repl));
2800 
2801  if (arrayLength <= 1)
2802  {
2803  /* removing only element, just set array to null */
2804  repl_null[Anum_pg_extension_extconfig - 1] = true;
2805  }
2806  else
2807  {
2808  /* squeeze out the target element */
2809  Datum *dvalues;
2810  int nelems;
2811  int i;
2812 
2813  /* We already checked there are no nulls */
2814  deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
2815 
2816  for (i = arrayIndex; i < arrayLength - 1; i++)
2817  dvalues[i] = dvalues[i + 1];
2818 
2819  a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
2820 
2821  repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2822  }
2823  repl_repl[Anum_pg_extension_extconfig - 1] = true;
2824 
2825  /* Modify or delete the extcondition value */
2826  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2827  RelationGetDescr(extRel), &isnull);
2828  if (isnull)
2829  {
2830  elog(ERROR, "extconfig and extcondition arrays do not match");
2831  }
2832  else
2833  {
2834  a = DatumGetArrayTypeP(arrayDatum);
2835 
2836  if (ARR_NDIM(a) != 1 ||
2837  ARR_LBOUND(a)[0] != 1 ||
2838  ARR_HASNULL(a) ||
2839  ARR_ELEMTYPE(a) != TEXTOID)
2840  elog(ERROR, "extcondition is not a 1-D text array");
2841  if (ARR_DIMS(a)[0] != arrayLength)
2842  elog(ERROR, "extconfig and extcondition arrays do not match");
2843  }
2844 
2845  if (arrayLength <= 1)
2846  {
2847  /* removing only element, just set array to null */
2848  repl_null[Anum_pg_extension_extcondition - 1] = true;
2849  }
2850  else
2851  {
2852  /* squeeze out the target element */
2853  Datum *dvalues;
2854  int nelems;
2855  int i;
2856 
2857  /* We already checked there are no nulls */
2858  deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
2859 
2860  for (i = arrayIndex; i < arrayLength - 1; i++)
2861  dvalues[i] = dvalues[i + 1];
2862 
2863  a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
2864 
2865  repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2866  }
2867  repl_repl[Anum_pg_extension_extcondition - 1] = true;
2868 
2869  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2870  repl_val, repl_null, repl_repl);
2871 
2872  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2873 
2874  systable_endscan(extScan);
2875 
2876  table_close(extRel, RowExclusiveLock);
2877 }
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291
#define ARR_LBOUND(a)
Definition: array.h:296
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3697
int i
Definition: isn.c:72

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

Referenced by ExecAlterExtensionContentsRecurse().

◆ extension_file_exists()

bool extension_file_exists ( const char *  extensionName)

Definition at line 2371 of file extension.c.

2372 {
2373  bool result = false;
2374  char *location;
2375  DIR *dir;
2376  struct dirent *de;
2377 
2378  location = get_extension_control_directory();
2379  dir = AllocateDir(location);
2380 
2381  /*
2382  * If the control directory doesn't exist, we want to silently return
2383  * false. Any other error will be reported by ReadDir.
2384  */
2385  if (dir == NULL && errno == ENOENT)
2386  {
2387  /* do nothing */
2388  }
2389  else
2390  {
2391  while ((de = ReadDir(dir, location)) != NULL)
2392  {
2393  char *extname;
2394 
2396  continue;
2397 
2398  /* extract extension name from 'name.control' filename */
2399  extname = pstrdup(de->d_name);
2400  *strrchr(extname, '.') = '\0';
2401 
2402  /* ignore it if it's an auxiliary control file */
2403  if (strstr(extname, "--"))
2404  continue;
2405 
2406  /* done if it matches request */
2407  if (strcmp(extname, extensionName) == 0)
2408  {
2409  result = true;
2410  break;
2411  }
2412  }
2413 
2414  FreeDir(dir);
2415  }
2416 
2417  return result;
2418 }
static bool is_extension_control_filename(const char *filename)
Definition: extension.c:316
static char * get_extension_control_directory(void)
Definition: extension.c:332
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2931
int FreeDir(DIR *dir)
Definition: fd.c:2983
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2865
char * pstrdup(const char *in)
Definition: mcxt.c:1696
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().

◆ extension_is_trusted()

static bool extension_is_trusted ( ExtensionControlFile control)
static

Definition at line 959 of file extension.c.

960 {
961  AclResult aclresult;
962 
963  /* Never trust unless extension's control file says it's okay */
964  if (!control->trusted)
965  return false;
966  /* Allow if user has CREATE privilege on current database */
967  aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
968  if (aclresult == ACLCHECK_OK)
969  return true;
970  return false;
971 }
Oid MyDatabaseId
Definition: globals.c:93

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

Referenced by execute_extension_script().

◆ find_install_path()

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

Definition at line 1514 of file extension.c.

1516 {
1517  ExtensionVersionInfo *evi_start = NULL;
1518  ListCell *lc;
1519 
1520  *best_path = NIL;
1521 
1522  /*
1523  * We don't expect to be called for an installable target, but if we are,
1524  * the answer is easy: just start from there, with an empty update path.
1525  */
1526  if (evi_target->installable)
1527  return evi_target;
1528 
1529  /* Consider all installable versions as start points */
1530  foreach(lc, evi_list)
1531  {
1533  List *path;
1534 
1535  if (!evi1->installable)
1536  continue;
1537 
1538  /*
1539  * Find shortest path from evi1 to evi_target; but no need to consider
1540  * paths going through other installable versions.
1541  */
1542  path = find_update_path(evi_list, evi1, evi_target, true, true);
1543  if (path == NIL)
1544  continue;
1545 
1546  /* Remember best path */
1547  if (evi_start == NULL ||
1548  list_length(path) < list_length(*best_path) ||
1549  (list_length(path) == list_length(*best_path) &&
1550  strcmp(evi_start->name, evi1->name) < 0))
1551  {
1552  evi_start = evi1;
1553  *best_path = path;
1554  }
1555  }
1556 
1557  return evi_start;
1558 }
static List * find_update_path(List *evi_list, ExtensionVersionInfo *evi_start, ExtensionVersionInfo *evi_target, bool reject_indirect, bool reinitialize)
Definition: extension.c:1421

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

Referenced by CreateExtensionInternal(), and get_available_versions_for_extension().

◆ find_update_path()

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

Definition at line 1421 of file extension.c.

1426 {
1427  List *result;
1428  ExtensionVersionInfo *evi;
1429  ListCell *lc;
1430 
1431  /* Caller error if start == target */
1432  Assert(evi_start != evi_target);
1433  /* Caller error if reject_indirect and target is installable */
1434  Assert(!(reject_indirect && evi_target->installable));
1435 
1436  if (reinitialize)
1437  {
1438  foreach(lc, evi_list)
1439  {
1440  evi = (ExtensionVersionInfo *) lfirst(lc);
1441  evi->distance_known = false;
1442  evi->distance = INT_MAX;
1443  evi->previous = NULL;
1444  }
1445  }
1446 
1447  evi_start->distance = 0;
1448 
1449  while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
1450  {
1451  if (evi->distance == INT_MAX)
1452  break; /* all remaining vertices are unreachable */
1453  evi->distance_known = true;
1454  if (evi == evi_target)
1455  break; /* found shortest path to target */
1456  foreach(lc, evi->reachable)
1457  {
1459  int newdist;
1460 
1461  /* if reject_indirect, treat installable versions as unreachable */
1462  if (reject_indirect && evi2->installable)
1463  continue;
1464  newdist = evi->distance + 1;
1465  if (newdist < evi2->distance)
1466  {
1467  evi2->distance = newdist;
1468  evi2->previous = evi;
1469  }
1470  else if (newdist == evi2->distance &&
1471  evi2->previous != NULL &&
1472  strcmp(evi->name, evi2->previous->name) < 0)
1473  {
1474  /*
1475  * Break ties in favor of the version name that comes first
1476  * according to strcmp(). This behavior is undocumented and
1477  * users shouldn't rely on it. We do it just to ensure that
1478  * if there is a tie, the update path that is chosen does not
1479  * depend on random factors like the order in which directory
1480  * entries get visited.
1481  */
1482  evi2->previous = evi;
1483  }
1484  }
1485  }
1486 
1487  /* Return NIL if target is not reachable from start */
1488  if (!evi_target->distance_known)
1489  return NIL;
1490 
1491  /* Build and return list of version names representing the update path */
1492  result = NIL;
1493  for (evi = evi_target; evi != evi_start; evi = evi->previous)
1494  result = lcons(evi->name, result);
1495 
1496  return result;
1497 }
static ExtensionVersionInfo * get_nearest_unprocessed_vertex(List *evi_list)
Definition: extension.c:1287
List * lcons(void *datum, List *list)
Definition: list.c:495
struct ExtensionVersionInfo * previous
Definition: extension.c:108

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

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

◆ get_available_versions_for_extension()

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

Definition at line 2257 of file extension.c.

2260 {
2261  List *evi_list;
2262  ListCell *lc;
2263 
2264  /* Extract the version update graph from the script directory */
2265  evi_list = get_ext_ver_list(pcontrol);
2266 
2267  /* For each installable version ... */
2268  foreach(lc, evi_list)
2269  {
2271  ExtensionControlFile *control;
2272  Datum values[8];
2273  bool nulls[8];
2274  ListCell *lc2;
2275 
2276  if (!evi->installable)
2277  continue;
2278 
2279  /*
2280  * Fetch parameters for specific version (pcontrol is not changed)
2281  */
2282  control = read_extension_aux_control_file(pcontrol, evi->name);
2283 
2284  memset(values, 0, sizeof(values));
2285  memset(nulls, 0, sizeof(nulls));
2286 
2287  /* name */
2289  CStringGetDatum(control->name));
2290  /* version */
2291  values[1] = CStringGetTextDatum(evi->name);
2292  /* superuser */
2293  values[2] = BoolGetDatum(control->superuser);
2294  /* trusted */
2295  values[3] = BoolGetDatum(control->trusted);
2296  /* relocatable */
2297  values[4] = BoolGetDatum(control->relocatable);
2298  /* schema */
2299  if (control->schema == NULL)
2300  nulls[5] = true;
2301  else
2303  CStringGetDatum(control->schema));
2304  /* requires */
2305  if (control->requires == NIL)
2306  nulls[6] = true;
2307  else
2308  values[6] = convert_requires_to_datum(control->requires);
2309  /* comment */
2310  if (control->comment == NULL)
2311  nulls[7] = true;
2312  else
2313  values[7] = CStringGetTextDatum(control->comment);
2314 
2315  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2316 
2317  /*
2318  * Find all non-directly-installable versions that would be installed
2319  * starting from this version, and report them, inheriting the
2320  * parameters that aren't changed in updates from this version.
2321  */
2322  foreach(lc2, evi_list)
2323  {
2325  List *best_path;
2326 
2327  if (evi2->installable)
2328  continue;
2329  if (find_install_path(evi_list, evi2, &best_path) == evi)
2330  {
2331  /*
2332  * Fetch parameters for this version (pcontrol is not changed)
2333  */
2334  control = read_extension_aux_control_file(pcontrol, evi2->name);
2335 
2336  /* name stays the same */
2337  /* version */
2338  values[1] = CStringGetTextDatum(evi2->name);
2339  /* superuser */
2340  values[2] = BoolGetDatum(control->superuser);
2341  /* trusted */
2342  values[3] = BoolGetDatum(control->trusted);
2343  /* relocatable */
2344  values[4] = BoolGetDatum(control->relocatable);
2345  /* schema stays the same */
2346  /* requires */
2347  if (control->requires == NIL)
2348  nulls[6] = true;
2349  else
2350  {
2351  values[6] = convert_requires_to_datum(control->requires);
2352  nulls[6] = false;
2353  }
2354  /* comment stays the same */
2355 
2356  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2357  }
2358  }
2359  }
2360 }
static Datum convert_requires_to_datum(List *requires)
Definition: extension.c:2424
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:784

References BoolGetDatum(), ExtensionControlFile::comment, convert_requires_to_datum(), CStringGetDatum(), CStringGetTextDatum, DirectFunctionCall1, find_install_path(), get_ext_ver_list(), ExtensionVersionInfo::installable, lfirst, ExtensionControlFile::name, ExtensionVersionInfo::name, namein(), NIL, read_extension_aux_control_file(), ExtensionControlFile::relocatable, ExtensionControlFile::requires, 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 1254 of file extension.c.

1255 {
1256  ExtensionVersionInfo *evi;
1257  ListCell *lc;
1258 
1259  foreach(lc, *evi_list)
1260  {
1261  evi = (ExtensionVersionInfo *) lfirst(lc);
1262  if (strcmp(evi->name, versionname) == 0)
1263  return evi;
1264  }
1265 
1267  evi->name = pstrdup(versionname);
1268  evi->reachable = NIL;
1269  evi->installable = false;
1270  /* initialize for later application of Dijkstra's algorithm */
1271  evi->distance_known = false;
1272  evi->distance = INT_MAX;
1273  evi->previous = NULL;
1274 
1275  *evi_list = lappend(*evi_list, evi);
1276 
1277  return evi;
1278 }
List * lappend(List *list, void *datum)
Definition: list.c:339

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

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

◆ get_ext_ver_list()

static List* get_ext_ver_list ( ExtensionControlFile control)
static

Definition at line 1315 of file extension.c.

1316 {
1317  List *evi_list = NIL;
1318  int extnamelen = strlen(control->name);
1319  char *location;
1320  DIR *dir;
1321  struct dirent *de;
1322 
1323  location = get_extension_script_directory(control);
1324  dir = AllocateDir(location);
1325  while ((de = ReadDir(dir, location)) != NULL)
1326  {
1327  char *vername;
1328  char *vername2;
1329  ExtensionVersionInfo *evi;
1330  ExtensionVersionInfo *evi2;
1331 
1332  /* must be a .sql file ... */
1334  continue;
1335 
1336  /* ... matching extension name followed by separator */
1337  if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
1338  de->d_name[extnamelen] != '-' ||
1339  de->d_name[extnamelen + 1] != '-')
1340  continue;
1341 
1342  /* extract version name(s) from 'extname--something.sql' filename */
1343  vername = pstrdup(de->d_name + extnamelen + 2);
1344  *strrchr(vername, '.') = '\0';
1345  vername2 = strstr(vername, "--");
1346  if (!vername2)
1347  {
1348  /* It's an install, not update, script; record its version name */
1349  evi = get_ext_ver_info(vername, &evi_list);
1350  evi->installable = true;
1351  continue;
1352  }
1353  *vername2 = '\0'; /* terminate first version */
1354  vername2 += 2; /* and point to second */
1355 
1356  /* if there's a third --, it's bogus, ignore it */
1357  if (strstr(vername2, "--"))
1358  continue;
1359 
1360  /* Create ExtensionVersionInfos and link them together */
1361  evi = get_ext_ver_info(vername, &evi_list);
1362  evi2 = get_ext_ver_info(vername2, &evi_list);
1363  evi->reachable = lappend(evi->reachable, evi2);
1364  }
1365  FreeDir(dir);
1366 
1367  return evi_list;
1368 }
static bool is_extension_script_filename(const char *filename)
Definition: extension.c:324
static char * get_extension_script_directory(ExtensionControlFile *control)
Definition: extension.c:359

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

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

◆ get_extension_aux_control_filename()

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

Definition at line 382 of file extension.c.

384 {
385  char *result;
386  char *scriptdir;
387 
388  scriptdir = get_extension_script_directory(control);
389 
390  result = (char *) palloc(MAXPGPATH);
391  snprintf(result, MAXPGPATH, "%s/%s--%s.control",
392  scriptdir, control->name, version);
393 
394  pfree(scriptdir);
395 
396  return result;
397 }
void pfree(void *pointer)
Definition: mcxt.c:1521
#define MAXPGPATH
#define snprintf
Definition: port.h:238

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

Referenced by parse_extension_control_file().

◆ get_extension_control_directory()

static char* get_extension_control_directory ( void  )
static

Definition at line 332 of file extension.c.

333 {
334  char sharepath[MAXPGPATH];
335  char *result;
336 
337  get_share_path(my_exec_path, sharepath);
338  result = (char *) palloc(MAXPGPATH);
339  snprintf(result, MAXPGPATH, "%s/extension", sharepath);
340 
341  return result;
342 }
char my_exec_path[MAXPGPATH]
Definition: globals.c:80
void get_share_path(const char *my_exec_path, char *ret_path)
Definition: path.c:825

References get_share_path(), MAXPGPATH, my_exec_path, palloc(), and snprintf.

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

◆ get_extension_control_filename()

static char* get_extension_control_filename ( const char *  extname)
static

Definition at line 345 of file extension.c.

346 {
347  char sharepath[MAXPGPATH];
348  char *result;
349 
350  get_share_path(my_exec_path, sharepath);
351  result = (char *) palloc(MAXPGPATH);
352  snprintf(result, MAXPGPATH, "%s/extension/%s.control",
353  sharepath, extname);
354 
355  return result;
356 }

References get_share_path(), MAXPGPATH, my_exec_path, palloc(), and snprintf.

Referenced by parse_extension_control_file().

◆ get_extension_name()

char* get_extension_name ( Oid  ext_oid)

Definition at line 180 of file extension.c.

181 {
182  char *result;
183  HeapTuple tuple;
184 
185  tuple = SearchSysCache1(EXTENSIONOID, ObjectIdGetDatum(ext_oid));
186 
187  if (!HeapTupleIsValid(tuple))
188  return NULL;
189 
190  result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
191  ReleaseSysCache(tuple);
192 
193  return result;
194 }
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221

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

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

◆ get_extension_oid()

Oid get_extension_oid ( const char *  extname,
bool  missing_ok 
)

Definition at line 158 of file extension.c.

159 {
160  Oid result;
161 
162  result = GetSysCacheOid1(EXTENSIONNAME, Anum_pg_extension_oid,
163  CStringGetDatum(extname));
164 
165  if (!OidIsValid(result) && !missing_ok)
166  ereport(ERROR,
167  (errcode(ERRCODE_UNDEFINED_OBJECT),
168  errmsg("extension \"%s\" does not exist",
169  extname)));
170 
171  return result;
172 }
#define GetSysCacheOid1(cacheId, oidcol, key1)
Definition: syscache.h:109

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

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

◆ get_extension_schema()

Oid get_extension_schema ( Oid  ext_oid)

Definition at line 202 of file extension.c.

203 {
204  Oid result;
205  HeapTuple tuple;
206 
207  tuple = SearchSysCache1(EXTENSIONOID, ObjectIdGetDatum(ext_oid));
208 
209  if (!HeapTupleIsValid(tuple))
210  return InvalidOid;
211 
212  result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
213  ReleaseSysCache(tuple);
214 
215  return result;
216 }

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

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

◆ get_extension_script_directory()

static char* get_extension_script_directory ( ExtensionControlFile control)
static

Definition at line 359 of file extension.c.

360 {
361  char sharepath[MAXPGPATH];
362  char *result;
363 
364  /*
365  * The directory parameter can be omitted, absolute, or relative to the
366  * installation's share directory.
367  */
368  if (!control->directory)
370 
371  if (is_absolute_path(control->directory))
372  return pstrdup(control->directory);
373 
374  get_share_path(my_exec_path, sharepath);
375  result = (char *) palloc(MAXPGPATH);
376  snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
377 
378  return result;
379 }
#define is_absolute_path(filename)
Definition: port.h:103

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

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

◆ get_extension_script_filename()

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

Definition at line 400 of file extension.c.

402 {
403  char *result;
404  char *scriptdir;
405 
406  scriptdir = get_extension_script_directory(control);
407 
408  result = (char *) palloc(MAXPGPATH);
409  if (from_version)
410  snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
411  scriptdir, control->name, from_version, version);
412  else
413  snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
414  scriptdir, control->name, version);
415 
416  pfree(scriptdir);
417 
418  return result;
419 }

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

Referenced by CreateExtensionInternal(), and execute_extension_script().

◆ get_nearest_unprocessed_vertex()

static ExtensionVersionInfo* get_nearest_unprocessed_vertex ( List evi_list)
static

Definition at line 1287 of file extension.c.

1288 {
1289  ExtensionVersionInfo *evi = NULL;
1290  ListCell *lc;
1291 
1292  foreach(lc, evi_list)
1293  {
1295 
1296  /* only vertices whose distance is still uncertain are candidates */
1297  if (evi2->distance_known)
1298  continue;
1299  /* remember the closest such vertex */
1300  if (evi == NULL ||
1301  evi->distance > evi2->distance)
1302  evi = evi2;
1303  }
1304 
1305  return evi;
1306 }

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

Referenced by find_update_path().

◆ get_required_extension()

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

Definition at line 1808 of file extension.c.

1814 {
1815  Oid reqExtensionOid;
1816 
1817  reqExtensionOid = get_extension_oid(reqExtensionName, true);
1818  if (!OidIsValid(reqExtensionOid))
1819  {
1820  if (cascade)
1821  {
1822  /* Must install it. */
1823  ObjectAddress addr;
1824  List *cascade_parents;
1825  ListCell *lc;
1826 
1827  /* Check extension name validity before trying to cascade. */
1828  check_valid_extension_name(reqExtensionName);
1829 
1830  /* Check for cyclic dependency between extensions. */
1831  foreach(lc, parents)
1832  {
1833  char *pname = (char *) lfirst(lc);
1834 
1835  if (strcmp(pname, reqExtensionName) == 0)
1836  ereport(ERROR,
1837  (errcode(ERRCODE_INVALID_RECURSION),
1838  errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
1839  reqExtensionName, extensionName)));
1840  }
1841 
1842  ereport(NOTICE,
1843  (errmsg("installing required extension \"%s\"",
1844  reqExtensionName)));
1845 
1846  /* Add current extension to list of parents to pass down. */
1847  cascade_parents = lappend(list_copy(parents), extensionName);
1848 
1849  /*
1850  * Create the required extension. We propagate the SCHEMA option
1851  * if any, and CASCADE, but no other options.
1852  */
1853  addr = CreateExtensionInternal(reqExtensionName,
1854  origSchemaName,
1855  NULL,
1856  cascade,
1857  cascade_parents,
1858  is_create);
1859 
1860  /* Get its newly-assigned OID. */
1861  reqExtensionOid = addr.objectId;
1862  }
1863  else
1864  ereport(ERROR,
1865  (errcode(ERRCODE_UNDEFINED_OBJECT),
1866  errmsg("required extension \"%s\" is not installed",
1867  reqExtensionName),
1868  is_create ?
1869  errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
1870  }
1871 
1872  return reqExtensionOid;
1873 }
List * list_copy(const List *oldlist)
Definition: list.c:1573

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

Referenced by ApplyExtensionUpdates(), and CreateExtensionInternal().

◆ identify_update_path()

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

Definition at line 1378 of file extension.c.

1380 {
1381  List *result;
1382  List *evi_list;
1383  ExtensionVersionInfo *evi_start;
1384  ExtensionVersionInfo *evi_target;
1385 
1386  /* Extract the version update graph from the script directory */
1387  evi_list = get_ext_ver_list(control);
1388 
1389  /* Initialize start and end vertices */
1390  evi_start = get_ext_ver_info(oldVersion, &evi_list);
1391  evi_target = get_ext_ver_info(newVersion, &evi_list);
1392 
1393  /* Find shortest path */
1394  result = find_update_path(evi_list, evi_start, evi_target, false, false);
1395 
1396  if (result == NIL)
1397  ereport(ERROR,
1398  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1399  errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
1400  control->name, oldVersion, newVersion)));
1401 
1402  return result;
1403 }

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

Referenced by ExecAlterExtensionStmt().

◆ InsertExtensionTuple()

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

Definition at line 1977 of file extension.c.

1981 {
1982  Oid extensionOid;
1983  Relation rel;
1984  Datum values[Natts_pg_extension];
1985  bool nulls[Natts_pg_extension];
1986  HeapTuple tuple;
1987  ObjectAddress myself;
1988  ObjectAddress nsp;
1989  ObjectAddresses *refobjs;
1990  ListCell *lc;
1991 
1992  /*
1993  * Build and insert the pg_extension tuple
1994  */
1995  rel = table_open(ExtensionRelationId, RowExclusiveLock);
1996 
1997  memset(values, 0, sizeof(values));
1998  memset(nulls, 0, sizeof(nulls));
1999 
2000  extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
2001  Anum_pg_extension_oid);
2002  values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
2003  values[Anum_pg_extension_extname - 1] =
2005  values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
2006  values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
2007  values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
2008  values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
2009 
2010  if (extConfig == PointerGetDatum(NULL))
2011  nulls[Anum_pg_extension_extconfig - 1] = true;
2012  else
2013  values[Anum_pg_extension_extconfig - 1] = extConfig;
2014 
2015  if (extCondition == PointerGetDatum(NULL))
2016  nulls[Anum_pg_extension_extcondition - 1] = true;
2017  else
2018  values[Anum_pg_extension_extcondition - 1] = extCondition;
2019 
2020  tuple = heap_form_tuple(rel->rd_att, values, nulls);
2021 
2022  CatalogTupleInsert(rel, tuple);
2023 
2024  heap_freetuple(tuple);
2026 
2027  /*
2028  * Record dependencies on owner, schema, and prerequisite extensions
2029  */
2030  recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
2031 
2032  refobjs = new_object_addresses();
2033 
2034  ObjectAddressSet(myself, ExtensionRelationId, extensionOid);
2035 
2036  ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
2037  add_exact_object_address(&nsp, refobjs);
2038 
2039  foreach(lc, requiredExtensions)
2040  {
2041  Oid reqext = lfirst_oid(lc);
2042  ObjectAddress otherext;
2043 
2044  ObjectAddressSet(otherext, ExtensionRelationId, reqext);
2045  add_exact_object_address(&otherext, refobjs);
2046  }
2047 
2048  /* Record all of them (this includes duplicate elimination) */
2050  free_object_addresses(refobjs);
2051 
2052  /* Post creation hook for new extension */
2053  InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
2054 
2055  return myself;
2056 }
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:419
void record_object_address_dependencies(const ObjectAddress *depender, ObjectAddresses *referenced, DependencyType behavior)
Definition: dependency.c:2742
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2533
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2773
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:168
TupleDesc rd_att
Definition: rel.h:112

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

Referenced by binary_upgrade_create_empty_extension(), and CreateExtensionInternal().

◆ is_extension_control_filename()

static bool is_extension_control_filename ( const char *  filename)
static

Definition at line 316 of file extension.c.

317 {
318  const char *extension = strrchr(filename, '.');
319 
320  return (extension != NULL) && (strcmp(extension, ".control") == 0);
321 }

References filename.

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

◆ is_extension_script_filename()

static bool is_extension_script_filename ( const char *  filename)
static

Definition at line 324 of file extension.c.

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

References filename.

Referenced by get_ext_ver_list().

◆ parse_extension_control_file()

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

Definition at line 432 of file extension.c.

434 {
435  char *filename;
436  FILE *file;
437  ConfigVariable *item,
438  *head = NULL,
439  *tail = NULL;
440 
441  /*
442  * Locate the file to read. Auxiliary files are optional.
443  */
444  if (version)
445  filename = get_extension_aux_control_filename(control, version);
446  else
448 
449  if ((file = AllocateFile(filename, "r")) == NULL)
450  {
451  if (errno == ENOENT)
452  {
453  /* no complaint for missing auxiliary file */
454  if (version)
455  {
456  pfree(filename);
457  return;
458  }
459 
460  /* missing control file indicates extension is not installed */
461  ereport(ERROR,
462  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
463  errmsg("extension \"%s\" is not available", control->name),
464  errdetail("Could not open extension control file \"%s\": %m.",
465  filename),
466  errhint("The extension must first be installed on the system where PostgreSQL is running.")));
467  }
468  ereport(ERROR,
470  errmsg("could not open extension control file \"%s\": %m",
471  filename)));
472  }
473 
474  /*
475  * Parse the file content, using GUC's file parsing code. We need not
476  * check the return value since any errors will be thrown at ERROR level.
477  */
479  &head, &tail);
480 
481  FreeFile(file);
482 
483  /*
484  * Convert the ConfigVariable list into ExtensionControlFile entries.
485  */
486  for (item = head; item != NULL; item = item->next)
487  {
488  if (strcmp(item->name, "directory") == 0)
489  {
490  if (version)
491  ereport(ERROR,
492  (errcode(ERRCODE_SYNTAX_ERROR),
493  errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
494  item->name)));
495 
496  control->directory = pstrdup(item->value);
497  }
498  else if (strcmp(item->name, "default_version") == 0)
499  {
500  if (version)
501  ereport(ERROR,
502  (errcode(ERRCODE_SYNTAX_ERROR),
503  errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
504  item->name)));
505 
506  control->default_version = pstrdup(item->value);
507  }
508  else if (strcmp(item->name, "module_pathname") == 0)
509  {
510  control->module_pathname = pstrdup(item->value);
511  }
512  else if (strcmp(item->name, "comment") == 0)
513  {
514  control->comment = pstrdup(item->value);
515  }
516  else if (strcmp(item->name, "schema") == 0)
517  {
518  control->schema = pstrdup(item->value);
519  }
520  else if (strcmp(item->name, "relocatable") == 0)
521  {
522  if (!parse_bool(item->value, &control->relocatable))
523  ereport(ERROR,
524  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
525  errmsg("parameter \"%s\" requires a Boolean value",
526  item->name)));
527  }
528  else if (strcmp(item->name, "superuser") == 0)
529  {
530  if (!parse_bool(item->value, &control->superuser))
531  ereport(ERROR,
532  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
533  errmsg("parameter \"%s\" requires a Boolean value",
534  item->name)));
535  }
536  else if (strcmp(item->name, "trusted") == 0)
537  {
538  if (!parse_bool(item->value, &control->trusted))
539  ereport(ERROR,
540  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
541  errmsg("parameter \"%s\" requires a Boolean value",
542  item->name)));
543  }
544  else if (strcmp(item->name, "encoding") == 0)
545  {
546  control->encoding = pg_valid_server_encoding(item->value);
547  if (control->encoding < 0)
548  ereport(ERROR,
549  (errcode(ERRCODE_UNDEFINED_OBJECT),
550  errmsg("\"%s\" is not a valid encoding name",
551  item->value)));
552  }
553  else if (strcmp(item->name, "requires") == 0)
554  {
555  /* Need a modifiable copy of string */
556  char *rawnames = pstrdup(item->value);
557 
558  /* Parse string into list of identifiers */
559  if (!SplitIdentifierString(rawnames, ',', &control->requires))
560  {
561  /* syntax error in name list */
562  ereport(ERROR,
563  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
564  errmsg("parameter \"%s\" must be a list of extension names",
565  item->name)));
566  }
567  }
568  else if (strcmp(item->name, "no_relocate") == 0)
569  {
570  /* Need a modifiable copy of string */
571  char *rawnames = pstrdup(item->value);
572 
573  /* Parse string into list of identifiers */
574  if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
575  {
576  /* syntax error in name list */
577  ereport(ERROR,
578  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
579  errmsg("parameter \"%s\" must be a list of extension names",
580  item->name)));
581  }
582  }
583  else
584  ereport(ERROR,
585  (errcode(ERRCODE_SYNTAX_ERROR),
586  errmsg("unrecognized parameter \"%s\" in file \"%s\"",
587  item->name, filename)));
588  }
589 
590  FreeConfigVariables(head);
591 
592  if (control->relocatable && control->schema != NULL)
593  ereport(ERROR,
594  (errcode(ERRCODE_SYNTAX_ERROR),
595  errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
596 
597  pfree(filename);
598 }
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:876
static char * get_extension_control_filename(const char *extname)
Definition: extension.c:345
static char * get_extension_aux_control_filename(ExtensionControlFile *control, const char *version)
Definition: extension.c:382
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2605
int FreeFile(FILE *file)
Definition: fd.c:2803
void FreeConfigVariables(ConfigVariable *list)
bool ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, ConfigVariable **head_p, ConfigVariable **tail_p)
#define pg_valid_server_encoding
Definition: pg_wchar.h:631
char * name
Definition: guc.h:137
struct ConfigVariable * next
Definition: guc.h:144
char * value
Definition: guc.h:138
bool SplitIdentifierString(char *rawstring, char separator, List **namelist)
Definition: varlena.c:3432

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

Referenced by 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 2199 of file extension.c.

2200 {
2201  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2202  char *location;
2203  DIR *dir;
2204  struct dirent *de;
2205 
2206  /* Build tuplestore to hold the result rows */
2207  InitMaterializedSRF(fcinfo, 0);
2208 
2209  location = get_extension_control_directory();
2210  dir = AllocateDir(location);
2211 
2212  /*
2213  * If the control directory doesn't exist, we want to silently return an
2214  * empty set. Any other error will be reported by ReadDir.
2215  */
2216  if (dir == NULL && errno == ENOENT)
2217  {
2218  /* do nothing */
2219  }
2220  else
2221  {
2222  while ((de = ReadDir(dir, location)) != NULL)
2223  {
2224  ExtensionControlFile *control;
2225  char *extname;
2226 
2227  if (!is_extension_control_filename(de->d_name))
2228  continue;
2229 
2230  /* extract extension name from 'name.control' filename */
2231  extname = pstrdup(de->d_name);
2232  *strrchr(extname, '.') = '\0';
2233 
2234  /* ignore it if it's an auxiliary control file */
2235  if (strstr(extname, "--"))
2236  continue;
2237 
2238  /* read the control file */
2239  control = read_extension_control_file(extname);
2240 
2241  /* scan extension's script directory for install scripts */
2243  rsinfo->setDesc);
2244  }
2245 
2246  FreeDir(dir);
2247  }
2248 
2249  return (Datum) 0;
2250 }
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol, Tuplestorestate *tupstore, TupleDesc tupdesc)
Definition: extension.c:2257
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
TupleDesc setDesc
Definition: execnodes.h:343
Tuplestorestate * setResult
Definition: execnodes.h:342

References AllocateDir(), FreeDir(), get_available_versions_for_extension(), get_extension_control_directory(), InitMaterializedSRF(), is_extension_control_filename(), pstrdup(), read_extension_control_file(), ReadDir(), ReturnSetInfo::setDesc, and ReturnSetInfo::setResult.

◆ pg_available_extensions()

Datum pg_available_extensions ( PG_FUNCTION_ARGS  )

Definition at line 2119 of file extension.c.

2120 {
2121  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2122  char *location;
2123  DIR *dir;
2124  struct dirent *de;
2125 
2126  /* Build tuplestore to hold the result rows */
2127  InitMaterializedSRF(fcinfo, 0);
2128 
2129  location = get_extension_control_directory();
2130  dir = AllocateDir(location);
2131 
2132  /*
2133  * If the control directory doesn't exist, we want to silently return an
2134  * empty set. Any other error will be reported by ReadDir.
2135  */
2136  if (dir == NULL && errno == ENOENT)
2137  {
2138  /* do nothing */
2139  }
2140  else
2141  {
2142  while ((de = ReadDir(dir, location)) != NULL)
2143  {
2144  ExtensionControlFile *control;
2145  char *extname;
2146  Datum values[3];
2147  bool nulls[3];
2148 
2149  if (!is_extension_control_filename(de->d_name))
2150  continue;
2151 
2152  /* extract extension name from 'name.control' filename */
2153  extname = pstrdup(de->d_name);
2154  *strrchr(extname, '.') = '\0';
2155 
2156  /* ignore it if it's an auxiliary control file */
2157  if (strstr(extname, "--"))
2158  continue;
2159 
2160  control = read_extension_control_file(extname);
2161 
2162  memset(values, 0, sizeof(values));
2163  memset(nulls, 0, sizeof(nulls));
2164 
2165  /* name */
2167  CStringGetDatum(control->name));
2168  /* default_version */
2169  if (control->default_version == NULL)
2170  nulls[1] = true;
2171  else
2173  /* comment */
2174  if (control->comment == NULL)
2175  nulls[2] = true;
2176  else
2177  values[2] = CStringGetTextDatum(control->comment);
2178 
2179  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2180  values, nulls);
2181  }
2182 
2183  FreeDir(dir);
2184  }
2185 
2186  return (Datum) 0;
2187 }

References AllocateDir(), ExtensionControlFile::comment, CStringGetDatum(), CStringGetTextDatum, ExtensionControlFile::default_version, DirectFunctionCall1, FreeDir(), get_extension_control_directory(), InitMaterializedSRF(), is_extension_control_filename(), ExtensionControlFile::name, namein(), pstrdup(), read_extension_control_file(), ReadDir(), ReturnSetInfo::setDesc, ReturnSetInfo::setResult, tuplestore_putvalues(), and values.

◆ pg_extension_config_dump()

Datum pg_extension_config_dump ( PG_FUNCTION_ARGS  )

Definition at line 2535 of file extension.c.

2536 {
2537  Oid tableoid = PG_GETARG_OID(0);
2538  text *wherecond = PG_GETARG_TEXT_PP(1);
2539  char *tablename;
2540  Relation extRel;
2541  ScanKeyData key[1];
2542  SysScanDesc extScan;
2543  HeapTuple extTup;
2544  Datum arrayDatum;
2545  Datum elementDatum;
2546  int arrayLength;
2547  int arrayIndex;
2548  bool isnull;
2549  Datum repl_val[Natts_pg_extension];
2550  bool repl_null[Natts_pg_extension];
2551  bool repl_repl[Natts_pg_extension];
2552  ArrayType *a;
2553 
2554  /*
2555  * We only allow this to be called from an extension's SQL script. We
2556  * shouldn't need any permissions check beyond that.
2557  */
2558  if (!creating_extension)
2559  ereport(ERROR,
2560  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2561  errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
2562  "pg_extension_config_dump()")));
2563 
2564  /*
2565  * Check that the table exists and is a member of the extension being
2566  * created. This ensures that we don't need to register an additional
2567  * dependency to protect the extconfig entry.
2568  */
2569  tablename = get_rel_name(tableoid);
2570  if (tablename == NULL)
2571  ereport(ERROR,
2573  errmsg("OID %u does not refer to a table", tableoid)));
2574  if (getExtensionOfObject(RelationRelationId, tableoid) !=
2576  ereport(ERROR,
2577  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2578  errmsg("table \"%s\" is not a member of the extension being created",
2579  tablename)));
2580 
2581  /*
2582  * Add the table OID and WHERE condition to the extension's extconfig and
2583  * extcondition arrays.
2584  *
2585  * If the table is already in extconfig, treat this as an update of the
2586  * WHERE condition.
2587  */
2588 
2589  /* Find the pg_extension tuple */
2590  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2591 
2592  ScanKeyInit(&key[0],
2593  Anum_pg_extension_oid,
2594  BTEqualStrategyNumber, F_OIDEQ,
2596 
2597  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2598  NULL, 1, key);
2599 
2600  extTup = systable_getnext(extScan);
2601 
2602  if (!HeapTupleIsValid(extTup)) /* should not happen */
2603  elog(ERROR, "could not find tuple for extension %u",
2605 
2606  memset(repl_val, 0, sizeof(repl_val));
2607  memset(repl_null, false, sizeof(repl_null));
2608  memset(repl_repl, false, sizeof(repl_repl));
2609 
2610  /* Build or modify the extconfig value */
2611  elementDatum = ObjectIdGetDatum(tableoid);
2612 
2613  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2614  RelationGetDescr(extRel), &isnull);
2615  if (isnull)
2616  {
2617  /* Previously empty extconfig, so build 1-element array */
2618  arrayLength = 0;
2619  arrayIndex = 1;
2620 
2621  a = construct_array_builtin(&elementDatum, 1, OIDOID);
2622  }
2623  else
2624  {
2625  /* Modify or extend existing extconfig array */
2626  Oid *arrayData;
2627  int i;
2628 
2629  a = DatumGetArrayTypeP(arrayDatum);
2630 
2631  arrayLength = ARR_DIMS(a)[0];
2632  if (ARR_NDIM(a) != 1 ||
2633  ARR_LBOUND(a)[0] != 1 ||
2634  arrayLength < 0 ||
2635  ARR_HASNULL(a) ||
2636  ARR_ELEMTYPE(a) != OIDOID)
2637  elog(ERROR, "extconfig is not a 1-D Oid array");
2638  arrayData = (Oid *) ARR_DATA_PTR(a);
2639 
2640  arrayIndex = arrayLength + 1; /* set up to add after end */
2641 
2642  for (i = 0; i < arrayLength; i++)
2643  {
2644  if (arrayData[i] == tableoid)
2645  {
2646  arrayIndex = i + 1; /* replace this element instead */
2647  break;
2648  }
2649  }
2650 
2651  a = array_set(a, 1, &arrayIndex,
2652  elementDatum,
2653  false,
2654  -1 /* varlena array */ ,
2655  sizeof(Oid) /* OID's typlen */ ,
2656  true /* OID's typbyval */ ,
2657  TYPALIGN_INT /* OID's typalign */ );
2658  }
2659  repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2660  repl_repl[Anum_pg_extension_extconfig - 1] = true;
2661 
2662  /* Build or modify the extcondition value */
2663  elementDatum = PointerGetDatum(wherecond);
2664 
2665  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2666  RelationGetDescr(extRel), &isnull);
2667  if (isnull)
2668  {
2669  if (arrayLength != 0)
2670  elog(ERROR, "extconfig and extcondition arrays do not match");
2671 
2672  a = construct_array_builtin(&elementDatum, 1, TEXTOID);
2673  }
2674  else
2675  {
2676  a = DatumGetArrayTypeP(arrayDatum);
2677 
2678  if (ARR_NDIM(a) != 1 ||
2679  ARR_LBOUND(a)[0] != 1 ||
2680  ARR_HASNULL(a) ||
2681  ARR_ELEMTYPE(a) != TEXTOID)
2682  elog(ERROR, "extcondition is not a 1-D text array");
2683  if (ARR_DIMS(a)[0] != arrayLength)
2684  elog(ERROR, "extconfig and extcondition arrays do not match");
2685 
2686  /* Add or replace at same index as in extconfig */
2687  a = array_set(a, 1, &arrayIndex,
2688  elementDatum,
2689  false,
2690  -1 /* varlena array */ ,
2691  -1 /* TEXT's typlen */ ,
2692  false /* TEXT's typbyval */ ,
2693  TYPALIGN_INT /* TEXT's typalign */ );
2694  }
2695  repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2696  repl_repl[Anum_pg_extension_extcondition - 1] = true;
2697 
2698  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2699  repl_val, repl_null, repl_repl);
2700 
2701  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2702 
2703  systable_endscan(extScan);
2704 
2705  table_close(extRel, RowExclusiveLock);
2706 
2707  PG_RETURN_VOID();
2708 }
ArrayType * array_set(ArrayType *array, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3163
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:78
Definition: c.h:641

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

◆ pg_extension_update_paths()

Datum pg_extension_update_paths ( PG_FUNCTION_ARGS  )

Definition at line 2450 of file extension.c.

2451 {
2452  Name extname = PG_GETARG_NAME(0);
2453  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2454  List *evi_list;
2455  ExtensionControlFile *control;
2456  ListCell *lc1;
2457 
2458  /* Check extension name validity before any filesystem access */
2460 
2461  /* Build tuplestore to hold the result rows */
2462  InitMaterializedSRF(fcinfo, 0);
2463 
2464  /* Read the extension's control file */
2465  control = read_extension_control_file(NameStr(*extname));
2466 
2467  /* Extract the version update graph from the script directory */
2468  evi_list = get_ext_ver_list(control);
2469 
2470  /* Iterate over all pairs of versions */
2471  foreach(lc1, evi_list)
2472  {
2474  ListCell *lc2;
2475 
2476  foreach(lc2, evi_list)
2477  {
2479  List *path;
2480  Datum values[3];
2481  bool nulls[3];
2482 
2483  if (evi1 == evi2)
2484  continue;
2485 
2486  /* Find shortest path from evi1 to evi2 */
2487  path = find_update_path(evi_list, evi1, evi2, false, true);
2488 
2489  /* Emit result row */
2490  memset(values, 0, sizeof(values));
2491  memset(nulls, 0, sizeof(nulls));
2492 
2493  /* source */
2494  values[0] = CStringGetTextDatum(evi1->name);
2495  /* target */
2496  values[1] = CStringGetTextDatum(evi2->name);
2497  /* path */
2498  if (path == NIL)
2499  nulls[2] = true;
2500  else
2501  {
2502  StringInfoData pathbuf;
2503  ListCell *lcv;
2504 
2505  initStringInfo(&pathbuf);
2506  /* The path doesn't include start vertex, but show it */
2507  appendStringInfoString(&pathbuf, evi1->name);
2508  foreach(lcv, path)
2509  {
2510  char *versionName = (char *) lfirst(lcv);
2511 
2512  appendStringInfoString(&pathbuf, "--");
2513  appendStringInfoString(&pathbuf, versionName);
2514  }
2515  values[2] = CStringGetTextDatum(pathbuf.data);
2516  pfree(pathbuf.data);
2517  }
2518 
2519  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2520  values, nulls);
2521  }
2522  }
2523 
2524  return (Datum) 0;
2525 }
#define PG_GETARG_NAME(n)
Definition: fmgr.h:278
Definition: c.h:695

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

◆ read_extension_aux_control_file()

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

Definition at line 633 of file extension.c.

635 {
636  ExtensionControlFile *acontrol;
637 
638  /*
639  * Flat-copy the struct. Pointer fields share values with original.
640  */
641  acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
642  memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
643 
644  /*
645  * Parse the auxiliary control file, overwriting struct fields
646  */
647  parse_extension_control_file(acontrol, version);
648 
649  return acontrol;
650 }
static void parse_extension_control_file(ExtensionControlFile *control, const char *version)
Definition: extension.c:432

References palloc(), and parse_extension_control_file().

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

◆ read_extension_control_file()

static ExtensionControlFile* read_extension_control_file ( const char *  extname)
static

Definition at line 604 of file extension.c.

605 {
606  ExtensionControlFile *control;
607 
608  /*
609  * Set up default values. Pointer fields are initially null.
610  */
611  control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
612  control->name = pstrdup(extname);
613  control->relocatable = false;
614  control->superuser = true;
615  control->trusted = false;
616  control->encoding = -1;
617 
618  /*
619  * Parse the primary control file.
620  */
621  parse_extension_control_file(control, NULL);
622 
623  return control;
624 }
void * palloc0(Size size)
Definition: mcxt.c:1347

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

Referenced by AlterExtensionNamespace(), CreateExtensionInternal(), ExecAlterExtensionStmt(), pg_available_extension_versions(), pg_available_extensions(), 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 656 of file extension.c.

658 {
659  int src_encoding;
660  char *src_str;
661  char *dest_str;
662  int len;
663 
664  src_str = read_whole_file(filename, &len);
665 
666  /* use database encoding if not given */
667  if (control->encoding < 0)
668  src_encoding = GetDatabaseEncoding();
669  else
670  src_encoding = control->encoding;
671 
672  /* make sure that source string is valid in the expected encoding */
673  (void) pg_verify_mbstr(src_encoding, src_str, len, false);
674 
675  /*
676  * Convert the encoding to the database encoding. read_whole_file
677  * null-terminated the string, so if no conversion happens the string is
678  * valid as is.
679  */
680  dest_str = pg_any_to_server(src_str, len, src_encoding);
681 
682  return dest_str;
683 }
static char * read_whole_file(const char *filename, int *length)
Definition: extension.c:3629
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676
int GetDatabaseEncoding(void)
Definition: mbutils.c:1261
bool pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError)
Definition: mbutils.c:1566
const void size_t len

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

Referenced by execute_extension_script().

◆ read_whole_file()

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

Definition at line 3629 of file extension.c.

3630 {
3631  char *buf;
3632  FILE *file;
3633  size_t bytes_to_read;
3634  struct stat fst;
3635 
3636  if (stat(filename, &fst) < 0)
3637  ereport(ERROR,
3639  errmsg("could not stat file \"%s\": %m", filename)));
3640 
3641  if (fst.st_size > (MaxAllocSize - 1))
3642  ereport(ERROR,
3643  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3644  errmsg("file \"%s\" is too large", filename)));
3645  bytes_to_read = (size_t) fst.st_size;
3646 
3647  if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
3648  ereport(ERROR,
3650  errmsg("could not open file \"%s\" for reading: %m",
3651  filename)));
3652 
3653  buf = (char *) palloc(bytes_to_read + 1);
3654 
3655  bytes_to_read = fread(buf, 1, bytes_to_read, file);
3656 
3657  if (ferror(file))
3658  ereport(ERROR,
3660  errmsg("could not read file \"%s\": %m", filename)));
3661 
3662  FreeFile(file);
3663 
3664  buf[bytes_to_read] = '\0';
3665 
3666  /*
3667  * On Windows, manually convert Windows-style newlines (\r\n) to the Unix
3668  * convention of \n only. This avoids gotchas due to script files
3669  * possibly getting converted when being transferred between platforms.
3670  * Ideally we'd do this by using text mode to read the file, but that also
3671  * causes control-Z to be treated as end-of-file. Historically we've
3672  * allowed control-Z in script files, so breaking that seems unwise.
3673  */
3674 #ifdef WIN32
3675  {
3676  char *s,
3677  *d;
3678 
3679  for (s = d = buf; *s; s++)
3680  {
3681  if (!(*s == '\r' && s[1] == '\n'))
3682  *d++ = *s;
3683  }
3684  *d = '\0';
3685  bytes_to_read = d - buf;
3686  }
3687 #endif
3688 
3689  *length = bytes_to_read;
3690  return buf;
3691 }
#define PG_BINARY_R
Definition: c.h:1229
#define MaxAllocSize
Definition: fe_memutils.h:22
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
static char * buf
Definition: pg_test_fsync.c:72

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

Referenced by read_extension_script_file().

◆ RemoveExtensionById()

void RemoveExtensionById ( Oid  extId)

Definition at line 2065 of file extension.c.

2066 {
2067  Relation rel;
2068  SysScanDesc scandesc;
2069  HeapTuple tuple;
2070  ScanKeyData entry[1];
2071 
2072  /*
2073  * Disallow deletion of any extension that's currently open for insertion;
2074  * else subsequent executions of recordDependencyOnCurrentExtension()
2075  * could create dangling pg_depend records that refer to a no-longer-valid
2076  * pg_extension OID. This is needed not so much because we think people
2077  * might write "DROP EXTENSION foo" in foo's own script files, as because
2078  * errors in dependency management in extension script files could give
2079  * rise to cases where an extension is dropped as a result of recursing
2080  * from some contained object. Because of that, we must test for the case
2081  * here, not at some higher level of the DROP EXTENSION command.
2082  */
2083  if (extId == CurrentExtensionObject)
2084  ereport(ERROR,
2085  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2086  errmsg("cannot drop extension \"%s\" because it is being modified",
2087  get_extension_name(extId))));
2088 
2089  rel = table_open(ExtensionRelationId, RowExclusiveLock);
2090 
2091  ScanKeyInit(&entry[0],
2092  Anum_pg_extension_oid,
2093  BTEqualStrategyNumber, F_OIDEQ,
2094  ObjectIdGetDatum(extId));
2095  scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
2096  NULL, 1, entry);
2097 
2098  tuple = systable_getnext(scandesc);
2099 
2100  /* We assume that there can be at most one matching tuple */
2101  if (HeapTupleIsValid(tuple))
2102  CatalogTupleDelete(rel, &tuple->t_self);
2103 
2104  systable_endscan(scandesc);
2105 
2107 }
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365

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

Referenced by doDeletion().

◆ script_error_callback()

static void script_error_callback ( void *  arg)
static

Definition at line 689 of file extension.c.

690 {
692  const char *query = callback_arg->sql;
693  int location = callback_arg->stmt_location;
694  int len = callback_arg->stmt_len;
695  int syntaxerrposition;
696  const char *lastslash;
697 
698  /*
699  * If there is a syntax error position, convert to internal syntax error;
700  * otherwise report the current query as an item of context stack.
701  *
702  * Note: we'll provide no context except the filename if there's neither
703  * an error position nor any known current query. That shouldn't happen
704  * though: all errors reported during raw parsing should come with an
705  * error position.
706  */
707  syntaxerrposition = geterrposition();
708  if (syntaxerrposition > 0)
709  {
710  /*
711  * If we do not know the bounds of the current statement (as would
712  * happen for an error occurring during initial raw parsing), we have
713  * to use a heuristic to decide how much of the script to show. We'll
714  * also use the heuristic in the unlikely case that syntaxerrposition
715  * is outside what we think the statement bounds are.
716  */
717  if (location < 0 || syntaxerrposition < location ||
718  (len > 0 && syntaxerrposition > location + len))
719  {
720  /*
721  * Our heuristic is pretty simple: look for semicolon-newline
722  * sequences, and break at the last one strictly before
723  * syntaxerrposition and the first one strictly after. It's
724  * certainly possible to fool this with semicolon-newline embedded
725  * in a string literal, but it seems better to do this than to
726  * show the entire extension script.
727  *
728  * Notice we cope with Windows-style newlines (\r\n) regardless of
729  * platform. This is because there might be such newlines in
730  * script files on other platforms.
731  */
732  int slen = strlen(query);
733 
734  location = len = 0;
735  for (int loc = 0; loc < slen; loc++)
736  {
737  if (query[loc] != ';')
738  continue;
739  if (query[loc + 1] == '\r')
740  loc++;
741  if (query[loc + 1] == '\n')
742  {
743  int bkpt = loc + 2;
744 
745  if (bkpt < syntaxerrposition)
746  location = bkpt;
747  else if (bkpt > syntaxerrposition)
748  {
749  len = bkpt - location;
750  break; /* no need to keep searching */
751  }
752  }
753  }
754  }
755 
756  /* Trim leading/trailing whitespace, for consistency */
757  query = CleanQuerytext(query, &location, &len);
758 
759  /*
760  * Adjust syntaxerrposition. It shouldn't be pointing into the
761  * whitespace we just trimmed, but cope if it is.
762  */
763  syntaxerrposition -= location;
764  if (syntaxerrposition < 0)
765  syntaxerrposition = 0;
766  else if (syntaxerrposition > len)
767  syntaxerrposition = len;
768 
769  /* And report. */
770  errposition(0);
771  internalerrposition(syntaxerrposition);
772  internalerrquery(pnstrdup(query, len));
773  }
774  else if (location >= 0)
775  {
776  /*
777  * Since no syntax cursor will be shown, it's okay and helpful to trim
778  * the reported query string to just the current statement.
779  */
780  query = CleanQuerytext(query, &location, &len);
781  errcontext("SQL statement \"%.*s\"", len, query);
782  }
783 
784  /*
785  * Trim the reported file name to remove the path. We know that
786  * get_extension_script_filename() inserted a '/', regardless of whether
787  * we're on Windows.
788  */
789  lastslash = strrchr(callback_arg->filename, '/');
790  if (lastslash)
791  lastslash++;
792  else
793  lastslash = callback_arg->filename; /* shouldn't happen, but cope */
794 
795  /*
796  * If we have a location (which, as said above, we really always should)
797  * then report a line number to aid in localizing problems in big scripts.
798  */
799  if (location >= 0)
800  {
801  int linenumber = 1;
802 
803  for (query = callback_arg->sql; *query; query++)
804  {
805  if (--location < 0)
806  break;
807  if (*query == '\n')
808  linenumber++;
809  }
810  errcontext("extension script file \"%s\", near line %d",
811  lastslash, linenumber);
812  }
813  else
814  errcontext("extension script file \"%s\"", lastslash);
815 }
int internalerrquery(const char *query)
Definition: elog.c:1482
int internalerrposition(int cursorpos)
Definition: elog.c:1462
int geterrposition(void)
Definition: elog.c:1595
int errposition(int cursorpos)
Definition: elog.c:1446
#define errcontext
Definition: elog.h:196
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:1707
void * arg
const char * CleanQuerytext(const char *query, int *location, int *len)

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

Referenced by execute_sql_string().

Variable Documentation

◆ creating_extension

◆ CurrentExtensionObject