Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * extension.c
4 : * Commands to manipulate extensions
5 : *
6 : * Extensions in PostgreSQL allow management of collections of SQL objects.
7 : *
8 : * All we need internally to manage an extension is an OID so that the
9 : * dependent objects can be associated with it. An extension is created by
10 : * populating the pg_extension catalog from a "control" file.
11 : * The extension control file is parsed with the same parser we use for
12 : * postgresql.conf. An extension also has an installation script file,
13 : * containing SQL commands to create the extension's objects.
14 : *
15 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
16 : * Portions Copyright (c) 1994, Regents of the University of California
17 : *
18 : *
19 : * IDENTIFICATION
20 : * src/backend/commands/extension.c
21 : *
22 : *-------------------------------------------------------------------------
23 : */
24 : #include "postgres.h"
25 :
26 : #include <dirent.h>
27 : #include <limits.h>
28 : #include <sys/file.h>
29 : #include <sys/stat.h>
30 : #include <unistd.h>
31 :
32 : #include "access/genam.h"
33 : #include "access/htup_details.h"
34 : #include "access/relation.h"
35 : #include "access/sysattr.h"
36 : #include "access/table.h"
37 : #include "access/xact.h"
38 : #include "catalog/catalog.h"
39 : #include "catalog/dependency.h"
40 : #include "catalog/indexing.h"
41 : #include "catalog/namespace.h"
42 : #include "catalog/objectaccess.h"
43 : #include "catalog/pg_authid.h"
44 : #include "catalog/pg_collation.h"
45 : #include "catalog/pg_database.h"
46 : #include "catalog/pg_depend.h"
47 : #include "catalog/pg_extension.h"
48 : #include "catalog/pg_namespace.h"
49 : #include "catalog/pg_type.h"
50 : #include "commands/alter.h"
51 : #include "commands/comment.h"
52 : #include "commands/defrem.h"
53 : #include "commands/extension.h"
54 : #include "commands/schemacmds.h"
55 : #include "funcapi.h"
56 : #include "mb/pg_wchar.h"
57 : #include "miscadmin.h"
58 : #include "nodes/makefuncs.h"
59 : #include "storage/fd.h"
60 : #include "tcop/utility.h"
61 : #include "utils/acl.h"
62 : #include "utils/builtins.h"
63 : #include "utils/conffiles.h"
64 : #include "utils/fmgroids.h"
65 : #include "utils/lsyscache.h"
66 : #include "utils/memutils.h"
67 : #include "utils/rel.h"
68 : #include "utils/snapmgr.h"
69 : #include "utils/varlena.h"
70 :
71 :
72 : /* Globally visible state variables */
73 : bool creating_extension = false;
74 : Oid CurrentExtensionObject = InvalidOid;
75 :
76 : /*
77 : * Internal data structure to hold the results of parsing a control file
78 : */
79 : typedef struct ExtensionControlFile
80 : {
81 : char *name; /* name of the extension */
82 : char *directory; /* directory for script files */
83 : char *default_version; /* default install target version, if any */
84 : char *module_pathname; /* string to substitute for
85 : * MODULE_PATHNAME */
86 : char *comment; /* comment, if any */
87 : char *schema; /* target schema (allowed if !relocatable) */
88 : bool relocatable; /* is ALTER EXTENSION SET SCHEMA supported? */
89 : bool superuser; /* must be superuser to install? */
90 : bool trusted; /* allow becoming superuser on the fly? */
91 : int encoding; /* encoding of the script file, or -1 */
92 : List *requires; /* names of prerequisite extensions */
93 : List *no_relocate; /* names of prerequisite extensions that
94 : * should not be relocated */
95 : } ExtensionControlFile;
96 :
97 : /*
98 : * Internal data structure for update path information
99 : */
100 : typedef struct ExtensionVersionInfo
101 : {
102 : char *name; /* name of the starting version */
103 : List *reachable; /* List of ExtensionVersionInfo's */
104 : bool installable; /* does this version have an install script? */
105 : /* working state for Dijkstra's algorithm: */
106 : bool distance_known; /* is distance from start known yet? */
107 : int distance; /* current worst-case distance estimate */
108 : struct ExtensionVersionInfo *previous; /* current best predecessor */
109 : } ExtensionVersionInfo;
110 :
111 : /* Local functions */
112 : static List *find_update_path(List *evi_list,
113 : ExtensionVersionInfo *evi_start,
114 : ExtensionVersionInfo *evi_target,
115 : bool reject_indirect,
116 : bool reinitialize);
117 : static Oid get_required_extension(char *reqExtensionName,
118 : char *extensionName,
119 : char *origSchemaName,
120 : bool cascade,
121 : List *parents,
122 : bool is_create);
123 : static void get_available_versions_for_extension(ExtensionControlFile *pcontrol,
124 : Tuplestorestate *tupstore,
125 : TupleDesc tupdesc);
126 : static Datum convert_requires_to_datum(List *requires);
127 : static void ApplyExtensionUpdates(Oid extensionOid,
128 : ExtensionControlFile *pcontrol,
129 : const char *initialVersion,
130 : List *updateVersions,
131 : char *origSchemaName,
132 : bool cascade,
133 : bool is_create);
134 : static char *read_whole_file(const char *filename, int *length);
135 :
136 :
137 : /*
138 : * get_extension_oid - given an extension name, look up the OID
139 : *
140 : * If missing_ok is false, throw an error if extension name not found. If
141 : * true, just return InvalidOid.
142 : */
143 : Oid
4443 tgl 144 GIC 1501 : get_extension_oid(const char *extname, bool missing_ok)
145 : {
146 : Oid result;
147 : Relation rel;
4443 tgl 148 ECB : SysScanDesc scandesc;
149 : HeapTuple tuple;
150 : ScanKeyData entry[1];
151 :
1539 andres 152 GIC 1501 : rel = table_open(ExtensionRelationId, AccessShareLock);
153 :
4443 tgl 154 1501 : ScanKeyInit(&entry[0],
155 : Anum_pg_extension_extname,
4443 tgl 156 ECB : BTEqualStrategyNumber, F_NAMEEQ,
157 : CStringGetDatum(extname));
158 :
4443 tgl 159 GIC 1501 : scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
160 : NULL, 1, entry);
161 :
162 1501 : tuple = systable_getnext(scandesc);
4443 tgl 163 ECB :
164 : /* We assume that there can be at most one matching tuple */
4443 tgl 165 GIC 1501 : if (HeapTupleIsValid(tuple))
1601 andres 166 CBC 1015 : result = ((Form_pg_extension) GETSTRUCT(tuple))->oid;
167 : else
4443 tgl 168 GIC 486 : result = InvalidOid;
4443 tgl 169 ECB :
4443 tgl 170 CBC 1501 : systable_endscan(scandesc);
171 :
1539 andres 172 1501 : table_close(rel, AccessShareLock);
173 :
4443 tgl 174 1501 : if (!OidIsValid(result) && !missing_ok)
4382 bruce 175 GIC 6 : ereport(ERROR,
4382 bruce 176 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
177 : errmsg("extension \"%s\" does not exist",
178 : extname)));
4443 tgl 179 :
4443 tgl 180 GIC 1495 : return result;
181 : }
182 :
183 : /*
4443 tgl 184 ECB : * get_extension_name - given an extension OID, look up the name
185 : *
186 : * Returns a palloc'd string, or NULL if no such extension.
187 : */
188 : char *
4443 tgl 189 GIC 54 : get_extension_name(Oid ext_oid)
190 : {
191 : char *result;
192 : Relation rel;
4443 tgl 193 ECB : SysScanDesc scandesc;
194 : HeapTuple tuple;
195 : ScanKeyData entry[1];
196 :
1539 andres 197 GIC 54 : rel = table_open(ExtensionRelationId, AccessShareLock);
198 :
4443 tgl 199 54 : ScanKeyInit(&entry[0],
200 : Anum_pg_extension_oid,
4443 tgl 201 ECB : BTEqualStrategyNumber, F_OIDEQ,
202 : ObjectIdGetDatum(ext_oid));
203 :
4443 tgl 204 GIC 54 : scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
205 : NULL, 1, entry);
206 :
207 54 : tuple = systable_getnext(scandesc);
4443 tgl 208 ECB :
209 : /* We assume that there can be at most one matching tuple */
4443 tgl 210 GIC 54 : if (HeapTupleIsValid(tuple))
4443 tgl 211 CBC 45 : result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
212 : else
4443 tgl 213 GIC 9 : result = NULL;
4443 tgl 214 ECB :
4443 tgl 215 CBC 54 : systable_endscan(scandesc);
216 :
1539 andres 217 54 : table_close(rel, AccessShareLock);
218 :
4443 tgl 219 54 : return result;
220 : }
4443 tgl 221 ECB :
222 : /*
223 : * get_extension_schema - given an extension OID, fetch its extnamespace
224 : *
225 : * Returns InvalidOid if no such extension.
226 : */
227 : Oid
4443 tgl 228 GIC 26 : get_extension_schema(Oid ext_oid)
229 : {
230 : Oid result;
231 : Relation rel;
4443 tgl 232 ECB : SysScanDesc scandesc;
233 : HeapTuple tuple;
234 : ScanKeyData entry[1];
235 :
1539 andres 236 GIC 26 : rel = table_open(ExtensionRelationId, AccessShareLock);
237 :
4443 tgl 238 26 : ScanKeyInit(&entry[0],
239 : Anum_pg_extension_oid,
4443 tgl 240 ECB : BTEqualStrategyNumber, F_OIDEQ,
241 : ObjectIdGetDatum(ext_oid));
242 :
4443 tgl 243 GIC 26 : scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
244 : NULL, 1, entry);
245 :
246 26 : tuple = systable_getnext(scandesc);
4443 tgl 247 ECB :
248 : /* We assume that there can be at most one matching tuple */
4443 tgl 249 GIC 26 : if (HeapTupleIsValid(tuple))
4443 tgl 250 CBC 26 : result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
251 : else
4443 tgl 252 UIC 0 : result = InvalidOid;
4443 tgl 253 ECB :
4443 tgl 254 CBC 26 : systable_endscan(scandesc);
255 :
1539 andres 256 GBC 26 : table_close(rel, AccessShareLock);
257 :
4443 tgl 258 CBC 26 : return result;
259 : }
4443 tgl 260 ECB :
261 : /*
4440 262 : * Utility functions to check validity of extension and version names
263 : */
264 : static void
4440 tgl 265 GIC 471 : check_valid_extension_name(const char *extensionname)
266 : {
4438 267 471 : int namelen = strlen(extensionname);
268 :
4440 tgl 269 ECB : /*
270 : * Disallow empty names (the parser rejects empty identifiers anyway, but
4382 bruce 271 : * let's check).
272 : */
4438 tgl 273 GIC 471 : if (namelen == 0)
4438 tgl 274 UIC 0 : ereport(ERROR,
275 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
276 : errmsg("invalid extension name: \"%s\"", extensionname),
4438 tgl 277 ECB : errdetail("Extension names must not be empty.")));
4438 tgl 278 EUB :
279 : /*
280 : * No double dashes, since that would make script filenames ambiguous.
281 : */
4438 tgl 282 GIC 471 : if (strstr(extensionname, "--"))
4438 tgl 283 UIC 0 : ereport(ERROR,
284 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
285 : errmsg("invalid extension name: \"%s\"", extensionname),
4438 tgl 286 ECB : errdetail("Extension names must not contain \"--\".")));
4438 tgl 287 EUB :
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 : */
4438 tgl 294 GIC 471 : if (extensionname[0] == '-' || extensionname[namelen - 1] == '-')
4438 tgl 295 UIC 0 : ereport(ERROR,
296 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
297 : errmsg("invalid extension name: \"%s\"", extensionname),
2118 tgl 298 ECB : errdetail("Extension names must not begin or end with \"-\".")));
4438 tgl 299 EUB :
300 : /*
301 : * No directory separators either (this is sufficient to prevent ".."
302 : * style attacks).
303 : */
4440 tgl 304 GIC 471 : if (first_dir_separator(extensionname) != NULL)
4440 tgl 305 UIC 0 : ereport(ERROR,
306 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
307 : errmsg("invalid extension name: \"%s\"", extensionname),
4440 tgl 308 ECB : errdetail("Extension names must not contain directory separator characters.")));
4440 tgl 309 GBC 471 : }
310 :
311 : static void
4440 tgl 312 GIC 480 : check_valid_version_name(const char *versionname)
4440 tgl 313 ECB : {
4438 tgl 314 GIC 480 : int namelen = strlen(versionname);
315 :
4438 tgl 316 ECB : /*
317 : * Disallow empty names (we could possibly allow this, but there seems
318 : * little point).
319 : */
4438 tgl 320 GIC 480 : if (namelen == 0)
4438 tgl 321 UIC 0 : ereport(ERROR,
322 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
323 : errmsg("invalid extension version name: \"%s\"", versionname),
4438 tgl 324 ECB : errdetail("Version names must not be empty.")));
4438 tgl 325 EUB :
326 : /*
327 : * No double dashes, since that would make script filenames ambiguous.
328 : */
4438 tgl 329 GIC 480 : if (strstr(versionname, "--"))
4438 tgl 330 UIC 0 : ereport(ERROR,
331 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
332 : errmsg("invalid extension version name: \"%s\"", versionname),
4438 tgl 333 ECB : errdetail("Version names must not contain \"--\".")));
4438 tgl 334 EUB :
335 : /*
336 : * No leading or trailing dash either.
337 : */
4438 tgl 338 GIC 480 : if (versionname[0] == '-' || versionname[namelen - 1] == '-')
4440 tgl 339 UIC 0 : ereport(ERROR,
340 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
341 : errmsg("invalid extension version name: \"%s\"", versionname),
2118 tgl 342 ECB : errdetail("Version names must not begin or end with \"-\".")));
4438 tgl 343 EUB :
344 : /*
345 : * No directory separators either (this is sufficient to prevent ".."
346 : * style attacks).
347 : */
4440 tgl 348 GIC 480 : if (first_dir_separator(versionname) != NULL)
4440 tgl 349 UIC 0 : ereport(ERROR,
350 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
351 : errmsg("invalid extension version name: \"%s\"", versionname),
4440 tgl 352 ECB : errdetail("Version names must not contain directory separator characters.")));
4440 tgl 353 GBC 480 : }
354 :
355 : /*
356 : * Utility functions to handle extension-related path names
4443 tgl 357 ECB : */
358 : static bool
4443 tgl 359 GIC 3696 : is_extension_control_filename(const char *filename)
360 : {
361 3696 : const char *extension = strrchr(filename, '.');
362 :
4443 tgl 363 CBC 3696 : return (extension != NULL) && (strcmp(extension, ".control") == 0);
364 : }
4443 tgl 365 ECB :
366 : static bool
4440 tgl 367 CBC 108724 : is_extension_script_filename(const char *filename)
368 : {
4440 tgl 369 GIC 108724 : const char *extension = strrchr(filename, '.');
370 :
4440 tgl 371 CBC 108724 : return (extension != NULL) && (strcmp(extension, ".sql") == 0);
372 : }
4440 tgl 373 ECB :
374 : static char *
4443 tgl 375 CBC 2576 : get_extension_control_directory(void)
376 : {
377 : char sharepath[MAXPGPATH];
378 : char *result;
4443 tgl 379 ECB :
4443 tgl 380 GIC 2576 : get_share_path(my_exec_path, sharepath);
381 2576 : result = (char *) palloc(MAXPGPATH);
4440 382 2576 : snprintf(result, MAXPGPATH, "%s/extension", sharepath);
383 :
4443 tgl 384 CBC 2576 : return result;
4443 tgl 385 ECB : }
386 :
387 : static char *
4443 tgl 388 CBC 1648 : get_extension_control_filename(const char *extname)
389 : {
390 : char sharepath[MAXPGPATH];
391 : char *result;
4443 tgl 392 ECB :
4443 tgl 393 GIC 1648 : get_share_path(my_exec_path, sharepath);
394 1648 : result = (char *) palloc(MAXPGPATH);
4440 395 1648 : snprintf(result, MAXPGPATH, "%s/extension/%s.control",
396 : sharepath, extname);
4443 tgl 397 ECB :
4443 tgl 398 CBC 1648 : return result;
4443 tgl 399 ECB : }
400 :
401 : static char *
4440 tgl 402 CBC 2564 : get_extension_script_directory(ExtensionControlFile *control)
403 : {
404 : char sharepath[MAXPGPATH];
405 : char *result;
4443 tgl 406 ECB :
407 : /*
408 : * The directory parameter can be omitted, absolute, or relative to the
409 : * installation's share directory.
410 : */
4440 tgl 411 GIC 2564 : if (!control->directory)
412 2564 : return get_extension_control_directory();
413 :
4440 tgl 414 UIC 0 : if (is_absolute_path(control->directory))
4440 tgl 415 LBC 0 : return pstrdup(control->directory);
4443 tgl 416 ECB :
4443 tgl 417 UIC 0 : get_share_path(my_exec_path, sharepath);
4443 tgl 418 UBC 0 : result = (char *) palloc(MAXPGPATH);
4382 bruce 419 0 : snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
420 :
4440 tgl 421 0 : return result;
4440 tgl 422 EUB : }
423 :
424 : static char *
4440 tgl 425 GBC 1110 : get_extension_aux_control_filename(ExtensionControlFile *control,
426 : const char *version)
427 : {
428 : char *result;
4440 tgl 429 ECB : char *scriptdir;
430 :
4440 tgl 431 GIC 1110 : scriptdir = get_extension_script_directory(control);
432 :
433 1110 : result = (char *) palloc(MAXPGPATH);
4438 434 1110 : snprintf(result, MAXPGPATH, "%s/%s--%s.control",
4438 tgl 435 ECB : scriptdir, control->name, version);
436 :
4440 tgl 437 CBC 1110 : pfree(scriptdir);
4440 tgl 438 ECB :
4440 tgl 439 GIC 1110 : return result;
440 : }
4440 tgl 441 ECB :
442 : static char *
4440 tgl 443 CBC 1101 : get_extension_script_filename(ExtensionControlFile *control,
444 : const char *from_version, const char *version)
445 : {
446 : char *result;
4440 tgl 447 ECB : char *scriptdir;
448 :
4440 tgl 449 GIC 1101 : scriptdir = get_extension_script_directory(control);
450 :
451 1101 : result = (char *) palloc(MAXPGPATH);
452 1101 : if (from_version)
4438 tgl 453 CBC 173 : snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
454 : scriptdir, control->name, from_version, version);
4440 tgl 455 ECB : else
4438 tgl 456 CBC 928 : snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
4438 tgl 457 ECB : scriptdir, control->name, version);
458 :
4440 tgl 459 GIC 1101 : pfree(scriptdir);
4443 tgl 460 ECB :
4443 tgl 461 GIC 1101 : return result;
462 : }
4443 tgl 463 ECB :
464 :
465 : /*
466 : * Parse contents of primary or auxiliary control file, and fill in
467 : * fields of *control. We parse primary file if version == NULL,
468 : * else the optional auxiliary file for that version.
469 : *
470 : * Control files are supposed to be very short, half a dozen lines,
471 : * so we don't worry about memory allocation risks here. Also we don't
472 : * worry about what encoding it's in; all values are expected to be ASCII.
473 : */
474 : static void
4440 tgl 475 GIC 2758 : parse_extension_control_file(ExtensionControlFile *control,
476 : const char *version)
477 : {
478 : char *filename;
4443 tgl 479 ECB : FILE *file;
480 : ConfigVariable *item,
4382 bruce 481 GIC 2758 : *head = NULL,
482 2758 : *tail = NULL;
483 :
484 : /*
4440 tgl 485 ECB : * Locate the file to read. Auxiliary files are optional.
4443 486 : */
4440 tgl 487 GIC 2758 : if (version)
488 1110 : filename = get_extension_aux_control_filename(control, version);
489 : else
490 1648 : filename = get_extension_control_filename(control->name);
4440 tgl 491 ECB :
4443 tgl 492 CBC 2758 : if ((file = AllocateFile(filename, "r")) == NULL)
493 : {
453 494 1110 : if (errno == ENOENT)
495 : {
453 tgl 496 ECB : /* no complaint for missing auxiliary file */
453 tgl 497 GIC 1110 : if (version)
453 tgl 498 ECB : {
453 tgl 499 GIC 1110 : pfree(filename);
500 1110 : return;
453 tgl 501 ECB : }
502 :
503 : /* missing control file indicates extension is not installed */
453 tgl 504 LBC 0 : 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.",
453 tgl 508 EUB : filename),
509 : errhint("The extension must first be installed on the system where PostgreSQL is running.")));
510 : }
4443 tgl 511 UIC 0 : ereport(ERROR,
512 : (errcode_for_file_access(),
513 : errmsg("could not open extension control file \"%s\": %m",
514 : filename)));
4440 tgl 515 EUB : }
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 : */
135 michael 521 GNC 1648 : (void) ParseConfigFp(file, filename, CONF_FILE_START_DEPTH, ERROR,
522 : &head, &tail);
523 :
4443 tgl 524 GIC 1648 : FreeFile(file);
525 :
4443 tgl 526 ECB : /*
527 : * Convert the ConfigVariable list into ExtensionControlFile entries.
528 : */
4443 tgl 529 CBC 9803 : for (item = head; item != NULL; item = item->next)
530 : {
4440 tgl 531 GIC 8155 : if (strcmp(item->name, "directory") == 0)
532 : {
4440 tgl 533 UIC 0 : if (version)
4440 tgl 534 LBC 0 : ereport(ERROR,
535 : (errcode(ERRCODE_SYNTAX_ERROR),
4438 tgl 536 ECB : errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
537 : item->name)));
4440 tgl 538 EUB :
4440 tgl 539 UBC 0 : control->directory = pstrdup(item->value);
540 : }
4440 tgl 541 GIC 8155 : else if (strcmp(item->name, "default_version") == 0)
542 : {
543 1648 : if (version)
4440 tgl 544 UBC 0 : ereport(ERROR,
545 : (errcode(ERRCODE_SYNTAX_ERROR),
4438 tgl 546 ECB : errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
547 : item->name)));
4440 548 :
4440 tgl 549 GBC 1648 : control->default_version = pstrdup(item->value);
550 : }
4438 tgl 551 GIC 6507 : else if (strcmp(item->name, "module_pathname") == 0)
552 : {
553 1384 : control->module_pathname = pstrdup(item->value);
4438 tgl 554 ECB : }
4443 tgl 555 GIC 5123 : else if (strcmp(item->name, "comment") == 0)
4443 tgl 556 ECB : {
4443 tgl 557 GIC 1648 : control->comment = pstrdup(item->value);
4443 tgl 558 ECB : }
4443 tgl 559 GIC 3475 : else if (strcmp(item->name, "schema") == 0)
4443 tgl 560 ECB : {
4443 tgl 561 GIC 478 : control->schema = pstrdup(item->value);
4443 tgl 562 ECB : }
4443 tgl 563 GIC 2997 : else if (strcmp(item->name, "relocatable") == 0)
4443 tgl 564 ECB : {
4443 tgl 565 GIC 1648 : if (!parse_bool(item->value, &control->relocatable))
4443 tgl 566 LBC 0 : ereport(ERROR,
567 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4443 tgl 568 ECB : errmsg("parameter \"%s\" requires a Boolean value",
569 : item->name)));
570 : }
4419 tgl 571 GBC 1349 : else if (strcmp(item->name, "superuser") == 0)
572 : {
4419 tgl 573 GIC 433 : if (!parse_bool(item->value, &control->superuser))
4419 tgl 574 UIC 0 : ereport(ERROR,
575 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4419 tgl 576 ECB : errmsg("parameter \"%s\" requires a Boolean value",
577 : item->name)));
578 : }
1166 tgl 579 GBC 916 : else if (strcmp(item->name, "trusted") == 0)
580 : {
1166 tgl 581 GIC 669 : if (!parse_bool(item->value, &control->trusted))
1166 tgl 582 UIC 0 : ereport(ERROR,
583 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1166 tgl 584 ECB : errmsg("parameter \"%s\" requires a Boolean value",
585 : item->name)));
586 : }
4443 tgl 587 GBC 247 : else if (strcmp(item->name, "encoding") == 0)
588 : {
4443 tgl 589 UIC 0 : control->encoding = pg_valid_server_encoding(item->value);
590 0 : if (control->encoding < 0)
591 0 : ereport(ERROR,
4443 tgl 592 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
593 : errmsg("\"%s\" is not a valid encoding name",
4443 tgl 594 EUB : item->value)));
595 : }
4443 tgl 596 GBC 247 : else if (strcmp(item->name, "requires") == 0)
597 : {
598 : /* Need a modifiable copy of string */
4443 tgl 599 GIC 232 : char *rawnames = pstrdup(item->value);
600 :
4443 tgl 601 ECB : /* Parse string into list of identifiers */
4443 tgl 602 GIC 232 : if (!SplitIdentifierString(rawnames, ',', &control->requires))
603 : {
4443 tgl 604 ECB : /* syntax error in name list */
4443 tgl 605 UIC 0 : ereport(ERROR,
606 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2118 tgl 607 ECB : errmsg("parameter \"%s\" must be a list of extension names",
608 : item->name)));
609 : }
4443 tgl 610 EUB : }
20 tgl 611 GNC 15 : else if (strcmp(item->name, "no_relocate") == 0)
612 : {
613 : /* Need a modifiable copy of string */
614 15 : char *rawnames = pstrdup(item->value);
615 :
616 : /* Parse string into list of identifiers */
617 15 : if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
618 : {
619 : /* syntax error in name list */
20 tgl 620 UNC 0 : 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
4443 tgl 627 UIC 0 : ereport(ERROR,
628 : (errcode(ERRCODE_SYNTAX_ERROR),
629 : errmsg("unrecognized parameter \"%s\" in file \"%s\"",
630 : item->name, filename)));
4443 tgl 631 ECB : }
632 :
4443 tgl 633 GIC 1648 : FreeConfigVariables(head);
4443 tgl 634 ECB :
4443 tgl 635 GIC 1648 : if (control->relocatable && control->schema != NULL)
4443 tgl 636 UIC 0 : ereport(ERROR,
4443 tgl 637 ECB : (errcode(ERRCODE_SYNTAX_ERROR),
638 : errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
639 :
4440 tgl 640 GBC 1648 : pfree(filename);
641 : }
642 :
643 : /*
644 : * Read the primary control file for the specified extension.
645 : */
646 : static ExtensionControlFile *
647 1648 : read_extension_control_file(const char *extname)
648 : {
649 : ExtensionControlFile *control;
650 :
651 : /*
652 : * Set up default values. Pointer fields are initially null.
4443 tgl 653 ECB : */
4440 tgl 654 GIC 1648 : control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
4440 tgl 655 CBC 1648 : control->name = pstrdup(extname);
4440 tgl 656 GBC 1648 : control->relocatable = false;
4419 tgl 657 GIC 1648 : control->superuser = true;
1166 658 1648 : control->trusted = false;
4440 659 1648 : control->encoding = -1;
4443 tgl 660 ECB :
661 : /*
662 : * Parse the primary control file.
663 : */
4440 tgl 664 GIC 1648 : parse_extension_control_file(control, NULL);
665 :
4443 666 1648 : return control;
4443 tgl 667 ECB : }
668 :
669 : /*
670 : * Read the auxiliary control file for the specified extension and version.
671 : *
672 : * Returns a new modified ExtensionControlFile struct; the original struct
673 : * (reflecting just the primary control file) is not modified.
4439 674 : */
675 : static ExtensionControlFile *
4439 tgl 676 CBC 1110 : read_extension_aux_control_file(const ExtensionControlFile *pcontrol,
4439 tgl 677 ECB : const char *version)
678 : {
679 : ExtensionControlFile *acontrol;
680 :
681 : /*
682 : * Flat-copy the struct. Pointer fields share values with original.
683 : */
4439 tgl 684 CBC 1110 : acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
4439 tgl 685 GIC 1110 : memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
4439 tgl 686 ECB :
687 : /*
688 : * Parse the auxiliary control file, overwriting struct fields
689 : */
4439 tgl 690 GIC 1110 : parse_extension_control_file(acontrol, version);
691 :
692 1110 : return acontrol;
693 : }
694 :
695 : /*
4309 peter_e 696 ECB : * Read an SQL script file into a string, and convert to database encoding
697 : */
698 : static char *
4443 tgl 699 GIC 632 : read_extension_script_file(const ExtensionControlFile *control,
700 : const char *filename)
701 : {
702 : int src_encoding;
703 : char *src_str;
4382 bruce 704 ECB : char *dest_str;
705 : int len;
706 :
2842 heikki.linnakangas 707 GIC 632 : src_str = read_whole_file(filename, &len);
708 :
709 : /* use database encoding if not given */
4443 tgl 710 CBC 632 : if (control->encoding < 0)
3332 tgl 711 GIC 632 : src_encoding = GetDatabaseEncoding();
4443 tgl 712 ECB : else
4443 tgl 713 UIC 0 : src_encoding = control->encoding;
714 :
715 : /* make sure that source string is valid in the expected encoding */
801 heikki.linnakangas 716 GIC 632 : (void) pg_verify_mbstr(src_encoding, src_str, len, false);
717 :
718 : /*
2842 heikki.linnakangas 719 ECB : * 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 : */
3332 tgl 723 GIC 632 : dest_str = pg_any_to_server(src_str, len, src_encoding);
724 :
4443 725 632 : return dest_str;
726 : }
4443 tgl 727 ECB :
728 : /*
729 : * Execute given SQL string.
730 : *
731 : * Note: it's tempting to just use SPI to execute the string, but that does
732 : * not work very well. The really serious problem is that SPI will parse,
4443 tgl 733 EUB : * analyze, and plan the whole string before executing any of it; of course
734 : * this fails if there are any plannable statements referring to objects
735 : * created earlier in the script. A lesser annoyance is that SPI insists
4443 tgl 736 ECB : * on printing the whole string as errcontext in case of any error, and that
737 : * could be very long.
738 : */
739 : static void
1669 michael 740 GIC 632 : execute_sql_string(const char *sql)
741 : {
742 : List *raw_parsetree_list;
4443 tgl 743 ECB : DestReceiver *dest;
744 : ListCell *lc1;
745 :
746 : /*
747 : * Parse the SQL string into a list of raw parse trees.
748 : */
4443 tgl 749 GIC 632 : raw_parsetree_list = pg_parse_query(sql);
750 :
751 : /* All output from SELECTs goes to the bit bucket */
752 632 : dest = CreateDestReceiver(DestNone);
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 6680 : foreach(lc1, raw_parsetree_list)
4443 tgl 760 ECB : {
2190 tgl 761 GIC 6059 : 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
1369 tgl 769 ECB : * limit the memory used when there are many commands in the string.
770 : */
771 : per_parsetree_context =
1369 tgl 772 CBC 6059 : AllocSetContextCreate(CurrentMemoryContext,
773 : "execute_sql_string per-statement context",
774 : ALLOCSET_DEFAULT_SIZES);
1369 tgl 775 GIC 6059 : oldcontext = MemoryContextSwitchTo(per_parsetree_context);
776 :
777 : /* Be sure parser can see any DDL done so far */
2168 778 6059 : CommandCounterIncrement();
2168 tgl 779 ECB :
401 peter 780 GIC 6059 : stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
332 tgl 781 ECB : sql,
782 : NULL,
783 : 0,
784 : NULL);
1105 fujii 785 GIC 6059 : stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
786 :
4443 tgl 787 12107 : foreach(lc2, stmt_list)
788 : {
2190 789 6059 : PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
790 :
4443 791 6059 : CommandCounterIncrement();
4443 tgl 792 ECB :
4443 tgl 793 GIC 6059 : PushActiveSnapshot(GetTransactionSnapshot());
794 :
2276 tgl 795 CBC 6059 : if (stmt->utilityStmt == NULL)
796 : {
797 : QueryDesc *qdesc;
4443 tgl 798 ECB :
2276 tgl 799 GIC 5 : qdesc = CreateQueryDesc(stmt,
4443 tgl 800 ECB : sql,
801 : GetActiveSnapshot(), NULL,
802 : dest, NULL, NULL, 0);
803 :
4443 tgl 804 GIC 5 : ExecutorStart(qdesc, 0);
2208 rhaas 805 CBC 5 : ExecutorRun(qdesc, ForwardScanDirection, 0, true);
4424 tgl 806 GIC 5 : ExecutorFinish(qdesc);
4443 tgl 807 CBC 5 : ExecutorEnd(qdesc);
808 :
809 5 : FreeQueryDesc(qdesc);
810 : }
4443 tgl 811 ECB : else
812 : {
2276 tgl 813 CBC 6054 : if (IsA(stmt->utilityStmt, TransactionStmt))
2276 tgl 814 UIC 0 : ereport(ERROR,
2276 tgl 815 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
816 : errmsg("transaction control statements are not allowed within an extension script")));
817 :
4443 tgl 818 GIC 6054 : ProcessUtility(stmt,
4443 tgl 819 ECB : sql,
820 : false,
821 : PROCESS_UTILITY_QUERY,
822 : NULL,
823 : NULL,
824 : dest,
3633 825 : NULL);
4443 826 : }
827 :
4443 tgl 828 GIC 6048 : PopActiveSnapshot();
4443 tgl 829 ECB : }
830 :
831 : /* Clean up per-parsetree context. */
1369 tgl 832 GIC 6048 : MemoryContextSwitchTo(oldcontext);
1369 tgl 833 CBC 6048 : MemoryContextDelete(per_parsetree_context);
4443 tgl 834 EUB : }
835 :
836 : /* Be sure to advance the command counter after the last script command */
4443 tgl 837 GIC 621 : CommandCounterIncrement();
4443 tgl 838 CBC 621 : }
839 :
840 : /*
841 : * Policy function: is the given extension trusted for installation by a
842 : * non-superuser?
843 : *
844 : * (Update the errhint logic below if you change this.)
845 : */
846 : static bool
1166 tgl 847 GIC 4 : extension_is_trusted(ExtensionControlFile *control)
1166 tgl 848 ECB : {
849 : AclResult aclresult;
850 :
851 : /* Never trust unless extension's control file says it's okay */
1166 tgl 852 CBC 4 : if (!control->trusted)
853 2 : return false;
854 : /* Allow if user has CREATE privilege on current database */
147 peter 855 GNC 2 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
1166 tgl 856 GIC 2 : if (aclresult == ACLCHECK_OK)
1166 tgl 857 CBC 1 : return true;
858 1 : return false;
859 : }
860 :
861 : /*
862 : * Execute the appropriate script file for installing or updating the extension
863 : *
864 : * If from_version isn't NULL, it's an update
865 : *
866 : * Note: requiredSchemas must be one-for-one with the control->requires list
867 : */
868 : static void
4443 869 635 : execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
870 : const char *from_version,
871 : const char *version,
872 : List *requiredSchemas,
873 : const char *schemaName, Oid schemaOid)
4443 tgl 874 ECB : {
1166 tgl 875 CBC 635 : bool switch_to_superuser = false;
876 : char *filename;
877 635 : Oid save_userid = 0;
878 635 : int save_sec_context = 0;
4204 tgl 879 ECB : int save_nestlevel;
4443 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 : */
4419 tgl 889 GIC 635 : if (control->superuser && !superuser())
890 : {
1166 891 4 : if (extension_is_trusted(control))
1166 tgl 892 CBC 1 : switch_to_superuser = true;
1166 tgl 893 GIC 3 : else if (from_version == NULL)
4419 894 3 : ereport(ERROR,
895 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
896 : errmsg("permission denied to create extension \"%s\"",
897 : control->name),
1166 tgl 898 ECB : control->trusted
899 : ? errhint("Must have CREATE privilege on current database to create this extension.")
900 : : errhint("Must be superuser to create this extension.")));
4419 901 : else
4419 tgl 902 UIC 0 : 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 :
4440 tgl 911 GIC 632 : filename = get_extension_script_filename(control, from_version, version);
4440 tgl 912 ECB :
281 jdavis 913 GNC 632 : if (from_version == NULL)
914 459 : elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
915 : else
916 173 : elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
917 :
918 : /*
1166 tgl 919 ECB : * 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 : */
1166 tgl 923 GIC 632 : if (switch_to_superuser)
924 : {
925 1 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
926 1 : SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID,
927 : save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
928 : }
929 :
4443 tgl 930 EUB : /*
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 : *
264 tgl 939 ECB : * log_min_messages can't be set by ordinary users, so for that one we
940 : * pretend to be superuser.
4443 941 : */
4204 tgl 942 CBC 632 : save_nestlevel = NewGUCNestLevel();
943 :
4443 944 632 : if (client_min_messages < WARNING)
4443 tgl 945 GIC 630 : (void) set_config_option("client_min_messages", "warning",
946 : PGC_USERSET, PGC_S_SESSION,
947 : GUC_ACTION_SAVE, true, 0, false);
948 632 : if (log_min_messages < WARNING)
264 949 2 : (void) set_config_option_ext("log_min_messages", "warning",
950 : PGC_SUSET, PGC_S_SESSION,
264 tgl 951 ECB : BOOTSTRAP_SUPERUSERID,
952 : GUC_ACTION_SAVE, true, 0, false);
4443 953 :
954 : /*
955 : * Similarly disable check_function_bodies, to ensure that SQL functions
956 : * won't be parsed during creation.
957 : */
972 tgl 958 GIC 632 : if (check_function_bodies)
959 632 : (void) set_config_option("check_function_bodies", "off",
960 : PGC_USERSET, PGC_S_SESSION,
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.
4443 tgl 970 ECB : *
971 : * Note: it might look tempting to use PushOverrideSearchPath for this,
4382 bruce 972 : * but we cannot do that. We have to actually set the search_path GUC in
4204 tgl 973 : * case the extension script examines or changes it. In any case, the
974 : * GUC_ACTION_SAVE method is just as convenient.
975 : */
4443 tgl 976 CBC 632 : initStringInfo(&pathbuf);
977 632 : appendStringInfoString(&pathbuf, quote_identifier(schemaName));
4443 tgl 978 GIC 657 : foreach(lc, requiredSchemas)
979 : {
980 25 : Oid reqschema = lfirst_oid(lc);
981 25 : char *reqname = get_namespace_name(reqschema);
982 :
972 983 25 : if (reqname && strcmp(reqname, "pg_catalog") != 0)
4443 984 15 : appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
985 : }
972 tgl 986 CBC 632 : appendStringInfoString(&pathbuf, ", pg_temp");
4443 tgl 987 ECB :
4443 tgl 988 GIC 632 : (void) set_config_option("search_path", pathbuf.data,
989 : PGC_USERSET, PGC_S_SESSION,
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 632 : creating_extension = true;
998 632 : CurrentExtensionObject = extensionOid;
999 632 : PG_TRY();
1000 : {
4197 1001 632 : char *c_sql = read_extension_script_file(control, filename);
1002 : Datum t_sql;
1003 :
4197 tgl 1004 ECB : /* We use various functions that want to operate on text datums */
4197 tgl 1005 CBC 632 : t_sql = CStringGetTextDatum(c_sql);
4197 tgl 1006 ECB :
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 : */
4197 tgl 1012 CBC 632 : t_sql = DirectFunctionCall4Coll(textregexreplace,
1013 : C_COLLATION_OID,
4197 tgl 1014 ECB : t_sql,
4197 tgl 1015 GIC 632 : CStringGetTextDatum("^\\\\echo.*$"),
4197 tgl 1016 CBC 632 : CStringGetTextDatum(""),
4197 tgl 1017 GIC 632 : CStringGetTextDatum("ng"));
1018 :
1019 : /*
1020 : * If the script uses @extowner@, substitute the calling username.
1021 : */
1166 1022 632 : if (strstr(c_sql, "@extowner@"))
1023 : {
1024 311 : Oid uid = switch_to_superuser ? save_userid : GetUserId();
1166 tgl 1025 CBC 311 : const char *userName = GetUserNameFromId(uid, false);
1026 311 : const char *qUserName = quote_identifier(userName);
1166 tgl 1027 ECB :
1166 tgl 1028 GIC 311 : t_sql = DirectFunctionCall3Coll(replace_text,
1166 tgl 1029 ECB : C_COLLATION_OID,
1030 : t_sql,
1166 tgl 1031 GIC 311 : CStringGetTextDatum("@extowner@"),
1032 311 : CStringGetTextDatum(qUserName));
1166 tgl 1033 ECB : }
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
4197 1040 : * any need for @extschema@, else it wouldn't be relocatable.
1041 : */
4443 tgl 1042 GIC 632 : if (!control->relocatable)
4443 tgl 1043 ECB : {
4382 bruce 1044 CBC 332 : const char *qSchemaName = quote_identifier(schemaName);
4443 tgl 1045 ECB :
1479 peter 1046 GIC 332 : t_sql = DirectFunctionCall3Coll(replace_text,
1047 : C_COLLATION_OID,
1048 : t_sql,
1418 tgl 1049 332 : CStringGetTextDatum("@extschema@"),
1418 tgl 1050 CBC 332 : CStringGetTextDatum(qSchemaName));
1051 : }
4443 tgl 1052 ECB :
1053 : /*
1054 : * Likewise, substitute required extensions' schema names for
1055 : * occurrences of @extschema:extension_name@.
1056 : */
20 tgl 1057 GNC 632 : Assert(list_length(control->requires) == list_length(requiredSchemas));
1058 657 : forboth(lc, control->requires, lc2, requiredSchemas)
1059 : {
1060 25 : char *reqextname = (char *) lfirst(lc);
1061 25 : Oid reqschema = lfirst_oid(lc2);
1062 25 : char *schemaName = get_namespace_name(reqschema);
1063 25 : const char *qSchemaName = quote_identifier(schemaName);
1064 : char *repltoken;
1065 :
1066 25 : repltoken = psprintf("@extschema:%s@", reqextname);
1067 25 : t_sql = DirectFunctionCall3Coll(replace_text,
1068 : C_COLLATION_OID,
1069 : t_sql,
1070 25 : CStringGetTextDatum(repltoken),
1071 25 : CStringGetTextDatum(qSchemaName));
1072 : }
1073 :
4438 tgl 1074 ECB : /*
1075 : * If module_pathname was set in the control file, substitute its
1076 : * value for occurrences of MODULE_PATHNAME.
1077 : */
4438 tgl 1078 GIC 632 : if (control->module_pathname)
1079 : {
1479 peter 1080 CBC 594 : t_sql = DirectFunctionCall3Coll(replace_text,
1479 peter 1081 ECB : C_COLLATION_OID,
1082 : t_sql,
1418 tgl 1083 GIC 594 : CStringGetTextDatum("MODULE_PATHNAME"),
1084 594 : CStringGetTextDatum(control->module_pathname));
1085 : }
1086 :
1087 : /* And now back to C string */
4197 1088 632 : c_sql = text_to_cstring(DatumGetTextPP(t_sql));
1089 :
1669 michael 1090 632 : execute_sql_string(c_sql);
4443 tgl 1091 ECB : }
1255 peter 1092 GIC 11 : PG_FINALLY();
4443 tgl 1093 ECB : {
4443 tgl 1094 GIC 632 : creating_extension = false;
4443 tgl 1095 CBC 632 : CurrentExtensionObject = InvalidOid;
1096 : }
4443 tgl 1097 GIC 632 : PG_END_TRY();
4443 tgl 1098 ECB :
1099 : /*
1100 : * Restore the GUC variables we set above.
1101 : */
4204 tgl 1102 GIC 621 : AtEOXact_GUC(true, save_nestlevel);
1103 :
1104 : /*
1105 : * Restore authentication state if needed.
1166 tgl 1106 ECB : */
1166 tgl 1107 CBC 621 : if (switch_to_superuser)
1166 tgl 1108 GIC 1 : SetUserIdAndSecContext(save_userid, save_sec_context);
4443 tgl 1109 CBC 621 : }
4443 tgl 1110 ECB :
4440 1111 : /*
1112 : * Find or create an ExtensionVersionInfo for the specified version name
1113 : *
1114 : * Currently, we just use a List of the ExtensionVersionInfo's. Searching
1115 : * for them therefore uses about O(N^2) time when there are N versions of
1116 : * the extension. We could change the data structure to a hash table if
1117 : * this ever becomes a bottleneck.
1118 : */
1119 : static ExtensionVersionInfo *
4440 tgl 1120 CBC 1780 : get_ext_ver_info(const char *versionname, List **evi_list)
1121 : {
1122 : ExtensionVersionInfo *evi;
1123 : ListCell *lc;
1124 :
4440 tgl 1125 GIC 6657 : foreach(lc, *evi_list)
1126 : {
4440 tgl 1127 CBC 5627 : evi = (ExtensionVersionInfo *) lfirst(lc);
4440 tgl 1128 GIC 5627 : if (strcmp(evi->name, versionname) == 0)
4440 tgl 1129 CBC 750 : return evi;
1130 : }
1131 :
1132 1030 : evi = (ExtensionVersionInfo *) palloc(sizeof(ExtensionVersionInfo));
1133 1030 : evi->name = pstrdup(versionname);
4440 tgl 1134 GIC 1030 : evi->reachable = NIL;
4437 1135 1030 : evi->installable = false;
1136 : /* initialize for later application of Dijkstra's algorithm */
4440 tgl 1137 CBC 1030 : evi->distance_known = false;
4440 tgl 1138 GIC 1030 : evi->distance = INT_MAX;
4440 tgl 1139 CBC 1030 : evi->previous = NULL;
1140 :
1141 1030 : *evi_list = lappend(*evi_list, evi);
1142 :
1143 1030 : return evi;
4440 tgl 1144 ECB : }
1145 :
1146 : /*
1147 : * Locate the nearest unprocessed ExtensionVersionInfo
1148 : *
1149 : * This part of the algorithm is also about O(N^2). A priority queue would
1150 : * make it much faster, but for now there's no need.
1151 : */
1152 : static ExtensionVersionInfo *
4440 tgl 1153 GIC 1513 : get_nearest_unprocessed_vertex(List *evi_list)
1154 : {
1155 1513 : ExtensionVersionInfo *evi = NULL;
4440 tgl 1156 ECB : ListCell *lc;
1157 :
4440 tgl 1158 CBC 13130 : foreach(lc, evi_list)
1159 : {
4440 tgl 1160 GIC 11617 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
1161 :
1162 : /* only vertices whose distance is still uncertain are candidates */
1163 11617 : if (evi2->distance_known)
1164 2910 : continue;
1165 : /* remember the closest such vertex */
1166 8707 : if (evi == NULL ||
1167 7194 : evi->distance > evi2->distance)
1168 2422 : evi = evi2;
4440 tgl 1169 ECB : }
1170 :
4440 tgl 1171 GIC 1513 : return evi;
1172 : }
1173 :
4440 tgl 1174 ECB : /*
1175 : * Obtain information about the set of update scripts available for the
1176 : * specified extension. The result is a List of ExtensionVersionInfo
1177 : * structs, each with a subsidiary list of the ExtensionVersionInfos for
1178 : * the versions that can be reached in one step from that version.
1179 : */
1180 : static List *
4440 tgl 1181 CBC 353 : get_ext_ver_list(ExtensionControlFile *control)
4440 tgl 1182 ECB : {
4440 tgl 1183 CBC 353 : List *evi_list = NIL;
1184 353 : int extnamelen = strlen(control->name);
1185 : char *location;
4440 tgl 1186 ECB : DIR *dir;
1187 : struct dirent *de;
1188 :
4440 tgl 1189 GIC 353 : location = get_extension_script_directory(control);
4382 bruce 1190 CBC 353 : dir = AllocateDir(location);
4440 tgl 1191 GIC 109077 : while ((de = ReadDir(dir, location)) != NULL)
4440 tgl 1192 ECB : {
1193 : char *vername;
1194 : char *vername2;
1195 : ExtensionVersionInfo *evi;
1196 : ExtensionVersionInfo *evi2;
1197 :
1198 : /* must be a .sql file ... */
4440 tgl 1199 GIC 108724 : if (!is_extension_script_filename(de->d_name))
1200 34947 : continue;
1201 :
4440 tgl 1202 ECB : /* ... matching extension name followed by separator */
4440 tgl 1203 GIC 73777 : if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
4438 tgl 1204 CBC 1080 : de->d_name[extnamelen] != '-' ||
4438 tgl 1205 GIC 1030 : de->d_name[extnamelen + 1] != '-')
4440 1206 72747 : continue;
4440 tgl 1207 ECB :
1208 : /* extract version name(s) from 'extname--something.sql' filename */
4438 tgl 1209 CBC 1030 : vername = pstrdup(de->d_name + extnamelen + 2);
4440 tgl 1210 GIC 1030 : *strrchr(vername, '.') = '\0';
4438 1211 1030 : vername2 = strstr(vername, "--");
4440 tgl 1212 CBC 1030 : if (!vername2)
4437 tgl 1213 ECB : {
1214 : /* It's an install, not update, script; record its version name */
4437 tgl 1215 CBC 353 : evi = get_ext_ver_info(vername, &evi_list);
1216 353 : evi->installable = true;
1217 353 : continue;
1218 : }
4438 tgl 1219 GIC 677 : *vername2 = '\0'; /* terminate first version */
4438 tgl 1220 CBC 677 : vername2 += 2; /* and point to second */
1221 :
1222 : /* if there's a third --, it's bogus, ignore it */
4437 tgl 1223 GIC 677 : if (strstr(vername2, "--"))
4437 tgl 1224 UIC 0 : continue;
1225 :
1226 : /* Create ExtensionVersionInfos and link them together */
4440 tgl 1227 GIC 677 : evi = get_ext_ver_info(vername, &evi_list);
1228 677 : evi2 = get_ext_ver_info(vername2, &evi_list);
1229 677 : evi->reachable = lappend(evi->reachable, evi2);
4440 tgl 1230 ECB : }
4440 tgl 1231 GIC 353 : FreeDir(dir);
4440 tgl 1232 ECB :
4440 tgl 1233 CBC 353 : return evi_list;
1234 : }
1235 :
1236 : /*
1237 : * Given an initial and final version name, identify the sequence of update
4440 tgl 1238 ECB : * scripts that have to be applied to perform that update.
1239 : *
4439 1240 : * Result is a List of names of versions to transition through (the initial
1241 : * version is *not* included).
1242 : */
1243 : static List *
4440 tgl 1244 GIC 11 : identify_update_path(ExtensionControlFile *control,
1245 : const char *oldVersion, const char *newVersion)
1246 : {
1247 : List *result;
4440 tgl 1248 ECB : List *evi_list;
1249 : ExtensionVersionInfo *evi_start;
1250 : ExtensionVersionInfo *evi_target;
1251 :
1252 : /* Extract the version update graph from the script directory */
4440 tgl 1253 CBC 11 : evi_list = get_ext_ver_list(control);
4440 tgl 1254 ECB :
1255 : /* Initialize start and end vertices */
4440 tgl 1256 GIC 11 : evi_start = get_ext_ver_info(oldVersion, &evi_list);
1257 11 : evi_target = get_ext_ver_info(newVersion, &evi_list);
4440 tgl 1258 ECB :
4437 1259 : /* Find shortest path */
2401 tgl 1260 CBC 11 : result = find_update_path(evi_list, evi_start, evi_target, false, false);
4437 tgl 1261 ECB :
4437 tgl 1262 GIC 11 : if (result == NIL)
4437 tgl 1263 UIC 0 : ereport(ERROR,
4437 tgl 1264 ECB : (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 :
4437 tgl 1268 CBC 11 : return result;
4437 tgl 1269 ECB : }
1270 :
1271 : /*
1272 : * Apply Dijkstra's algorithm to find the shortest path from evi_start to
4437 tgl 1273 EUB : * evi_target.
1274 : *
1275 : * If reject_indirect is true, ignore paths that go through installable
2401 tgl 1276 ECB : * versions. This saves work when the caller will consider starting from
1277 : * all installable versions anyway.
1278 : *
1279 : * If reinitialize is false, assume the ExtensionVersionInfo list has not
4437 1280 : * been used for this before, and the initialization done by get_ext_ver_info
1281 : * is still good. Otherwise, reinitialize all transient fields used here.
1282 : *
1283 : * Result is a List of names of versions to transition through (the initial
1284 : * version is *not* included). Returns NIL if no such path.
1285 : */
1286 : static List *
4437 tgl 1287 GIC 398 : find_update_path(List *evi_list,
1288 : ExtensionVersionInfo *evi_start,
1289 : ExtensionVersionInfo *evi_target,
1290 : bool reject_indirect,
1291 : bool reinitialize)
1292 : {
4437 tgl 1293 ECB : List *result;
1294 : ExtensionVersionInfo *evi;
1295 : ListCell *lc;
1296 :
1297 : /* Caller error if start == target */
4437 tgl 1298 GIC 398 : Assert(evi_start != evi_target);
1299 : /* Caller error if reject_indirect and target is installable */
2401 1300 398 : Assert(!(reject_indirect && evi_target->installable));
1301 :
4437 tgl 1302 CBC 398 : if (reinitialize)
1303 : {
4437 tgl 1304 GIC 2851 : foreach(lc, evi_list)
4437 tgl 1305 ECB : {
4437 tgl 1306 CBC 2464 : evi = (ExtensionVersionInfo *) lfirst(lc);
4437 tgl 1307 GIC 2464 : evi->distance_known = false;
1308 2464 : evi->distance = INT_MAX;
4437 tgl 1309 CBC 2464 : evi->previous = NULL;
1310 : }
4437 tgl 1311 ECB : }
4437 tgl 1312 EUB :
4440 tgl 1313 GIC 398 : evi_start->distance = 0;
1314 :
1315 1513 : while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
1316 : {
4440 tgl 1317 CBC 1513 : if (evi->distance == INT_MAX)
4440 tgl 1318 GIC 159 : break; /* all remaining vertices are unreachable */
1319 1354 : evi->distance_known = true;
1320 1354 : if (evi == evi_target)
1321 239 : break; /* found shortest path to target */
1322 2071 : foreach(lc, evi->reachable)
1323 : {
1324 956 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
1325 : int newdist;
1326 :
1327 : /* if reject_indirect, treat installable versions as unreachable */
2401 1328 956 : if (reject_indirect && evi2->installable)
2401 tgl 1329 UIC 0 : continue;
4440 tgl 1330 GIC 956 : newdist = evi->distance + 1;
1331 956 : if (newdist < evi2->distance)
1332 : {
1333 956 : evi2->distance = newdist;
1334 956 : evi2->previous = evi;
1335 : }
4438 tgl 1336 LBC 0 : else if (newdist == evi2->distance &&
4438 tgl 1337 UIC 0 : evi2->previous != NULL &&
1338 0 : 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.
4438 tgl 1347 ECB : */
4438 tgl 1348 UIC 0 : evi2->previous = evi;
4438 tgl 1349 ECB : }
1350 : }
4440 1351 : }
1352 :
4437 1353 : /* Return NIL if target is not reachable from start */
4440 tgl 1354 GIC 398 : if (!evi_target->distance_known)
4437 tgl 1355 CBC 159 : return NIL;
4440 tgl 1356 ECB :
1357 : /* Build and return list of version names representing the update path */
4440 tgl 1358 CBC 239 : result = NIL;
4440 tgl 1359 GIC 814 : for (evi = evi_target; evi != evi_start; evi = evi->previous)
1360 575 : result = lcons(evi->name, result);
1361 :
4440 tgl 1362 CBC 239 : return result;
1363 : }
4440 tgl 1364 ECB :
1365 : /*
2401 1366 : * Given a target version that is not directly installable, find the
1367 : * best installation sequence starting from a directly-installable version.
1368 : *
1369 : * evi_list: previously-collected version update graph
1370 : * evi_target: member of that list that we want to reach
1371 : *
1372 : * Returns the best starting-point version, or NULL if there is none.
1373 : * On success, *best_path is set to the path from the start point.
1374 : *
1375 : * If there's more than one possible start point, prefer shorter update paths,
1376 : * and break any ties arbitrarily on the basis of strcmp'ing the starting
1377 : * versions' names.
2401 tgl 1378 EUB : */
2401 tgl 1379 ECB : static ExtensionVersionInfo *
2401 tgl 1380 CBC 387 : find_install_path(List *evi_list, ExtensionVersionInfo *evi_target,
1381 : List **best_path)
2401 tgl 1382 ECB : {
2401 tgl 1383 CBC 387 : ExtensionVersionInfo *evi_start = NULL;
1384 : ListCell *lc;
2401 tgl 1385 EUB :
2401 tgl 1386 GBC 387 : *best_path = NIL;
2401 tgl 1387 EUB :
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 : */
2401 tgl 1392 GIC 387 : if (evi_target->installable)
2401 tgl 1393 UIC 0 : return evi_target;
1394 :
1395 : /* Consider all installable versions as start points */
2401 tgl 1396 GIC 2851 : foreach(lc, evi_list)
2401 tgl 1397 EUB : {
2401 tgl 1398 GIC 2464 : ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc);
1399 : List *path;
1400 :
1401 2464 : if (!evi1->installable)
1402 2077 : continue;
2401 tgl 1403 ECB :
1404 : /*
1405 : * Find shortest path from evi1 to evi_target; but no need to consider
1406 : * paths going through other installable versions.
1407 : */
2401 tgl 1408 CBC 387 : path = find_update_path(evi_list, evi1, evi_target, true, true);
1409 387 : if (path == NIL)
2401 tgl 1410 GIC 159 : continue;
2401 tgl 1411 ECB :
1412 : /* Remember best path */
2401 tgl 1413 GIC 228 : if (evi_start == NULL ||
2401 tgl 1414 UIC 0 : list_length(path) < list_length(*best_path) ||
1415 0 : (list_length(path) == list_length(*best_path) &&
1416 0 : strcmp(evi_start->name, evi1->name) < 0))
1417 : {
2401 tgl 1418 GIC 228 : evi_start = evi1;
1419 228 : *best_path = path;
1420 : }
1421 : }
1422 :
1423 387 : return evi_start;
1424 : }
1425 :
1426 : /*
1427 : * CREATE EXTENSION worker
1428 : *
2407 tgl 1429 ECB : * When CASCADE is specified, CreateExtensionInternal() recurses if required
1430 : * extensions need to be installed. To sanely handle cyclic dependencies,
1431 : * the "parents" list contains a list of names of extensions already being
1432 : * installed, allowing us to error out if we recurse to one of those.
1433 : */
1434 : static ObjectAddress
2401 tgl 1435 CBC 469 : CreateExtensionInternal(char *extensionName,
1436 : char *schemaName,
1437 : const char *versionName,
1438 : bool cascade,
1439 : List *parents,
1440 : bool is_create)
4443 tgl 1441 ECB : {
2401 tgl 1442 GBC 469 : char *origSchemaName = schemaName;
2745 andres 1443 GIC 469 : Oid schemaOid = InvalidOid;
4443 tgl 1444 469 : Oid extowner = GetUserId();
4439 tgl 1445 ECB : ExtensionControlFile *pcontrol;
1446 : ExtensionControlFile *control;
1145 1447 : char *filename;
1448 : struct stat fst;
1449 : List *updateVersions;
4443 1450 : List *requiredExtensions;
1451 : List *requiredSchemas;
1452 : Oid extensionOid;
1453 : ObjectAddress address;
1454 : ListCell *lc;
1455 :
1456 : /*
4440 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 : */
2401 tgl 1461 GIC 469 : pcontrol = read_extension_control_file(extensionName);
4443 tgl 1462 ECB :
4440 tgl 1463 EUB : /*
1464 : * Determine the version to install
1465 : */
2401 tgl 1466 GIC 469 : if (versionName == NULL)
4440 tgl 1467 ECB : {
2401 tgl 1468 CBC 466 : if (pcontrol->default_version)
2401 tgl 1469 GIC 466 : versionName = pcontrol->default_version;
1470 : else
2401 tgl 1471 UIC 0 : ereport(ERROR,
2401 tgl 1472 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1473 : errmsg("version to install must be specified")));
1474 : }
4440 tgl 1475 GIC 469 : 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 : */
1060 1483 469 : filename = get_extension_script_filename(pcontrol, NULL, versionName);
1060 tgl 1484 CBC 469 : if (stat(filename, &fst) == 0)
1485 : {
1486 : /* Easy, no extra scripts */
1060 tgl 1487 GIC 418 : updateVersions = NIL;
1488 : }
1489 : else
1490 : {
1060 tgl 1491 ECB : /* 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 */
1060 tgl 1497 GIC 51 : evi_list = get_ext_ver_list(pcontrol);
1498 :
1499 : /* Identify the target version */
1500 51 : evi_target = get_ext_ver_info(versionName, &evi_list);
1501 :
1502 : /* Identify best path to reach target */
1503 51 : evi_start = find_install_path(evi_list, evi_target,
1504 : &updateVersions);
1505 :
1506 : /* Fail if no path ... */
1507 51 : if (evi_start == NULL)
1060 tgl 1508 UIC 0 : ereport(ERROR,
1509 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1060 tgl 1510 ECB : 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 */
1060 tgl 1514 GIC 51 : versionName = evi_start->name;
1060 tgl 1515 ECB : }
1516 :
4439 1517 : /*
1518 : * Fetch control parameters for installation target version
1519 : */
4439 tgl 1520 GBC 469 : control = read_extension_aux_control_file(pcontrol, versionName);
1521 :
1522 : /*
1523 : * Determine the target schema to install the extension into
4443 tgl 1524 ECB : */
2401 tgl 1525 GIC 469 : if (schemaName)
1526 : {
1527 : /* If the user is giving us the schema name, it must exist already. */
4443 1528 22 : schemaOid = get_namespace_oid(schemaName, false);
1529 : }
1530 :
2745 andres 1531 467 : if (control->schema != NULL)
4443 tgl 1532 ECB : {
1533 : /*
1534 : * The extension is not relocatable and the author gave us a schema
1535 : * for it.
2745 andres 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 : */
2745 andres 1540 GIC 331 : if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1541 2 : !cascade)
1542 1 : ereport(ERROR,
1543 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1544 : errmsg("extension \"%s\" must be installed in schema \"%s\"",
1545 : control->name,
2118 tgl 1546 ECB : control->schema)));
1547 :
1548 : /* Always use the schema from control file for current extension. */
4443 tgl 1549 CBC 330 : schemaName = control->schema;
1550 :
1551 : /* Find or create the schema in case it does not exist. */
1552 330 : schemaOid = get_namespace_oid(schemaName, true);
1553 :
2745 andres 1554 GIC 330 : if (!OidIsValid(schemaOid))
1555 : {
4247 tgl 1556 CBC 3 : CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
4247 tgl 1557 EUB :
4247 tgl 1558 GIC 3 : csstmt->schemaname = schemaName;
2953 alvherre 1559 3 : csstmt->authrole = NULL; /* will be created by current user */
4247 tgl 1560 3 : csstmt->schemaElts = NIL;
3840 1561 3 : csstmt->if_not_exists = false;
2276 1562 3 : CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
2276 tgl 1563 ECB : -1, -1);
1564 :
1565 : /*
1566 : * CreateSchemaCommand includes CommandCounterIncrement, so new
1567 : * schema is now visible.
1568 : */
4247 tgl 1569 CBC 3 : schemaOid = get_namespace_oid(schemaName, false);
1570 : }
1571 : }
2745 andres 1572 GIC 136 : else if (!OidIsValid(schemaOid))
1573 : {
4443 tgl 1574 ECB : /*
1575 : * Neither user nor author of the extension specified schema; use the
1576 : * current default creation namespace, which is the first explicit
2745 andres 1577 : * entry in the search_path.
1578 : */
4382 bruce 1579 GIC 118 : List *search_path = fetch_search_path(false);
4443 tgl 1580 ECB :
3260 bruce 1581 GIC 118 : if (search_path == NIL) /* nothing valid in search_path? */
3596 tgl 1582 UIC 0 : ereport(ERROR,
1583 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1584 : errmsg("no schema has been selected to create in")));
4443 tgl 1585 GIC 118 : schemaOid = linitial_oid(search_path);
1586 118 : schemaName = get_namespace_name(schemaOid);
4382 bruce 1587 118 : if (schemaName == NULL) /* recently-deleted namespace? */
3596 tgl 1588 UIC 0 : ereport(ERROR,
3596 tgl 1589 ECB : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1590 : errmsg("no schema has been selected to create in")));
4443 1591 :
4443 tgl 1592 GIC 118 : list_free(search_path);
1593 : }
1594 :
1595 : /*
1596 : * Make note if a temporary namespace has been accessed in this
1597 : * transaction.
1542 michael 1598 ECB : */
1542 michael 1599 GIC 466 : if (isTempNamespace(schemaOid))
1600 2 : MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
1542 michael 1601 ECB :
1602 : /*
4419 tgl 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
4382 bruce 1607 : * yet we don't want to restrict the command to users with ACL_CREATE for
1608 : * pg_catalog.
4443 tgl 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 : */
4443 tgl 1615 GIC 466 : requiredExtensions = NIL;
1616 466 : requiredSchemas = NIL;
1617 491 : foreach(lc, control->requires)
4443 tgl 1618 ECB : {
4443 tgl 1619 GIC 29 : char *curreq = (char *) lfirst(lc);
1620 : Oid reqext;
4443 tgl 1621 ECB : Oid reqschema;
1622 :
2401 tgl 1623 GIC 29 : reqext = get_required_extension(curreq,
1624 : extensionName,
1625 : origSchemaName,
1626 : cascade,
1627 : parents,
2401 tgl 1628 ECB : is_create);
4443 tgl 1629 GIC 25 : reqschema = get_extension_schema(reqext);
4443 tgl 1630 CBC 25 : requiredExtensions = lappend_oid(requiredExtensions, reqext);
4443 tgl 1631 GBC 25 : requiredSchemas = lappend_oid(requiredSchemas, reqschema);
1632 : }
1633 :
4443 tgl 1634 ECB : /*
4442 1635 : * Insert new tuple into pg_extension, and create dependency entries.
1636 : */
2959 alvherre 1637 GBC 462 : address = InsertExtensionTuple(control->name, extowner,
2959 alvherre 1638 GIC 462 : schemaOid, control->relocatable,
1639 : versionName,
1640 : PointerGetDatum(NULL),
2959 alvherre 1641 ECB : PointerGetDatum(NULL),
1642 : requiredExtensions);
2959 alvherre 1643 GIC 462 : extensionOid = address.objectId;
1644 :
1645 : /*
1646 : * Apply any control-file comment on extension
1647 : */
4442 tgl 1648 CBC 462 : if (control->comment != NULL)
1649 462 : CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
1650 :
1651 : /*
1652 : * Execute the installation script file
1653 : */
4439 tgl 1654 GIC 462 : 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 448 : ApplyExtensionUpdates(extensionOid, pcontrol,
2401 tgl 1664 ECB : versionName, updateVersions,
1665 : origSchemaName, cascade, is_create);
3759 rhaas 1666 :
2959 alvherre 1667 GIC 448 : return address;
4442 tgl 1668 ECB : }
1669 :
1670 : /*
1671 : * Get the OID of an extension listed in "requires", possibly creating it.
2401 1672 : */
1673 : static Oid
2401 tgl 1674 GIC 29 : get_required_extension(char *reqExtensionName,
1675 : char *extensionName,
1676 : char *origSchemaName,
1677 : bool cascade,
2401 tgl 1678 ECB : List *parents,
1679 : bool is_create)
1680 : {
1681 : Oid reqExtensionOid;
1682 :
2401 tgl 1683 GIC 29 : reqExtensionOid = get_extension_oid(reqExtensionName, true);
1684 29 : if (!OidIsValid(reqExtensionOid))
1685 : {
2401 tgl 1686 CBC 21 : if (cascade)
2401 tgl 1687 ECB : {
1688 : /* Must install it. */
1689 : ObjectAddress addr;
1690 : List *cascade_parents;
1691 : ListCell *lc;
1692 :
1693 : /* Check extension name validity before trying to cascade. */
2401 tgl 1694 GIC 19 : check_valid_extension_name(reqExtensionName);
1695 :
1696 : /* Check for cyclic dependency between extensions. */
2401 tgl 1697 CBC 21 : foreach(lc, parents)
2401 tgl 1698 ECB : {
2401 tgl 1699 GIC 3 : char *pname = (char *) lfirst(lc);
1700 :
1701 3 : if (strcmp(pname, reqExtensionName) == 0)
1702 1 : ereport(ERROR,
2401 tgl 1703 ECB : (errcode(ERRCODE_INVALID_RECURSION),
1704 : errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
1705 : reqExtensionName, extensionName)));
1706 : }
1707 :
2401 tgl 1708 GIC 18 : ereport(NOTICE,
1709 : (errmsg("installing required extension \"%s\"",
1710 : reqExtensionName)));
1711 :
2401 tgl 1712 ECB : /* Add current extension to list of parents to pass down. */
2401 tgl 1713 GIC 18 : cascade_parents = lappend(list_copy(parents), extensionName);
1714 :
1715 : /*
2401 tgl 1716 ECB : * Create the required extension. We propagate the SCHEMA option
1717 : * if any, and CASCADE, but no other options.
1718 : */
2401 tgl 1719 GIC 18 : addr = CreateExtensionInternal(reqExtensionName,
1720 : origSchemaName,
1721 : NULL,
1722 : cascade,
2401 tgl 1723 ECB : cascade_parents,
1724 : is_create);
1725 :
1726 : /* Get its newly-assigned OID. */
2401 tgl 1727 GIC 17 : reqExtensionOid = addr.objectId;
1728 : }
1729 : else
1730 2 : ereport(ERROR,
1731 : (errcode(ERRCODE_UNDEFINED_OBJECT),
2401 tgl 1732 ECB : 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 :
2401 tgl 1738 GIC 25 : return reqExtensionOid;
1739 : }
1740 :
1741 : /*
1742 : * CREATE EXTENSION
2745 andres 1743 ECB : */
1744 : ObjectAddress
2406 peter_e 1745 GIC 452 : CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
2745 andres 1746 ECB : {
2401 tgl 1747 GIC 452 : DefElem *d_schema = NULL;
2401 tgl 1748 CBC 452 : DefElem *d_new_version = NULL;
2401 tgl 1749 GIC 452 : DefElem *d_cascade = NULL;
2401 tgl 1750 CBC 452 : char *schemaName = NULL;
1751 452 : char *versionName = NULL;
2401 tgl 1752 GIC 452 : bool cascade = false;
1753 : ListCell *lc;
1754 :
1755 : /* Check extension name validity before any filesystem access */
2745 andres 1756 452 : check_valid_extension_name(stmt->extname);
2745 andres 1757 ECB :
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 : */
2745 andres 1764 GIC 452 : if (get_extension_oid(stmt->extname, true) != InvalidOid)
1765 : {
1766 1 : if (stmt->if_not_exists)
1767 : {
2745 andres 1768 CBC 1 : ereport(NOTICE,
1769 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1770 : errmsg("extension \"%s\" already exists, skipping",
1771 : stmt->extname)));
2745 andres 1772 GIC 1 : return InvalidObjectAddress;
1773 : }
1774 : else
2745 andres 1775 UIC 0 : ereport(ERROR,
2745 andres 1776 ECB : (errcode(ERRCODE_DUPLICATE_OBJECT),
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 : */
2745 andres 1785 GIC 451 : if (creating_extension)
2745 andres 1786 UIC 0 : ereport(ERROR,
2745 andres 1787 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1788 : errmsg("nested CREATE EXTENSION is not supported")));
1789 :
1790 : /* Deconstruct the statement option list */
2401 tgl 1791 GIC 490 : foreach(lc, stmt->options)
1792 : {
1793 39 : DefElem *defel = (DefElem *) lfirst(lc);
2401 tgl 1794 ECB :
2401 tgl 1795 GIC 39 : if (strcmp(defel->defname, "schema") == 0)
2401 tgl 1796 ECB : {
2401 tgl 1797 CBC 17 : if (d_schema)
633 dean.a.rasheed 1798 LBC 0 : errorConflictingDefElem(defel, pstate);
2401 tgl 1799 CBC 17 : d_schema = defel;
1800 17 : schemaName = defGetString(d_schema);
2401 tgl 1801 ECB : }
2401 tgl 1802 GIC 22 : else if (strcmp(defel->defname, "new_version") == 0)
1803 : {
1804 3 : if (d_new_version)
633 dean.a.rasheed 1805 LBC 0 : errorConflictingDefElem(defel, pstate);
2401 tgl 1806 GIC 3 : d_new_version = defel;
1807 3 : versionName = defGetString(d_new_version);
1808 : }
1809 19 : else if (strcmp(defel->defname, "cascade") == 0)
1810 : {
1811 19 : if (d_cascade)
633 dean.a.rasheed 1812 UIC 0 : errorConflictingDefElem(defel, pstate);
2401 tgl 1813 CBC 19 : d_cascade = defel;
2401 tgl 1814 GIC 19 : cascade = defGetBoolean(d_cascade);
2401 tgl 1815 ECB : }
1816 : else
2401 tgl 1817 LBC 0 : elog(ERROR, "unrecognized option: %s", defel->defname);
1818 : }
1819 :
1820 : /* Call CreateExtensionInternal to do the real work. */
2401 tgl 1821 CBC 451 : return CreateExtensionInternal(stmt->extname,
1822 : schemaName,
1823 : versionName,
2401 tgl 1824 EUB : cascade,
1825 : NIL,
1826 : true);
1827 : }
1828 :
1829 : /*
1830 : * InsertExtensionTuple
1831 : *
1832 : * Insert the new pg_extension row, and create extension's dependency entries.
1833 : * Return the OID assigned to the new row.
4442 tgl 1834 ECB : *
4442 tgl 1835 EUB : * This is exported for the benefit of pg_upgrade, which has to create a
1836 : * pg_extension entry (and the extension-level dependencies) without
1837 : * actually running the extension's script.
1838 : *
1839 : * extConfig and extCondition should be arrays or PointerGetDatum(NULL).
4442 tgl 1840 ECB : * We declare them as plain Datum to avoid needing array.h in extension.h.
1841 : */
2959 alvherre 1842 : ObjectAddress
4442 tgl 1843 GIC 462 : InsertExtensionTuple(const char *extName, Oid extOwner,
4442 tgl 1844 ECB : Oid schemaOid, bool relocatable, const char *extVersion,
1845 : Datum extConfig, Datum extCondition,
1846 : List *requiredExtensions)
4442 tgl 1847 EUB : {
4442 tgl 1848 ECB : Oid extensionOid;
1849 : Relation rel;
1850 : Datum values[Natts_pg_extension];
1851 : bool nulls[Natts_pg_extension];
1852 : HeapTuple tuple;
1853 : ObjectAddress myself;
4442 tgl 1854 EUB : ObjectAddress nsp;
1012 michael 1855 ECB : ObjectAddresses *refobjs;
4442 tgl 1856 : ListCell *lc;
1857 :
1858 : /*
1859 : * Build and insert the pg_extension tuple
4443 1860 : */
1539 andres 1861 GBC 462 : rel = table_open(ExtensionRelationId, RowExclusiveLock);
4443 tgl 1862 ECB :
4443 tgl 1863 CBC 462 : memset(values, 0, sizeof(values));
4443 tgl 1864 GIC 462 : memset(nulls, 0, sizeof(nulls));
1865 :
1601 andres 1866 GBC 462 : extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
1867 : Anum_pg_extension_oid);
1601 andres 1868 GIC 462 : values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
4443 tgl 1869 462 : values[Anum_pg_extension_extname - 1] =
4442 tgl 1870 CBC 462 : DirectFunctionCall1(namein, CStringGetDatum(extName));
4442 tgl 1871 GIC 462 : values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
4443 1872 462 : values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
4442 1873 462 : values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
4440 1874 462 : values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
1875 :
4442 1876 462 : if (extConfig == PointerGetDatum(NULL))
1877 462 : nulls[Anum_pg_extension_extconfig - 1] = true;
1878 : else
4442 tgl 1879 UIC 0 : values[Anum_pg_extension_extconfig - 1] = extConfig;
1880 :
4442 tgl 1881 GIC 462 : if (extCondition == PointerGetDatum(NULL))
1882 462 : nulls[Anum_pg_extension_extcondition - 1] = true;
1883 : else
4442 tgl 1884 UIC 0 : values[Anum_pg_extension_extcondition - 1] = extCondition;
1885 :
4443 tgl 1886 GIC 462 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
1887 :
1601 andres 1888 462 : CatalogTupleInsert(rel, tuple);
1889 :
4443 tgl 1890 462 : heap_freetuple(tuple);
1539 andres 1891 462 : table_close(rel, RowExclusiveLock);
4443 tgl 1892 ECB :
1893 : /*
1894 : * Record dependencies on owner, schema, and prerequisite extensions
1895 : */
4442 tgl 1896 GIC 462 : recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
1897 :
1012 michael 1898 462 : refobjs = new_object_addresses();
1899 :
1900 462 : ObjectAddressSet(myself, ExtensionRelationId, extensionOid);
1901 :
1902 462 : ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
1903 462 : add_exact_object_address(&nsp, refobjs);
1904 :
4443 tgl 1905 487 : foreach(lc, requiredExtensions)
1906 : {
1907 25 : Oid reqext = lfirst_oid(lc);
1908 : ObjectAddress otherext;
1909 :
1012 michael 1910 CBC 25 : ObjectAddressSet(otherext, ExtensionRelationId, reqext);
1012 michael 1911 GIC 25 : add_exact_object_address(&otherext, refobjs);
4443 tgl 1912 ECB : }
1012 michael 1913 :
1914 : /* Record all of them (this includes duplicate elimination) */
1012 michael 1915 CBC 462 : record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
1012 michael 1916 GIC 462 : free_object_addresses(refobjs);
1012 michael 1917 ECB :
4399 rhaas 1918 : /* Post creation hook for new extension */
3686 rhaas 1919 CBC 462 : InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
4443 tgl 1920 ECB :
2959 alvherre 1921 CBC 462 : return myself;
4443 tgl 1922 ECB : }
1923 :
1924 : /*
1925 : * Guts of extension deletion.
1926 : *
1927 : * All we need do here is remove the pg_extension tuple itself. Everything
4443 tgl 1928 EUB : * else is taken care of by the dependency infrastructure.
1929 : */
4443 tgl 1930 ECB : void
4443 tgl 1931 CBC 48 : RemoveExtensionById(Oid extId)
1932 : {
4443 tgl 1933 EUB : Relation rel;
1934 : SysScanDesc scandesc;
4443 tgl 1935 ECB : HeapTuple tuple;
1936 : ScanKeyData entry[1];
1937 :
1938 : /*
4150 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 : */
4150 tgl 1949 CBC 48 : if (extId == CurrentExtensionObject)
4150 tgl 1950 UIC 0 : ereport(ERROR,
4150 tgl 1951 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2118 1952 : errmsg("cannot drop extension \"%s\" because it is being modified",
1953 : get_extension_name(extId))));
4150 1954 :
1539 andres 1955 GIC 48 : rel = table_open(ExtensionRelationId, RowExclusiveLock);
4443 tgl 1956 ECB :
4443 tgl 1957 GIC 48 : ScanKeyInit(&entry[0],
1958 : Anum_pg_extension_oid,
4443 tgl 1959 ECB : BTEqualStrategyNumber, F_OIDEQ,
1960 : ObjectIdGetDatum(extId));
4443 tgl 1961 GIC 48 : scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
1962 : NULL, 1, entry);
1963 :
4443 tgl 1964 CBC 48 : tuple = systable_getnext(scandesc);
4443 tgl 1965 ECB :
1966 : /* We assume that there can be at most one matching tuple */
4443 tgl 1967 GIC 48 : if (HeapTupleIsValid(tuple))
2258 tgl 1968 CBC 48 : CatalogTupleDelete(rel, &tuple->t_self);
1969 :
4443 1970 48 : systable_endscan(scandesc);
1971 :
1539 andres 1972 GIC 48 : table_close(rel, RowExclusiveLock);
4443 tgl 1973 48 : }
1974 :
1975 : /*
1976 : * This function lists the available extensions (one row per primary control
1977 : * file in the control directory). We parse each control file and report the
1978 : * interesting fields.
1979 : *
4443 tgl 1980 ECB : * The system view pg_available_extensions provides a user interface to this
1981 : * SRF, adding information about whether the extensions are installed in the
1982 : * current DB.
1983 : */
1984 : Datum
4443 tgl 1985 GIC 9 : pg_available_extensions(PG_FUNCTION_ARGS)
1986 : {
4382 bruce 1987 9 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1988 : char *location;
1989 : DIR *dir;
1990 : struct dirent *de;
1991 :
1992 : /* Build tuplestore to hold the result rows */
173 michael 1993 9 : InitMaterializedSRF(fcinfo, 0);
1994 :
4443 tgl 1995 9 : location = get_extension_control_directory();
4382 bruce 1996 9 : dir = AllocateDir(location);
1997 :
4443 tgl 1998 ECB : /*
4382 bruce 1999 EUB : * 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 : */
4443 tgl 2002 GIC 9 : if (dir == NULL && errno == ENOENT)
2003 : {
4443 tgl 2004 ECB : /* do nothing */
2005 : }
2006 : else
2007 : {
4443 tgl 2008 GIC 2781 : while ((de = ReadDir(dir, location)) != NULL)
2009 : {
4443 tgl 2010 ECB : ExtensionControlFile *control;
2011 : char *extname;
2012 : Datum values[3];
4437 2013 : bool nulls[3];
2014 :
4443 tgl 2015 GIC 2772 : if (!is_extension_control_filename(de->d_name))
4443 tgl 2016 CBC 1899 : continue;
4443 tgl 2017 ECB :
2018 : /* extract extension name from 'name.control' filename */
4443 tgl 2019 CBC 873 : extname = pstrdup(de->d_name);
4443 tgl 2020 GIC 873 : *strrchr(extname, '.') = '\0';
4443 tgl 2021 ECB :
4438 2022 : /* ignore it if it's an auxiliary control file */
4438 tgl 2023 GIC 873 : if (strstr(extname, "--"))
4438 tgl 2024 UIC 0 : continue;
2025 :
4443 tgl 2026 GIC 873 : control = read_extension_control_file(extname);
2027 :
2028 873 : memset(values, 0, sizeof(values));
2029 873 : memset(nulls, 0, sizeof(nulls));
2030 :
2031 : /* name */
2032 873 : values[0] = DirectFunctionCall1(namein,
2033 : CStringGetDatum(control->name));
4440 tgl 2034 ECB : /* default_version */
4440 tgl 2035 GIC 873 : if (control->default_version == NULL)
4443 tgl 2036 LBC 0 : nulls[1] = true;
2037 : else
4440 tgl 2038 GIC 873 : values[1] = CStringGetTextDatum(control->default_version);
2039 : /* comment */
4443 2040 873 : if (control->comment == NULL)
4437 tgl 2041 UIC 0 : nulls[2] = true;
4443 tgl 2042 ECB : else
4437 tgl 2043 GIC 873 : values[2] = CStringGetTextDatum(control->comment);
4443 tgl 2044 ECB :
398 michael 2045 CBC 873 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2046 : values, nulls);
2047 : }
2048 :
4443 tgl 2049 GIC 9 : FreeDir(dir);
2050 : }
4443 tgl 2051 ECB :
4443 tgl 2052 GIC 9 : return (Datum) 0;
2053 : }
2054 :
2055 : /*
2056 : * This function lists the available extension versions (one row per
3260 bruce 2057 ECB : * extension installation script). For each version, we parse the related
2058 : * control file(s) and report the interesting fields.
2059 : *
2060 : * The system view pg_available_extension_versions provides a user interface
2061 : * to this SRF, adding information about which versions are installed in the
2062 : * current DB.
2063 : */
4437 tgl 2064 : Datum
4437 tgl 2065 CBC 3 : pg_available_extension_versions(PG_FUNCTION_ARGS)
2066 : {
4382 bruce 2067 GIC 3 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
4382 bruce 2068 ECB : char *location;
2069 : DIR *dir;
2070 : struct dirent *de;
2071 :
4437 tgl 2072 : /* Build tuplestore to hold the result rows */
173 michael 2073 GBC 3 : InitMaterializedSRF(fcinfo, 0);
2074 :
4437 tgl 2075 CBC 3 : location = get_extension_control_directory();
4382 bruce 2076 GIC 3 : dir = AllocateDir(location);
4437 tgl 2077 ECB :
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 : */
4437 tgl 2082 GIC 3 : if (dir == NULL && errno == ENOENT)
2083 : {
4437 tgl 2084 ECB : /* do nothing */
4437 tgl 2085 EUB : }
2086 : else
4437 tgl 2087 ECB : {
4437 tgl 2088 GIC 927 : while ((de = ReadDir(dir, location)) != NULL)
4437 tgl 2089 ECB : {
4437 tgl 2090 EUB : ExtensionControlFile *control;
2091 : char *extname;
4437 tgl 2092 ECB :
4437 tgl 2093 GIC 924 : if (!is_extension_control_filename(de->d_name))
4437 tgl 2094 CBC 633 : continue;
2095 :
2096 : /* extract extension name from 'name.control' filename */
4437 tgl 2097 GIC 291 : extname = pstrdup(de->d_name);
4437 tgl 2098 CBC 291 : *strrchr(extname, '.') = '\0';
2099 :
2100 : /* ignore it if it's an auxiliary control file */
2101 291 : if (strstr(extname, "--"))
4437 tgl 2102 UIC 0 : continue;
2103 :
2104 : /* read the control file */
4437 tgl 2105 GIC 291 : control = read_extension_control_file(extname);
2106 :
2107 : /* scan extension's script directory for install scripts */
398 michael 2108 291 : get_available_versions_for_extension(control, rsinfo->setResult,
2109 : rsinfo->setDesc);
2110 : }
2111 :
4437 tgl 2112 3 : FreeDir(dir);
2113 : }
4437 tgl 2114 ECB :
4437 tgl 2115 GIC 3 : return (Datum) 0;
4437 tgl 2116 ECB : }
2117 :
2118 : /*
2119 : * Inner loop for pg_available_extension_versions:
2120 : * read versions of one extension, add rows to tupstore
2121 : */
2122 : static void
4437 tgl 2123 GIC 291 : get_available_versions_for_extension(ExtensionControlFile *pcontrol,
4437 tgl 2124 ECB : Tuplestorestate *tupstore,
2125 : TupleDesc tupdesc)
2126 : {
2127 : List *evi_list;
2128 : ListCell *lc;
2129 :
2130 : /* Extract the version update graph from the script directory */
2401 tgl 2131 CBC 291 : evi_list = get_ext_ver_list(pcontrol);
2132 :
2133 : /* For each installable version ... */
2401 tgl 2134 GIC 918 : foreach(lc, evi_list)
2135 : {
2136 627 : ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
4437 tgl 2137 ECB : ExtensionControlFile *control;
2138 : Datum values[8];
2139 : bool nulls[8];
2140 : ListCell *lc2;
2141 :
2401 tgl 2142 CBC 627 : if (!evi->installable)
4437 2143 336 : continue;
2144 :
2145 : /*
4437 tgl 2146 ECB : * Fetch parameters for specific version (pcontrol is not changed)
2147 : */
2401 tgl 2148 GIC 291 : control = read_extension_aux_control_file(pcontrol, evi->name);
2149 :
4437 tgl 2150 CBC 291 : memset(values, 0, sizeof(values));
4437 tgl 2151 GBC 291 : memset(nulls, 0, sizeof(nulls));
2152 :
2153 : /* name */
4437 tgl 2154 CBC 291 : values[0] = DirectFunctionCall1(namein,
2155 : CStringGetDatum(control->name));
2156 : /* version */
2401 2157 291 : values[1] = CStringGetTextDatum(evi->name);
2158 : /* superuser */
4419 tgl 2159 GIC 291 : values[2] = BoolGetDatum(control->superuser);
2160 : /* trusted */
1166 tgl 2161 CBC 291 : values[3] = BoolGetDatum(control->trusted);
2162 : /* relocatable */
1166 tgl 2163 GIC 291 : values[4] = BoolGetDatum(control->relocatable);
4437 tgl 2164 ECB : /* schema */
4437 tgl 2165 GIC 291 : if (control->schema == NULL)
1166 2166 255 : nulls[5] = true;
2167 : else
2168 36 : values[5] = DirectFunctionCall1(namein,
2169 : CStringGetDatum(control->schema));
2170 : /* requires */
4437 2171 291 : if (control->requires == NIL)
1166 tgl 2172 CBC 240 : nulls[6] = true;
2173 : else
1166 tgl 2174 GIC 51 : values[6] = convert_requires_to_datum(control->requires);
2175 : /* comment */
4437 2176 291 : if (control->comment == NULL)
1166 tgl 2177 UIC 0 : nulls[7] = true;
2178 : else
1166 tgl 2179 GIC 291 : values[7] = CStringGetTextDatum(control->comment);
4437 tgl 2180 ECB :
4437 tgl 2181 GIC 291 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2182 :
2401 tgl 2183 ECB : /*
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 : */
2401 tgl 2188 GIC 918 : foreach(lc2, evi_list)
2189 : {
2190 627 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
2401 tgl 2191 ECB : List *best_path;
2192 :
2401 tgl 2193 GIC 627 : if (evi2->installable)
2194 291 : continue;
2195 336 : if (find_install_path(evi_list, evi2, &best_path) == evi)
2196 : {
2401 tgl 2197 ECB : /*
2198 : * Fetch parameters for this version (pcontrol is not changed)
2199 : */
2401 tgl 2200 CBC 177 : control = read_extension_aux_control_file(pcontrol, evi2->name);
2201 :
2202 : /* name stays the same */
2401 tgl 2203 ECB : /* version */
2401 tgl 2204 GIC 177 : values[1] = CStringGetTextDatum(evi2->name);
2205 : /* superuser */
2401 tgl 2206 CBC 177 : values[2] = BoolGetDatum(control->superuser);
2207 : /* trusted */
1166 2208 177 : values[3] = BoolGetDatum(control->trusted);
2209 : /* relocatable */
2210 177 : values[4] = BoolGetDatum(control->relocatable);
2211 : /* schema stays the same */
2401 tgl 2212 ECB : /* requires */
2401 tgl 2213 GIC 177 : if (control->requires == NIL)
1166 tgl 2214 CBC 177 : nulls[6] = true;
2401 tgl 2215 ECB : else
2216 : {
1166 tgl 2217 LBC 0 : values[6] = convert_requires_to_datum(control->requires);
1166 tgl 2218 UIC 0 : nulls[6] = false;
2219 : }
2401 tgl 2220 ECB : /* comment stays the same */
2221 :
2401 tgl 2222 GIC 177 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2401 tgl 2223 ECB : }
2224 : }
4437 2225 : }
2401 tgl 2226 GBC 291 : }
2227 :
1166 tgl 2228 ECB : /*
2229 : * Test whether the given extension exists (not whether it's installed)
2230 : *
2231 : * This checks for the existence of a matching control file in the extension
2232 : * directory. That's not a bulletproof check, since the file might be
2233 : * invalid, but this is only used for hints so it doesn't have to be 100%
2234 : * right.
2235 : */
2236 : bool
1166 tgl 2237 LBC 0 : extension_file_exists(const char *extensionName)
2238 : {
2239 0 : bool result = false;
2240 : char *location;
2241 : DIR *dir;
1166 tgl 2242 ECB : struct dirent *de;
2243 :
1166 tgl 2244 LBC 0 : location = get_extension_control_directory();
1166 tgl 2245 UIC 0 : dir = AllocateDir(location);
2246 :
2247 : /*
2248 : * If the control directory doesn't exist, we want to silently return
1166 tgl 2249 ECB : * false. Any other error will be reported by ReadDir.
2250 : */
1166 tgl 2251 UIC 0 : if (dir == NULL && errno == ENOENT)
2252 : {
1166 tgl 2253 ECB : /* do nothing */
2254 : }
2255 : else
2256 : {
1166 tgl 2257 LBC 0 : while ((de = ReadDir(dir, location)) != NULL)
2258 : {
1166 tgl 2259 ECB : char *extname;
2260 :
1166 tgl 2261 UIC 0 : if (!is_extension_control_filename(de->d_name))
1166 tgl 2262 LBC 0 : continue;
1166 tgl 2263 ECB :
2264 : /* extract extension name from 'name.control' filename */
1166 tgl 2265 UIC 0 : extname = pstrdup(de->d_name);
1166 tgl 2266 UBC 0 : *strrchr(extname, '.') = '\0';
1166 tgl 2267 EUB :
2268 : /* ignore it if it's an auxiliary control file */
1166 tgl 2269 UIC 0 : if (strstr(extname, "--"))
2270 0 : continue;
1166 tgl 2271 ECB :
2272 : /* done if it matches request */
1166 tgl 2273 UIC 0 : if (strcmp(extname, extensionName) == 0)
2274 : {
1166 tgl 2275 LBC 0 : result = true;
1166 tgl 2276 UIC 0 : break;
2277 : }
2278 : }
2279 :
2280 0 : FreeDir(dir);
2281 : }
2282 :
2283 0 : return result;
2284 : }
2285 :
2401 tgl 2286 EUB : /*
2287 : * Convert a list of extension names to a name[] Datum
2288 : */
2289 : static Datum
2401 tgl 2290 GIC 51 : convert_requires_to_datum(List *requires)
2291 : {
2292 : Datum *datums;
2401 tgl 2293 EUB : int ndatums;
2294 : ArrayType *a;
2295 : ListCell *lc;
2296 :
2401 tgl 2297 GIC 51 : ndatums = list_length(requires);
2298 51 : datums = (Datum *) palloc(ndatums * sizeof(Datum));
2299 51 : ndatums = 0;
2401 tgl 2300 GBC 123 : foreach(lc, requires)
2301 : {
2401 tgl 2302 GIC 72 : char *curreq = (char *) lfirst(lc);
2303 :
2304 72 : datums[ndatums++] =
2305 72 : DirectFunctionCall1(namein, CStringGetDatum(curreq));
2401 tgl 2306 EUB : }
282 peter 2307 GNC 51 : a = construct_array_builtin(datums, ndatums, NAMEOID);
2401 tgl 2308 GBC 51 : return PointerGetDatum(a);
4437 tgl 2309 EUB : }
2310 :
2311 : /*
2312 : * This function reports the version update paths that exist for the
2313 : * specified extension.
2314 : */
2315 : Datum
4437 tgl 2316 UBC 0 : pg_extension_update_paths(PG_FUNCTION_ARGS)
4437 tgl 2317 EUB : {
4437 tgl 2318 UIC 0 : Name extname = PG_GETARG_NAME(0);
4382 bruce 2319 0 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
4437 tgl 2320 EUB : List *evi_list;
2321 : ExtensionControlFile *control;
2322 : ListCell *lc1;
2323 :
2324 : /* Check extension name validity before any filesystem access */
4437 tgl 2325 UIC 0 : check_valid_extension_name(NameStr(*extname));
2326 :
4437 tgl 2327 EUB : /* Build tuplestore to hold the result rows */
173 michael 2328 UIC 0 : InitMaterializedSRF(fcinfo, 0);
2329 :
4437 tgl 2330 EUB : /* Read the extension's control file */
4437 tgl 2331 UIC 0 : control = read_extension_control_file(NameStr(*extname));
2332 :
2333 : /* Extract the version update graph from the script directory */
2334 0 : evi_list = get_ext_ver_list(control);
2335 :
2336 : /* Iterate over all pairs of versions */
4437 tgl 2337 LBC 0 : foreach(lc1, evi_list)
2338 : {
4437 tgl 2339 UIC 0 : ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc1);
2340 : ListCell *lc2;
2341 :
2342 0 : foreach(lc2, evi_list)
2343 : {
4437 tgl 2344 LBC 0 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
4437 tgl 2345 ECB : List *path;
2346 : Datum values[3];
2347 : bool nulls[3];
2348 :
4437 tgl 2349 LBC 0 : if (evi1 == evi2)
4437 tgl 2350 UIC 0 : continue;
4437 tgl 2351 ECB :
2352 : /* Find shortest path from evi1 to evi2 */
2401 tgl 2353 UIC 0 : path = find_update_path(evi_list, evi1, evi2, false, true);
4437 tgl 2354 ECB :
2355 : /* Emit result row */
4437 tgl 2356 UIC 0 : memset(values, 0, sizeof(values));
2357 0 : memset(nulls, 0, sizeof(nulls));
2358 :
2359 : /* source */
2360 0 : values[0] = CStringGetTextDatum(evi1->name);
2361 : /* target */
2362 0 : values[1] = CStringGetTextDatum(evi2->name);
4437 tgl 2363 EUB : /* path */
4437 tgl 2364 UIC 0 : if (path == NIL)
4437 tgl 2365 UBC 0 : nulls[2] = true;
4437 tgl 2366 EUB : else
2367 : {
2368 : StringInfoData pathbuf;
2369 : ListCell *lcv;
2370 :
4437 tgl 2371 UIC 0 : initStringInfo(&pathbuf);
4437 tgl 2372 EUB : /* The path doesn't include start vertex, but show it */
4437 tgl 2373 UIC 0 : appendStringInfoString(&pathbuf, evi1->name);
2374 0 : foreach(lcv, path)
4437 tgl 2375 EUB : {
4437 tgl 2376 UIC 0 : char *versionName = (char *) lfirst(lcv);
2377 :
4437 tgl 2378 UBC 0 : appendStringInfoString(&pathbuf, "--");
4437 tgl 2379 UIC 0 : appendStringInfoString(&pathbuf, versionName);
2380 : }
4437 tgl 2381 UBC 0 : values[2] = CStringGetTextDatum(pathbuf.data);
4437 tgl 2382 UIC 0 : pfree(pathbuf.data);
2383 : }
4437 tgl 2384 EUB :
398 michael 2385 UIC 0 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
398 michael 2386 EUB : values, nulls);
2387 : }
2388 : }
4437 tgl 2389 :
4437 tgl 2390 UIC 0 : return (Datum) 0;
4437 tgl 2391 EUB : }
2392 :
2393 : /*
2394 : * pg_extension_config_dump
2395 : *
4443 2396 : * Record information about a configuration table that belongs to an
2397 : * extension being created, but whose contents should be dumped in whole
2398 : * or in part during pg_dump.
2399 : */
2400 : Datum
4443 tgl 2401 GIC 4 : pg_extension_config_dump(PG_FUNCTION_ARGS)
2402 : {
4443 tgl 2403 GBC 4 : Oid tableoid = PG_GETARG_OID(0);
2219 noah 2404 4 : text *wherecond = PG_GETARG_TEXT_PP(1);
2405 : char *tablename;
2406 : Relation extRel;
4382 bruce 2407 EUB : ScanKeyData key[1];
2408 : SysScanDesc extScan;
4443 tgl 2409 : HeapTuple extTup;
2410 : Datum arrayDatum;
2411 : Datum elementDatum;
3762 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];
4443 2418 : ArrayType *a;
2419 :
2420 : /*
4382 bruce 2421 : * We only allow this to be called from an extension's SQL script. We
2422 : * shouldn't need any permissions check beyond that.
4443 tgl 2423 : */
4443 tgl 2424 GIC 4 : if (!creating_extension)
4443 tgl 2425 UBC 0 : ereport(ERROR,
4443 tgl 2426 EUB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2427 : errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
1572 alvherre 2428 : "pg_extension_config_dump()")));
4443 tgl 2429 :
2430 : /*
2431 : * Check that the table exists and is a member of the extension being
3762 2432 : * created. This ensures that we don't need to register an additional
2433 : * dependency to protect the extconfig entry.
2434 : */
4443 tgl 2435 GIC 4 : tablename = get_rel_name(tableoid);
2436 4 : if (tablename == NULL)
4443 tgl 2437 UBC 0 : ereport(ERROR,
2438 : (errcode(ERRCODE_UNDEFINED_TABLE),
2439 : errmsg("OID %u does not refer to a table", tableoid)));
4443 tgl 2440 GIC 4 : if (getExtensionOfObject(RelationRelationId, tableoid) !=
2441 : CurrentExtensionObject)
4443 tgl 2442 UIC 0 : 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 : /*
4382 bruce 2448 ECB : * Add the table OID and WHERE condition to the extension's extconfig and
2449 : * extcondition arrays.
3762 tgl 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 */
1539 andres 2456 GIC 4 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2457 :
4443 tgl 2458 4 : ScanKeyInit(&key[0],
2459 : Anum_pg_extension_oid,
2460 : BTEqualStrategyNumber, F_OIDEQ,
2461 : ObjectIdGetDatum(CurrentExtensionObject));
2462 :
2463 4 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2464 : NULL, 1, key);
2465 :
2466 4 : extTup = systable_getnext(extScan);
2467 :
2118 2468 4 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2135 tgl 2469 UIC 0 : elog(ERROR, "could not find tuple for extension %u",
2470 : CurrentExtensionObject);
4443 tgl 2471 ECB :
4443 tgl 2472 GBC 4 : memset(repl_val, 0, sizeof(repl_val));
4443 tgl 2473 GIC 4 : memset(repl_null, false, sizeof(repl_null));
2474 4 : memset(repl_repl, false, sizeof(repl_repl));
2475 :
2476 : /* Build or modify the extconfig value */
2477 4 : elementDatum = ObjectIdGetDatum(tableoid);
2478 :
2479 4 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2480 : RelationGetDescr(extRel), &isnull);
2481 4 : if (isnull)
4443 tgl 2482 ECB : {
3762 2483 : /* Previously empty extconfig, so build 1-element array */
3762 tgl 2484 GBC 2 : arrayLength = 0;
3762 tgl 2485 GIC 2 : arrayIndex = 1;
2486 :
282 peter 2487 GNC 2 : 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 :
4443 tgl 2495 GIC 2 : a = DatumGetArrayTypeP(arrayDatum);
2496 :
3762 2497 2 : arrayLength = ARR_DIMS(a)[0];
2498 2 : if (ARR_NDIM(a) != 1 ||
2499 2 : ARR_LBOUND(a)[0] != 1 ||
2500 2 : arrayLength < 0 ||
3762 tgl 2501 CBC 2 : ARR_HASNULL(a) ||
3762 tgl 2502 GIC 2 : ARR_ELEMTYPE(a) != OIDOID)
3762 tgl 2503 LBC 0 : elog(ERROR, "extconfig is not a 1-D Oid array");
3762 tgl 2504 GIC 2 : arrayData = (Oid *) ARR_DATA_PTR(a);
2505 :
2506 2 : arrayIndex = arrayLength + 1; /* set up to add after end */
2507 :
3762 tgl 2508 CBC 4 : for (i = 0; i < arrayLength; i++)
2509 : {
3762 tgl 2510 GIC 2 : if (arrayData[i] == tableoid)
3762 tgl 2511 ECB : {
2118 tgl 2512 UIC 0 : arrayIndex = i + 1; /* replace this element instead */
3762 tgl 2513 LBC 0 : break;
3762 tgl 2514 EUB : }
2515 : }
2516 :
4443 tgl 2517 CBC 2 : a = array_set(a, 1, &arrayIndex,
4443 tgl 2518 ECB : 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 : }
4443 tgl 2525 GIC 4 : repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
4443 tgl 2526 CBC 4 : repl_repl[Anum_pg_extension_extconfig - 1] = true;
2527 :
2528 : /* Build or modify the extcondition value */
2529 4 : elementDatum = PointerGetDatum(wherecond);
4443 tgl 2530 ECB :
4443 tgl 2531 GIC 4 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
4443 tgl 2532 ECB : RelationGetDescr(extRel), &isnull);
4443 tgl 2533 GIC 4 : if (isnull)
2534 : {
3762 2535 2 : if (arrayLength != 0)
3762 tgl 2536 UIC 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
2537 :
282 peter 2538 GNC 2 : a = construct_array_builtin(&elementDatum, 1, TEXTOID);
2539 : }
4443 tgl 2540 ECB : else
2541 : {
4443 tgl 2542 CBC 2 : a = DatumGetArrayTypeP(arrayDatum);
4443 tgl 2543 ECB :
3762 tgl 2544 CBC 2 : if (ARR_NDIM(a) != 1 ||
2545 2 : ARR_LBOUND(a)[0] != 1 ||
3762 tgl 2546 GBC 2 : ARR_HASNULL(a) ||
3762 tgl 2547 CBC 2 : ARR_ELEMTYPE(a) != TEXTOID)
3762 tgl 2548 UIC 0 : elog(ERROR, "extcondition is not a 1-D text array");
3762 tgl 2549 CBC 2 : if (ARR_DIMS(a)[0] != arrayLength)
3762 tgl 2550 UIC 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
4443 tgl 2551 ECB :
2552 : /* Add or replace at same index as in extconfig */
4443 tgl 2553 CBC 2 : a = array_set(a, 1, &arrayIndex,
2554 : elementDatum,
4443 tgl 2555 EUB : false,
2556 : -1 /* varlena array */ ,
2557 : -1 /* TEXT's typlen */ ,
2558 : false /* TEXT's typbyval */ ,
2559 : TYPALIGN_INT /* TEXT's typalign */ );
4443 tgl 2560 ECB : }
4443 tgl 2561 GIC 4 : repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2562 4 : repl_repl[Anum_pg_extension_extcondition - 1] = true;
2563 :
2564 4 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2565 : repl_val, repl_null, repl_repl);
2566 :
2259 alvherre 2567 4 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
4443 tgl 2568 ECB :
4443 tgl 2569 CBC 4 : systable_endscan(extScan);
2570 :
1539 andres 2571 GIC 4 : table_close(extRel, RowExclusiveLock);
4443 tgl 2572 ECB :
4443 tgl 2573 GIC 4 : PG_RETURN_VOID();
4443 tgl 2574 ECB : }
2575 :
3762 2576 : /*
2577 : * extension_config_remove
2578 : *
3762 tgl 2579 EUB : * Remove the specified table OID from extension's extconfig, if present.
2580 : * This is not currently exposed as a function, but it could be;
3762 tgl 2581 ECB : * for now, we just invoke it from ALTER EXTENSION DROP.
2582 : */
2583 : static void
3762 tgl 2584 GIC 16 : extension_config_remove(Oid extensionoid, Oid tableoid)
3762 tgl 2585 ECB : {
2586 : Relation extRel;
2587 : ScanKeyData key[1];
2588 : SysScanDesc extScan;
2589 : HeapTuple extTup;
2590 : Datum arrayDatum;
3762 tgl 2591 EUB : int arrayLength;
3762 tgl 2592 ECB : int arrayIndex;
3762 tgl 2593 EUB : bool isnull;
2594 : Datum repl_val[Natts_pg_extension];
2595 : bool repl_null[Natts_pg_extension];
3762 tgl 2596 ECB : bool repl_repl[Natts_pg_extension];
2597 : ArrayType *a;
2598 :
2599 : /* Find the pg_extension tuple */
1539 andres 2600 GIC 16 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2601 :
3762 tgl 2602 16 : ScanKeyInit(&key[0],
2603 : Anum_pg_extension_oid,
3762 tgl 2604 ECB : BTEqualStrategyNumber, F_OIDEQ,
2605 : ObjectIdGetDatum(extensionoid));
2606 :
3762 tgl 2607 CBC 16 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2608 : NULL, 1, key);
2609 :
2610 16 : extTup = systable_getnext(extScan);
2611 :
2118 2612 16 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2135 tgl 2613 UIC 0 : elog(ERROR, "could not find tuple for extension %u",
3762 tgl 2614 ECB : extensionoid);
2615 :
2616 : /* Search extconfig for the tableoid */
3762 tgl 2617 GIC 16 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2618 : RelationGetDescr(extRel), &isnull);
2619 16 : if (isnull)
2620 : {
2621 : /* nothing to do */
2622 12 : a = NULL;
2623 12 : arrayLength = 0;
2624 12 : arrayIndex = -1;
2625 : }
2626 : else
3762 tgl 2627 ECB : {
2628 : Oid *arrayData;
2629 : int i;
2630 :
3762 tgl 2631 GIC 4 : a = DatumGetArrayTypeP(arrayDatum);
2632 :
2633 4 : arrayLength = ARR_DIMS(a)[0];
2634 4 : if (ARR_NDIM(a) != 1 ||
2635 4 : ARR_LBOUND(a)[0] != 1 ||
2636 4 : arrayLength < 0 ||
2637 4 : ARR_HASNULL(a) ||
2638 4 : ARR_ELEMTYPE(a) != OIDOID)
3762 tgl 2639 UIC 0 : elog(ERROR, "extconfig is not a 1-D Oid array");
3762 tgl 2640 GIC 4 : arrayData = (Oid *) ARR_DATA_PTR(a);
2641 :
2642 4 : arrayIndex = -1; /* flag for no deletion needed */
3762 tgl 2643 ECB :
3762 tgl 2644 GIC 12 : for (i = 0; i < arrayLength; i++)
3762 tgl 2645 ECB : {
3762 tgl 2646 GIC 8 : if (arrayData[i] == tableoid)
2647 : {
3762 tgl 2648 UIC 0 : arrayIndex = i; /* index to remove */
2649 0 : break;
3762 tgl 2650 ECB : }
2651 : }
2652 : }
2653 :
2654 : /* If tableoid is not in extconfig, nothing to do */
3762 tgl 2655 CBC 16 : if (arrayIndex < 0)
3762 tgl 2656 EUB : {
3762 tgl 2657 GIC 16 : systable_endscan(extScan);
1539 andres 2658 16 : table_close(extRel, RowExclusiveLock);
3762 tgl 2659 16 : return;
3762 tgl 2660 ECB : }
2661 :
2662 : /* Modify or delete the extconfig value */
3762 tgl 2663 UIC 0 : memset(repl_val, 0, sizeof(repl_val));
2664 0 : memset(repl_null, false, sizeof(repl_null));
3762 tgl 2665 LBC 0 : memset(repl_repl, false, sizeof(repl_repl));
3762 tgl 2666 ECB :
3762 tgl 2667 LBC 0 : if (arrayLength <= 1)
2668 : {
2669 : /* removing only element, just set array to null */
3762 tgl 2670 UIC 0 : repl_null[Anum_pg_extension_extconfig - 1] = true;
2671 : }
2672 : else
2673 : {
3762 tgl 2674 ECB : /* squeeze out the target element */
2675 : Datum *dvalues;
2676 : int nelems;
2677 : int i;
2678 :
1609 2679 : /* We already checked there are no nulls */
282 peter 2680 UNC 0 : deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
3762 tgl 2681 EUB :
3762 tgl 2682 LBC 0 : for (i = arrayIndex; i < arrayLength - 1; i++)
3762 tgl 2683 UIC 0 : dvalues[i] = dvalues[i + 1];
3762 tgl 2684 ECB :
282 peter 2685 UNC 0 : a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
2686 :
3762 tgl 2687 LBC 0 : repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2688 : }
3762 tgl 2689 UBC 0 : repl_repl[Anum_pg_extension_extconfig - 1] = true;
3762 tgl 2690 EUB :
2691 : /* Modify or delete the extcondition value */
3762 tgl 2692 UIC 0 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2693 : RelationGetDescr(extRel), &isnull);
2694 0 : if (isnull)
2695 : {
3762 tgl 2696 LBC 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
2697 : }
3762 tgl 2698 ECB : else
2699 : {
3762 tgl 2700 LBC 0 : a = DatumGetArrayTypeP(arrayDatum);
2701 :
3762 tgl 2702 UIC 0 : if (ARR_NDIM(a) != 1 ||
2703 0 : ARR_LBOUND(a)[0] != 1 ||
3762 tgl 2704 UBC 0 : ARR_HASNULL(a) ||
2705 0 : ARR_ELEMTYPE(a) != TEXTOID)
2706 0 : elog(ERROR, "extcondition is not a 1-D text array");
3762 tgl 2707 UIC 0 : if (ARR_DIMS(a)[0] != arrayLength)
3762 tgl 2708 UBC 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
2709 : }
2710 :
2711 0 : if (arrayLength <= 1)
2712 : {
2713 : /* removing only element, just set array to null */
3762 tgl 2714 UIC 0 : repl_null[Anum_pg_extension_extcondition - 1] = true;
2715 : }
2716 : else
2717 : {
2718 : /* squeeze out the target element */
2719 : Datum *dvalues;
2720 : int nelems;
3762 tgl 2721 EUB : int i;
2722 :
1609 2723 : /* We already checked there are no nulls */
282 peter 2724 UNC 0 : deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
3762 tgl 2725 EUB :
3762 tgl 2726 UIC 0 : for (i = arrayIndex; i < arrayLength - 1; i++)
3762 tgl 2727 UBC 0 : dvalues[i] = dvalues[i + 1];
2728 :
282 peter 2729 UNC 0 : a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
2730 :
3762 tgl 2731 UBC 0 : repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2732 : }
2733 0 : repl_repl[Anum_pg_extension_extcondition - 1] = true;
2734 :
2735 0 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2736 : repl_val, repl_null, repl_repl);
2737 :
2259 alvherre 2738 UIC 0 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3762 tgl 2739 EUB :
3762 tgl 2740 UIC 0 : systable_endscan(extScan);
3762 tgl 2741 EUB :
1539 andres 2742 UBC 0 : table_close(extRel, RowExclusiveLock);
3762 tgl 2743 EUB : }
2744 :
4443 2745 : /*
2746 : * Execute ALTER EXTENSION SET SCHEMA
2747 : */
2748 : ObjectAddress
2339 peter_e 2749 GIC 3 : AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *oldschema)
4443 tgl 2750 EUB : {
2751 : Oid extensionOid;
2752 : Oid nspOid;
4443 tgl 2753 GBC 3 : 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;
3812 alvherre 2763 EUB : ObjectAddresses *objsMoved;
2764 : ObjectAddress extAddr;
4443 tgl 2765 :
4443 tgl 2766 GBC 3 : extensionOid = get_extension_oid(extensionName, false);
2767 :
2768 3 : nspOid = LookupCreationNamespace(newschema);
2769 :
4419 tgl 2770 EUB : /*
2771 : * Permission check: must own extension. Note that we don't bother to
2772 : * check ownership of the individual member objects ...
2773 : */
147 peter 2774 GNC 3 : if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
1954 peter_e 2775 UIC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
2776 : extensionName);
4419 tgl 2777 EUB :
2778 : /* Permission check: must have creation rights in target namespace */
147 peter 2779 GNC 3 : aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
4419 tgl 2780 GIC 3 : if (aclresult != ACLCHECK_OK)
1954 peter_e 2781 UBC 0 : 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 : */
3889 tgl 2787 GIC 3 : if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
3889 tgl 2788 LBC 0 : 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",
3889 tgl 2792 ECB : extensionName, newschema)));
2793 :
2794 : /* Locate the pg_extension tuple */
1539 andres 2795 GIC 3 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2796 :
4443 tgl 2797 3 : ScanKeyInit(&key[0],
2798 : Anum_pg_extension_oid,
2799 : BTEqualStrategyNumber, F_OIDEQ,
2800 : ObjectIdGetDatum(extensionOid));
2801 :
2802 3 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2803 : NULL, 1, key);
2804 :
4443 tgl 2805 CBC 3 : extTup = systable_getnext(extScan);
2806 :
2118 2807 3 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2135 tgl 2808 UIC 0 : elog(ERROR, "could not find tuple for extension %u",
2809 : extensionOid);
2810 :
2811 : /* Copy tuple so we can modify it below */
4443 tgl 2812 GIC 3 : extTup = heap_copytuple(extTup);
4443 tgl 2813 CBC 3 : extForm = (Form_pg_extension) GETSTRUCT(extTup);
4443 tgl 2814 EUB :
4443 tgl 2815 GIC 3 : systable_endscan(extScan);
2816 :
2817 : /*
4382 bruce 2818 ECB : * If the extension is already in the target schema, just silently do
2819 : * nothing.
4443 tgl 2820 EUB : */
4443 tgl 2821 GIC 3 : if (extForm->extnamespace == nspOid)
2822 : {
1539 andres 2823 UIC 0 : table_close(extRel, RowExclusiveLock);
2959 alvherre 2824 0 : return InvalidObjectAddress;
2825 : }
4443 tgl 2826 ECB :
4443 tgl 2827 EUB : /* Check extension is supposed to be relocatable */
4443 tgl 2828 GIC 3 : if (!extForm->extrelocatable)
4443 tgl 2829 UIC 0 : ereport(ERROR,
2830 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2831 : errmsg("extension \"%s\" does not support SET SCHEMA",
2832 : NameStr(extForm->extname))));
2833 :
3812 alvherre 2834 CBC 3 : objsMoved = new_object_addresses();
2835 :
4443 tgl 2836 ECB : /*
2837 : * Scan pg_depend to find objects that depend directly on the extension,
2838 : * and alter each one's schema.
2839 : */
1539 andres 2840 GIC 3 : depRel = table_open(DependRelationId, AccessShareLock);
4443 tgl 2841 ECB :
4443 tgl 2842 GIC 3 : ScanKeyInit(&key[0],
2843 : Anum_pg_depend_refclassid,
4443 tgl 2844 ECB : BTEqualStrategyNumber, F_OIDEQ,
2845 : ObjectIdGetDatum(ExtensionRelationId));
4443 tgl 2846 CBC 3 : ScanKeyInit(&key[1],
4443 tgl 2847 EUB : Anum_pg_depend_refobjid,
2848 : BTEqualStrategyNumber, F_OIDEQ,
2849 : ObjectIdGetDatum(extensionOid));
2850 :
4443 tgl 2851 CBC 3 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
3568 rhaas 2852 ECB : NULL, 2, key);
2853 :
4443 tgl 2854 CBC 9 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2855 : {
4443 tgl 2856 GIC 7 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2857 : ObjectAddress dep;
2858 : Oid dep_oldNspOid;
2859 :
4443 tgl 2860 ECB : /*
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 : */
20 tgl 2869 GNC 7 : if (pg_depend->deptype == DEPENDENCY_NORMAL &&
2870 4 : pg_depend->classid == ExtensionRelationId)
2871 : {
2872 4 : char *depextname = get_extension_name(pg_depend->objid);
2873 : ExtensionControlFile *dcontrol;
2874 : ListCell *lc;
2875 :
2876 4 : dcontrol = read_extension_control_file(depextname);
2877 5 : foreach(lc, dcontrol->no_relocate)
2878 : {
2879 2 : char *nrextname = (char *) lfirst(lc);
2880 :
2881 2 : if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
2882 : {
2883 1 : 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 : */
4443 tgl 2899 GIC 6 : if (pg_depend->deptype != DEPENDENCY_EXTENSION)
2900 3 : continue;
4443 tgl 2901 ECB :
4443 tgl 2902 GBC 3 : dep.classId = pg_depend->classid;
4443 tgl 2903 GIC 3 : dep.objectId = pg_depend->objid;
2904 3 : dep.objectSubId = pg_depend->objsubid;
2905 :
2118 2906 3 : if (dep.objectSubId != 0) /* should not happen */
4443 tgl 2907 LBC 0 : elog(ERROR, "extension should not have a sub-object dependency");
2908 :
2909 : /* Relocate the object */
4443 tgl 2910 GIC 3 : dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
2911 : dep.objectId,
2912 : nspOid,
3812 alvherre 2913 ECB : objsMoved);
2914 :
4443 tgl 2915 : /*
2916 : * Remember previous namespace of first object that has one
2917 : */
4443 tgl 2918 GIC 3 : if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid)
4443 tgl 2919 CBC 3 : 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.
4443 tgl 2924 ECB : */
4443 tgl 2925 GIC 3 : if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
4443 tgl 2926 UIC 0 : ereport(ERROR,
4443 tgl 2927 ECB : (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 */
2959 alvherre 2936 GIC 2 : if (oldschema)
2937 2 : *oldschema = oldNspOid;
2938 :
4443 tgl 2939 2 : systable_endscan(depScan);
2940 :
2941 2 : relation_close(depRel, AccessShareLock);
4443 tgl 2942 ECB :
2943 : /* Now adjust pg_extension.extnamespace */
4443 tgl 2944 GIC 2 : extForm->extnamespace = nspOid;
4443 tgl 2945 ECB :
2259 alvherre 2946 GIC 2 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2947 :
1539 andres 2948 2 : table_close(extRel, RowExclusiveLock);
4443 tgl 2949 ECB :
2950 : /* update dependencies to point to the new schema */
4443 tgl 2951 GIC 2 : changeDependencyFor(ExtensionRelationId, extensionOid,
4443 tgl 2952 ECB : NamespaceRelationId, oldNspOid, nspOid);
2953 :
3675 rhaas 2954 CBC 2 : InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
2955 :
2959 alvherre 2956 2 : ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
2957 :
2959 alvherre 2958 GIC 2 : return extAddr;
2959 : }
2960 :
2961 : /*
2962 : * Execute ALTER EXTENSION UPDATE
2963 : */
2964 : ObjectAddress
2406 peter_e 2965 11 : ExecAlterExtensionStmt(ParseState *pstate, AlterExtensionStmt *stmt)
2966 : {
4440 tgl 2967 11 : DefElem *d_new_version = NULL;
2968 : char *versionName;
2969 : char *oldVersionName;
2970 : ExtensionControlFile *control;
2971 : Oid extensionOid;
4440 tgl 2972 ECB : Relation extRel;
4382 bruce 2973 : ScanKeyData key[1];
2974 : SysScanDesc extScan;
4440 tgl 2975 : HeapTuple extTup;
2976 : List *updateVersions;
2977 : Datum datum;
2978 : bool isnull;
2979 : ListCell *lc;
2959 alvherre 2980 EUB : ObjectAddress address;
2981 :
2982 : /*
4382 bruce 2983 ECB : * We use global variables to track the extension being created, so we can
2984 : * create/update only one extension at the same time.
2985 : */
4440 tgl 2986 GIC 11 : if (creating_extension)
4440 tgl 2987 UIC 0 : ereport(ERROR,
2988 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2989 : errmsg("nested ALTER EXTENSION is not supported")));
2990 :
4439 tgl 2991 ECB : /*
2992 : * Look up the extension --- it must already exist in pg_extension
2993 : */
1539 andres 2994 GIC 11 : extRel = table_open(ExtensionRelationId, AccessShareLock);
2995 :
4440 tgl 2996 11 : ScanKeyInit(&key[0],
2997 : Anum_pg_extension_extname,
4440 tgl 2998 ECB : BTEqualStrategyNumber, F_NAMEEQ,
4440 tgl 2999 GBC 11 : CStringGetDatum(stmt->extname));
3000 :
4440 tgl 3001 GIC 11 : extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
3002 : NULL, 1, key);
3003 :
3004 11 : extTup = systable_getnext(extScan);
3005 :
3006 11 : if (!HeapTupleIsValid(extTup))
4382 bruce 3007 UIC 0 : ereport(ERROR,
3008 : (errcode(ERRCODE_UNDEFINED_OBJECT),
4382 bruce 3009 ECB : errmsg("extension \"%s\" does not exist",
3010 : stmt->extname)));
3011 :
1601 andres 3012 CBC 11 : extensionOid = ((Form_pg_extension) GETSTRUCT(extTup))->oid;
3013 :
4439 tgl 3014 ECB : /*
3015 : * Determine the existing version we are updating from
3016 : */
4439 tgl 3017 CBC 11 : datum = heap_getattr(extTup, Anum_pg_extension_extversion,
3018 : RelationGetDescr(extRel), &isnull);
3019 11 : if (isnull)
4439 tgl 3020 UIC 0 : elog(ERROR, "extversion is null");
4439 tgl 3021 CBC 11 : oldVersionName = text_to_cstring(DatumGetTextPP(datum));
3022 :
4440 tgl 3023 GIC 11 : systable_endscan(extScan);
4440 tgl 3024 ECB :
1539 andres 3025 GIC 11 : table_close(extRel, AccessShareLock);
3026 :
4419 tgl 3027 ECB : /* Permission check: must own extension */
147 peter 3028 GNC 11 : if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
1954 peter_e 3029 LBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
4419 tgl 3030 UIC 0 : stmt->extname);
4419 tgl 3031 ECB :
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 : */
4440 tgl 3037 GIC 11 : control = read_extension_control_file(stmt->extname);
4440 tgl 3038 ECB :
3039 : /*
3040 : * Read the statement option list
3041 : */
4440 tgl 3042 GIC 22 : foreach(lc, stmt->options)
3043 : {
3044 11 : DefElem *defel = (DefElem *) lfirst(lc);
3045 :
3046 11 : if (strcmp(defel->defname, "new_version") == 0)
3047 : {
3048 11 : if (d_new_version)
633 dean.a.rasheed 3049 UIC 0 : errorConflictingDefElem(defel, pstate);
4440 tgl 3050 GIC 11 : d_new_version = defel;
3051 : }
3052 : else
4440 tgl 3053 UIC 0 : elog(ERROR, "unrecognized option: %s", defel->defname);
3054 : }
3055 :
3056 : /*
3057 : * Determine the version to update to
3058 : */
4440 tgl 3059 CBC 11 : if (d_new_version && d_new_version->arg)
4440 tgl 3060 GBC 11 : versionName = strVal(d_new_version->arg);
4440 tgl 3061 UIC 0 : else if (control->default_version)
3062 0 : versionName = control->default_version;
3063 : else
3064 : {
3065 0 : ereport(ERROR,
3066 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4440 tgl 3067 ECB : errmsg("version to install must be specified")));
3068 : versionName = NULL; /* keep compiler quiet */
3069 : }
4440 tgl 3070 GIC 11 : check_valid_version_name(versionName);
3071 :
4435 tgl 3072 ECB : /*
3073 : * If we're already at that version, just say so
3074 : */
4435 tgl 3075 GIC 11 : if (strcmp(oldVersionName, versionName) == 0)
3076 : {
4435 tgl 3077 LBC 0 : ereport(NOTICE,
3078 : (errmsg("version \"%s\" of extension \"%s\" is already installed",
2118 tgl 3079 ECB : versionName, stmt->extname)));
2959 alvherre 3080 UBC 0 : return InvalidObjectAddress;
3081 : }
3082 :
3083 : /*
3084 : * Identify the series of update script files we need to execute
4440 tgl 3085 ECB : */
4439 tgl 3086 GIC 11 : updateVersions = identify_update_path(control,
3087 : oldVersionName,
3088 : versionName);
3089 :
4440 tgl 3090 ECB : /*
3091 : * Update the pg_extension row and execute the update scripts, one at a
4439 3092 : * time
4440 tgl 3093 EUB : */
4439 tgl 3094 CBC 11 : ApplyExtensionUpdates(extensionOid, control,
3095 : oldVersionName, updateVersions,
2401 tgl 3096 ECB : NULL, false, false);
3097 :
2959 alvherre 3098 CBC 11 : ObjectAddressSet(address, ExtensionRelationId, extensionOid);
3099 :
2959 alvherre 3100 GIC 11 : return address;
4439 tgl 3101 ECB : }
4440 tgl 3102 EUB :
4439 3103 : /*
3104 : * Apply a series of update scripts as though individual ALTER EXTENSION
3105 : * UPDATE commands had been given, including altering the pg_extension row
3106 : * and dependencies each time.
3107 : *
3108 : * This might be more work than necessary, but it ensures that old update
3109 : * scripts don't break if newer versions have different control parameters.
4439 tgl 3110 ECB : */
3111 : static void
4439 tgl 3112 GIC 459 : ApplyExtensionUpdates(Oid extensionOid,
3113 : ExtensionControlFile *pcontrol,
3114 : const char *initialVersion,
2401 tgl 3115 ECB : List *updateVersions,
3116 : char *origSchemaName,
3117 : bool cascade,
3118 : bool is_create)
4439 3119 : {
4439 tgl 3120 GIC 459 : const char *oldVersionName = initialVersion;
4439 tgl 3121 ECB : ListCell *lcv;
4440 tgl 3122 EUB :
4439 tgl 3123 CBC 632 : foreach(lcv, updateVersions)
3124 : {
4439 tgl 3125 GIC 173 : char *versionName = (char *) lfirst(lcv);
4439 tgl 3126 EUB : ExtensionControlFile *control;
3127 : char *schemaName;
3128 : Oid schemaOid;
3129 : List *requiredExtensions;
3130 : List *requiredSchemas;
3131 : Relation extRel;
4382 bruce 3132 ECB : ScanKeyData key[1];
3133 : SysScanDesc extScan;
4439 tgl 3134 EUB : 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 : /*
4439 tgl 3143 ECB : * Fetch parameters for specific version (pcontrol is not changed)
3144 : */
4439 tgl 3145 GIC 173 : control = read_extension_aux_control_file(pcontrol, versionName);
3146 :
3147 : /* Find the pg_extension tuple */
1539 andres 3148 CBC 173 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3149 :
4439 tgl 3150 GBC 173 : ScanKeyInit(&key[0],
3151 : Anum_pg_extension_oid,
3152 : BTEqualStrategyNumber, F_OIDEQ,
4439 tgl 3153 EUB : ObjectIdGetDatum(extensionOid));
3154 :
4439 tgl 3155 GIC 173 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3156 : NULL, 1, key);
3157 :
3158 173 : extTup = systable_getnext(extScan);
4440 tgl 3159 ECB :
4382 bruce 3160 GIC 173 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2135 tgl 3161 UIC 0 : elog(ERROR, "could not find tuple for extension %u",
3162 : extensionOid);
3163 :
4439 tgl 3164 GIC 173 : extForm = (Form_pg_extension) GETSTRUCT(extTup);
3165 :
3166 : /*
4439 tgl 3167 ECB : * Determine the target schema (set by original install)
3168 : */
4439 tgl 3169 GIC 173 : schemaOid = extForm->extnamespace;
3170 173 : schemaName = get_namespace_name(schemaOid);
4440 tgl 3171 ECB :
3172 : /*
4439 3173 : * Modify extrelocatable and extversion in the pg_extension tuple
3174 : */
4439 tgl 3175 GIC 173 : memset(values, 0, sizeof(values));
3176 173 : memset(nulls, 0, sizeof(nulls));
3177 173 : memset(repl, 0, sizeof(repl));
3178 :
3179 173 : values[Anum_pg_extension_extrelocatable - 1] =
3180 173 : BoolGetDatum(control->relocatable);
3181 173 : repl[Anum_pg_extension_extrelocatable - 1] = true;
3182 173 : values[Anum_pg_extension_extversion - 1] =
3183 173 : CStringGetTextDatum(versionName);
3184 173 : repl[Anum_pg_extension_extversion - 1] = true;
4440 tgl 3185 ECB :
4439 tgl 3186 GIC 173 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3187 : values, nulls, repl);
3188 :
2259 alvherre 3189 173 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3190 :
4439 tgl 3191 173 : systable_endscan(extScan);
3192 :
1539 andres 3193 CBC 173 : table_close(extRel, RowExclusiveLock);
3194 :
3195 : /*
2401 tgl 3196 ECB : * 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 : */
4439 tgl 3200 GIC 173 : requiredExtensions = NIL;
3201 173 : requiredSchemas = NIL;
3202 173 : foreach(lc, control->requires)
3203 : {
4439 tgl 3204 UIC 0 : char *curreq = (char *) lfirst(lc);
3205 : Oid reqext;
3206 : Oid reqschema;
3207 :
2401 3208 0 : reqext = get_required_extension(curreq,
3209 : control->name,
3210 : origSchemaName,
3211 : cascade,
3212 : NIL,
3213 : is_create);
4439 3214 0 : reqschema = get_extension_schema(reqext);
3215 0 : requiredExtensions = lappend_oid(requiredExtensions, reqext);
3216 0 : requiredSchemas = lappend_oid(requiredSchemas, reqschema);
3217 : }
4439 tgl 3218 ECB :
3219 : /*
3220 : * Remove and recreate dependencies on prerequisite extensions
3221 : */
4439 tgl 3222 GIC 173 : deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
4439 tgl 3223 ECB : ExtensionRelationId,
3224 : DEPENDENCY_NORMAL);
3225 :
4439 tgl 3226 GIC 173 : myself.classId = ExtensionRelationId;
3227 173 : myself.objectId = extensionOid;
4439 tgl 3228 CBC 173 : myself.objectSubId = 0;
3229 :
4439 tgl 3230 GIC 173 : foreach(lc, requiredExtensions)
4439 tgl 3231 ECB : {
4439 tgl 3232 UIC 0 : Oid reqext = lfirst_oid(lc);
4439 tgl 3233 ECB : ObjectAddress otherext;
4439 tgl 3234 EUB :
4439 tgl 3235 UIC 0 : otherext.classId = ExtensionRelationId;
3236 0 : otherext.objectId = reqext;
4439 tgl 3237 LBC 0 : otherext.objectSubId = 0;
3238 :
4439 tgl 3239 UIC 0 : recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
3240 : }
3241 :
3675 rhaas 3242 CBC 173 : InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3675 rhaas 3243 ECB :
3244 : /*
3245 : * Finally, execute the update script file
3246 : */
4440 tgl 3247 GIC 173 : execute_extension_script(extensionOid, control,
4439 tgl 3248 ECB : oldVersionName, versionName,
4440 3249 : requiredSchemas,
3250 : schemaName, schemaOid);
3251 :
4439 3252 : /*
4382 bruce 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.
4439 tgl 3256 : */
4439 tgl 3257 CBC 173 : oldVersionName = versionName;
3258 : }
4440 3259 459 : }
3260 :
3261 : /*
4441 tgl 3262 ECB : * Execute ALTER EXTENSION ADD/DROP
3263 : *
2959 alvherre 3264 : * Return value is the address of the altered extension.
3265 : *
3266 : * objAddr is an output argument which, if not NULL, is set to the address of
3267 : * the added/dropped object.
3268 : */
3269 : ObjectAddress
2959 alvherre 3270 GIC 87 : ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
3271 : ObjectAddress *objAddr)
3272 : {
4382 bruce 3273 ECB : ObjectAddress extension;
3274 : ObjectAddress object;
3275 : Relation relation;
3276 : Oid oldExtension;
4442 tgl 3277 EUB :
1030 peter 3278 GIC 87 : switch (stmt->objtype)
3279 : {
3280 1 : case OBJECT_DATABASE:
1030 peter 3281 EUB : 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:
1030 peter 3288 GBC 1 : ereport(ERROR,
697 tgl 3289 EUB : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3290 : errmsg("cannot add an object of this type to an extension")));
3291 : break;
1030 peter 3292 GIC 86 : default:
3293 : /* OK */
3294 86 : break;
1030 peter 3295 ECB : }
3296 :
3297 : /*
3298 : * Find the extension and acquire a lock on it, to ensure it doesn't get
637 tgl 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 : */
637 tgl 3305 GBC 86 : extension = get_object_address(OBJECT_EXTENSION,
637 tgl 3306 GIC 86 : (Node *) makeString(stmt->extname),
3307 : &relation, AccessShareLock, false);
4442 tgl 3308 EUB :
4419 3309 : /* Permission check: must own extension */
147 peter 3310 GNC 86 : if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
1954 peter_e 3311 UIC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
4419 tgl 3312 UBC 0 : stmt->extname);
3313 :
3314 : /*
4382 bruce 3315 ECB : * 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 : */
2339 peter_e 3320 CBC 86 : object = get_object_address(stmt->objtype, stmt->object,
3321 : &relation, ShareUpdateExclusiveLock, false);
3322 :
2959 alvherre 3323 GIC 86 : Assert(object.objectSubId == 0);
3324 86 : if (objAddr)
3325 86 : *objAddr = object;
3326 :
3327 : /* Permission check: must own target object, too */
4419 tgl 3328 86 : check_object_ownership(GetUserId(), stmt->objtype, object,
3329 : stmt->object, relation);
4419 tgl 3330 ECB :
3331 : /*
4441 3332 : * Check existing extension membership.
3333 : */
4441 tgl 3334 GIC 86 : oldExtension = getExtensionOfObject(object.classId, object.objectId);
3335 :
3336 86 : if (stmt->action > 0)
3337 : {
3338 : /*
3339 : * ADD, so complain if object is already attached to some extension.
3340 : */
3341 28 : if (OidIsValid(oldExtension))
4441 tgl 3342 UIC 0 : ereport(ERROR,
4441 tgl 3343 ECB : (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.
3889 3351 : */
3889 tgl 3352 GIC 29 : if (object.classId == NamespaceRelationId &&
3889 tgl 3353 CBC 1 : object.objectId == get_extension_schema(extension.objectId))
3889 tgl 3354 UIC 0 : 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 :
4441 tgl 3361 ECB : /*
3362 : * OK, add the dependency.
3363 : */
4441 tgl 3364 GIC 28 : recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
2261 sfrost 3365 ECB :
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 : */
2261 sfrost 3374 GIC 28 : recordExtObjInitPriv(object.objectId, object.classId);
3375 : }
3376 : else
3377 : {
4441 tgl 3378 ECB : /*
3379 : * DROP, so complain if it's not a member.
3380 : */
4441 tgl 3381 GIC 58 : if (oldExtension != extension.objectId)
4441 tgl 3382 UIC 0 : ereport(ERROR,
4441 tgl 3383 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4441 tgl 3384 EUB : errmsg("%s is not a member of extension \"%s\"",
998 michael 3385 : getObjectDescription(&object, false),
3386 : stmt->extname)));
3387 :
3388 : /*
3389 : * OK, drop the dependency.
3390 : */
4441 tgl 3391 GIC 58 : if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3392 : ExtensionRelationId,
4441 tgl 3393 ECB : DEPENDENCY_EXTENSION) != 1)
4441 tgl 3394 UIC 0 : elog(ERROR, "unexpected number of extension dependency records");
3395 :
3762 tgl 3396 ECB : /*
3397 : * If it's a relation, it might have an entry in the extension's
3398 : * extconfig array, which we must remove.
3399 : */
3762 tgl 3400 GIC 58 : if (object.classId == RelationRelationId)
3762 tgl 3401 CBC 16 : 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
2261 sfrost 3407 ECB : * 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 : */
2261 sfrost 3411 GIC 58 : removeExtObjInitPriv(object.objectId, object.classId);
3412 : }
3413 :
3675 rhaas 3414 CBC 86 : InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
3675 rhaas 3415 EUB :
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 : */
4442 tgl 3422 GIC 86 : if (relation != NULL)
3423 23 : relation_close(relation, NoLock);
3424 :
2959 alvherre 3425 CBC 86 : return extension;
4442 tgl 3426 ECB : }
2842 heikki.linnakangas 3427 EUB :
3428 : /*
3429 : * Read the whole of file into memory.
3430 : *
3431 : * The file contents are returned as a single palloc'd chunk. For convenience
3432 : * of the callers, an extra \0 byte is added to the end.
3433 : */
3434 : static char *
2842 heikki.linnakangas 3435 GIC 632 : read_whole_file(const char *filename, int *length)
3436 : {
2842 heikki.linnakangas 3437 ECB : char *buf;
3438 : FILE *file;
3439 : size_t bytes_to_read;
3440 : struct stat fst;
3441 :
2842 heikki.linnakangas 3442 GIC 632 : if (stat(filename, &fst) < 0)
2842 heikki.linnakangas 3443 UIC 0 : ereport(ERROR,
3444 : (errcode_for_file_access(),
3445 : errmsg("could not stat file \"%s\": %m", filename)));
3446 :
2842 heikki.linnakangas 3447 CBC 632 : if (fst.st_size > (MaxAllocSize - 1))
2842 heikki.linnakangas 3448 UIC 0 : ereport(ERROR,
3449 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3450 : errmsg("file \"%s\" is too large", filename)));
2842 heikki.linnakangas 3451 GIC 632 : bytes_to_read = (size_t) fst.st_size;
3452 :
3453 632 : if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
2842 heikki.linnakangas 3454 LBC 0 : ereport(ERROR,
2842 heikki.linnakangas 3455 EUB : (errcode_for_file_access(),
3456 : errmsg("could not open file \"%s\" for reading: %m",
3457 : filename)));
3458 :
2842 heikki.linnakangas 3459 GIC 632 : buf = (char *) palloc(bytes_to_read + 1);
3460 :
3461 632 : *length = fread(buf, 1, bytes_to_read, file);
3462 :
3463 632 : if (ferror(file))
2842 heikki.linnakangas 3464 LBC 0 : ereport(ERROR,
3465 : (errcode_for_file_access(),
3466 : errmsg("could not read file \"%s\": %m", filename)));
2842 heikki.linnakangas 3467 EUB :
2842 heikki.linnakangas 3468 GIC 632 : FreeFile(file);
3469 :
3470 632 : buf[*length] = '\0';
3471 632 : return buf;
3472 : }
|