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/sysattr.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/makefuncs.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/varlena.h"
Include dependency graph for extension.c:

Go to the source code of this file.

Data Structures

struct  ExtensionControlFile
 
struct  ExtensionVersionInfo
 

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 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 execute_sql_string (const char *sql)
 
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 2749 of file extension.c.

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

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

Referenced by ExecAlterObjectSchemaStmt().

◆ ApplyExtensionUpdates()

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

Definition at line 3112 of file extension.c.

3119 {
3120  const char *oldVersionName = initialVersion;
3121  ListCell *lcv;
3122 
3123  foreach(lcv, updateVersions)
3124  {
3125  char *versionName = (char *) lfirst(lcv);
3126  ExtensionControlFile *control;
3127  char *schemaName;
3128  Oid schemaOid;
3129  List *requiredExtensions;
3130  List *requiredSchemas;
3131  Relation extRel;
3132  ScanKeyData key[1];
3133  SysScanDesc extScan;
3134  HeapTuple extTup;
3135  Form_pg_extension extForm;
3136  Datum values[Natts_pg_extension];
3137  bool nulls[Natts_pg_extension];
3138  bool repl[Natts_pg_extension];
3139  ObjectAddress myself;
3140  ListCell *lc;
3141 
3142  /*
3143  * Fetch parameters for specific version (pcontrol is not changed)
3144  */
3145  control = read_extension_aux_control_file(pcontrol, versionName);
3146 
3147  /* Find the pg_extension tuple */
3148  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3149 
3150  ScanKeyInit(&key[0],
3151  Anum_pg_extension_oid,
3152  BTEqualStrategyNumber, F_OIDEQ,
3153  ObjectIdGetDatum(extensionOid));
3154 
3155  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3156  NULL, 1, key);
3157 
3158  extTup = systable_getnext(extScan);
3159 
3160  if (!HeapTupleIsValid(extTup)) /* should not happen */
3161  elog(ERROR, "could not find tuple for extension %u",
3162  extensionOid);
3163 
3164  extForm = (Form_pg_extension) GETSTRUCT(extTup);
3165 
3166  /*
3167  * Determine the target schema (set by original install)
3168  */
3169  schemaOid = extForm->extnamespace;
3170  schemaName = get_namespace_name(schemaOid);
3171 
3172  /*
3173  * Modify extrelocatable and extversion in the pg_extension tuple
3174  */
3175  memset(values, 0, sizeof(values));
3176  memset(nulls, 0, sizeof(nulls));
3177  memset(repl, 0, sizeof(repl));
3178 
3179  values[Anum_pg_extension_extrelocatable - 1] =
3180  BoolGetDatum(control->relocatable);
3181  repl[Anum_pg_extension_extrelocatable - 1] = true;
3182  values[Anum_pg_extension_extversion - 1] =
3183  CStringGetTextDatum(versionName);
3184  repl[Anum_pg_extension_extversion - 1] = true;
3185 
3186  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3187  values, nulls, repl);
3188 
3189  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3190 
3191  systable_endscan(extScan);
3192 
3193  table_close(extRel, RowExclusiveLock);
3194 
3195  /*
3196  * Look up the prerequisite extensions for this version, install them
3197  * if necessary, and build lists of their OIDs and the OIDs of their
3198  * target schemas.
3199  */
3200  requiredExtensions = NIL;
3201  requiredSchemas = NIL;
3202  foreach(lc, control->requires)
3203  {
3204  char *curreq = (char *) lfirst(lc);
3205  Oid reqext;
3206  Oid reqschema;
3207 
3208  reqext = get_required_extension(curreq,
3209  control->name,
3210  origSchemaName,
3211  cascade,
3212  NIL,
3213  is_create);
3214  reqschema = get_extension_schema(reqext);
3215  requiredExtensions = lappend_oid(requiredExtensions, reqext);
3216  requiredSchemas = lappend_oid(requiredSchemas, reqschema);
3217  }
3218 
3219  /*
3220  * Remove and recreate dependencies on prerequisite extensions
3221  */
3222  deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
3223  ExtensionRelationId,
3225 
3226  myself.classId = ExtensionRelationId;
3227  myself.objectId = extensionOid;
3228  myself.objectSubId = 0;
3229 
3230  foreach(lc, requiredExtensions)
3231  {
3232  Oid reqext = lfirst_oid(lc);
3233  ObjectAddress otherext;
3234 
3235  otherext.classId = ExtensionRelationId;
3236  otherext.objectId = reqext;
3237  otherext.objectSubId = 0;
3238 
3239  recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
3240  }
3241 
3242  InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3243 
3244  /*
3245  * Finally, execute the update script file
3246  */
3247  execute_extension_script(extensionOid, control,
3248  oldVersionName, versionName,
3249  requiredSchemas,
3250  schemaName, schemaOid);
3251 
3252  /*
3253  * Update prior-version name and loop around. Since
3254  * execute_sql_string did a final CommandCounterIncrement, we can
3255  * update the pg_extension row again.
3256  */
3257  oldVersionName = versionName;
3258  }
3259 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define CStringGetTextDatum(s)
Definition: builtins.h:94
Oid get_extension_schema(Oid ext_oid)
Definition: extension.c:228
static Oid get_required_extension(char *reqExtensionName, char *extensionName, char *origSchemaName, bool cascade, List *parents, bool is_create)
Definition: extension.c:1674
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:869
static ExtensionControlFile * read_extension_aux_control_file(const ExtensionControlFile *pcontrol, const char *version)
Definition: extension.c:676
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1113
List * lappend_oid(List *list, Oid datum)
Definition: list.c:374
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:350
#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:529
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 265 of file extension.c.

266 {
267  int namelen = strlen(extensionname);
268 
269  /*
270  * Disallow empty names (the parser rejects empty identifiers anyway, but
271  * let's check).
272  */
273  if (namelen == 0)
274  ereport(ERROR,
275  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
276  errmsg("invalid extension name: \"%s\"", extensionname),
277  errdetail("Extension names must not be empty.")));
278 
279  /*
280  * No double dashes, since that would make script filenames ambiguous.
281  */
282  if (strstr(extensionname, "--"))
283  ereport(ERROR,
284  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
285  errmsg("invalid extension name: \"%s\"", extensionname),
286  errdetail("Extension names must not contain \"--\".")));
287 
288  /*
289  * No leading or trailing dash either. (We could probably allow this, but
290  * it would require much care in filename parsing and would make filenames
291  * visually if not formally ambiguous. Since there's no real-world use
292  * case, let's just forbid it.)
293  */
294  if (extensionname[0] == '-' || extensionname[namelen - 1] == '-')
295  ereport(ERROR,
296  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
297  errmsg("invalid extension name: \"%s\"", extensionname),
298  errdetail("Extension names must not begin or end with \"-\".")));
299 
300  /*
301  * No directory separators either (this is sufficient to prevent ".."
302  * style attacks).
303  */
304  if (first_dir_separator(extensionname) != NULL)
305  ereport(ERROR,
306  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
307  errmsg("invalid extension name: \"%s\"", extensionname),
308  errdetail("Extension names must not contain directory separator characters.")));
309 }
char * first_dir_separator(const char *filename)
Definition: path.c:104

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

313 {
314  int namelen = strlen(versionname);
315 
316  /*
317  * Disallow empty names (we could possibly allow this, but there seems
318  * little point).
319  */
320  if (namelen == 0)
321  ereport(ERROR,
322  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
323  errmsg("invalid extension version name: \"%s\"", versionname),
324  errdetail("Version names must not be empty.")));
325 
326  /*
327  * No double dashes, since that would make script filenames ambiguous.
328  */
329  if (strstr(versionname, "--"))
330  ereport(ERROR,
331  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
332  errmsg("invalid extension version name: \"%s\"", versionname),
333  errdetail("Version names must not contain \"--\".")));
334 
335  /*
336  * No leading or trailing dash either.
337  */
338  if (versionname[0] == '-' || versionname[namelen - 1] == '-')
339  ereport(ERROR,
340  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
341  errmsg("invalid extension version name: \"%s\"", versionname),
342  errdetail("Version names must not begin or end with \"-\".")));
343 
344  /*
345  * No directory separators either (this is sufficient to prevent ".."
346  * style attacks).
347  */
348  if (first_dir_separator(versionname) != NULL)
349  ereport(ERROR,
350  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
351  errmsg("invalid extension version name: \"%s\"", versionname),
352  errdetail("Version names must not contain directory separator characters.")));
353 }

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

2291 {
2292  Datum *datums;
2293  int ndatums;
2294  ArrayType *a;
2295  ListCell *lc;
2296 
2297  ndatums = list_length(requires);
2298  datums = (Datum *) palloc(ndatums * sizeof(Datum));
2299  ndatums = 0;
2300  foreach(lc, requires)
2301  {
2302  char *curreq = (char *) lfirst(lc);
2303 
2304  datums[ndatums++] =
2306  }
2307  a = construct_array_builtin(datums, ndatums, NAMEOID);
2308  return PointerGetDatum(a);
2309 }
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3361
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
int a
Definition: isn.c:69
void * palloc(Size size)
Definition: mcxt.c:1210
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 1745 of file extension.c.

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

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

Referenced by ProcessUtilitySlow().

◆ CreateExtensionInternal()

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

Definition at line 1435 of file extension.c.

1441 {
1442  char *origSchemaName = schemaName;
1443  Oid schemaOid = InvalidOid;
1444  Oid extowner = GetUserId();
1445  ExtensionControlFile *pcontrol;
1446  ExtensionControlFile *control;
1447  char *filename;
1448  struct stat fst;
1449  List *updateVersions;
1450  List *requiredExtensions;
1451  List *requiredSchemas;
1452  Oid extensionOid;
1453  ObjectAddress address;
1454  ListCell *lc;
1455 
1456  /*
1457  * Read the primary control file. Note we assume that it does not contain
1458  * any non-ASCII data, so there is no need to worry about encoding at this
1459  * point.
1460  */
1461  pcontrol = read_extension_control_file(extensionName);
1462 
1463  /*
1464  * Determine the version to install
1465  */
1466  if (versionName == NULL)
1467  {
1468  if (pcontrol->default_version)
1469  versionName = pcontrol->default_version;
1470  else
1471  ereport(ERROR,
1472  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1473  errmsg("version to install must be specified")));
1474  }
1475  check_valid_version_name(versionName);
1476 
1477  /*
1478  * Figure out which script(s) we need to run to install the desired
1479  * version of the extension. If we do not have a script that directly
1480  * does what is needed, we try to find a sequence of update scripts that
1481  * will get us there.
1482  */
1483  filename = get_extension_script_filename(pcontrol, NULL, versionName);
1484  if (stat(filename, &fst) == 0)
1485  {
1486  /* Easy, no extra scripts */
1487  updateVersions = NIL;
1488  }
1489  else
1490  {
1491  /* Look for best way to install this version */
1492  List *evi_list;
1493  ExtensionVersionInfo *evi_start;
1494  ExtensionVersionInfo *evi_target;
1495 
1496  /* Extract the version update graph from the script directory */
1497  evi_list = get_ext_ver_list(pcontrol);
1498 
1499  /* Identify the target version */
1500  evi_target = get_ext_ver_info(versionName, &evi_list);
1501 
1502  /* Identify best path to reach target */
1503  evi_start = find_install_path(evi_list, evi_target,
1504  &updateVersions);
1505 
1506  /* Fail if no path ... */
1507  if (evi_start == NULL)
1508  ereport(ERROR,
1509  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1510  errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
1511  pcontrol->name, versionName)));
1512 
1513  /* Otherwise, install best starting point and then upgrade */
1514  versionName = evi_start->name;
1515  }
1516 
1517  /*
1518  * Fetch control parameters for installation target version
1519  */
1520  control = read_extension_aux_control_file(pcontrol, versionName);
1521 
1522  /*
1523  * Determine the target schema to install the extension into
1524  */
1525  if (schemaName)
1526  {
1527  /* If the user is giving us the schema name, it must exist already. */
1528  schemaOid = get_namespace_oid(schemaName, false);
1529  }
1530 
1531  if (control->schema != NULL)
1532  {
1533  /*
1534  * The extension is not relocatable and the author gave us a schema
1535  * for it.
1536  *
1537  * Unless CASCADE parameter was given, it's an error to give a schema
1538  * different from control->schema if control->schema is specified.
1539  */
1540  if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1541  !cascade)
1542  ereport(ERROR,
1543  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1544  errmsg("extension \"%s\" must be installed in schema \"%s\"",
1545  control->name,
1546  control->schema)));
1547 
1548  /* Always use the schema from control file for current extension. */
1549  schemaName = control->schema;
1550 
1551  /* Find or create the schema in case it does not exist. */
1552  schemaOid = get_namespace_oid(schemaName, true);
1553 
1554  if (!OidIsValid(schemaOid))
1555  {
1557 
1558  csstmt->schemaname = schemaName;
1559  csstmt->authrole = NULL; /* will be created by current user */
1560  csstmt->schemaElts = NIL;
1561  csstmt->if_not_exists = false;
1562  CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
1563  -1, -1);
1564 
1565  /*
1566  * CreateSchemaCommand includes CommandCounterIncrement, so new
1567  * schema is now visible.
1568  */
1569  schemaOid = get_namespace_oid(schemaName, false);
1570  }
1571  }
1572  else if (!OidIsValid(schemaOid))
1573  {
1574  /*
1575  * Neither user nor author of the extension specified schema; use the
1576  * current default creation namespace, which is the first explicit
1577  * entry in the search_path.
1578  */
1579  List *search_path = fetch_search_path(false);
1580 
1581  if (search_path == NIL) /* nothing valid in search_path? */
1582  ereport(ERROR,
1583  (errcode(ERRCODE_UNDEFINED_SCHEMA),
1584  errmsg("no schema has been selected to create in")));
1585  schemaOid = linitial_oid(search_path);
1586  schemaName = get_namespace_name(schemaOid);
1587  if (schemaName == NULL) /* recently-deleted namespace? */
1588  ereport(ERROR,
1589  (errcode(ERRCODE_UNDEFINED_SCHEMA),
1590  errmsg("no schema has been selected to create in")));
1591 
1592  list_free(search_path);
1593  }
1594 
1595  /*
1596  * Make note if a temporary namespace has been accessed in this
1597  * transaction.
1598  */
1599  if (isTempNamespace(schemaOid))
1601 
1602  /*
1603  * We don't check creation rights on the target namespace here. If the
1604  * extension script actually creates any objects there, it will fail if
1605  * the user doesn't have such permissions. But there are cases such as
1606  * procedural languages where it's convenient to set schema = pg_catalog
1607  * yet we don't want to restrict the command to users with ACL_CREATE for
1608  * pg_catalog.
1609  */
1610 
1611  /*
1612  * Look up the prerequisite extensions, install them if necessary, and
1613  * build lists of their OIDs and the OIDs of their target schemas.
1614  */
1615  requiredExtensions = NIL;
1616  requiredSchemas = NIL;
1617  foreach(lc, control->requires)
1618  {
1619  char *curreq = (char *) lfirst(lc);
1620  Oid reqext;
1621  Oid reqschema;
1622 
1623  reqext = get_required_extension(curreq,
1624  extensionName,
1625  origSchemaName,
1626  cascade,
1627  parents,
1628  is_create);
1629  reqschema = get_extension_schema(reqext);
1630  requiredExtensions = lappend_oid(requiredExtensions, reqext);
1631  requiredSchemas = lappend_oid(requiredSchemas, reqschema);
1632  }
1633 
1634  /*
1635  * Insert new tuple into pg_extension, and create dependency entries.
1636  */
1637  address = InsertExtensionTuple(control->name, extowner,
1638  schemaOid, control->relocatable,
1639  versionName,
1640  PointerGetDatum(NULL),
1641  PointerGetDatum(NULL),
1642  requiredExtensions);
1643  extensionOid = address.objectId;
1644 
1645  /*
1646  * Apply any control-file comment on extension
1647  */
1648  if (control->comment != NULL)
1649  CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
1650 
1651  /*
1652  * Execute the installation script file
1653  */
1654  execute_extension_script(extensionOid, control,
1655  NULL, versionName,
1656  requiredSchemas,
1657  schemaName, schemaOid);
1658 
1659  /*
1660  * If additional update scripts have to be executed, apply the updates as
1661  * though a series of ALTER EXTENSION UPDATE commands were given
1662  */
1663  ApplyExtensionUpdates(extensionOid, pcontrol,
1664  versionName, updateVersions,
1665  origSchemaName, cascade, is_create);
1666 
1667  return address;
1668 }
#define OidIsValid(objectId)
Definition: c.h:759
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:1120
ObjectAddress InsertExtensionTuple(const char *extName, Oid extOwner, Oid schemaOid, bool relocatable, const char *extVersion, Datum extConfig, Datum extCondition, List *requiredExtensions)
Definition: extension.c:1843
static void check_valid_version_name(const char *versionname)
Definition: extension.c:312
static char * get_extension_script_filename(ExtensionControlFile *control, const char *from_version, const char *version)
Definition: extension.c:443
static List * get_ext_ver_list(ExtensionControlFile *control)
Definition: extension.c:1181
static ExtensionVersionInfo * find_install_path(List *evi_list, ExtensionVersionInfo *evi_target, List **best_path)
Definition: extension.c:1380
static void ApplyExtensionUpdates(Oid extensionOid, ExtensionControlFile *pcontrol, const char *initialVersion, List *updateVersions, char *origSchemaName, bool cascade, bool is_create)
Definition: extension.c:3112
void list_free(List *list)
Definition: list.c:1545
bool isTempNamespace(Oid namespaceId)
Definition: namespace.c:3200
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4428
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition: namespace.c:3086
#define makeNode(_type_)
Definition: nodes.h:176
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:51
RoleSpec * authrole
Definition: parsenodes.h:2148
char * default_version
Definition: extension.c:83
#define stat
Definition: win32_port.h:286
int MyXactFlags
Definition: xact.c:136
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102

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

Referenced by CreateExtension(), and get_required_extension().

◆ ExecAlterExtensionContentsStmt()

ObjectAddress ExecAlterExtensionContentsStmt ( AlterExtensionContentsStmt stmt,
ObjectAddress objAddr 
)

Definition at line 3270 of file extension.c.

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

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

Referenced by ProcessUtilitySlow().

◆ ExecAlterExtensionStmt()

ObjectAddress ExecAlterExtensionStmt ( ParseState pstate,
AlterExtensionStmt stmt 
)

Definition at line 2965 of file extension.c.

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

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

Referenced by ProcessUtilitySlow().

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

874 {
875  bool switch_to_superuser = false;
876  char *filename;
877  Oid save_userid = 0;
878  int save_sec_context = 0;
879  int save_nestlevel;
880  StringInfoData pathbuf;
881  ListCell *lc;
882  ListCell *lc2;
883 
884  /*
885  * Enforce superuser-ness if appropriate. We postpone these checks until
886  * here so that the control flags are correctly associated with the right
887  * script(s) if they happen to be set in secondary control files.
888  */
889  if (control->superuser && !superuser())
890  {
891  if (extension_is_trusted(control))
892  switch_to_superuser = true;
893  else if (from_version == NULL)
894  ereport(ERROR,
895  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
896  errmsg("permission denied to create extension \"%s\"",
897  control->name),
898  control->trusted
899  ? errhint("Must have CREATE privilege on current database to create this extension.")
900  : errhint("Must be superuser to create this extension.")));
901  else
902  ereport(ERROR,
903  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
904  errmsg("permission denied to update extension \"%s\"",
905  control->name),
906  control->trusted
907  ? errhint("Must have CREATE privilege on current database to update this extension.")
908  : errhint("Must be superuser to update this extension.")));
909  }
910 
911  filename = get_extension_script_filename(control, from_version, version);
912 
913  if (from_version == NULL)
914  elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
915  else
916  elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
917 
918  /*
919  * If installing a trusted extension on behalf of a non-superuser, become
920  * the bootstrap superuser. (This switch will be cleaned up automatically
921  * if the transaction aborts, as will the GUC changes below.)
922  */
923  if (switch_to_superuser)
924  {
925  GetUserIdAndSecContext(&save_userid, &save_sec_context);
926  SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID,
927  save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
928  }
929 
930  /*
931  * Force client_min_messages and log_min_messages to be at least WARNING,
932  * so that we won't spam the user with useless NOTICE messages from common
933  * script actions like creating shell types.
934  *
935  * We use the equivalent of a function SET option to allow the setting to
936  * persist for exactly the duration of the script execution. guc.c also
937  * takes care of undoing the setting on error.
938  *
939  * log_min_messages can't be set by ordinary users, so for that one we
940  * pretend to be superuser.
941  */
942  save_nestlevel = NewGUCNestLevel();
943 
945  (void) set_config_option("client_min_messages", "warning",
947  GUC_ACTION_SAVE, true, 0, false);
949  (void) set_config_option_ext("log_min_messages", "warning",
951  BOOTSTRAP_SUPERUSERID,
952  GUC_ACTION_SAVE, true, 0, false);
953 
954  /*
955  * Similarly disable check_function_bodies, to ensure that SQL functions
956  * won't be parsed during creation.
957  */
959  (void) set_config_option("check_function_bodies", "off",
961  GUC_ACTION_SAVE, true, 0, false);
962 
963  /*
964  * Set up the search path to have the target schema first, making it be
965  * the default creation target namespace. Then add the schemas of any
966  * prerequisite extensions, unless they are in pg_catalog which would be
967  * searched anyway. (Listing pg_catalog explicitly in a non-first
968  * position would be bad for security.) Finally add pg_temp to ensure
969  * that temp objects can't take precedence over others.
970  *
971  * Note: it might look tempting to use PushOverrideSearchPath for this,
972  * but we cannot do that. We have to actually set the search_path GUC in
973  * case the extension script examines or changes it. In any case, the
974  * GUC_ACTION_SAVE method is just as convenient.
975  */
976  initStringInfo(&pathbuf);
977  appendStringInfoString(&pathbuf, quote_identifier(schemaName));
978  foreach(lc, requiredSchemas)
979  {
980  Oid reqschema = lfirst_oid(lc);
981  char *reqname = get_namespace_name(reqschema);
982 
983  if (reqname && strcmp(reqname, "pg_catalog") != 0)
984  appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
985  }
986  appendStringInfoString(&pathbuf, ", pg_temp");
987 
988  (void) set_config_option("search_path", pathbuf.data,
990  GUC_ACTION_SAVE, true, 0, false);
991 
992  /*
993  * Set creating_extension and related variables so that
994  * recordDependencyOnCurrentExtension and other functions do the right
995  * things. On failure, ensure we reset these variables.
996  */
997  creating_extension = true;
998  CurrentExtensionObject = extensionOid;
999  PG_TRY();
1000  {
1001  char *c_sql = read_extension_script_file(control, filename);
1002  Datum t_sql;
1003 
1004  /* We use various functions that want to operate on text datums */
1005  t_sql = CStringGetTextDatum(c_sql);
1006 
1007  /*
1008  * Reduce any lines beginning with "\echo" to empty. This allows
1009  * scripts to contain messages telling people not to run them via
1010  * psql, which has been found to be necessary due to old habits.
1011  */
1013  C_COLLATION_OID,
1014  t_sql,
1015  CStringGetTextDatum("^\\\\echo.*$"),
1016  CStringGetTextDatum(""),
1017  CStringGetTextDatum("ng"));
1018 
1019  /*
1020  * If the script uses @extowner@, substitute the calling username.
1021  */
1022  if (strstr(c_sql, "@extowner@"))
1023  {
1024  Oid uid = switch_to_superuser ? save_userid : GetUserId();
1025  const char *userName = GetUserNameFromId(uid, false);
1026  const char *qUserName = quote_identifier(userName);
1027 
1029  C_COLLATION_OID,
1030  t_sql,
1031  CStringGetTextDatum("@extowner@"),
1032  CStringGetTextDatum(qUserName));
1033  }
1034 
1035  /*
1036  * If it's not relocatable, substitute the target schema name for
1037  * occurrences of @extschema@.
1038  *
1039  * For a relocatable extension, we needn't do this. There cannot be
1040  * any need for @extschema@, else it wouldn't be relocatable.
1041  */
1042  if (!control->relocatable)
1043  {
1044  const char *qSchemaName = quote_identifier(schemaName);
1045 
1047  C_COLLATION_OID,
1048  t_sql,
1049  CStringGetTextDatum("@extschema@"),
1050  CStringGetTextDatum(qSchemaName));
1051  }
1052 
1053  /*
1054  * Likewise, substitute required extensions' schema names for
1055  * occurrences of @extschema:extension_name@.
1056  */
1057  Assert(list_length(control->requires) == list_length(requiredSchemas));
1058  forboth(lc, control->requires, lc2, requiredSchemas)
1059  {
1060  char *reqextname = (char *) lfirst(lc);
1061  Oid reqschema = lfirst_oid(lc2);
1062  char *schemaName = get_namespace_name(reqschema);
1063  const char *qSchemaName = quote_identifier(schemaName);
1064  char *repltoken;
1065 
1066  repltoken = psprintf("@extschema:%s@", reqextname);
1068  C_COLLATION_OID,
1069  t_sql,
1070  CStringGetTextDatum(repltoken),
1071  CStringGetTextDatum(qSchemaName));
1072  }
1073 
1074  /*
1075  * If module_pathname was set in the control file, substitute its
1076  * value for occurrences of MODULE_PATHNAME.
1077  */
1078  if (control->module_pathname)
1079  {
1081  C_COLLATION_OID,
1082  t_sql,
1083  CStringGetTextDatum("MODULE_PATHNAME"),
1085  }
1086 
1087  /* And now back to C string */
1088  c_sql = text_to_cstring(DatumGetTextPP(t_sql));
1089 
1090  execute_sql_string(c_sql);
1091  }
1092  PG_FINALLY();
1093  {
1094  creating_extension = false;
1096  }
1097  PG_END_TRY();
1098 
1099  /*
1100  * Restore the GUC variables we set above.
1101  */
1102  AtEOXact_GUC(true, save_nestlevel);
1103 
1104  /*
1105  * Restore authentication state if needed.
1106  */
1107  if (switch_to_superuser)
1108  SetUserIdAndSecContext(save_userid, save_sec_context);
1109 }
int errhint(const char *fmt,...)
Definition: elog.c:1316
#define PG_TRY(...)
Definition: elog.h:370
#define WARNING
Definition: elog.h:36
#define PG_END_TRY(...)
Definition: elog.h:395
#define DEBUG1
Definition: elog.h:30
#define PG_FINALLY(...)
Definition: elog.h:387
static char * read_extension_script_file(const ExtensionControlFile *control, const char *filename)
Definition: extension.c:699
static void execute_sql_string(const char *sql)
Definition: extension.c:740
Oid CurrentExtensionObject
Definition: extension.c:74
static bool extension_is_trusted(ExtensionControlFile *control)
Definition: extension.c:847
Datum DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4)
Definition: fmgr.c:831
Datum DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:806
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:3324
int NewGUCNestLevel(void)
Definition: guc.c:2201
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2215
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:3284
@ GUC_ACTION_SAVE
Definition: guc.h:199
@ 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:501
int client_min_messages
Definition: guc_tables.c:512
int log_min_messages
Definition: guc_tables.c:511
#define SECURITY_LOCAL_USERID_CHANGE
Definition: miscadmin.h:304
char * GetUserNameFromId(Oid roleid, bool noerr)
Definition: miscinit.c:984
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:631
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:638
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:467
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
Datum textregexreplace(PG_FUNCTION_ARGS)
Definition: regexp.c:644
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:11751
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
char * module_pathname
Definition: extension.c:84
bool superuser(void)
Definition: superuser.c:46
Datum replace_text(PG_FUNCTION_ARGS)
Definition: varlena.c:3994

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)
static

Definition at line 740 of file extension.c.

741 {
742  List *raw_parsetree_list;
744  ListCell *lc1;
745 
746  /*
747  * Parse the SQL string into a list of raw parse trees.
748  */
749  raw_parsetree_list = pg_parse_query(sql);
750 
751  /* All output from SELECTs goes to the bit bucket */
753 
754  /*
755  * Do parse analysis, rule rewrite, planning, and execution for each raw
756  * parsetree. We must fully execute each query before beginning parse
757  * analysis on the next one, since there may be interdependencies.
758  */
759  foreach(lc1, raw_parsetree_list)
760  {
761  RawStmt *parsetree = lfirst_node(RawStmt, lc1);
762  MemoryContext per_parsetree_context,
763  oldcontext;
764  List *stmt_list;
765  ListCell *lc2;
766 
767  /*
768  * We do the work for each parsetree in a short-lived context, to
769  * limit the memory used when there are many commands in the string.
770  */
771  per_parsetree_context =
773  "execute_sql_string per-statement context",
775  oldcontext = MemoryContextSwitchTo(per_parsetree_context);
776 
777  /* Be sure parser can see any DDL done so far */
779 
780  stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
781  sql,
782  NULL,
783  0,
784  NULL);
785  stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
786 
787  foreach(lc2, stmt_list)
788  {
790 
792 
794 
795  if (stmt->utilityStmt == NULL)
796  {
797  QueryDesc *qdesc;
798 
799  qdesc = CreateQueryDesc(stmt,
800  sql,
801  GetActiveSnapshot(), NULL,
802  dest, NULL, NULL, 0);
803 
804  ExecutorStart(qdesc, 0);
805  ExecutorRun(qdesc, ForwardScanDirection, 0, true);
806  ExecutorFinish(qdesc);
807  ExecutorEnd(qdesc);
808 
809  FreeQueryDesc(qdesc);
810  }
811  else
812  {
813  if (IsA(stmt->utilityStmt, TransactionStmt))
814  ereport(ERROR,
815  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
816  errmsg("transaction control statements are not allowed within an extension script")));
817 
819  sql,
820  false,
822  NULL,
823  NULL,
824  dest,
825  NULL);
826  }
827 
829  }
830 
831  /* Clean up per-parsetree context. */
832  MemoryContextSwitchTo(oldcontext);
833  MemoryContextDelete(per_parsetree_context);
834  }
835 
836  /* Be sure to advance the command counter after the last script command */
838 }
DestReceiver * CreateDestReceiver(CommandDest dest)
Definition: dest.c:113
@ DestNone
Definition: dest.h:87
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:462
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:402
void ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:132
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:301
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:387
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
#define CURSOR_OPT_PARALLEL_OK
Definition: parsenodes.h:3124
#define lfirst_node(type, lc)
Definition: pg_list.h:176
List * pg_parse_query(const char *query_string)
Definition: postgres.c:577
List * pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char *query_string, const Oid *paramTypes, int numParams, QueryEnvironment *queryEnv)
Definition: postgres.c:637
List * pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:938
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
@ ForwardScanDirection
Definition: sdir.h:28
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:251
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:683
void PopActiveSnapshot(void)
Definition: snapmgr.c:778
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:805
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:506
@ PROCESS_UTILITY_QUERY
Definition: utility.h:23
void CommandCounterIncrement(void)
Definition: xact.c:1078

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, CommandCounterIncrement(), CreateDestReceiver(), CreateQueryDesc(), CurrentMemoryContext, CURSOR_OPT_PARALLEL_OK, generate_unaccent_rules::dest, DestNone, ereport, errcode(), errmsg(), ERROR, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), ForwardScanDirection, FreeQueryDesc(), GetActiveSnapshot(), GetTransactionSnapshot(), IsA, lfirst_node, MemoryContextDelete(), MemoryContextSwitchTo(), pg_analyze_and_rewrite_fixedparams(), pg_parse_query(), pg_plan_queries(), PopActiveSnapshot(), PROCESS_UTILITY_QUERY, ProcessUtility(), PushActiveSnapshot(), and stmt.

Referenced by execute_extension_script().

◆ extension_config_remove()

static void extension_config_remove ( Oid  extensionoid,
Oid  tableoid 
)
static

Definition at line 2584 of file extension.c.

2585 {
2586  Relation extRel;
2587  ScanKeyData key[1];
2588  SysScanDesc extScan;
2589  HeapTuple extTup;
2590  Datum arrayDatum;
2591  int arrayLength;
2592  int arrayIndex;
2593  bool isnull;
2594  Datum repl_val[Natts_pg_extension];
2595  bool repl_null[Natts_pg_extension];
2596  bool repl_repl[Natts_pg_extension];
2597  ArrayType *a;
2598 
2599  /* Find the pg_extension tuple */
2600  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2601 
2602  ScanKeyInit(&key[0],
2603  Anum_pg_extension_oid,
2604  BTEqualStrategyNumber, F_OIDEQ,
2605  ObjectIdGetDatum(extensionoid));
2606 
2607  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2608  NULL, 1, key);
2609 
2610  extTup = systable_getnext(extScan);
2611 
2612  if (!HeapTupleIsValid(extTup)) /* should not happen */
2613  elog(ERROR, "could not find tuple for extension %u",
2614  extensionoid);
2615 
2616  /* Search extconfig for the tableoid */
2617  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2618  RelationGetDescr(extRel), &isnull);
2619  if (isnull)
2620  {
2621  /* nothing to do */
2622  a = NULL;
2623  arrayLength = 0;
2624  arrayIndex = -1;
2625  }
2626  else
2627  {
2628  Oid *arrayData;
2629  int i;
2630 
2631  a = DatumGetArrayTypeP(arrayDatum);
2632 
2633  arrayLength = ARR_DIMS(a)[0];
2634  if (ARR_NDIM(a) != 1 ||
2635  ARR_LBOUND(a)[0] != 1 ||
2636  arrayLength < 0 ||
2637  ARR_HASNULL(a) ||
2638  ARR_ELEMTYPE(a) != OIDOID)
2639  elog(ERROR, "extconfig is not a 1-D Oid array");
2640  arrayData = (Oid *) ARR_DATA_PTR(a);
2641 
2642  arrayIndex = -1; /* flag for no deletion needed */
2643 
2644  for (i = 0; i < arrayLength; i++)
2645  {
2646  if (arrayData[i] == tableoid)
2647  {
2648  arrayIndex = i; /* index to remove */
2649  break;
2650  }
2651  }
2652  }
2653 
2654  /* If tableoid is not in extconfig, nothing to do */
2655  if (arrayIndex < 0)
2656  {
2657  systable_endscan(extScan);
2658  table_close(extRel, RowExclusiveLock);
2659  return;
2660  }
2661 
2662  /* Modify or delete the extconfig value */
2663  memset(repl_val, 0, sizeof(repl_val));
2664  memset(repl_null, false, sizeof(repl_null));
2665  memset(repl_repl, false, sizeof(repl_repl));
2666 
2667  if (arrayLength <= 1)
2668  {
2669  /* removing only element, just set array to null */
2670  repl_null[Anum_pg_extension_extconfig - 1] = true;
2671  }
2672  else
2673  {
2674  /* squeeze out the target element */
2675  Datum *dvalues;
2676  int nelems;
2677  int i;
2678 
2679  /* We already checked there are no nulls */
2680  deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
2681 
2682  for (i = arrayIndex; i < arrayLength - 1; i++)
2683  dvalues[i] = dvalues[i + 1];
2684 
2685  a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
2686 
2687  repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2688  }
2689  repl_repl[Anum_pg_extension_extconfig - 1] = true;
2690 
2691  /* Modify or delete the extcondition value */
2692  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2693  RelationGetDescr(extRel), &isnull);
2694  if (isnull)
2695  {
2696  elog(ERROR, "extconfig and extcondition arrays do not match");
2697  }
2698  else
2699  {
2700  a = DatumGetArrayTypeP(arrayDatum);
2701 
2702  if (ARR_NDIM(a) != 1 ||
2703  ARR_LBOUND(a)[0] != 1 ||
2704  ARR_HASNULL(a) ||
2705  ARR_ELEMTYPE(a) != TEXTOID)
2706  elog(ERROR, "extcondition is not a 1-D text array");
2707  if (ARR_DIMS(a)[0] != arrayLength)
2708  elog(ERROR, "extconfig and extcondition arrays do not match");
2709  }
2710 
2711  if (arrayLength <= 1)
2712  {
2713  /* removing only element, just set array to null */
2714  repl_null[Anum_pg_extension_extcondition - 1] = true;
2715  }
2716  else
2717  {
2718  /* squeeze out the target element */
2719  Datum *dvalues;
2720  int nelems;
2721  int i;
2722 
2723  /* We already checked there are no nulls */
2724  deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
2725 
2726  for (i = arrayIndex; i < arrayLength - 1; i++)
2727  dvalues[i] = dvalues[i + 1];
2728 
2729  a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
2730 
2731  repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2732  }
2733  repl_repl[Anum_pg_extension_extcondition - 1] = true;
2734 
2735  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2736  repl_val, repl_null, repl_repl);
2737 
2738  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2739 
2740  systable_endscan(extScan);
2741 
2742  table_close(extRel, RowExclusiveLock);
2743 }
#define ARR_NDIM(a)
Definition: array.h:283
#define ARR_DATA_PTR(a)
Definition: array.h:315
#define DatumGetArrayTypeP(X)
Definition: array.h:254
#define ARR_ELEMTYPE(a)
Definition: array.h:285
#define ARR_DIMS(a)
Definition: array.h:287
#define ARR_HASNULL(a)
Definition: array.h:284
#define ARR_LBOUND(a)
Definition: array.h:289
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3666
int i
Definition: isn.c:73

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 ExecAlterExtensionContentsStmt().

◆ extension_file_exists()

bool extension_file_exists ( const char *  extensionName)

Definition at line 2237 of file extension.c.

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

848 {
849  AclResult aclresult;
850 
851  /* Never trust unless extension's control file says it's okay */
852  if (!control->trusted)
853  return false;
854  /* Allow if user has CREATE privilege on current database */
855  aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
856  if (aclresult == ACLCHECK_OK)
857  return true;
858  return false;
859 }
Oid MyDatabaseId
Definition: globals.c:89

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

1382 {
1383  ExtensionVersionInfo *evi_start = NULL;
1384  ListCell *lc;
1385 
1386  *best_path = NIL;
1387 
1388  /*
1389  * We don't expect to be called for an installable target, but if we are,
1390  * the answer is easy: just start from there, with an empty update path.
1391  */
1392  if (evi_target->installable)
1393  return evi_target;
1394 
1395  /* Consider all installable versions as start points */
1396  foreach(lc, evi_list)
1397  {
1399  List *path;
1400 
1401  if (!evi1->installable)
1402  continue;
1403 
1404  /*
1405  * Find shortest path from evi1 to evi_target; but no need to consider
1406  * paths going through other installable versions.
1407  */
1408  path = find_update_path(evi_list, evi1, evi_target, true, true);
1409  if (path == NIL)
1410  continue;
1411 
1412  /* Remember best path */
1413  if (evi_start == NULL ||
1414  list_length(path) < list_length(*best_path) ||
1415  (list_length(path) == list_length(*best_path) &&
1416  strcmp(evi_start->name, evi1->name) < 0))
1417  {
1418  evi_start = evi1;
1419  *best_path = path;
1420  }
1421  }
1422 
1423  return evi_start;
1424 }
static List * find_update_path(List *evi_list, ExtensionVersionInfo *evi_start, ExtensionVersionInfo *evi_target, bool reject_indirect, bool reinitialize)
Definition: extension.c:1287

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

1292 {
1293  List *result;
1294  ExtensionVersionInfo *evi;
1295  ListCell *lc;
1296 
1297  /* Caller error if start == target */
1298  Assert(evi_start != evi_target);
1299  /* Caller error if reject_indirect and target is installable */
1300  Assert(!(reject_indirect && evi_target->installable));
1301 
1302  if (reinitialize)
1303  {
1304  foreach(lc, evi_list)
1305  {
1306  evi = (ExtensionVersionInfo *) lfirst(lc);
1307  evi->distance_known = false;
1308  evi->distance = INT_MAX;
1309  evi->previous = NULL;
1310  }
1311  }
1312 
1313  evi_start->distance = 0;
1314 
1315  while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
1316  {
1317  if (evi->distance == INT_MAX)
1318  break; /* all remaining vertices are unreachable */
1319  evi->distance_known = true;
1320  if (evi == evi_target)
1321  break; /* found shortest path to target */
1322  foreach(lc, evi->reachable)
1323  {
1325  int newdist;
1326 
1327  /* if reject_indirect, treat installable versions as unreachable */
1328  if (reject_indirect && evi2->installable)
1329  continue;
1330  newdist = evi->distance + 1;
1331  if (newdist < evi2->distance)
1332  {
1333  evi2->distance = newdist;
1334  evi2->previous = evi;
1335  }
1336  else if (newdist == evi2->distance &&
1337  evi2->previous != NULL &&
1338  strcmp(evi->name, evi2->previous->name) < 0)
1339  {
1340  /*
1341  * Break ties in favor of the version name that comes first
1342  * according to strcmp(). This behavior is undocumented and
1343  * users shouldn't rely on it. We do it just to ensure that
1344  * if there is a tie, the update path that is chosen does not
1345  * depend on random factors like the order in which directory
1346  * entries get visited.
1347  */
1348  evi2->previous = evi;
1349  }
1350  }
1351  }
1352 
1353  /* Return NIL if target is not reachable from start */
1354  if (!evi_target->distance_known)
1355  return NIL;
1356 
1357  /* Build and return list of version names representing the update path */
1358  result = NIL;
1359  for (evi = evi_target; evi != evi_start; evi = evi->previous)
1360  result = lcons(evi->name, result);
1361 
1362  return result;
1363 }
static ExtensionVersionInfo * get_nearest_unprocessed_vertex(List *evi_list)
Definition: extension.c:1153
List * lcons(void *datum, List *list)
Definition: list.c:494
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 2123 of file extension.c.

2126 {
2127  List *evi_list;
2128  ListCell *lc;
2129 
2130  /* Extract the version update graph from the script directory */
2131  evi_list = get_ext_ver_list(pcontrol);
2132 
2133  /* For each installable version ... */
2134  foreach(lc, evi_list)
2135  {
2137  ExtensionControlFile *control;
2138  Datum values[8];
2139  bool nulls[8];
2140  ListCell *lc2;
2141 
2142  if (!evi->installable)
2143  continue;
2144 
2145  /*
2146  * Fetch parameters for specific version (pcontrol is not changed)
2147  */
2148  control = read_extension_aux_control_file(pcontrol, evi->name);
2149 
2150  memset(values, 0, sizeof(values));
2151  memset(nulls, 0, sizeof(nulls));
2152 
2153  /* name */
2155  CStringGetDatum(control->name));
2156  /* version */
2157  values[1] = CStringGetTextDatum(evi->name);
2158  /* superuser */
2159  values[2] = BoolGetDatum(control->superuser);
2160  /* trusted */
2161  values[3] = BoolGetDatum(control->trusted);
2162  /* relocatable */
2163  values[4] = BoolGetDatum(control->relocatable);
2164  /* schema */
2165  if (control->schema == NULL)
2166  nulls[5] = true;
2167  else
2169  CStringGetDatum(control->schema));
2170  /* requires */
2171  if (control->requires == NIL)
2172  nulls[6] = true;
2173  else
2174  values[6] = convert_requires_to_datum(control->requires);
2175  /* comment */
2176  if (control->comment == NULL)
2177  nulls[7] = true;
2178  else
2179  values[7] = CStringGetTextDatum(control->comment);
2180 
2181  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2182 
2183  /*
2184  * Find all non-directly-installable versions that would be installed
2185  * starting from this version, and report them, inheriting the
2186  * parameters that aren't changed in updates from this version.
2187  */
2188  foreach(lc2, evi_list)
2189  {
2191  List *best_path;
2192 
2193  if (evi2->installable)
2194  continue;
2195  if (find_install_path(evi_list, evi2, &best_path) == evi)
2196  {
2197  /*
2198  * Fetch parameters for this version (pcontrol is not changed)
2199  */
2200  control = read_extension_aux_control_file(pcontrol, evi2->name);
2201 
2202  /* name stays the same */
2203  /* version */
2204  values[1] = CStringGetTextDatum(evi2->name);
2205  /* superuser */
2206  values[2] = BoolGetDatum(control->superuser);
2207  /* trusted */
2208  values[3] = BoolGetDatum(control->trusted);
2209  /* relocatable */
2210  values[4] = BoolGetDatum(control->relocatable);
2211  /* schema stays the same */
2212  /* requires */
2213  if (control->requires == NIL)
2214  nulls[6] = true;
2215  else
2216  {
2217  values[6] = convert_requires_to_datum(control->requires);
2218  nulls[6] = false;
2219  }
2220  /* comment stays the same */
2221 
2222  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2223  }
2224  }
2225  }
2226 }
static Datum convert_requires_to_datum(List *requires)
Definition: extension.c:2290
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750

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

1121 {
1122  ExtensionVersionInfo *evi;
1123  ListCell *lc;
1124 
1125  foreach(lc, *evi_list)
1126  {
1127  evi = (ExtensionVersionInfo *) lfirst(lc);
1128  if (strcmp(evi->name, versionname) == 0)
1129  return evi;
1130  }
1131 
1133  evi->name = pstrdup(versionname);
1134  evi->reachable = NIL;
1135  evi->installable = false;
1136  /* initialize for later application of Dijkstra's algorithm */
1137  evi->distance_known = false;
1138  evi->distance = INT_MAX;
1139  evi->previous = NULL;
1140 
1141  *evi_list = lappend(*evi_list, evi);
1142 
1143  return evi;
1144 }
List * lappend(List *list, void *datum)
Definition: list.c:338

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

1182 {
1183  List *evi_list = NIL;
1184  int extnamelen = strlen(control->name);
1185  char *location;
1186  DIR *dir;
1187  struct dirent *de;
1188 
1189  location = get_extension_script_directory(control);
1190  dir = AllocateDir(location);
1191  while ((de = ReadDir(dir, location)) != NULL)
1192  {
1193  char *vername;
1194  char *vername2;
1195  ExtensionVersionInfo *evi;
1196  ExtensionVersionInfo *evi2;
1197 
1198  /* must be a .sql file ... */
1200  continue;
1201 
1202  /* ... matching extension name followed by separator */
1203  if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
1204  de->d_name[extnamelen] != '-' ||
1205  de->d_name[extnamelen + 1] != '-')
1206  continue;
1207 
1208  /* extract version name(s) from 'extname--something.sql' filename */
1209  vername = pstrdup(de->d_name + extnamelen + 2);
1210  *strrchr(vername, '.') = '\0';
1211  vername2 = strstr(vername, "--");
1212  if (!vername2)
1213  {
1214  /* It's an install, not update, script; record its version name */
1215  evi = get_ext_ver_info(vername, &evi_list);
1216  evi->installable = true;
1217  continue;
1218  }
1219  *vername2 = '\0'; /* terminate first version */
1220  vername2 += 2; /* and point to second */
1221 
1222  /* if there's a third --, it's bogus, ignore it */
1223  if (strstr(vername2, "--"))
1224  continue;
1225 
1226  /* Create ExtensionVersionInfos and link them together */
1227  evi = get_ext_ver_info(vername, &evi_list);
1228  evi2 = get_ext_ver_info(vername2, &evi_list);
1229  evi->reachable = lappend(evi->reachable, evi2);
1230  }
1231  FreeDir(dir);
1232 
1233  return evi_list;
1234 }
static bool is_extension_script_filename(const char *filename)
Definition: extension.c:367
static char * get_extension_script_directory(ExtensionControlFile *control)
Definition: extension.c:402

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

427 {
428  char *result;
429  char *scriptdir;
430 
431  scriptdir = get_extension_script_directory(control);
432 
433  result = (char *) palloc(MAXPGPATH);
434  snprintf(result, MAXPGPATH, "%s/%s--%s.control",
435  scriptdir, control->name, version);
436 
437  pfree(scriptdir);
438 
439  return result;
440 }
void pfree(void *pointer)
Definition: mcxt.c:1436
#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 375 of file extension.c.

376 {
377  char sharepath[MAXPGPATH];
378  char *result;
379 
380  get_share_path(my_exec_path, sharepath);
381  result = (char *) palloc(MAXPGPATH);
382  snprintf(result, MAXPGPATH, "%s/extension", sharepath);
383 
384  return result;
385 }
char my_exec_path[MAXPGPATH]
Definition: globals.c:76
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 388 of file extension.c.

389 {
390  char sharepath[MAXPGPATH];
391  char *result;
392 
393  get_share_path(my_exec_path, sharepath);
394  result = (char *) palloc(MAXPGPATH);
395  snprintf(result, MAXPGPATH, "%s/extension/%s.control",
396  sharepath, extname);
397 
398  return result;
399 }

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

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

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

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

◆ get_extension_oid()

Oid get_extension_oid ( const char *  extname,
bool  missing_ok 
)

Definition at line 144 of file extension.c.

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

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

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

◆ get_extension_schema()

Oid get_extension_schema ( Oid  ext_oid)

Definition at line 228 of file extension.c.

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

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

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

◆ get_extension_script_directory()

static char* get_extension_script_directory ( ExtensionControlFile control)
static

Definition at line 402 of file extension.c.

403 {
404  char sharepath[MAXPGPATH];
405  char *result;
406 
407  /*
408  * The directory parameter can be omitted, absolute, or relative to the
409  * installation's share directory.
410  */
411  if (!control->directory)
413 
414  if (is_absolute_path(control->directory))
415  return pstrdup(control->directory);
416 
417  get_share_path(my_exec_path, sharepath);
418  result = (char *) palloc(MAXPGPATH);
419  snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
420 
421  return result;
422 }
#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 443 of file extension.c.

445 {
446  char *result;
447  char *scriptdir;
448 
449  scriptdir = get_extension_script_directory(control);
450 
451  result = (char *) palloc(MAXPGPATH);
452  if (from_version)
453  snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
454  scriptdir, control->name, from_version, version);
455  else
456  snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
457  scriptdir, control->name, version);
458 
459  pfree(scriptdir);
460 
461  return result;
462 }

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

1154 {
1155  ExtensionVersionInfo *evi = NULL;
1156  ListCell *lc;
1157 
1158  foreach(lc, evi_list)
1159  {
1161 
1162  /* only vertices whose distance is still uncertain are candidates */
1163  if (evi2->distance_known)
1164  continue;
1165  /* remember the closest such vertex */
1166  if (evi == NULL ||
1167  evi->distance > evi2->distance)
1168  evi = evi2;
1169  }
1170 
1171  return evi;
1172 }

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

1680 {
1681  Oid reqExtensionOid;
1682 
1683  reqExtensionOid = get_extension_oid(reqExtensionName, true);
1684  if (!OidIsValid(reqExtensionOid))
1685  {
1686  if (cascade)
1687  {
1688  /* Must install it. */
1689  ObjectAddress addr;
1690  List *cascade_parents;
1691  ListCell *lc;
1692 
1693  /* Check extension name validity before trying to cascade. */
1694  check_valid_extension_name(reqExtensionName);
1695 
1696  /* Check for cyclic dependency between extensions. */
1697  foreach(lc, parents)
1698  {
1699  char *pname = (char *) lfirst(lc);
1700 
1701  if (strcmp(pname, reqExtensionName) == 0)
1702  ereport(ERROR,
1703  (errcode(ERRCODE_INVALID_RECURSION),
1704  errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
1705  reqExtensionName, extensionName)));
1706  }
1707 
1708  ereport(NOTICE,
1709  (errmsg("installing required extension \"%s\"",
1710  reqExtensionName)));
1711 
1712  /* Add current extension to list of parents to pass down. */
1713  cascade_parents = lappend(list_copy(parents), extensionName);
1714 
1715  /*
1716  * Create the required extension. We propagate the SCHEMA option
1717  * if any, and CASCADE, but no other options.
1718  */
1719  addr = CreateExtensionInternal(reqExtensionName,
1720  origSchemaName,
1721  NULL,
1722  cascade,
1723  cascade_parents,
1724  is_create);
1725 
1726  /* Get its newly-assigned OID. */
1727  reqExtensionOid = addr.objectId;
1728  }
1729  else
1730  ereport(ERROR,
1731  (errcode(ERRCODE_UNDEFINED_OBJECT),
1732  errmsg("required extension \"%s\" is not installed",
1733  reqExtensionName),
1734  is_create ?
1735  errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
1736  }
1737 
1738  return reqExtensionOid;
1739 }
List * list_copy(const List *oldlist)
Definition: list.c:1572

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

1246 {
1247  List *result;
1248  List *evi_list;
1249  ExtensionVersionInfo *evi_start;
1250  ExtensionVersionInfo *evi_target;
1251 
1252  /* Extract the version update graph from the script directory */
1253  evi_list = get_ext_ver_list(control);
1254 
1255  /* Initialize start and end vertices */
1256  evi_start = get_ext_ver_info(oldVersion, &evi_list);
1257  evi_target = get_ext_ver_info(newVersion, &evi_list);
1258 
1259  /* Find shortest path */
1260  result = find_update_path(evi_list, evi_start, evi_target, false, false);
1261 
1262  if (result == NIL)
1263  ereport(ERROR,
1264  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1265  errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
1266  control->name, oldVersion, newVersion)));
1267 
1268  return result;
1269 }

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

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

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

360 {
361  const char *extension = strrchr(filename, '.');
362 
363  return (extension != NULL) && (strcmp(extension, ".control") == 0);
364 }

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

368 {
369  const char *extension = strrchr(filename, '.');
370 
371  return (extension != NULL) && (strcmp(extension, ".sql") == 0);
372 }

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

477 {
478  char *filename;
479  FILE *file;
480  ConfigVariable *item,
481  *head = NULL,
482  *tail = NULL;
483 
484  /*
485  * Locate the file to read. Auxiliary files are optional.
486  */
487  if (version)
488  filename = get_extension_aux_control_filename(control, version);
489  else
491 
492  if ((file = AllocateFile(filename, "r")) == NULL)
493  {
494  if (errno == ENOENT)
495  {
496  /* no complaint for missing auxiliary file */
497  if (version)
498  {
499  pfree(filename);
500  return;
501  }
502 
503  /* missing control file indicates extension is not installed */
504  ereport(ERROR,
505  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
506  errmsg("extension \"%s\" is not available", control->name),
507  errdetail("Could not open extension control file \"%s\": %m.",
508  filename),
509  errhint("The extension must first be installed on the system where PostgreSQL is running.")));
510  }
511  ereport(ERROR,
513  errmsg("could not open extension control file \"%s\": %m",
514  filename)));
515  }
516 
517  /*
518  * Parse the file content, using GUC's file parsing code. We need not
519  * check the return value since any errors will be thrown at ERROR level.
520  */
522  &head, &tail);
523 
524  FreeFile(file);
525 
526  /*
527  * Convert the ConfigVariable list into ExtensionControlFile entries.
528  */
529  for (item = head; item != NULL; item = item->next)
530  {
531  if (strcmp(item->name, "directory") == 0)
532  {
533  if (version)
534  ereport(ERROR,
535  (errcode(ERRCODE_SYNTAX_ERROR),
536  errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
537  item->name)));
538 
539  control->directory = pstrdup(item->value);
540  }
541  else if (strcmp(item->name, "default_version") == 0)
542  {
543  if (version)
544  ereport(ERROR,
545  (errcode(ERRCODE_SYNTAX_ERROR),
546  errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
547  item->name)));
548 
549  control->default_version = pstrdup(item->value);
550  }
551  else if (strcmp(item->name, "module_pathname") == 0)
552  {
553  control->module_pathname = pstrdup(item->value);
554  }
555  else if (strcmp(item->name, "comment") == 0)
556  {
557  control->comment = pstrdup(item->value);
558  }
559  else if (strcmp(item->name, "schema") == 0)
560  {
561  control->schema = pstrdup(item->value);
562  }
563  else if (strcmp(item->name, "relocatable") == 0)
564  {
565  if (!parse_bool(item->value, &control->relocatable))
566  ereport(ERROR,
567  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
568  errmsg("parameter \"%s\" requires a Boolean value",
569  item->name)));
570  }
571  else if (strcmp(item->name, "superuser") == 0)
572  {
573  if (!parse_bool(item->value, &control->superuser))
574  ereport(ERROR,
575  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
576  errmsg("parameter \"%s\" requires a Boolean value",
577  item->name)));
578  }
579  else if (strcmp(item->name, "trusted") == 0)
580  {
581  if (!parse_bool(item->value, &control->trusted))
582  ereport(ERROR,
583  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
584  errmsg("parameter \"%s\" requires a Boolean value",
585  item->name)));
586  }
587  else if (strcmp(item->name, "encoding") == 0)
588  {
589  control->encoding = pg_valid_server_encoding(item->value);
590  if (control->encoding < 0)
591  ereport(ERROR,
592  (errcode(ERRCODE_UNDEFINED_OBJECT),
593  errmsg("\"%s\" is not a valid encoding name",
594  item->value)));
595  }
596  else if (strcmp(item->name, "requires") == 0)
597  {
598  /* Need a modifiable copy of string */
599  char *rawnames = pstrdup(item->value);
600 
601  /* Parse string into list of identifiers */
602  if (!SplitIdentifierString(rawnames, ',', &control->requires))
603  {
604  /* syntax error in name list */
605  ereport(ERROR,
606  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
607  errmsg("parameter \"%s\" must be a list of extension names",
608  item->name)));
609  }
610  }
611  else if (strcmp(item->name, "no_relocate") == 0)
612  {
613  /* Need a modifiable copy of string */
614  char *rawnames = pstrdup(item->value);
615 
616  /* Parse string into list of identifiers */
617  if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
618  {
619  /* syntax error in name list */
620  ereport(ERROR,
621  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
622  errmsg("parameter \"%s\" must be a list of extension names",
623  item->name)));
624  }
625  }
626  else
627  ereport(ERROR,
628  (errcode(ERRCODE_SYNTAX_ERROR),
629  errmsg("unrecognized parameter \"%s\" in file \"%s\"",
630  item->name, filename)));
631  }
632 
633  FreeConfigVariables(head);
634 
635  if (control->relocatable && control->schema != NULL)
636  ereport(ERROR,
637  (errcode(ERRCODE_SYNTAX_ERROR),
638  errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
639 
640  pfree(filename);
641 }
bool parse_bool(const char *value, bool *result)
Definition: bool.c:30
#define CONF_FILE_START_DEPTH
Definition: conffiles.h:17
int errcode_for_file_access(void)
Definition: elog.c:881
int pg_valid_server_encoding(const char *name)
Definition: encnames.c:500
static char * get_extension_control_filename(const char *extname)
Definition: extension.c:388
static char * get_extension_aux_control_filename(ExtensionControlFile *control, const char *version)
Definition: extension.c:425
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2384
int FreeFile(FILE *file)
Definition: fd.c:2582
void FreeConfigVariables(ConfigVariable *list)
bool ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, ConfigVariable **head_p, ConfigVariable **tail_p)
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:3455

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

2066 {
2067  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2068  char *location;
2069  DIR *dir;
2070  struct dirent *de;
2071 
2072  /* Build tuplestore to hold the result rows */
2073  InitMaterializedSRF(fcinfo, 0);
2074 
2075  location = get_extension_control_directory();
2076  dir = AllocateDir(location);
2077 
2078  /*
2079  * If the control directory doesn't exist, we want to silently return an
2080  * empty set. Any other error will be reported by ReadDir.
2081  */
2082  if (dir == NULL && errno == ENOENT)
2083  {
2084  /* do nothing */
2085  }
2086  else
2087  {
2088  while ((de = ReadDir(dir, location)) != NULL)
2089  {
2090  ExtensionControlFile *control;
2091  char *extname;
2092 
2093  if (!is_extension_control_filename(de->d_name))
2094  continue;
2095 
2096  /* extract extension name from 'name.control' filename */
2097  extname = pstrdup(de->d_name);
2098  *strrchr(extname, '.') = '\0';
2099 
2100  /* ignore it if it's an auxiliary control file */
2101  if (strstr(extname, "--"))
2102  continue;
2103 
2104  /* read the control file */
2105  control = read_extension_control_file(extname);
2106 
2107  /* scan extension's script directory for install scripts */
2109  rsinfo->setDesc);
2110  }
2111 
2112  FreeDir(dir);
2113  }
2114 
2115  return (Datum) 0;
2116 }
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol, Tuplestorestate *tupstore, TupleDesc tupdesc)
Definition: extension.c:2123
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
TupleDesc setDesc
Definition: execnodes.h:334
Tuplestorestate * setResult
Definition: execnodes.h:333

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

1986 {
1987  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1988  char *location;
1989  DIR *dir;
1990  struct dirent *de;
1991 
1992  /* Build tuplestore to hold the result rows */
1993  InitMaterializedSRF(fcinfo, 0);
1994 
1995  location = get_extension_control_directory();
1996  dir = AllocateDir(location);
1997 
1998  /*
1999  * If the control directory doesn't exist, we want to silently return an
2000  * empty set. Any other error will be reported by ReadDir.
2001  */
2002  if (dir == NULL && errno == ENOENT)
2003  {
2004  /* do nothing */
2005  }
2006  else
2007  {
2008  while ((de = ReadDir(dir, location)) != NULL)
2009  {
2010  ExtensionControlFile *control;
2011  char *extname;
2012  Datum values[3];
2013  bool nulls[3];
2014 
2015  if (!is_extension_control_filename(de->d_name))
2016  continue;
2017 
2018  /* extract extension name from 'name.control' filename */
2019  extname = pstrdup(de->d_name);
2020  *strrchr(extname, '.') = '\0';
2021 
2022  /* ignore it if it's an auxiliary control file */
2023  if (strstr(extname, "--"))
2024  continue;
2025 
2026  control = read_extension_control_file(extname);
2027 
2028  memset(values, 0, sizeof(values));
2029  memset(nulls, 0, sizeof(nulls));
2030 
2031  /* name */
2033  CStringGetDatum(control->name));
2034  /* default_version */
2035  if (control->default_version == NULL)
2036  nulls[1] = true;
2037  else
2039  /* comment */
2040  if (control->comment == NULL)
2041  nulls[2] = true;
2042  else
2043  values[2] = CStringGetTextDatum(control->comment);
2044 
2045  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2046  values, nulls);
2047  }
2048 
2049  FreeDir(dir);
2050  }
2051 
2052  return (Datum) 0;
2053 }

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

2402 {
2403  Oid tableoid = PG_GETARG_OID(0);
2404  text *wherecond = PG_GETARG_TEXT_PP(1);
2405  char *tablename;
2406  Relation extRel;
2407  ScanKeyData key[1];
2408  SysScanDesc extScan;
2409  HeapTuple extTup;
2410  Datum arrayDatum;
2411  Datum elementDatum;
2412  int arrayLength;
2413  int arrayIndex;
2414  bool isnull;
2415  Datum repl_val[Natts_pg_extension];
2416  bool repl_null[Natts_pg_extension];
2417  bool repl_repl[Natts_pg_extension];
2418  ArrayType *a;
2419 
2420  /*
2421  * We only allow this to be called from an extension's SQL script. We
2422  * shouldn't need any permissions check beyond that.
2423  */
2424  if (!creating_extension)
2425  ereport(ERROR,
2426  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2427  errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
2428  "pg_extension_config_dump()")));
2429 
2430  /*
2431  * Check that the table exists and is a member of the extension being
2432  * created. This ensures that we don't need to register an additional
2433  * dependency to protect the extconfig entry.
2434  */
2435  tablename = get_rel_name(tableoid);
2436  if (tablename == NULL)
2437  ereport(ERROR,
2439  errmsg("OID %u does not refer to a table", tableoid)));
2440  if (getExtensionOfObject(RelationRelationId, tableoid) !=
2442  ereport(ERROR,
2443  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2444  errmsg("table \"%s\" is not a member of the extension being created",
2445  tablename)));
2446 
2447  /*
2448  * Add the table OID and WHERE condition to the extension's extconfig and
2449  * extcondition arrays.
2450  *
2451  * If the table is already in extconfig, treat this as an update of the
2452  * WHERE condition.
2453  */
2454 
2455  /* Find the pg_extension tuple */
2456  extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2457 
2458  ScanKeyInit(&key[0],
2459  Anum_pg_extension_oid,
2460  BTEqualStrategyNumber, F_OIDEQ,
2462 
2463  extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2464  NULL, 1, key);
2465 
2466  extTup = systable_getnext(extScan);
2467 
2468  if (!HeapTupleIsValid(extTup)) /* should not happen */
2469  elog(ERROR, "could not find tuple for extension %u",
2471 
2472  memset(repl_val, 0, sizeof(repl_val));
2473  memset(repl_null, false, sizeof(repl_null));
2474  memset(repl_repl, false, sizeof(repl_repl));
2475 
2476  /* Build or modify the extconfig value */
2477  elementDatum = ObjectIdGetDatum(tableoid);
2478 
2479  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2480  RelationGetDescr(extRel), &isnull);
2481  if (isnull)
2482  {
2483  /* Previously empty extconfig, so build 1-element array */
2484  arrayLength = 0;
2485  arrayIndex = 1;
2486 
2487  a = construct_array_builtin(&elementDatum, 1, OIDOID);
2488  }
2489  else
2490  {
2491  /* Modify or extend existing extconfig array */
2492  Oid *arrayData;
2493  int i;
2494 
2495  a = DatumGetArrayTypeP(arrayDatum);
2496 
2497  arrayLength = ARR_DIMS(a)[0];
2498  if (ARR_NDIM(a) != 1 ||
2499  ARR_LBOUND(a)[0] != 1 ||
2500  arrayLength < 0 ||
2501  ARR_HASNULL(a) ||
2502  ARR_ELEMTYPE(a) != OIDOID)
2503  elog(ERROR, "extconfig is not a 1-D Oid array");
2504  arrayData = (Oid *) ARR_DATA_PTR(a);
2505 
2506  arrayIndex = arrayLength + 1; /* set up to add after end */
2507 
2508  for (i = 0; i < arrayLength; i++)
2509  {
2510  if (arrayData[i] == tableoid)
2511  {
2512  arrayIndex = i + 1; /* replace this element instead */
2513  break;
2514  }
2515  }
2516 
2517  a = array_set(a, 1, &arrayIndex,
2518  elementDatum,
2519  false,
2520  -1 /* varlena array */ ,
2521  sizeof(Oid) /* OID's typlen */ ,
2522  true /* OID's typbyval */ ,
2523  TYPALIGN_INT /* OID's typalign */ );
2524  }
2525  repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2526  repl_repl[Anum_pg_extension_extconfig - 1] = true;
2527 
2528  /* Build or modify the extcondition value */
2529  elementDatum = PointerGetDatum(wherecond);
2530 
2531  arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2532  RelationGetDescr(extRel), &isnull);
2533  if (isnull)
2534  {
2535  if (arrayLength != 0)
2536  elog(ERROR, "extconfig and extcondition arrays do not match");
2537 
2538  a = construct_array_builtin(&elementDatum, 1, TEXTOID);
2539  }
2540  else
2541  {
2542  a = DatumGetArrayTypeP(arrayDatum);
2543 
2544  if (ARR_NDIM(a) != 1 ||
2545  ARR_LBOUND(a)[0] != 1 ||
2546  ARR_HASNULL(a) ||
2547  ARR_ELEMTYPE(a) != TEXTOID)
2548  elog(ERROR, "extcondition is not a 1-D text array");
2549  if (ARR_DIMS(a)[0] != arrayLength)
2550  elog(ERROR, "extconfig and extcondition arrays do not match");
2551 
2552  /* Add or replace at same index as in extconfig */
2553  a = array_set(a, 1, &arrayIndex,
2554  elementDatum,
2555  false,
2556  -1 /* varlena array */ ,
2557  -1 /* TEXT's typlen */ ,
2558  false /* TEXT's typbyval */ ,
2559  TYPALIGN_INT /* TEXT's typalign */ );
2560  }
2561  repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2562  repl_repl[Anum_pg_extension_extcondition - 1] = true;
2563 
2564  extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2565  repl_val, repl_null, repl_repl);
2566 
2567  CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2568 
2569  systable_endscan(extScan);
2570 
2571  table_close(extRel, RowExclusiveLock);
2572 
2573  PG_RETURN_VOID();
2574 }
ArrayType * array_set(ArrayType *array, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3143
#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:1910
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:78
Definition: c.h:671

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

2317 {
2318  Name extname = PG_GETARG_NAME(0);
2319  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2320  List *evi_list;
2321  ExtensionControlFile *control;
2322  ListCell *lc1;
2323 
2324  /* Check extension name validity before any filesystem access */
2326 
2327  /* Build tuplestore to hold the result rows */
2328  InitMaterializedSRF(fcinfo, 0);
2329 
2330  /* Read the extension's control file */
2331  control = read_extension_control_file(NameStr(*extname));
2332 
2333  /* Extract the version update graph from the script directory */
2334  evi_list = get_ext_ver_list(control);
2335 
2336  /* Iterate over all pairs of versions */
2337  foreach(lc1, evi_list)
2338  {
2340  ListCell *lc2;
2341 
2342  foreach(lc2, evi_list)
2343  {
2345  List *path;
2346  Datum values[3];
2347  bool nulls[3];
2348 
2349  if (evi1 == evi2)
2350  continue;
2351 
2352  /* Find shortest path from evi1 to evi2 */
2353  path = find_update_path(evi_list, evi1, evi2, false, true);
2354 
2355  /* Emit result row */
2356  memset(values, 0, sizeof(values));
2357  memset(nulls, 0, sizeof(nulls));
2358 
2359  /* source */
2360  values[0] = CStringGetTextDatum(evi1->name);
2361  /* target */
2362  values[1] = CStringGetTextDatum(evi2->name);
2363  /* path */
2364  if (path == NIL)
2365  nulls[2] = true;
2366  else
2367  {
2368  StringInfoData pathbuf;
2369  ListCell *lcv;
2370 
2371  initStringInfo(&pathbuf);
2372  /* The path doesn't include start vertex, but show it */
2373  appendStringInfoString(&pathbuf, evi1->name);
2374  foreach(lcv, path)
2375  {
2376  char *versionName = (char *) lfirst(lcv);
2377 
2378  appendStringInfoString(&pathbuf, "--");
2379  appendStringInfoString(&pathbuf, versionName);
2380  }
2381  values[2] = CStringGetTextDatum(pathbuf.data);
2382  pfree(pathbuf.data);
2383  }
2384 
2385  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2386  values, nulls);
2387  }
2388  }
2389 
2390  return (Datum) 0;
2391 }
#define PG_GETARG_NAME(n)
Definition: fmgr.h:278
Definition: c.h:725

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

678 {
679  ExtensionControlFile *acontrol;
680 
681  /*
682  * Flat-copy the struct. Pointer fields share values with original.
683  */
684  acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
685  memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
686 
687  /*
688  * Parse the auxiliary control file, overwriting struct fields
689  */
690  parse_extension_control_file(acontrol, version);
691 
692  return acontrol;
693 }
static void parse_extension_control_file(ExtensionControlFile *control, const char *version)
Definition: extension.c:475

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

648 {
649  ExtensionControlFile *control;
650 
651  /*
652  * Set up default values. Pointer fields are initially null.
653  */
654  control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
655  control->name = pstrdup(extname);
656  control->relocatable = false;
657  control->superuser = true;
658  control->trusted = false;
659  control->encoding = -1;
660 
661  /*
662  * Parse the primary control file.
663  */
664  parse_extension_control_file(control, NULL);
665 
666  return control;
667 }
void * palloc0(Size size)
Definition: mcxt.c:1241

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

701 {
702  int src_encoding;
703  char *src_str;
704  char *dest_str;
705  int len;
706 
707  src_str = read_whole_file(filename, &len);
708 
709  /* use database encoding if not given */
710  if (control->encoding < 0)
711  src_encoding = GetDatabaseEncoding();
712  else
713  src_encoding = control->encoding;
714 
715  /* make sure that source string is valid in the expected encoding */
716  (void) pg_verify_mbstr(src_encoding, src_str, len, false);
717 
718  /*
719  * Convert the encoding to the database encoding. read_whole_file
720  * null-terminated the string, so if no conversion happens the string is
721  * valid as is.
722  */
723  dest_str = pg_any_to_server(src_str, len, src_encoding);
724 
725  return dest_str;
726 }
static char * read_whole_file(const char *filename, int *length)
Definition: extension.c:3435
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:677
int GetDatabaseEncoding(void)
Definition: mbutils.c:1268
bool pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError)
Definition: mbutils.c:1573
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 3435 of file extension.c.

3436 {
3437  char *buf;
3438  FILE *file;
3439  size_t bytes_to_read;
3440  struct stat fst;
3441 
3442  if (stat(filename, &fst) < 0)
3443  ereport(ERROR,
3445  errmsg("could not stat file \"%s\": %m", filename)));
3446 
3447  if (fst.st_size > (MaxAllocSize - 1))
3448  ereport(ERROR,
3449  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3450  errmsg("file \"%s\" is too large", filename)));
3451  bytes_to_read = (size_t) fst.st_size;
3452 
3453  if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
3454  ereport(ERROR,
3456  errmsg("could not open file \"%s\" for reading: %m",
3457  filename)));
3458 
3459  buf = (char *) palloc(bytes_to_read + 1);
3460 
3461  *length = fread(buf, 1, bytes_to_read, file);
3462 
3463  if (ferror(file))
3464  ereport(ERROR,
3466  errmsg("could not read file \"%s\": %m", filename)));
3467 
3468  FreeFile(file);
3469 
3470  buf[*length] = '\0';
3471  return buf;
3472 }
#define PG_BINARY_R
Definition: c.h:1262
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
#define MaxAllocSize
Definition: memutils.h:40
static char * buf
Definition: pg_test_fsync.c:67

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

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

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

Referenced by doDeletion().

Variable Documentation

◆ creating_extension

◆ CurrentExtensionObject