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
144 3002 : get_extension_oid(const char *extname, bool missing_ok)
145 : {
146 : Oid result;
147 : Relation rel;
148 : SysScanDesc scandesc;
149 : HeapTuple tuple;
150 : ScanKeyData entry[1];
151 :
152 3002 : rel = table_open(ExtensionRelationId, AccessShareLock);
153 :
154 3002 : ScanKeyInit(&entry[0],
155 : Anum_pg_extension_extname,
156 : BTEqualStrategyNumber, F_NAMEEQ,
157 : CStringGetDatum(extname));
158 :
159 3002 : scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
160 : NULL, 1, entry);
161 :
162 3002 : tuple = systable_getnext(scandesc);
163 :
164 : /* We assume that there can be at most one matching tuple */
165 3002 : if (HeapTupleIsValid(tuple))
166 2036 : result = ((Form_pg_extension) GETSTRUCT(tuple))->oid;
167 : else
168 966 : result = InvalidOid;
169 :
170 3002 : systable_endscan(scandesc);
171 :
172 3002 : table_close(rel, AccessShareLock);
173 :
174 3002 : if (!OidIsValid(result) && !missing_ok)
175 12 : ereport(ERROR,
176 : (errcode(ERRCODE_UNDEFINED_OBJECT),
177 : errmsg("extension \"%s\" does not exist",
178 : extname)));
179 :
180 2990 : return result;
181 : }
182 :
183 : /*
184 : * 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 *
189 110 : get_extension_name(Oid ext_oid)
190 : {
191 : char *result;
192 : Relation rel;
193 : SysScanDesc scandesc;
194 : HeapTuple tuple;
195 : ScanKeyData entry[1];
196 :
197 110 : rel = table_open(ExtensionRelationId, AccessShareLock);
198 :
199 110 : ScanKeyInit(&entry[0],
200 : Anum_pg_extension_oid,
201 : BTEqualStrategyNumber, F_OIDEQ,
202 : ObjectIdGetDatum(ext_oid));
203 :
204 110 : scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
205 : NULL, 1, entry);
206 :
207 110 : tuple = systable_getnext(scandesc);
208 :
209 : /* We assume that there can be at most one matching tuple */
210 110 : if (HeapTupleIsValid(tuple))
211 92 : result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
212 : else
213 18 : result = NULL;
214 :
215 110 : systable_endscan(scandesc);
216 :
217 110 : table_close(rel, AccessShareLock);
218 :
219 110 : return result;
220 : }
221 :
222 : /*
223 : * get_extension_schema - given an extension OID, fetch its extnamespace
224 : *
225 : * Returns InvalidOid if no such extension.
226 : */
227 : Oid
228 52 : get_extension_schema(Oid ext_oid)
229 : {
230 : Oid result;
231 : Relation rel;
232 : SysScanDesc scandesc;
233 : HeapTuple tuple;
234 : ScanKeyData entry[1];
235 :
236 52 : rel = table_open(ExtensionRelationId, AccessShareLock);
237 :
238 52 : ScanKeyInit(&entry[0],
239 : Anum_pg_extension_oid,
240 : BTEqualStrategyNumber, F_OIDEQ,
241 : ObjectIdGetDatum(ext_oid));
242 :
243 52 : scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
244 : NULL, 1, entry);
245 :
246 52 : tuple = systable_getnext(scandesc);
247 :
248 : /* We assume that there can be at most one matching tuple */
249 52 : if (HeapTupleIsValid(tuple))
250 52 : result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
251 : else
252 0 : result = InvalidOid;
253 :
254 52 : systable_endscan(scandesc);
255 :
256 52 : table_close(rel, AccessShareLock);
257 :
258 52 : return result;
259 : }
260 :
261 : /*
262 : * Utility functions to check validity of extension and version names
263 : */
264 : static void
265 936 : check_valid_extension_name(const char *extensionname)
266 : {
267 936 : int namelen = strlen(extensionname);
268 :
269 : /*
270 : * Disallow empty names (the parser rejects empty identifiers anyway, but
271 : * let's check).
272 : */
273 936 : if (namelen == 0)
274 0 : ereport(ERROR,
275 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
276 : errmsg("invalid extension name: \"%s\"", extensionname),
277 : errdetail("Extension names must not be empty.")));
278 :
279 : /*
280 : * No double dashes, since that would make script filenames ambiguous.
281 : */
282 936 : if (strstr(extensionname, "--"))
283 0 : ereport(ERROR,
284 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
285 : errmsg("invalid extension name: \"%s\"", extensionname),
286 : errdetail("Extension names must not contain \"--\".")));
287 :
288 : /*
289 : * No leading or trailing dash either. (We could probably allow this, but
290 : * it would require much care in filename parsing and would make filenames
291 : * visually if not formally ambiguous. Since there's no real-world use
292 : * case, let's just forbid it.)
293 : */
294 936 : if (extensionname[0] == '-' || extensionname[namelen - 1] == '-')
295 0 : ereport(ERROR,
296 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
297 : errmsg("invalid extension name: \"%s\"", extensionname),
298 : errdetail("Extension names must not begin or end with \"-\".")));
299 :
300 : /*
301 : * No directory separators either (this is sufficient to prevent ".."
302 : * style attacks).
303 : */
304 936 : if (first_dir_separator(extensionname) != NULL)
305 0 : ereport(ERROR,
306 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
307 : errmsg("invalid extension name: \"%s\"", extensionname),
308 : errdetail("Extension names must not contain directory separator characters.")));
309 936 : }
310 :
311 : static void
312 956 : check_valid_version_name(const char *versionname)
313 : {
314 956 : int namelen = strlen(versionname);
315 :
316 : /*
317 : * Disallow empty names (we could possibly allow this, but there seems
318 : * little point).
319 : */
320 956 : if (namelen == 0)
321 0 : ereport(ERROR,
322 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
323 : errmsg("invalid extension version name: \"%s\"", versionname),
324 : errdetail("Version names must not be empty.")));
325 :
326 : /*
327 : * No double dashes, since that would make script filenames ambiguous.
328 : */
329 956 : if (strstr(versionname, "--"))
330 0 : ereport(ERROR,
331 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
332 : errmsg("invalid extension version name: \"%s\"", versionname),
333 : errdetail("Version names must not contain \"--\".")));
334 :
335 : /*
336 : * No leading or trailing dash either.
337 : */
338 956 : if (versionname[0] == '-' || versionname[namelen - 1] == '-')
339 0 : ereport(ERROR,
340 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
341 : errmsg("invalid extension version name: \"%s\"", versionname),
342 : errdetail("Version names must not begin or end with \"-\".")));
343 :
344 : /*
345 : * No directory separators either (this is sufficient to prevent ".."
346 : * style attacks).
347 : */
348 956 : if (first_dir_separator(versionname) != NULL)
349 0 : ereport(ERROR,
350 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
351 : errmsg("invalid extension version name: \"%s\"", versionname),
352 : errdetail("Version names must not contain directory separator characters.")));
353 956 : }
354 :
355 : /*
356 : * Utility functions to handle extension-related path names
357 : */
358 : static bool
359 7272 : is_extension_control_filename(const char *filename)
360 : {
361 7272 : const char *extension = strrchr(filename, '.');
362 :
363 7272 : return (extension != NULL) && (strcmp(extension, ".control") == 0);
364 : }
365 :
366 : static bool
367 211494 : is_extension_script_filename(const char *filename)
368 : {
369 211494 : const char *extension = strrchr(filename, '.');
370 :
371 211494 : return (extension != NULL) && (strcmp(extension, ".sql") == 0);
372 : }
373 :
374 : static char *
375 5122 : get_extension_control_directory(void)
376 : {
377 : char sharepath[MAXPGPATH];
378 : char *result;
379 :
380 5122 : get_share_path(my_exec_path, sharepath);
381 5122 : result = (char *) palloc(MAXPGPATH);
382 5122 : snprintf(result, MAXPGPATH, "%s/extension", sharepath);
383 :
384 5122 : return result;
385 : }
386 :
387 : static char *
388 3244 : get_extension_control_filename(const char *extname)
389 : {
390 : char sharepath[MAXPGPATH];
391 : char *result;
392 :
393 3244 : get_share_path(my_exec_path, sharepath);
394 3244 : result = (char *) palloc(MAXPGPATH);
395 3244 : snprintf(result, MAXPGPATH, "%s/extension/%s.control",
396 : sharepath, extname);
397 :
398 3244 : return result;
399 : }
400 :
401 : static char *
402 5098 : get_extension_script_directory(ExtensionControlFile *control)
403 : {
404 : char sharepath[MAXPGPATH];
405 : char *result;
406 :
407 : /*
408 : * The directory parameter can be omitted, absolute, or relative to the
409 : * installation's share directory.
410 : */
411 5098 : if (!control->directory)
412 5098 : return get_extension_control_directory();
413 :
414 0 : if (is_absolute_path(control->directory))
415 0 : return pstrdup(control->directory);
416 :
417 0 : get_share_path(my_exec_path, sharepath);
418 0 : result = (char *) palloc(MAXPGPATH);
419 0 : snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
420 :
421 0 : return result;
422 : }
423 :
424 : static char *
425 2206 : get_extension_aux_control_filename(ExtensionControlFile *control,
426 : const char *version)
427 : {
428 : char *result;
429 : char *scriptdir;
430 :
431 2206 : scriptdir = get_extension_script_directory(control);
432 :
433 2206 : result = (char *) palloc(MAXPGPATH);
434 2206 : snprintf(result, MAXPGPATH, "%s/%s--%s.control",
435 : scriptdir, control->name, version);
436 :
437 2206 : pfree(scriptdir);
438 :
439 2206 : return result;
440 : }
441 :
442 : static char *
443 2194 : get_extension_script_filename(ExtensionControlFile *control,
444 : const char *from_version, const char *version)
445 : {
446 : char *result;
447 : char *scriptdir;
448 :
449 2194 : scriptdir = get_extension_script_directory(control);
450 :
451 2194 : result = (char *) palloc(MAXPGPATH);
452 2194 : if (from_version)
453 350 : snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
454 : scriptdir, control->name, from_version, version);
455 : else
456 1844 : snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
457 : scriptdir, control->name, version);
458 :
459 2194 : pfree(scriptdir);
460 :
461 2194 : return result;
462 : }
463 :
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
475 5450 : parse_extension_control_file(ExtensionControlFile *control,
476 : const char *version)
477 : {
478 : char *filename;
479 : FILE *file;
480 : ConfigVariable *item,
481 5450 : *head = NULL,
482 5450 : *tail = NULL;
483 :
484 : /*
485 : * Locate the file to read. Auxiliary files are optional.
486 : */
487 5450 : if (version)
488 2206 : filename = get_extension_aux_control_filename(control, version);
489 : else
490 3244 : filename = get_extension_control_filename(control->name);
491 :
492 5450 : if ((file = AllocateFile(filename, "r")) == NULL)
493 : {
494 2206 : if (errno == ENOENT)
495 : {
496 : /* no complaint for missing auxiliary file */
497 2206 : if (version)
498 : {
499 2206 : pfree(filename);
500 2206 : return;
501 : }
502 :
503 : /* missing control file indicates extension is not installed */
504 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.",
508 : filename),
509 : errhint("The extension must first be installed on the system where PostgreSQL is running.")));
510 : }
511 0 : ereport(ERROR,
512 : (errcode_for_file_access(),
513 : errmsg("could not open extension control file \"%s\": %m",
514 : filename)));
515 : }
516 :
517 : /*
518 : * Parse the file content, using GUC's file parsing code. We need not
519 : * check the return value since any errors will be thrown at ERROR level.
520 : */
521 3244 : (void) ParseConfigFp(file, filename, CONF_FILE_START_DEPTH, ERROR,
522 : &head, &tail);
523 :
524 3244 : FreeFile(file);
525 :
526 : /*
527 : * Convert the ConfigVariable list into ExtensionControlFile entries.
528 : */
529 19260 : for (item = head; item != NULL; item = item->next)
530 : {
531 16016 : if (strcmp(item->name, "directory") == 0)
532 : {
533 0 : if (version)
534 0 : ereport(ERROR,
535 : (errcode(ERRCODE_SYNTAX_ERROR),
536 : errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
537 : item->name)));
538 :
539 0 : control->directory = pstrdup(item->value);
540 : }
541 16016 : else if (strcmp(item->name, "default_version") == 0)
542 : {
543 3244 : if (version)
544 0 : ereport(ERROR,
545 : (errcode(ERRCODE_SYNTAX_ERROR),
546 : errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
547 : item->name)));
548 :
549 3244 : control->default_version = pstrdup(item->value);
550 : }
551 12772 : else if (strcmp(item->name, "module_pathname") == 0)
552 : {
553 2716 : control->module_pathname = pstrdup(item->value);
554 : }
555 10056 : else if (strcmp(item->name, "comment") == 0)
556 : {
557 3244 : control->comment = pstrdup(item->value);
558 : }
559 6812 : else if (strcmp(item->name, "schema") == 0)
560 : {
561 952 : control->schema = pstrdup(item->value);
562 : }
563 5860 : else if (strcmp(item->name, "relocatable") == 0)
564 : {
565 3244 : if (!parse_bool(item->value, &control->relocatable))
566 0 : ereport(ERROR,
567 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
568 : errmsg("parameter \"%s\" requires a Boolean value",
569 : item->name)));
570 : }
571 2616 : else if (strcmp(item->name, "superuser") == 0)
572 : {
573 836 : if (!parse_bool(item->value, &control->superuser))
574 0 : ereport(ERROR,
575 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
576 : errmsg("parameter \"%s\" requires a Boolean value",
577 : item->name)));
578 : }
579 1780 : else if (strcmp(item->name, "trusted") == 0)
580 : {
581 1286 : if (!parse_bool(item->value, &control->trusted))
582 0 : ereport(ERROR,
583 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
584 : errmsg("parameter \"%s\" requires a Boolean value",
585 : item->name)));
586 : }
587 494 : else if (strcmp(item->name, "encoding") == 0)
588 : {
589 0 : control->encoding = pg_valid_server_encoding(item->value);
590 0 : if (control->encoding < 0)
591 0 : ereport(ERROR,
592 : (errcode(ERRCODE_UNDEFINED_OBJECT),
593 : errmsg("\"%s\" is not a valid encoding name",
594 : item->value)));
595 : }
596 494 : else if (strcmp(item->name, "requires") == 0)
597 : {
598 : /* Need a modifiable copy of string */
599 464 : char *rawnames = pstrdup(item->value);
600 :
601 : /* Parse string into list of identifiers */
602 464 : if (!SplitIdentifierString(rawnames, ',', &control->requires))
603 : {
604 : /* syntax error in name list */
605 0 : ereport(ERROR,
606 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
607 : errmsg("parameter \"%s\" must be a list of extension names",
608 : item->name)));
609 : }
610 : }
611 30 : else if (strcmp(item->name, "no_relocate") == 0)
612 : {
613 : /* Need a modifiable copy of string */
614 30 : char *rawnames = pstrdup(item->value);
615 :
616 : /* Parse string into list of identifiers */
617 30 : if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
618 : {
619 : /* syntax error in name list */
620 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
627 0 : ereport(ERROR,
628 : (errcode(ERRCODE_SYNTAX_ERROR),
629 : errmsg("unrecognized parameter \"%s\" in file \"%s\"",
630 : item->name, filename)));
631 : }
632 :
633 3244 : FreeConfigVariables(head);
634 :
635 3244 : if (control->relocatable && control->schema != NULL)
636 0 : ereport(ERROR,
637 : (errcode(ERRCODE_SYNTAX_ERROR),
638 : errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
639 :
640 3244 : pfree(filename);
641 : }
642 :
643 : /*
644 : * Read the primary control file for the specified extension.
645 : */
646 : static ExtensionControlFile *
647 3244 : read_extension_control_file(const char *extname)
648 : {
649 : ExtensionControlFile *control;
650 :
651 : /*
652 : * Set up default values. Pointer fields are initially null.
653 : */
654 3244 : control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
655 3244 : control->name = pstrdup(extname);
656 3244 : control->relocatable = false;
657 3244 : control->superuser = true;
658 3244 : control->trusted = false;
659 3244 : control->encoding = -1;
660 :
661 : /*
662 : * Parse the primary control file.
663 : */
664 3244 : parse_extension_control_file(control, NULL);
665 :
666 3244 : return control;
667 : }
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.
674 : */
675 : static ExtensionControlFile *
676 2206 : read_extension_aux_control_file(const ExtensionControlFile *pcontrol,
677 : const char *version)
678 : {
679 : ExtensionControlFile *acontrol;
680 :
681 : /*
682 : * Flat-copy the struct. Pointer fields share values with original.
683 : */
684 2206 : acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
685 2206 : memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
686 :
687 : /*
688 : * Parse the auxiliary control file, overwriting struct fields
689 : */
690 2206 : parse_extension_control_file(acontrol, version);
691 :
692 2206 : return acontrol;
693 : }
694 :
695 : /*
696 : * Read an SQL script file into a string, and convert to database encoding
697 : */
698 : static char *
699 1262 : read_extension_script_file(const ExtensionControlFile *control,
700 : const char *filename)
701 : {
702 : int src_encoding;
703 : char *src_str;
704 : char *dest_str;
705 : int len;
706 :
707 1262 : src_str = read_whole_file(filename, &len);
708 :
709 : /* use database encoding if not given */
710 1262 : if (control->encoding < 0)
711 1262 : src_encoding = GetDatabaseEncoding();
712 : else
713 0 : src_encoding = control->encoding;
714 :
715 : /* make sure that source string is valid in the expected encoding */
716 1262 : (void) pg_verify_mbstr(src_encoding, src_str, len, false);
717 :
718 : /*
719 : * Convert the encoding to the database encoding. read_whole_file
720 : * null-terminated the string, so if no conversion happens the string is
721 : * valid as is.
722 : */
723 1262 : dest_str = pg_any_to_server(src_str, len, src_encoding);
724 :
725 1262 : return dest_str;
726 : }
727 :
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,
733 : * 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
736 : * on printing the whole string as errcontext in case of any error, and that
737 : * could be very long.
738 : */
739 : static void
740 1262 : execute_sql_string(const char *sql)
741 : {
742 : List *raw_parsetree_list;
743 : DestReceiver *dest;
744 : ListCell *lc1;
745 :
746 : /*
747 : * Parse the SQL string into a list of raw parse trees.
748 : */
749 1262 : raw_parsetree_list = pg_parse_query(sql);
750 :
751 : /* All output from SELECTs goes to the bit bucket */
752 1262 : 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 13474 : foreach(lc1, raw_parsetree_list)
760 : {
761 12234 : RawStmt *parsetree = lfirst_node(RawStmt, lc1);
762 : MemoryContext per_parsetree_context,
763 : oldcontext;
764 : List *stmt_list;
765 : ListCell *lc2;
766 :
767 : /*
768 : * We do the work for each parsetree in a short-lived context, to
769 : * limit the memory used when there are many commands in the string.
770 : */
771 : per_parsetree_context =
772 12234 : AllocSetContextCreate(CurrentMemoryContext,
773 : "execute_sql_string per-statement context",
774 : ALLOCSET_DEFAULT_SIZES);
775 12234 : oldcontext = MemoryContextSwitchTo(per_parsetree_context);
776 :
777 : /* Be sure parser can see any DDL done so far */
778 12234 : CommandCounterIncrement();
779 :
780 12234 : stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
781 : sql,
782 : NULL,
783 : 0,
784 : NULL);
785 12234 : stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
786 :
787 24446 : foreach(lc2, stmt_list)
788 : {
789 12234 : PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
790 :
791 12234 : CommandCounterIncrement();
792 :
793 12234 : PushActiveSnapshot(GetTransactionSnapshot());
794 :
795 12234 : if (stmt->utilityStmt == NULL)
796 : {
797 : QueryDesc *qdesc;
798 :
799 10 : qdesc = CreateQueryDesc(stmt,
800 : sql,
801 : GetActiveSnapshot(), NULL,
802 : dest, NULL, NULL, 0);
803 :
804 10 : ExecutorStart(qdesc, 0);
805 10 : ExecutorRun(qdesc, ForwardScanDirection, 0, true);
806 10 : ExecutorFinish(qdesc);
807 10 : ExecutorEnd(qdesc);
808 :
809 10 : FreeQueryDesc(qdesc);
810 : }
811 : else
812 : {
813 12224 : if (IsA(stmt->utilityStmt, TransactionStmt))
814 0 : ereport(ERROR,
815 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
816 : errmsg("transaction control statements are not allowed within an extension script")));
817 :
818 12224 : ProcessUtility(stmt,
819 : sql,
820 : false,
821 : PROCESS_UTILITY_QUERY,
822 : NULL,
823 : NULL,
824 : dest,
825 : NULL);
826 : }
827 :
828 12212 : PopActiveSnapshot();
829 : }
830 :
831 : /* Clean up per-parsetree context. */
832 12212 : MemoryContextSwitchTo(oldcontext);
833 12212 : MemoryContextDelete(per_parsetree_context);
834 : }
835 :
836 : /* Be sure to advance the command counter after the last script command */
837 1240 : CommandCounterIncrement();
838 1240 : }
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
847 14 : extension_is_trusted(ExtensionControlFile *control)
848 : {
849 : AclResult aclresult;
850 :
851 : /* Never trust unless extension's control file says it's okay */
852 14 : if (!control->trusted)
853 4 : return false;
854 : /* Allow if user has CREATE privilege on current database */
855 10 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
856 10 : if (aclresult == ACLCHECK_OK)
857 8 : return true;
858 2 : 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
869 1268 : 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)
874 : {
875 1268 : bool switch_to_superuser = false;
876 : char *filename;
877 1268 : Oid save_userid = 0;
878 1268 : int save_sec_context = 0;
879 : int save_nestlevel;
880 : StringInfoData pathbuf;
881 : ListCell *lc;
882 : ListCell *lc2;
883 :
884 : /*
885 : * Enforce superuser-ness if appropriate. We postpone these checks until
886 : * here so that the control flags are correctly associated with the right
887 : * script(s) if they happen to be set in secondary control files.
888 : */
889 1268 : if (control->superuser && !superuser())
890 : {
891 14 : if (extension_is_trusted(control))
892 8 : switch_to_superuser = true;
893 6 : else if (from_version == NULL)
894 6 : ereport(ERROR,
895 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
896 : errmsg("permission denied to create extension \"%s\"",
897 : control->name),
898 : control->trusted
899 : ? errhint("Must have CREATE privilege on current database to create this extension.")
900 : : errhint("Must be superuser to create this extension.")));
901 : else
902 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 :
911 1262 : filename = get_extension_script_filename(control, from_version, version);
912 :
913 1262 : if (from_version == NULL)
914 912 : elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
915 : else
916 350 : elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
917 :
918 : /*
919 : * If installing a trusted extension on behalf of a non-superuser, become
920 : * the bootstrap superuser. (This switch will be cleaned up automatically
921 : * if the transaction aborts, as will the GUC changes below.)
922 : */
923 1262 : if (switch_to_superuser)
924 : {
925 8 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
926 8 : SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID,
927 : save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
928 : }
929 :
930 : /*
931 : * Force client_min_messages and log_min_messages to be at least WARNING,
932 : * so that we won't spam the user with useless NOTICE messages from common
933 : * script actions like creating shell types.
934 : *
935 : * We use the equivalent of a function SET option to allow the setting to
936 : * persist for exactly the duration of the script execution. guc.c also
937 : * takes care of undoing the setting on error.
938 : *
939 : * log_min_messages can't be set by ordinary users, so for that one we
940 : * pretend to be superuser.
941 : */
942 1262 : save_nestlevel = NewGUCNestLevel();
943 :
944 1262 : if (client_min_messages < WARNING)
945 1258 : (void) set_config_option("client_min_messages", "warning",
946 : PGC_USERSET, PGC_S_SESSION,
947 : GUC_ACTION_SAVE, true, 0, false);
948 1262 : if (log_min_messages < WARNING)
949 4 : (void) set_config_option_ext("log_min_messages", "warning",
950 : PGC_SUSET, PGC_S_SESSION,
951 : BOOTSTRAP_SUPERUSERID,
952 : GUC_ACTION_SAVE, true, 0, false);
953 :
954 : /*
955 : * Similarly disable check_function_bodies, to ensure that SQL functions
956 : * won't be parsed during creation.
957 : */
958 1262 : if (check_function_bodies)
959 1262 : (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.
970 : *
971 : * Note: it might look tempting to use PushOverrideSearchPath for this,
972 : * but we cannot do that. We have to actually set the search_path GUC in
973 : * case the extension script examines or changes it. In any case, the
974 : * GUC_ACTION_SAVE method is just as convenient.
975 : */
976 1262 : initStringInfo(&pathbuf);
977 1262 : appendStringInfoString(&pathbuf, quote_identifier(schemaName));
978 1312 : foreach(lc, requiredSchemas)
979 : {
980 50 : Oid reqschema = lfirst_oid(lc);
981 50 : char *reqname = get_namespace_name(reqschema);
982 :
983 50 : if (reqname && strcmp(reqname, "pg_catalog") != 0)
984 30 : appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
985 : }
986 1262 : appendStringInfoString(&pathbuf, ", pg_temp");
987 :
988 1262 : (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 1262 : creating_extension = true;
998 1262 : CurrentExtensionObject = extensionOid;
999 1262 : PG_TRY();
1000 : {
1001 1262 : char *c_sql = read_extension_script_file(control, filename);
1002 : Datum t_sql;
1003 :
1004 : /* We use various functions that want to operate on text datums */
1005 1262 : t_sql = CStringGetTextDatum(c_sql);
1006 :
1007 : /*
1008 : * Reduce any lines beginning with "\echo" to empty. This allows
1009 : * scripts to contain messages telling people not to run them via
1010 : * psql, which has been found to be necessary due to old habits.
1011 : */
1012 1262 : t_sql = DirectFunctionCall4Coll(textregexreplace,
1013 : C_COLLATION_OID,
1014 : t_sql,
1015 1262 : CStringGetTextDatum("^\\\\echo.*$"),
1016 1262 : CStringGetTextDatum(""),
1017 1262 : CStringGetTextDatum("ng"));
1018 :
1019 : /*
1020 : * If the script uses @extowner@, substitute the calling username.
1021 : */
1022 1262 : if (strstr(c_sql, "@extowner@"))
1023 : {
1024 618 : Oid uid = switch_to_superuser ? save_userid : GetUserId();
1025 618 : const char *userName = GetUserNameFromId(uid, false);
1026 618 : const char *qUserName = quote_identifier(userName);
1027 :
1028 618 : t_sql = DirectFunctionCall3Coll(replace_text,
1029 : C_COLLATION_OID,
1030 : t_sql,
1031 618 : CStringGetTextDatum("@extowner@"),
1032 618 : CStringGetTextDatum(qUserName));
1033 : }
1034 :
1035 : /*
1036 : * If it's not relocatable, substitute the target schema name for
1037 : * occurrences of @extschema@.
1038 : *
1039 : * For a relocatable extension, we needn't do this. There cannot be
1040 : * any need for @extschema@, else it wouldn't be relocatable.
1041 : */
1042 1262 : if (!control->relocatable)
1043 : {
1044 660 : const char *qSchemaName = quote_identifier(schemaName);
1045 :
1046 660 : t_sql = DirectFunctionCall3Coll(replace_text,
1047 : C_COLLATION_OID,
1048 : t_sql,
1049 660 : CStringGetTextDatum("@extschema@"),
1050 660 : CStringGetTextDatum(qSchemaName));
1051 : }
1052 :
1053 : /*
1054 : * Likewise, substitute required extensions' schema names for
1055 : * occurrences of @extschema:extension_name@.
1056 : */
1057 : Assert(list_length(control->requires) == list_length(requiredSchemas));
1058 1312 : forboth(lc, control->requires, lc2, requiredSchemas)
1059 : {
1060 50 : char *reqextname = (char *) lfirst(lc);
1061 50 : Oid reqschema = lfirst_oid(lc2);
1062 50 : char *schemaName = get_namespace_name(reqschema);
1063 50 : const char *qSchemaName = quote_identifier(schemaName);
1064 : char *repltoken;
1065 :
1066 50 : repltoken = psprintf("@extschema:%s@", reqextname);
1067 50 : t_sql = DirectFunctionCall3Coll(replace_text,
1068 : C_COLLATION_OID,
1069 : t_sql,
1070 50 : CStringGetTextDatum(repltoken),
1071 50 : CStringGetTextDatum(qSchemaName));
1072 : }
1073 :
1074 : /*
1075 : * If module_pathname was set in the control file, substitute its
1076 : * value for occurrences of MODULE_PATHNAME.
1077 : */
1078 1262 : if (control->module_pathname)
1079 : {
1080 1186 : t_sql = DirectFunctionCall3Coll(replace_text,
1081 : C_COLLATION_OID,
1082 : t_sql,
1083 1186 : CStringGetTextDatum("MODULE_PATHNAME"),
1084 1186 : CStringGetTextDatum(control->module_pathname));
1085 : }
1086 :
1087 : /* And now back to C string */
1088 1262 : c_sql = text_to_cstring(DatumGetTextPP(t_sql));
1089 :
1090 1262 : execute_sql_string(c_sql);
1091 : }
1092 22 : PG_FINALLY();
1093 : {
1094 1262 : creating_extension = false;
1095 1262 : CurrentExtensionObject = InvalidOid;
1096 : }
1097 1262 : PG_END_TRY();
1098 :
1099 : /*
1100 : * Restore the GUC variables we set above.
1101 : */
1102 1240 : AtEOXact_GUC(true, save_nestlevel);
1103 :
1104 : /*
1105 : * Restore authentication state if needed.
1106 : */
1107 1240 : if (switch_to_superuser)
1108 8 : SetUserIdAndSecContext(save_userid, save_sec_context);
1109 1240 : }
1110 :
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 *
1120 3578 : get_ext_ver_info(const char *versionname, List **evi_list)
1121 : {
1122 : ExtensionVersionInfo *evi;
1123 : ListCell *lc;
1124 :
1125 13574 : foreach(lc, *evi_list)
1126 : {
1127 11512 : evi = (ExtensionVersionInfo *) lfirst(lc);
1128 11512 : if (strcmp(evi->name, versionname) == 0)
1129 1516 : return evi;
1130 : }
1131 :
1132 2062 : evi = (ExtensionVersionInfo *) palloc(sizeof(ExtensionVersionInfo));
1133 2062 : evi->name = pstrdup(versionname);
1134 2062 : evi->reachable = NIL;
1135 2062 : evi->installable = false;
1136 : /* initialize for later application of Dijkstra's algorithm */
1137 2062 : evi->distance_known = false;
1138 2062 : evi->distance = INT_MAX;
1139 2062 : evi->previous = NULL;
1140 :
1141 2062 : *evi_list = lappend(*evi_list, evi);
1142 :
1143 2062 : return evi;
1144 : }
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 *
1153 3022 : get_nearest_unprocessed_vertex(List *evi_list)
1154 : {
1155 3022 : ExtensionVersionInfo *evi = NULL;
1156 : ListCell *lc;
1157 :
1158 26272 : foreach(lc, evi_list)
1159 : {
1160 23250 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
1161 :
1162 : /* only vertices whose distance is still uncertain are candidates */
1163 23250 : if (evi2->distance_known)
1164 5818 : continue;
1165 : /* remember the closest such vertex */
1166 17432 : if (evi == NULL ||
1167 14410 : evi->distance > evi2->distance)
1168 4796 : evi = evi2;
1169 : }
1170 :
1171 3022 : return evi;
1172 : }
1173 :
1174 : /*
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 *
1181 698 : get_ext_ver_list(ExtensionControlFile *control)
1182 : {
1183 698 : List *evi_list = NIL;
1184 698 : int extnamelen = strlen(control->name);
1185 : char *location;
1186 : DIR *dir;
1187 : struct dirent *de;
1188 :
1189 698 : location = get_extension_script_directory(control);
1190 698 : dir = AllocateDir(location);
1191 212192 : while ((de = ReadDir(dir, location)) != NULL)
1192 : {
1193 : char *vername;
1194 : char *vername2;
1195 : ExtensionVersionInfo *evi;
1196 : ExtensionVersionInfo *evi2;
1197 :
1198 : /* must be a .sql file ... */
1199 211494 : if (!is_extension_script_filename(de->d_name))
1200 67706 : continue;
1201 :
1202 : /* ... matching extension name followed by separator */
1203 143788 : if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
1204 2162 : de->d_name[extnamelen] != '-' ||
1205 2062 : de->d_name[extnamelen + 1] != '-')
1206 141726 : continue;
1207 :
1208 : /* extract version name(s) from 'extname--something.sql' filename */
1209 2062 : vername = pstrdup(de->d_name + extnamelen + 2);
1210 2062 : *strrchr(vername, '.') = '\0';
1211 2062 : vername2 = strstr(vername, "--");
1212 2062 : if (!vername2)
1213 : {
1214 : /* It's an install, not update, script; record its version name */
1215 698 : evi = get_ext_ver_info(vername, &evi_list);
1216 698 : evi->installable = true;
1217 698 : continue;
1218 : }
1219 1364 : *vername2 = '\0'; /* terminate first version */
1220 1364 : vername2 += 2; /* and point to second */
1221 :
1222 : /* if there's a third --, it's bogus, ignore it */
1223 1364 : if (strstr(vername2, "--"))
1224 0 : continue;
1225 :
1226 : /* Create ExtensionVersionInfos and link them together */
1227 1364 : evi = get_ext_ver_info(vername, &evi_list);
1228 1364 : evi2 = get_ext_ver_info(vername2, &evi_list);
1229 1364 : evi->reachable = lappend(evi->reachable, evi2);
1230 : }
1231 698 : FreeDir(dir);
1232 :
1233 698 : return evi_list;
1234 : }
1235 :
1236 : /*
1237 : * Given an initial and final version name, identify the sequence of update
1238 : * scripts that have to be applied to perform that update.
1239 : *
1240 : * Result is a List of names of versions to transition through (the initial
1241 : * version is *not* included).
1242 : */
1243 : static List *
1244 24 : identify_update_path(ExtensionControlFile *control,
1245 : const char *oldVersion, const char *newVersion)
1246 : {
1247 : List *result;
1248 : List *evi_list;
1249 : ExtensionVersionInfo *evi_start;
1250 : ExtensionVersionInfo *evi_target;
1251 :
1252 : /* Extract the version update graph from the script directory */
1253 24 : evi_list = get_ext_ver_list(control);
1254 :
1255 : /* Initialize start and end vertices */
1256 24 : evi_start = get_ext_ver_info(oldVersion, &evi_list);
1257 24 : evi_target = get_ext_ver_info(newVersion, &evi_list);
1258 :
1259 : /* Find shortest path */
1260 24 : result = find_update_path(evi_list, evi_start, evi_target, false, false);
1261 :
1262 24 : if (result == NIL)
1263 0 : ereport(ERROR,
1264 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1265 : errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
1266 : control->name, oldVersion, newVersion)));
1267 :
1268 24 : return result;
1269 : }
1270 :
1271 : /*
1272 : * Apply Dijkstra's algorithm to find the shortest path from evi_start to
1273 : * evi_target.
1274 : *
1275 : * If reject_indirect is true, ignore paths that go through installable
1276 : * 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
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 *
1287 794 : find_update_path(List *evi_list,
1288 : ExtensionVersionInfo *evi_start,
1289 : ExtensionVersionInfo *evi_target,
1290 : bool reject_indirect,
1291 : bool reinitialize)
1292 : {
1293 : List *result;
1294 : ExtensionVersionInfo *evi;
1295 : ListCell *lc;
1296 :
1297 : /* Caller error if start == target */
1298 : Assert(evi_start != evi_target);
1299 : /* Caller error if reject_indirect and target is installable */
1300 : Assert(!(reject_indirect && evi_target->installable));
1301 :
1302 794 : if (reinitialize)
1303 : {
1304 5696 : foreach(lc, evi_list)
1305 : {
1306 4926 : evi = (ExtensionVersionInfo *) lfirst(lc);
1307 4926 : evi->distance_known = false;
1308 4926 : evi->distance = INT_MAX;
1309 4926 : evi->previous = NULL;
1310 : }
1311 : }
1312 :
1313 794 : evi_start->distance = 0;
1314 :
1315 3022 : while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
1316 : {
1317 3022 : if (evi->distance == INT_MAX)
1318 312 : break; /* all remaining vertices are unreachable */
1319 2710 : evi->distance_known = true;
1320 2710 : if (evi == evi_target)
1321 482 : break; /* found shortest path to target */
1322 4144 : foreach(lc, evi->reachable)
1323 : {
1324 1916 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
1325 : int newdist;
1326 :
1327 : /* if reject_indirect, treat installable versions as unreachable */
1328 1916 : if (reject_indirect && evi2->installable)
1329 0 : continue;
1330 1916 : newdist = evi->distance + 1;
1331 1916 : if (newdist < evi2->distance)
1332 : {
1333 1916 : evi2->distance = newdist;
1334 1916 : evi2->previous = evi;
1335 : }
1336 0 : else if (newdist == evi2->distance &&
1337 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.
1347 : */
1348 0 : evi2->previous = evi;
1349 : }
1350 : }
1351 : }
1352 :
1353 : /* Return NIL if target is not reachable from start */
1354 794 : if (!evi_target->distance_known)
1355 312 : return NIL;
1356 :
1357 : /* Build and return list of version names representing the update path */
1358 482 : result = NIL;
1359 1636 : for (evi = evi_target; evi != evi_start; evi = evi->previous)
1360 1154 : result = lcons(evi->name, result);
1361 :
1362 482 : return result;
1363 : }
1364 :
1365 : /*
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.
1378 : */
1379 : static ExtensionVersionInfo *
1380 770 : find_install_path(List *evi_list, ExtensionVersionInfo *evi_target,
1381 : List **best_path)
1382 : {
1383 770 : ExtensionVersionInfo *evi_start = NULL;
1384 : ListCell *lc;
1385 :
1386 770 : *best_path = NIL;
1387 :
1388 : /*
1389 : * We don't expect to be called for an installable target, but if we are,
1390 : * the answer is easy: just start from there, with an empty update path.
1391 : */
1392 770 : if (evi_target->installable)
1393 0 : return evi_target;
1394 :
1395 : /* Consider all installable versions as start points */
1396 5696 : foreach(lc, evi_list)
1397 : {
1398 4926 : ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc);
1399 : List *path;
1400 :
1401 4926 : if (!evi1->installable)
1402 4156 : continue;
1403 :
1404 : /*
1405 : * Find shortest path from evi1 to evi_target; but no need to consider
1406 : * paths going through other installable versions.
1407 : */
1408 770 : path = find_update_path(evi_list, evi1, evi_target, true, true);
1409 770 : if (path == NIL)
1410 312 : continue;
1411 :
1412 : /* Remember best path */
1413 458 : if (evi_start == NULL ||
1414 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 : {
1418 458 : evi_start = evi1;
1419 458 : *best_path = path;
1420 : }
1421 : }
1422 :
1423 770 : return evi_start;
1424 : }
1425 :
1426 : /*
1427 : * CREATE EXTENSION worker
1428 : *
1429 : * 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
1435 932 : CreateExtensionInternal(char *extensionName,
1436 : char *schemaName,
1437 : const char *versionName,
1438 : bool cascade,
1439 : List *parents,
1440 : bool is_create)
1441 : {
1442 932 : char *origSchemaName = schemaName;
1443 932 : Oid schemaOid = InvalidOid;
1444 932 : Oid extowner = GetUserId();
1445 : ExtensionControlFile *pcontrol;
1446 : ExtensionControlFile *control;
1447 : char *filename;
1448 : struct stat fst;
1449 : List *updateVersions;
1450 : List *requiredExtensions;
1451 : List *requiredSchemas;
1452 : Oid extensionOid;
1453 : ObjectAddress address;
1454 : ListCell *lc;
1455 :
1456 : /*
1457 : * Read the primary control file. Note we assume that it does not contain
1458 : * any non-ASCII data, so there is no need to worry about encoding at this
1459 : * point.
1460 : */
1461 932 : pcontrol = read_extension_control_file(extensionName);
1462 :
1463 : /*
1464 : * Determine the version to install
1465 : */
1466 932 : if (versionName == NULL)
1467 : {
1468 924 : if (pcontrol->default_version)
1469 924 : versionName = pcontrol->default_version;
1470 : else
1471 0 : ereport(ERROR,
1472 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1473 : errmsg("version to install must be specified")));
1474 : }
1475 932 : check_valid_version_name(versionName);
1476 :
1477 : /*
1478 : * Figure out which script(s) we need to run to install the desired
1479 : * version of the extension. If we do not have a script that directly
1480 : * does what is needed, we try to find a sequence of update scripts that
1481 : * will get us there.
1482 : */
1483 932 : filename = get_extension_script_filename(pcontrol, NULL, versionName);
1484 932 : if (stat(filename, &fst) == 0)
1485 : {
1486 : /* Easy, no extra scripts */
1487 828 : updateVersions = NIL;
1488 : }
1489 : else
1490 : {
1491 : /* Look for best way to install this version */
1492 : List *evi_list;
1493 : ExtensionVersionInfo *evi_start;
1494 : ExtensionVersionInfo *evi_target;
1495 :
1496 : /* Extract the version update graph from the script directory */
1497 104 : evi_list = get_ext_ver_list(pcontrol);
1498 :
1499 : /* Identify the target version */
1500 104 : evi_target = get_ext_ver_info(versionName, &evi_list);
1501 :
1502 : /* Identify best path to reach target */
1503 104 : evi_start = find_install_path(evi_list, evi_target,
1504 : &updateVersions);
1505 :
1506 : /* Fail if no path ... */
1507 104 : if (evi_start == NULL)
1508 0 : ereport(ERROR,
1509 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1510 : errmsg("extension \"%s\" has no installation script nor update path for version \"%s\"",
1511 : pcontrol->name, versionName)));
1512 :
1513 : /* Otherwise, install best starting point and then upgrade */
1514 104 : versionName = evi_start->name;
1515 : }
1516 :
1517 : /*
1518 : * Fetch control parameters for installation target version
1519 : */
1520 932 : control = read_extension_aux_control_file(pcontrol, versionName);
1521 :
1522 : /*
1523 : * Determine the target schema to install the extension into
1524 : */
1525 932 : if (schemaName)
1526 : {
1527 : /* If the user is giving us the schema name, it must exist already. */
1528 44 : schemaOid = get_namespace_oid(schemaName, false);
1529 : }
1530 :
1531 928 : if (control->schema != NULL)
1532 : {
1533 : /*
1534 : * The extension is not relocatable and the author gave us a schema
1535 : * for it.
1536 : *
1537 : * Unless CASCADE parameter was given, it's an error to give a schema
1538 : * different from control->schema if control->schema is specified.
1539 : */
1540 658 : if (schemaName && strcmp(control->schema, schemaName) != 0 &&
1541 4 : !cascade)
1542 2 : ereport(ERROR,
1543 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1544 : errmsg("extension \"%s\" must be installed in schema \"%s\"",
1545 : control->name,
1546 : control->schema)));
1547 :
1548 : /* Always use the schema from control file for current extension. */
1549 656 : schemaName = control->schema;
1550 :
1551 : /* Find or create the schema in case it does not exist. */
1552 656 : schemaOid = get_namespace_oid(schemaName, true);
1553 :
1554 656 : if (!OidIsValid(schemaOid))
1555 : {
1556 6 : CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
1557 :
1558 6 : csstmt->schemaname = schemaName;
1559 6 : csstmt->authrole = NULL; /* will be created by current user */
1560 6 : csstmt->schemaElts = NIL;
1561 6 : csstmt->if_not_exists = false;
1562 6 : CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
1563 : -1, -1);
1564 :
1565 : /*
1566 : * CreateSchemaCommand includes CommandCounterIncrement, so new
1567 : * schema is now visible.
1568 : */
1569 6 : schemaOid = get_namespace_oid(schemaName, false);
1570 : }
1571 : }
1572 270 : else if (!OidIsValid(schemaOid))
1573 : {
1574 : /*
1575 : * Neither user nor author of the extension specified schema; use the
1576 : * current default creation namespace, which is the first explicit
1577 : * entry in the search_path.
1578 : */
1579 234 : List *search_path = fetch_search_path(false);
1580 :
1581 234 : if (search_path == NIL) /* nothing valid in search_path? */
1582 0 : ereport(ERROR,
1583 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1584 : errmsg("no schema has been selected to create in")));
1585 234 : schemaOid = linitial_oid(search_path);
1586 234 : schemaName = get_namespace_name(schemaOid);
1587 234 : if (schemaName == NULL) /* recently-deleted namespace? */
1588 0 : ereport(ERROR,
1589 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1590 : errmsg("no schema has been selected to create in")));
1591 :
1592 234 : list_free(search_path);
1593 : }
1594 :
1595 : /*
1596 : * Make note if a temporary namespace has been accessed in this
1597 : * transaction.
1598 : */
1599 926 : if (isTempNamespace(schemaOid))
1600 4 : MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
1601 :
1602 : /*
1603 : * We don't check creation rights on the target namespace here. If the
1604 : * extension script actually creates any objects there, it will fail if
1605 : * the user doesn't have such permissions. But there are cases such as
1606 : * procedural languages where it's convenient to set schema = pg_catalog
1607 : * yet we don't want to restrict the command to users with ACL_CREATE for
1608 : * pg_catalog.
1609 : */
1610 :
1611 : /*
1612 : * Look up the prerequisite extensions, install them if necessary, and
1613 : * build lists of their OIDs and the OIDs of their target schemas.
1614 : */
1615 926 : requiredExtensions = NIL;
1616 926 : requiredSchemas = NIL;
1617 976 : foreach(lc, control->requires)
1618 : {
1619 58 : char *curreq = (char *) lfirst(lc);
1620 : Oid reqext;
1621 : Oid reqschema;
1622 :
1623 58 : reqext = get_required_extension(curreq,
1624 : extensionName,
1625 : origSchemaName,
1626 : cascade,
1627 : parents,
1628 : is_create);
1629 50 : reqschema = get_extension_schema(reqext);
1630 50 : requiredExtensions = lappend_oid(requiredExtensions, reqext);
1631 50 : requiredSchemas = lappend_oid(requiredSchemas, reqschema);
1632 : }
1633 :
1634 : /*
1635 : * Insert new tuple into pg_extension, and create dependency entries.
1636 : */
1637 918 : address = InsertExtensionTuple(control->name, extowner,
1638 918 : schemaOid, control->relocatable,
1639 : versionName,
1640 : PointerGetDatum(NULL),
1641 : PointerGetDatum(NULL),
1642 : requiredExtensions);
1643 918 : extensionOid = address.objectId;
1644 :
1645 : /*
1646 : * Apply any control-file comment on extension
1647 : */
1648 918 : if (control->comment != NULL)
1649 918 : CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
1650 :
1651 : /*
1652 : * Execute the installation script file
1653 : */
1654 918 : 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 890 : ApplyExtensionUpdates(extensionOid, pcontrol,
1664 : versionName, updateVersions,
1665 : origSchemaName, cascade, is_create);
1666 :
1667 890 : return address;
1668 : }
1669 :
1670 : /*
1671 : * Get the OID of an extension listed in "requires", possibly creating it.
1672 : */
1673 : static Oid
1674 58 : get_required_extension(char *reqExtensionName,
1675 : char *extensionName,
1676 : char *origSchemaName,
1677 : bool cascade,
1678 : List *parents,
1679 : bool is_create)
1680 : {
1681 : Oid reqExtensionOid;
1682 :
1683 58 : reqExtensionOid = get_extension_oid(reqExtensionName, true);
1684 58 : if (!OidIsValid(reqExtensionOid))
1685 : {
1686 42 : if (cascade)
1687 : {
1688 : /* Must install it. */
1689 : ObjectAddress addr;
1690 : List *cascade_parents;
1691 : ListCell *lc;
1692 :
1693 : /* Check extension name validity before trying to cascade. */
1694 38 : check_valid_extension_name(reqExtensionName);
1695 :
1696 : /* Check for cyclic dependency between extensions. */
1697 42 : foreach(lc, parents)
1698 : {
1699 6 : char *pname = (char *) lfirst(lc);
1700 :
1701 6 : if (strcmp(pname, reqExtensionName) == 0)
1702 2 : ereport(ERROR,
1703 : (errcode(ERRCODE_INVALID_RECURSION),
1704 : errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
1705 : reqExtensionName, extensionName)));
1706 : }
1707 :
1708 36 : ereport(NOTICE,
1709 : (errmsg("installing required extension \"%s\"",
1710 : reqExtensionName)));
1711 :
1712 : /* Add current extension to list of parents to pass down. */
1713 36 : cascade_parents = lappend(list_copy(parents), extensionName);
1714 :
1715 : /*
1716 : * Create the required extension. We propagate the SCHEMA option
1717 : * if any, and CASCADE, but no other options.
1718 : */
1719 36 : addr = CreateExtensionInternal(reqExtensionName,
1720 : origSchemaName,
1721 : NULL,
1722 : cascade,
1723 : cascade_parents,
1724 : is_create);
1725 :
1726 : /* Get its newly-assigned OID. */
1727 34 : reqExtensionOid = addr.objectId;
1728 : }
1729 : else
1730 4 : ereport(ERROR,
1731 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1732 : errmsg("required extension \"%s\" is not installed",
1733 : reqExtensionName),
1734 : is_create ?
1735 : errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
1736 : }
1737 :
1738 50 : return reqExtensionOid;
1739 : }
1740 :
1741 : /*
1742 : * CREATE EXTENSION
1743 : */
1744 : ObjectAddress
1745 898 : CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
1746 : {
1747 898 : DefElem *d_schema = NULL;
1748 898 : DefElem *d_new_version = NULL;
1749 898 : DefElem *d_cascade = NULL;
1750 898 : char *schemaName = NULL;
1751 898 : char *versionName = NULL;
1752 898 : bool cascade = false;
1753 : ListCell *lc;
1754 :
1755 : /* Check extension name validity before any filesystem access */
1756 898 : check_valid_extension_name(stmt->extname);
1757 :
1758 : /*
1759 : * Check for duplicate extension name. The unique index on
1760 : * pg_extension.extname would catch this anyway, and serves as a backstop
1761 : * in case of race conditions; but this is a friendlier error message, and
1762 : * besides we need a check to support IF NOT EXISTS.
1763 : */
1764 898 : if (get_extension_oid(stmt->extname, true) != InvalidOid)
1765 : {
1766 2 : if (stmt->if_not_exists)
1767 : {
1768 2 : ereport(NOTICE,
1769 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1770 : errmsg("extension \"%s\" already exists, skipping",
1771 : stmt->extname)));
1772 2 : return InvalidObjectAddress;
1773 : }
1774 : else
1775 0 : ereport(ERROR,
1776 : (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 : */
1785 896 : if (creating_extension)
1786 0 : ereport(ERROR,
1787 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1788 : errmsg("nested CREATE EXTENSION is not supported")));
1789 :
1790 : /* Deconstruct the statement option list */
1791 976 : foreach(lc, stmt->options)
1792 : {
1793 80 : DefElem *defel = (DefElem *) lfirst(lc);
1794 :
1795 80 : if (strcmp(defel->defname, "schema") == 0)
1796 : {
1797 34 : if (d_schema)
1798 0 : errorConflictingDefElem(defel, pstate);
1799 34 : d_schema = defel;
1800 34 : schemaName = defGetString(d_schema);
1801 : }
1802 46 : else if (strcmp(defel->defname, "new_version") == 0)
1803 : {
1804 8 : if (d_new_version)
1805 0 : errorConflictingDefElem(defel, pstate);
1806 8 : d_new_version = defel;
1807 8 : versionName = defGetString(d_new_version);
1808 : }
1809 38 : else if (strcmp(defel->defname, "cascade") == 0)
1810 : {
1811 38 : if (d_cascade)
1812 0 : errorConflictingDefElem(defel, pstate);
1813 38 : d_cascade = defel;
1814 38 : cascade = defGetBoolean(d_cascade);
1815 : }
1816 : else
1817 0 : elog(ERROR, "unrecognized option: %s", defel->defname);
1818 : }
1819 :
1820 : /* Call CreateExtensionInternal to do the real work. */
1821 896 : return CreateExtensionInternal(stmt->extname,
1822 : schemaName,
1823 : versionName,
1824 : 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.
1834 : *
1835 : * 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).
1840 : * We declare them as plain Datum to avoid needing array.h in extension.h.
1841 : */
1842 : ObjectAddress
1843 918 : InsertExtensionTuple(const char *extName, Oid extOwner,
1844 : Oid schemaOid, bool relocatable, const char *extVersion,
1845 : Datum extConfig, Datum extCondition,
1846 : List *requiredExtensions)
1847 : {
1848 : Oid extensionOid;
1849 : Relation rel;
1850 : Datum values[Natts_pg_extension];
1851 : bool nulls[Natts_pg_extension];
1852 : HeapTuple tuple;
1853 : ObjectAddress myself;
1854 : ObjectAddress nsp;
1855 : ObjectAddresses *refobjs;
1856 : ListCell *lc;
1857 :
1858 : /*
1859 : * Build and insert the pg_extension tuple
1860 : */
1861 918 : rel = table_open(ExtensionRelationId, RowExclusiveLock);
1862 :
1863 918 : memset(values, 0, sizeof(values));
1864 918 : memset(nulls, 0, sizeof(nulls));
1865 :
1866 918 : extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
1867 : Anum_pg_extension_oid);
1868 918 : values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
1869 918 : values[Anum_pg_extension_extname - 1] =
1870 918 : DirectFunctionCall1(namein, CStringGetDatum(extName));
1871 918 : values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
1872 918 : values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
1873 918 : values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
1874 918 : values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
1875 :
1876 918 : if (extConfig == PointerGetDatum(NULL))
1877 918 : nulls[Anum_pg_extension_extconfig - 1] = true;
1878 : else
1879 0 : values[Anum_pg_extension_extconfig - 1] = extConfig;
1880 :
1881 918 : if (extCondition == PointerGetDatum(NULL))
1882 918 : nulls[Anum_pg_extension_extcondition - 1] = true;
1883 : else
1884 0 : values[Anum_pg_extension_extcondition - 1] = extCondition;
1885 :
1886 918 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
1887 :
1888 918 : CatalogTupleInsert(rel, tuple);
1889 :
1890 918 : heap_freetuple(tuple);
1891 918 : table_close(rel, RowExclusiveLock);
1892 :
1893 : /*
1894 : * Record dependencies on owner, schema, and prerequisite extensions
1895 : */
1896 918 : recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
1897 :
1898 918 : refobjs = new_object_addresses();
1899 :
1900 918 : ObjectAddressSet(myself, ExtensionRelationId, extensionOid);
1901 :
1902 918 : ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
1903 918 : add_exact_object_address(&nsp, refobjs);
1904 :
1905 968 : foreach(lc, requiredExtensions)
1906 : {
1907 50 : Oid reqext = lfirst_oid(lc);
1908 : ObjectAddress otherext;
1909 :
1910 50 : ObjectAddressSet(otherext, ExtensionRelationId, reqext);
1911 50 : add_exact_object_address(&otherext, refobjs);
1912 : }
1913 :
1914 : /* Record all of them (this includes duplicate elimination) */
1915 918 : record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
1916 918 : free_object_addresses(refobjs);
1917 :
1918 : /* Post creation hook for new extension */
1919 918 : InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
1920 :
1921 918 : return myself;
1922 : }
1923 :
1924 : /*
1925 : * Guts of extension deletion.
1926 : *
1927 : * All we need do here is remove the pg_extension tuple itself. Everything
1928 : * else is taken care of by the dependency infrastructure.
1929 : */
1930 : void
1931 98 : RemoveExtensionById(Oid extId)
1932 : {
1933 : Relation rel;
1934 : SysScanDesc scandesc;
1935 : HeapTuple tuple;
1936 : ScanKeyData entry[1];
1937 :
1938 : /*
1939 : * Disallow deletion of any extension that's currently open for insertion;
1940 : * else subsequent executions of recordDependencyOnCurrentExtension()
1941 : * could create dangling pg_depend records that refer to a no-longer-valid
1942 : * pg_extension OID. This is needed not so much because we think people
1943 : * might write "DROP EXTENSION foo" in foo's own script files, as because
1944 : * errors in dependency management in extension script files could give
1945 : * rise to cases where an extension is dropped as a result of recursing
1946 : * from some contained object. Because of that, we must test for the case
1947 : * here, not at some higher level of the DROP EXTENSION command.
1948 : */
1949 98 : if (extId == CurrentExtensionObject)
1950 0 : ereport(ERROR,
1951 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1952 : errmsg("cannot drop extension \"%s\" because it is being modified",
1953 : get_extension_name(extId))));
1954 :
1955 98 : rel = table_open(ExtensionRelationId, RowExclusiveLock);
1956 :
1957 98 : ScanKeyInit(&entry[0],
1958 : Anum_pg_extension_oid,
1959 : BTEqualStrategyNumber, F_OIDEQ,
1960 : ObjectIdGetDatum(extId));
1961 98 : scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
1962 : NULL, 1, entry);
1963 :
1964 98 : tuple = systable_getnext(scandesc);
1965 :
1966 : /* We assume that there can be at most one matching tuple */
1967 98 : if (HeapTupleIsValid(tuple))
1968 98 : CatalogTupleDelete(rel, &tuple->t_self);
1969 :
1970 98 : systable_endscan(scandesc);
1971 :
1972 98 : table_close(rel, RowExclusiveLock);
1973 98 : }
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 : *
1980 : * 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
1985 18 : pg_available_extensions(PG_FUNCTION_ARGS)
1986 : {
1987 18 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1988 : char *location;
1989 : DIR *dir;
1990 : struct dirent *de;
1991 :
1992 : /* Build tuplestore to hold the result rows */
1993 18 : InitMaterializedSRF(fcinfo, 0);
1994 :
1995 18 : location = get_extension_control_directory();
1996 18 : dir = AllocateDir(location);
1997 :
1998 : /*
1999 : * If the control directory doesn't exist, we want to silently return an
2000 : * empty set. Any other error will be reported by ReadDir.
2001 : */
2002 18 : if (dir == NULL && errno == ENOENT)
2003 : {
2004 : /* do nothing */
2005 : }
2006 : else
2007 : {
2008 5472 : while ((de = ReadDir(dir, location)) != NULL)
2009 : {
2010 : ExtensionControlFile *control;
2011 : char *extname;
2012 : Datum values[3];
2013 : bool nulls[3];
2014 :
2015 5454 : if (!is_extension_control_filename(de->d_name))
2016 3744 : continue;
2017 :
2018 : /* extract extension name from 'name.control' filename */
2019 1710 : extname = pstrdup(de->d_name);
2020 1710 : *strrchr(extname, '.') = '\0';
2021 :
2022 : /* ignore it if it's an auxiliary control file */
2023 1710 : if (strstr(extname, "--"))
2024 0 : continue;
2025 :
2026 1710 : control = read_extension_control_file(extname);
2027 :
2028 1710 : memset(values, 0, sizeof(values));
2029 1710 : memset(nulls, 0, sizeof(nulls));
2030 :
2031 : /* name */
2032 1710 : values[0] = DirectFunctionCall1(namein,
2033 : CStringGetDatum(control->name));
2034 : /* default_version */
2035 1710 : if (control->default_version == NULL)
2036 0 : nulls[1] = true;
2037 : else
2038 1710 : values[1] = CStringGetTextDatum(control->default_version);
2039 : /* comment */
2040 1710 : if (control->comment == NULL)
2041 0 : nulls[2] = true;
2042 : else
2043 1710 : values[2] = CStringGetTextDatum(control->comment);
2044 :
2045 1710 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2046 : values, nulls);
2047 : }
2048 :
2049 18 : FreeDir(dir);
2050 : }
2051 :
2052 18 : return (Datum) 0;
2053 : }
2054 :
2055 : /*
2056 : * This function lists the available extension versions (one row per
2057 : * 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 : */
2064 : Datum
2065 6 : pg_available_extension_versions(PG_FUNCTION_ARGS)
2066 : {
2067 6 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2068 : char *location;
2069 : DIR *dir;
2070 : struct dirent *de;
2071 :
2072 : /* Build tuplestore to hold the result rows */
2073 6 : InitMaterializedSRF(fcinfo, 0);
2074 :
2075 6 : location = get_extension_control_directory();
2076 6 : dir = AllocateDir(location);
2077 :
2078 : /*
2079 : * If the control directory doesn't exist, we want to silently return an
2080 : * empty set. Any other error will be reported by ReadDir.
2081 : */
2082 6 : if (dir == NULL && errno == ENOENT)
2083 : {
2084 : /* do nothing */
2085 : }
2086 : else
2087 : {
2088 1824 : while ((de = ReadDir(dir, location)) != NULL)
2089 : {
2090 : ExtensionControlFile *control;
2091 : char *extname;
2092 :
2093 1818 : if (!is_extension_control_filename(de->d_name))
2094 1248 : continue;
2095 :
2096 : /* extract extension name from 'name.control' filename */
2097 570 : extname = pstrdup(de->d_name);
2098 570 : *strrchr(extname, '.') = '\0';
2099 :
2100 : /* ignore it if it's an auxiliary control file */
2101 570 : if (strstr(extname, "--"))
2102 0 : continue;
2103 :
2104 : /* read the control file */
2105 570 : control = read_extension_control_file(extname);
2106 :
2107 : /* scan extension's script directory for install scripts */
2108 570 : get_available_versions_for_extension(control, rsinfo->setResult,
2109 : rsinfo->setDesc);
2110 : }
2111 :
2112 6 : FreeDir(dir);
2113 : }
2114 :
2115 6 : return (Datum) 0;
2116 : }
2117 :
2118 : /*
2119 : * Inner loop for pg_available_extension_versions:
2120 : * read versions of one extension, add rows to tupstore
2121 : */
2122 : static void
2123 570 : get_available_versions_for_extension(ExtensionControlFile *pcontrol,
2124 : Tuplestorestate *tupstore,
2125 : TupleDesc tupdesc)
2126 : {
2127 : List *evi_list;
2128 : ListCell *lc;
2129 :
2130 : /* Extract the version update graph from the script directory */
2131 570 : evi_list = get_ext_ver_list(pcontrol);
2132 :
2133 : /* For each installable version ... */
2134 1806 : foreach(lc, evi_list)
2135 : {
2136 1236 : ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
2137 : ExtensionControlFile *control;
2138 : Datum values[8];
2139 : bool nulls[8];
2140 : ListCell *lc2;
2141 :
2142 1236 : if (!evi->installable)
2143 666 : continue;
2144 :
2145 : /*
2146 : * Fetch parameters for specific version (pcontrol is not changed)
2147 : */
2148 570 : control = read_extension_aux_control_file(pcontrol, evi->name);
2149 :
2150 570 : memset(values, 0, sizeof(values));
2151 570 : memset(nulls, 0, sizeof(nulls));
2152 :
2153 : /* name */
2154 570 : values[0] = DirectFunctionCall1(namein,
2155 : CStringGetDatum(control->name));
2156 : /* version */
2157 570 : values[1] = CStringGetTextDatum(evi->name);
2158 : /* superuser */
2159 570 : values[2] = BoolGetDatum(control->superuser);
2160 : /* trusted */
2161 570 : values[3] = BoolGetDatum(control->trusted);
2162 : /* relocatable */
2163 570 : values[4] = BoolGetDatum(control->relocatable);
2164 : /* schema */
2165 570 : if (control->schema == NULL)
2166 498 : nulls[5] = true;
2167 : else
2168 72 : values[5] = DirectFunctionCall1(namein,
2169 : CStringGetDatum(control->schema));
2170 : /* requires */
2171 570 : if (control->requires == NIL)
2172 468 : nulls[6] = true;
2173 : else
2174 102 : values[6] = convert_requires_to_datum(control->requires);
2175 : /* comment */
2176 570 : if (control->comment == NULL)
2177 0 : nulls[7] = true;
2178 : else
2179 570 : values[7] = CStringGetTextDatum(control->comment);
2180 :
2181 570 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2182 :
2183 : /*
2184 : * Find all non-directly-installable versions that would be installed
2185 : * starting from this version, and report them, inheriting the
2186 : * parameters that aren't changed in updates from this version.
2187 : */
2188 1806 : foreach(lc2, evi_list)
2189 : {
2190 1236 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
2191 : List *best_path;
2192 :
2193 1236 : if (evi2->installable)
2194 570 : continue;
2195 666 : if (find_install_path(evi_list, evi2, &best_path) == evi)
2196 : {
2197 : /*
2198 : * Fetch parameters for this version (pcontrol is not changed)
2199 : */
2200 354 : control = read_extension_aux_control_file(pcontrol, evi2->name);
2201 :
2202 : /* name stays the same */
2203 : /* version */
2204 354 : values[1] = CStringGetTextDatum(evi2->name);
2205 : /* superuser */
2206 354 : values[2] = BoolGetDatum(control->superuser);
2207 : /* trusted */
2208 354 : values[3] = BoolGetDatum(control->trusted);
2209 : /* relocatable */
2210 354 : values[4] = BoolGetDatum(control->relocatable);
2211 : /* schema stays the same */
2212 : /* requires */
2213 354 : if (control->requires == NIL)
2214 354 : nulls[6] = true;
2215 : else
2216 : {
2217 0 : values[6] = convert_requires_to_datum(control->requires);
2218 0 : nulls[6] = false;
2219 : }
2220 : /* comment stays the same */
2221 :
2222 354 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2223 : }
2224 : }
2225 : }
2226 570 : }
2227 :
2228 : /*
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
2237 0 : extension_file_exists(const char *extensionName)
2238 : {
2239 0 : bool result = false;
2240 : char *location;
2241 : DIR *dir;
2242 : struct dirent *de;
2243 :
2244 0 : location = get_extension_control_directory();
2245 0 : dir = AllocateDir(location);
2246 :
2247 : /*
2248 : * If the control directory doesn't exist, we want to silently return
2249 : * false. Any other error will be reported by ReadDir.
2250 : */
2251 0 : if (dir == NULL && errno == ENOENT)
2252 : {
2253 : /* do nothing */
2254 : }
2255 : else
2256 : {
2257 0 : while ((de = ReadDir(dir, location)) != NULL)
2258 : {
2259 : char *extname;
2260 :
2261 0 : if (!is_extension_control_filename(de->d_name))
2262 0 : continue;
2263 :
2264 : /* extract extension name from 'name.control' filename */
2265 0 : extname = pstrdup(de->d_name);
2266 0 : *strrchr(extname, '.') = '\0';
2267 :
2268 : /* ignore it if it's an auxiliary control file */
2269 0 : if (strstr(extname, "--"))
2270 0 : continue;
2271 :
2272 : /* done if it matches request */
2273 0 : if (strcmp(extname, extensionName) == 0)
2274 : {
2275 0 : result = true;
2276 0 : break;
2277 : }
2278 : }
2279 :
2280 0 : FreeDir(dir);
2281 : }
2282 :
2283 0 : return result;
2284 : }
2285 :
2286 : /*
2287 : * Convert a list of extension names to a name[] Datum
2288 : */
2289 : static Datum
2290 102 : convert_requires_to_datum(List *requires)
2291 : {
2292 : Datum *datums;
2293 : int ndatums;
2294 : ArrayType *a;
2295 : ListCell *lc;
2296 :
2297 102 : ndatums = list_length(requires);
2298 102 : datums = (Datum *) palloc(ndatums * sizeof(Datum));
2299 102 : ndatums = 0;
2300 246 : foreach(lc, requires)
2301 : {
2302 144 : char *curreq = (char *) lfirst(lc);
2303 :
2304 144 : datums[ndatums++] =
2305 144 : DirectFunctionCall1(namein, CStringGetDatum(curreq));
2306 : }
2307 102 : a = construct_array_builtin(datums, ndatums, NAMEOID);
2308 102 : return PointerGetDatum(a);
2309 : }
2310 :
2311 : /*
2312 : * This function reports the version update paths that exist for the
2313 : * specified extension.
2314 : */
2315 : Datum
2316 0 : pg_extension_update_paths(PG_FUNCTION_ARGS)
2317 : {
2318 0 : Name extname = PG_GETARG_NAME(0);
2319 0 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2320 : List *evi_list;
2321 : ExtensionControlFile *control;
2322 : ListCell *lc1;
2323 :
2324 : /* Check extension name validity before any filesystem access */
2325 0 : check_valid_extension_name(NameStr(*extname));
2326 :
2327 : /* Build tuplestore to hold the result rows */
2328 0 : InitMaterializedSRF(fcinfo, 0);
2329 :
2330 : /* Read the extension's control file */
2331 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 */
2337 0 : foreach(lc1, evi_list)
2338 : {
2339 0 : ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc1);
2340 : ListCell *lc2;
2341 :
2342 0 : foreach(lc2, evi_list)
2343 : {
2344 0 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
2345 : List *path;
2346 : Datum values[3];
2347 : bool nulls[3];
2348 :
2349 0 : if (evi1 == evi2)
2350 0 : continue;
2351 :
2352 : /* Find shortest path from evi1 to evi2 */
2353 0 : path = find_update_path(evi_list, evi1, evi2, false, true);
2354 :
2355 : /* Emit result row */
2356 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);
2363 : /* path */
2364 0 : if (path == NIL)
2365 0 : nulls[2] = true;
2366 : else
2367 : {
2368 : StringInfoData pathbuf;
2369 : ListCell *lcv;
2370 :
2371 0 : initStringInfo(&pathbuf);
2372 : /* The path doesn't include start vertex, but show it */
2373 0 : appendStringInfoString(&pathbuf, evi1->name);
2374 0 : foreach(lcv, path)
2375 : {
2376 0 : char *versionName = (char *) lfirst(lcv);
2377 :
2378 0 : appendStringInfoString(&pathbuf, "--");
2379 0 : appendStringInfoString(&pathbuf, versionName);
2380 : }
2381 0 : values[2] = CStringGetTextDatum(pathbuf.data);
2382 0 : pfree(pathbuf.data);
2383 : }
2384 :
2385 0 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2386 : values, nulls);
2387 : }
2388 : }
2389 :
2390 0 : return (Datum) 0;
2391 : }
2392 :
2393 : /*
2394 : * pg_extension_config_dump
2395 : *
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
2401 8 : pg_extension_config_dump(PG_FUNCTION_ARGS)
2402 : {
2403 8 : Oid tableoid = PG_GETARG_OID(0);
2404 8 : text *wherecond = PG_GETARG_TEXT_PP(1);
2405 : char *tablename;
2406 : Relation extRel;
2407 : ScanKeyData key[1];
2408 : SysScanDesc extScan;
2409 : HeapTuple extTup;
2410 : Datum arrayDatum;
2411 : Datum elementDatum;
2412 : int arrayLength;
2413 : int arrayIndex;
2414 : bool isnull;
2415 : Datum repl_val[Natts_pg_extension];
2416 : bool repl_null[Natts_pg_extension];
2417 : bool repl_repl[Natts_pg_extension];
2418 : ArrayType *a;
2419 :
2420 : /*
2421 : * We only allow this to be called from an extension's SQL script. We
2422 : * shouldn't need any permissions check beyond that.
2423 : */
2424 8 : if (!creating_extension)
2425 0 : ereport(ERROR,
2426 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2427 : errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
2428 : "pg_extension_config_dump()")));
2429 :
2430 : /*
2431 : * Check that the table exists and is a member of the extension being
2432 : * created. This ensures that we don't need to register an additional
2433 : * dependency to protect the extconfig entry.
2434 : */
2435 8 : tablename = get_rel_name(tableoid);
2436 8 : if (tablename == NULL)
2437 0 : ereport(ERROR,
2438 : (errcode(ERRCODE_UNDEFINED_TABLE),
2439 : errmsg("OID %u does not refer to a table", tableoid)));
2440 8 : if (getExtensionOfObject(RelationRelationId, tableoid) !=
2441 : CurrentExtensionObject)
2442 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 : /*
2448 : * Add the table OID and WHERE condition to the extension's extconfig and
2449 : * extcondition arrays.
2450 : *
2451 : * If the table is already in extconfig, treat this as an update of the
2452 : * WHERE condition.
2453 : */
2454 :
2455 : /* Find the pg_extension tuple */
2456 8 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2457 :
2458 8 : ScanKeyInit(&key[0],
2459 : Anum_pg_extension_oid,
2460 : BTEqualStrategyNumber, F_OIDEQ,
2461 : ObjectIdGetDatum(CurrentExtensionObject));
2462 :
2463 8 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2464 : NULL, 1, key);
2465 :
2466 8 : extTup = systable_getnext(extScan);
2467 :
2468 8 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2469 0 : elog(ERROR, "could not find tuple for extension %u",
2470 : CurrentExtensionObject);
2471 :
2472 8 : memset(repl_val, 0, sizeof(repl_val));
2473 8 : memset(repl_null, false, sizeof(repl_null));
2474 8 : memset(repl_repl, false, sizeof(repl_repl));
2475 :
2476 : /* Build or modify the extconfig value */
2477 8 : elementDatum = ObjectIdGetDatum(tableoid);
2478 :
2479 8 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2480 : RelationGetDescr(extRel), &isnull);
2481 8 : if (isnull)
2482 : {
2483 : /* Previously empty extconfig, so build 1-element array */
2484 4 : arrayLength = 0;
2485 4 : arrayIndex = 1;
2486 :
2487 4 : a = construct_array_builtin(&elementDatum, 1, OIDOID);
2488 : }
2489 : else
2490 : {
2491 : /* Modify or extend existing extconfig array */
2492 : Oid *arrayData;
2493 : int i;
2494 :
2495 4 : a = DatumGetArrayTypeP(arrayDatum);
2496 :
2497 4 : arrayLength = ARR_DIMS(a)[0];
2498 4 : if (ARR_NDIM(a) != 1 ||
2499 4 : ARR_LBOUND(a)[0] != 1 ||
2500 4 : arrayLength < 0 ||
2501 4 : ARR_HASNULL(a) ||
2502 4 : ARR_ELEMTYPE(a) != OIDOID)
2503 0 : elog(ERROR, "extconfig is not a 1-D Oid array");
2504 4 : arrayData = (Oid *) ARR_DATA_PTR(a);
2505 :
2506 4 : arrayIndex = arrayLength + 1; /* set up to add after end */
2507 :
2508 8 : for (i = 0; i < arrayLength; i++)
2509 : {
2510 4 : if (arrayData[i] == tableoid)
2511 : {
2512 0 : arrayIndex = i + 1; /* replace this element instead */
2513 0 : break;
2514 : }
2515 : }
2516 :
2517 4 : a = array_set(a, 1, &arrayIndex,
2518 : elementDatum,
2519 : false,
2520 : -1 /* varlena array */ ,
2521 : sizeof(Oid) /* OID's typlen */ ,
2522 : true /* OID's typbyval */ ,
2523 : TYPALIGN_INT /* OID's typalign */ );
2524 : }
2525 8 : repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2526 8 : repl_repl[Anum_pg_extension_extconfig - 1] = true;
2527 :
2528 : /* Build or modify the extcondition value */
2529 8 : elementDatum = PointerGetDatum(wherecond);
2530 :
2531 8 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2532 : RelationGetDescr(extRel), &isnull);
2533 8 : if (isnull)
2534 : {
2535 4 : if (arrayLength != 0)
2536 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
2537 :
2538 4 : a = construct_array_builtin(&elementDatum, 1, TEXTOID);
2539 : }
2540 : else
2541 : {
2542 4 : a = DatumGetArrayTypeP(arrayDatum);
2543 :
2544 4 : if (ARR_NDIM(a) != 1 ||
2545 4 : ARR_LBOUND(a)[0] != 1 ||
2546 4 : ARR_HASNULL(a) ||
2547 4 : ARR_ELEMTYPE(a) != TEXTOID)
2548 0 : elog(ERROR, "extcondition is not a 1-D text array");
2549 4 : if (ARR_DIMS(a)[0] != arrayLength)
2550 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
2551 :
2552 : /* Add or replace at same index as in extconfig */
2553 4 : a = array_set(a, 1, &arrayIndex,
2554 : elementDatum,
2555 : false,
2556 : -1 /* varlena array */ ,
2557 : -1 /* TEXT's typlen */ ,
2558 : false /* TEXT's typbyval */ ,
2559 : TYPALIGN_INT /* TEXT's typalign */ );
2560 : }
2561 8 : repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
2562 8 : repl_repl[Anum_pg_extension_extcondition - 1] = true;
2563 :
2564 8 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
2565 : repl_val, repl_null, repl_repl);
2566 :
2567 8 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2568 :
2569 8 : systable_endscan(extScan);
2570 :
2571 8 : table_close(extRel, RowExclusiveLock);
2572 :
2573 8 : PG_RETURN_VOID();
2574 : }
2575 :
2576 : /*
2577 : * extension_config_remove
2578 : *
2579 : * Remove the specified table OID from extension's extconfig, if present.
2580 : * This is not currently exposed as a function, but it could be;
2581 : * for now, we just invoke it from ALTER EXTENSION DROP.
2582 : */
2583 : static void
2584 32 : extension_config_remove(Oid extensionoid, Oid tableoid)
2585 : {
2586 : Relation extRel;
2587 : ScanKeyData key[1];
2588 : SysScanDesc extScan;
2589 : HeapTuple extTup;
2590 : Datum arrayDatum;
2591 : int arrayLength;
2592 : int arrayIndex;
2593 : bool isnull;
2594 : Datum repl_val[Natts_pg_extension];
2595 : bool repl_null[Natts_pg_extension];
2596 : bool repl_repl[Natts_pg_extension];
2597 : ArrayType *a;
2598 :
2599 : /* Find the pg_extension tuple */
2600 32 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2601 :
2602 32 : ScanKeyInit(&key[0],
2603 : Anum_pg_extension_oid,
2604 : BTEqualStrategyNumber, F_OIDEQ,
2605 : ObjectIdGetDatum(extensionoid));
2606 :
2607 32 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2608 : NULL, 1, key);
2609 :
2610 32 : extTup = systable_getnext(extScan);
2611 :
2612 32 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2613 0 : elog(ERROR, "could not find tuple for extension %u",
2614 : extensionoid);
2615 :
2616 : /* Search extconfig for the tableoid */
2617 32 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2618 : RelationGetDescr(extRel), &isnull);
2619 32 : if (isnull)
2620 : {
2621 : /* nothing to do */
2622 24 : a = NULL;
2623 24 : arrayLength = 0;
2624 24 : arrayIndex = -1;
2625 : }
2626 : else
2627 : {
2628 : Oid *arrayData;
2629 : int i;
2630 :
2631 8 : a = DatumGetArrayTypeP(arrayDatum);
2632 :
2633 8 : arrayLength = ARR_DIMS(a)[0];
2634 8 : if (ARR_NDIM(a) != 1 ||
2635 8 : ARR_LBOUND(a)[0] != 1 ||
2636 8 : arrayLength < 0 ||
2637 8 : ARR_HASNULL(a) ||
2638 8 : ARR_ELEMTYPE(a) != OIDOID)
2639 0 : elog(ERROR, "extconfig is not a 1-D Oid array");
2640 8 : arrayData = (Oid *) ARR_DATA_PTR(a);
2641 :
2642 8 : arrayIndex = -1; /* flag for no deletion needed */
2643 :
2644 24 : for (i = 0; i < arrayLength; i++)
2645 : {
2646 16 : if (arrayData[i] == tableoid)
2647 : {
2648 0 : arrayIndex = i; /* index to remove */
2649 0 : break;
2650 : }
2651 : }
2652 : }
2653 :
2654 : /* If tableoid is not in extconfig, nothing to do */
2655 32 : if (arrayIndex < 0)
2656 : {
2657 32 : systable_endscan(extScan);
2658 32 : table_close(extRel, RowExclusiveLock);
2659 32 : return;
2660 : }
2661 :
2662 : /* Modify or delete the extconfig value */
2663 0 : memset(repl_val, 0, sizeof(repl_val));
2664 0 : memset(repl_null, false, sizeof(repl_null));
2665 0 : memset(repl_repl, false, sizeof(repl_repl));
2666 :
2667 0 : if (arrayLength <= 1)
2668 : {
2669 : /* removing only element, just set array to null */
2670 0 : repl_null[Anum_pg_extension_extconfig - 1] = true;
2671 : }
2672 : else
2673 : {
2674 : /* squeeze out the target element */
2675 : Datum *dvalues;
2676 : int nelems;
2677 : int i;
2678 :
2679 : /* We already checked there are no nulls */
2680 0 : deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
2681 :
2682 0 : for (i = arrayIndex; i < arrayLength - 1; i++)
2683 0 : dvalues[i] = dvalues[i + 1];
2684 :
2685 0 : a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
2686 :
2687 0 : repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2688 : }
2689 0 : repl_repl[Anum_pg_extension_extconfig - 1] = true;
2690 :
2691 : /* Modify or delete the extcondition value */
2692 0 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2693 : RelationGetDescr(extRel), &isnull);
2694 0 : if (isnull)
2695 : {
2696 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
2697 : }
2698 : else
2699 : {
2700 0 : a = DatumGetArrayTypeP(arrayDatum);
2701 :
2702 0 : if (ARR_NDIM(a) != 1 ||
2703 0 : ARR_LBOUND(a)[0] != 1 ||
2704 0 : ARR_HASNULL(a) ||
2705 0 : ARR_ELEMTYPE(a) != TEXTOID)
2706 0 : elog(ERROR, "extcondition is not a 1-D text array");
2707 0 : if (ARR_DIMS(a)[0] != arrayLength)
2708 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 */
2714 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;
2721 : int i;
2722 :
2723 : /* We already checked there are no nulls */
2724 0 : deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
2725 :
2726 0 : for (i = arrayIndex; i < arrayLength - 1; i++)
2727 0 : dvalues[i] = dvalues[i + 1];
2728 :
2729 0 : a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
2730 :
2731 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 :
2738 0 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2739 :
2740 0 : systable_endscan(extScan);
2741 :
2742 0 : table_close(extRel, RowExclusiveLock);
2743 : }
2744 :
2745 : /*
2746 : * Execute ALTER EXTENSION SET SCHEMA
2747 : */
2748 : ObjectAddress
2749 6 : AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *oldschema)
2750 : {
2751 : Oid extensionOid;
2752 : Oid nspOid;
2753 6 : Oid oldNspOid = InvalidOid;
2754 : AclResult aclresult;
2755 : Relation extRel;
2756 : ScanKeyData key[2];
2757 : SysScanDesc extScan;
2758 : HeapTuple extTup;
2759 : Form_pg_extension extForm;
2760 : Relation depRel;
2761 : SysScanDesc depScan;
2762 : HeapTuple depTup;
2763 : ObjectAddresses *objsMoved;
2764 : ObjectAddress extAddr;
2765 :
2766 6 : extensionOid = get_extension_oid(extensionName, false);
2767 :
2768 6 : nspOid = LookupCreationNamespace(newschema);
2769 :
2770 : /*
2771 : * Permission check: must own extension. Note that we don't bother to
2772 : * check ownership of the individual member objects ...
2773 : */
2774 6 : if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
2775 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
2776 : extensionName);
2777 :
2778 : /* Permission check: must have creation rights in target namespace */
2779 6 : aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
2780 6 : if (aclresult != ACLCHECK_OK)
2781 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 : */
2787 6 : if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
2788 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",
2792 : extensionName, newschema)));
2793 :
2794 : /* Locate the pg_extension tuple */
2795 6 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2796 :
2797 6 : ScanKeyInit(&key[0],
2798 : Anum_pg_extension_oid,
2799 : BTEqualStrategyNumber, F_OIDEQ,
2800 : ObjectIdGetDatum(extensionOid));
2801 :
2802 6 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2803 : NULL, 1, key);
2804 :
2805 6 : extTup = systable_getnext(extScan);
2806 :
2807 6 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2808 0 : elog(ERROR, "could not find tuple for extension %u",
2809 : extensionOid);
2810 :
2811 : /* Copy tuple so we can modify it below */
2812 6 : extTup = heap_copytuple(extTup);
2813 6 : extForm = (Form_pg_extension) GETSTRUCT(extTup);
2814 :
2815 6 : systable_endscan(extScan);
2816 :
2817 : /*
2818 : * If the extension is already in the target schema, just silently do
2819 : * nothing.
2820 : */
2821 6 : if (extForm->extnamespace == nspOid)
2822 : {
2823 0 : table_close(extRel, RowExclusiveLock);
2824 0 : return InvalidObjectAddress;
2825 : }
2826 :
2827 : /* Check extension is supposed to be relocatable */
2828 6 : if (!extForm->extrelocatable)
2829 0 : ereport(ERROR,
2830 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2831 : errmsg("extension \"%s\" does not support SET SCHEMA",
2832 : NameStr(extForm->extname))));
2833 :
2834 6 : objsMoved = new_object_addresses();
2835 :
2836 : /*
2837 : * Scan pg_depend to find objects that depend directly on the extension,
2838 : * and alter each one's schema.
2839 : */
2840 6 : depRel = table_open(DependRelationId, AccessShareLock);
2841 :
2842 6 : ScanKeyInit(&key[0],
2843 : Anum_pg_depend_refclassid,
2844 : BTEqualStrategyNumber, F_OIDEQ,
2845 : ObjectIdGetDatum(ExtensionRelationId));
2846 6 : ScanKeyInit(&key[1],
2847 : Anum_pg_depend_refobjid,
2848 : BTEqualStrategyNumber, F_OIDEQ,
2849 : ObjectIdGetDatum(extensionOid));
2850 :
2851 6 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
2852 : NULL, 2, key);
2853 :
2854 18 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
2855 : {
2856 14 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
2857 : ObjectAddress dep;
2858 : Oid dep_oldNspOid;
2859 :
2860 : /*
2861 : * If a dependent extension has a no_relocate request for this
2862 : * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
2863 : * the same loop that's actually executing the renames: we may detect
2864 : * the error condition only after having expended a fair amount of
2865 : * work. However, the alternative is to do two scans of pg_depend,
2866 : * which seems like optimizing for failure cases. The rename work
2867 : * will all roll back cleanly enough if we do fail here.)
2868 : */
2869 14 : if (pg_depend->deptype == DEPENDENCY_NORMAL &&
2870 8 : pg_depend->classid == ExtensionRelationId)
2871 : {
2872 8 : char *depextname = get_extension_name(pg_depend->objid);
2873 : ExtensionControlFile *dcontrol;
2874 : ListCell *lc;
2875 :
2876 8 : dcontrol = read_extension_control_file(depextname);
2877 10 : foreach(lc, dcontrol->no_relocate)
2878 : {
2879 4 : char *nrextname = (char *) lfirst(lc);
2880 :
2881 4 : if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
2882 : {
2883 2 : ereport(ERROR,
2884 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2885 : errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
2886 : NameStr(extForm->extname)),
2887 : errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
2888 : depextname,
2889 : NameStr(extForm->extname))));
2890 : }
2891 : }
2892 : }
2893 :
2894 : /*
2895 : * Otherwise, ignore non-membership dependencies. (Currently, the
2896 : * only other case we could see here is a normal dependency from
2897 : * another extension.)
2898 : */
2899 12 : if (pg_depend->deptype != DEPENDENCY_EXTENSION)
2900 6 : continue;
2901 :
2902 6 : dep.classId = pg_depend->classid;
2903 6 : dep.objectId = pg_depend->objid;
2904 6 : dep.objectSubId = pg_depend->objsubid;
2905 :
2906 6 : if (dep.objectSubId != 0) /* should not happen */
2907 0 : elog(ERROR, "extension should not have a sub-object dependency");
2908 :
2909 : /* Relocate the object */
2910 6 : dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
2911 : dep.objectId,
2912 : nspOid,
2913 : objsMoved);
2914 :
2915 : /*
2916 : * Remember previous namespace of first object that has one
2917 : */
2918 6 : if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid)
2919 6 : oldNspOid = dep_oldNspOid;
2920 :
2921 : /*
2922 : * If not all the objects had the same old namespace (ignoring any
2923 : * that are not in namespaces), complain.
2924 : */
2925 6 : if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
2926 0 : ereport(ERROR,
2927 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2928 : errmsg("extension \"%s\" does not support SET SCHEMA",
2929 : NameStr(extForm->extname)),
2930 : errdetail("%s is not in the extension's schema \"%s\"",
2931 : getObjectDescription(&dep, false),
2932 : get_namespace_name(oldNspOid))));
2933 : }
2934 :
2935 : /* report old schema, if caller wants it */
2936 4 : if (oldschema)
2937 4 : *oldschema = oldNspOid;
2938 :
2939 4 : systable_endscan(depScan);
2940 :
2941 4 : relation_close(depRel, AccessShareLock);
2942 :
2943 : /* Now adjust pg_extension.extnamespace */
2944 4 : extForm->extnamespace = nspOid;
2945 :
2946 4 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
2947 :
2948 4 : table_close(extRel, RowExclusiveLock);
2949 :
2950 : /* update dependencies to point to the new schema */
2951 4 : changeDependencyFor(ExtensionRelationId, extensionOid,
2952 : NamespaceRelationId, oldNspOid, nspOid);
2953 :
2954 4 : InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
2955 :
2956 4 : ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
2957 :
2958 4 : return extAddr;
2959 : }
2960 :
2961 : /*
2962 : * Execute ALTER EXTENSION UPDATE
2963 : */
2964 : ObjectAddress
2965 24 : ExecAlterExtensionStmt(ParseState *pstate, AlterExtensionStmt *stmt)
2966 : {
2967 24 : DefElem *d_new_version = NULL;
2968 : char *versionName;
2969 : char *oldVersionName;
2970 : ExtensionControlFile *control;
2971 : Oid extensionOid;
2972 : Relation extRel;
2973 : ScanKeyData key[1];
2974 : SysScanDesc extScan;
2975 : HeapTuple extTup;
2976 : List *updateVersions;
2977 : Datum datum;
2978 : bool isnull;
2979 : ListCell *lc;
2980 : ObjectAddress address;
2981 :
2982 : /*
2983 : * We use global variables to track the extension being created, so we can
2984 : * create/update only one extension at the same time.
2985 : */
2986 24 : if (creating_extension)
2987 0 : ereport(ERROR,
2988 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2989 : errmsg("nested ALTER EXTENSION is not supported")));
2990 :
2991 : /*
2992 : * Look up the extension --- it must already exist in pg_extension
2993 : */
2994 24 : extRel = table_open(ExtensionRelationId, AccessShareLock);
2995 :
2996 24 : ScanKeyInit(&key[0],
2997 : Anum_pg_extension_extname,
2998 : BTEqualStrategyNumber, F_NAMEEQ,
2999 24 : CStringGetDatum(stmt->extname));
3000 :
3001 24 : extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
3002 : NULL, 1, key);
3003 :
3004 24 : extTup = systable_getnext(extScan);
3005 :
3006 24 : if (!HeapTupleIsValid(extTup))
3007 0 : ereport(ERROR,
3008 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3009 : errmsg("extension \"%s\" does not exist",
3010 : stmt->extname)));
3011 :
3012 24 : extensionOid = ((Form_pg_extension) GETSTRUCT(extTup))->oid;
3013 :
3014 : /*
3015 : * Determine the existing version we are updating from
3016 : */
3017 24 : datum = heap_getattr(extTup, Anum_pg_extension_extversion,
3018 : RelationGetDescr(extRel), &isnull);
3019 24 : if (isnull)
3020 0 : elog(ERROR, "extversion is null");
3021 24 : oldVersionName = text_to_cstring(DatumGetTextPP(datum));
3022 :
3023 24 : systable_endscan(extScan);
3024 :
3025 24 : table_close(extRel, AccessShareLock);
3026 :
3027 : /* Permission check: must own extension */
3028 24 : if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3029 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
3030 0 : stmt->extname);
3031 :
3032 : /*
3033 : * Read the primary control file. Note we assume that it does not contain
3034 : * any non-ASCII data, so there is no need to worry about encoding at this
3035 : * point.
3036 : */
3037 24 : control = read_extension_control_file(stmt->extname);
3038 :
3039 : /*
3040 : * Read the statement option list
3041 : */
3042 48 : foreach(lc, stmt->options)
3043 : {
3044 24 : DefElem *defel = (DefElem *) lfirst(lc);
3045 :
3046 24 : if (strcmp(defel->defname, "new_version") == 0)
3047 : {
3048 24 : if (d_new_version)
3049 0 : errorConflictingDefElem(defel, pstate);
3050 24 : d_new_version = defel;
3051 : }
3052 : else
3053 0 : elog(ERROR, "unrecognized option: %s", defel->defname);
3054 : }
3055 :
3056 : /*
3057 : * Determine the version to update to
3058 : */
3059 24 : if (d_new_version && d_new_version->arg)
3060 24 : versionName = strVal(d_new_version->arg);
3061 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),
3067 : errmsg("version to install must be specified")));
3068 : versionName = NULL; /* keep compiler quiet */
3069 : }
3070 24 : check_valid_version_name(versionName);
3071 :
3072 : /*
3073 : * If we're already at that version, just say so
3074 : */
3075 24 : if (strcmp(oldVersionName, versionName) == 0)
3076 : {
3077 0 : ereport(NOTICE,
3078 : (errmsg("version \"%s\" of extension \"%s\" is already installed",
3079 : versionName, stmt->extname)));
3080 0 : return InvalidObjectAddress;
3081 : }
3082 :
3083 : /*
3084 : * Identify the series of update script files we need to execute
3085 : */
3086 24 : updateVersions = identify_update_path(control,
3087 : oldVersionName,
3088 : versionName);
3089 :
3090 : /*
3091 : * Update the pg_extension row and execute the update scripts, one at a
3092 : * time
3093 : */
3094 24 : ApplyExtensionUpdates(extensionOid, control,
3095 : oldVersionName, updateVersions,
3096 : NULL, false, false);
3097 :
3098 24 : ObjectAddressSet(address, ExtensionRelationId, extensionOid);
3099 :
3100 24 : return address;
3101 : }
3102 :
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.
3110 : */
3111 : static void
3112 914 : ApplyExtensionUpdates(Oid extensionOid,
3113 : ExtensionControlFile *pcontrol,
3114 : const char *initialVersion,
3115 : List *updateVersions,
3116 : char *origSchemaName,
3117 : bool cascade,
3118 : bool is_create)
3119 : {
3120 914 : const char *oldVersionName = initialVersion;
3121 : ListCell *lcv;
3122 :
3123 1264 : foreach(lcv, updateVersions)
3124 : {
3125 350 : char *versionName = (char *) lfirst(lcv);
3126 : ExtensionControlFile *control;
3127 : char *schemaName;
3128 : Oid schemaOid;
3129 : List *requiredExtensions;
3130 : List *requiredSchemas;
3131 : Relation extRel;
3132 : ScanKeyData key[1];
3133 : SysScanDesc extScan;
3134 : HeapTuple extTup;
3135 : Form_pg_extension extForm;
3136 : Datum values[Natts_pg_extension];
3137 : bool nulls[Natts_pg_extension];
3138 : bool repl[Natts_pg_extension];
3139 : ObjectAddress myself;
3140 : ListCell *lc;
3141 :
3142 : /*
3143 : * Fetch parameters for specific version (pcontrol is not changed)
3144 : */
3145 350 : control = read_extension_aux_control_file(pcontrol, versionName);
3146 :
3147 : /* Find the pg_extension tuple */
3148 350 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3149 :
3150 350 : ScanKeyInit(&key[0],
3151 : Anum_pg_extension_oid,
3152 : BTEqualStrategyNumber, F_OIDEQ,
3153 : ObjectIdGetDatum(extensionOid));
3154 :
3155 350 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3156 : NULL, 1, key);
3157 :
3158 350 : extTup = systable_getnext(extScan);
3159 :
3160 350 : if (!HeapTupleIsValid(extTup)) /* should not happen */
3161 0 : elog(ERROR, "could not find tuple for extension %u",
3162 : extensionOid);
3163 :
3164 350 : extForm = (Form_pg_extension) GETSTRUCT(extTup);
3165 :
3166 : /*
3167 : * Determine the target schema (set by original install)
3168 : */
3169 350 : schemaOid = extForm->extnamespace;
3170 350 : schemaName = get_namespace_name(schemaOid);
3171 :
3172 : /*
3173 : * Modify extrelocatable and extversion in the pg_extension tuple
3174 : */
3175 350 : memset(values, 0, sizeof(values));
3176 350 : memset(nulls, 0, sizeof(nulls));
3177 350 : memset(repl, 0, sizeof(repl));
3178 :
3179 350 : values[Anum_pg_extension_extrelocatable - 1] =
3180 350 : BoolGetDatum(control->relocatable);
3181 350 : repl[Anum_pg_extension_extrelocatable - 1] = true;
3182 350 : values[Anum_pg_extension_extversion - 1] =
3183 350 : CStringGetTextDatum(versionName);
3184 350 : repl[Anum_pg_extension_extversion - 1] = true;
3185 :
3186 350 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3187 : values, nulls, repl);
3188 :
3189 350 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3190 :
3191 350 : systable_endscan(extScan);
3192 :
3193 350 : table_close(extRel, RowExclusiveLock);
3194 :
3195 : /*
3196 : * Look up the prerequisite extensions for this version, install them
3197 : * if necessary, and build lists of their OIDs and the OIDs of their
3198 : * target schemas.
3199 : */
3200 350 : requiredExtensions = NIL;
3201 350 : requiredSchemas = NIL;
3202 350 : foreach(lc, control->requires)
3203 : {
3204 0 : char *curreq = (char *) lfirst(lc);
3205 : Oid reqext;
3206 : Oid reqschema;
3207 :
3208 0 : reqext = get_required_extension(curreq,
3209 : control->name,
3210 : origSchemaName,
3211 : cascade,
3212 : NIL,
3213 : is_create);
3214 0 : reqschema = get_extension_schema(reqext);
3215 0 : requiredExtensions = lappend_oid(requiredExtensions, reqext);
3216 0 : requiredSchemas = lappend_oid(requiredSchemas, reqschema);
3217 : }
3218 :
3219 : /*
3220 : * Remove and recreate dependencies on prerequisite extensions
3221 : */
3222 350 : deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
3223 : ExtensionRelationId,
3224 : DEPENDENCY_NORMAL);
3225 :
3226 350 : myself.classId = ExtensionRelationId;
3227 350 : myself.objectId = extensionOid;
3228 350 : myself.objectSubId = 0;
3229 :
3230 350 : foreach(lc, requiredExtensions)
3231 : {
3232 0 : Oid reqext = lfirst_oid(lc);
3233 : ObjectAddress otherext;
3234 :
3235 0 : otherext.classId = ExtensionRelationId;
3236 0 : otherext.objectId = reqext;
3237 0 : otherext.objectSubId = 0;
3238 :
3239 0 : recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
3240 : }
3241 :
3242 350 : InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3243 :
3244 : /*
3245 : * Finally, execute the update script file
3246 : */
3247 350 : execute_extension_script(extensionOid, control,
3248 : oldVersionName, versionName,
3249 : requiredSchemas,
3250 : schemaName, schemaOid);
3251 :
3252 : /*
3253 : * Update prior-version name and loop around. Since
3254 : * execute_sql_string did a final CommandCounterIncrement, we can
3255 : * update the pg_extension row again.
3256 : */
3257 350 : oldVersionName = versionName;
3258 : }
3259 914 : }
3260 :
3261 : /*
3262 : * Execute ALTER EXTENSION ADD/DROP
3263 : *
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
3270 178 : ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
3271 : ObjectAddress *objAddr)
3272 : {
3273 : ObjectAddress extension;
3274 : ObjectAddress object;
3275 : Relation relation;
3276 : Oid oldExtension;
3277 :
3278 178 : switch (stmt->objtype)
3279 : {
3280 2 : case OBJECT_DATABASE:
3281 : case OBJECT_EXTENSION:
3282 : case OBJECT_INDEX:
3283 : case OBJECT_PUBLICATION:
3284 : case OBJECT_ROLE:
3285 : case OBJECT_STATISTIC_EXT:
3286 : case OBJECT_SUBSCRIPTION:
3287 : case OBJECT_TABLESPACE:
3288 2 : ereport(ERROR,
3289 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3290 : errmsg("cannot add an object of this type to an extension")));
3291 : break;
3292 176 : default:
3293 : /* OK */
3294 176 : break;
3295 : }
3296 :
3297 : /*
3298 : * Find the extension and acquire a lock on it, to ensure it doesn't get
3299 : * dropped concurrently. A sharable lock seems sufficient: there's no
3300 : * reason not to allow other sorts of manipulations, such as add/drop of
3301 : * other objects, to occur concurrently. Concurrently adding/dropping the
3302 : * *same* object would be bad, but we prevent that by using a non-sharable
3303 : * lock on the individual object, below.
3304 : */
3305 176 : extension = get_object_address(OBJECT_EXTENSION,
3306 176 : (Node *) makeString(stmt->extname),
3307 : &relation, AccessShareLock, false);
3308 :
3309 : /* Permission check: must own extension */
3310 176 : if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
3311 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
3312 0 : stmt->extname);
3313 :
3314 : /*
3315 : * Translate the parser representation that identifies the object into an
3316 : * ObjectAddress. get_object_address() will throw an error if the object
3317 : * does not exist, and will also acquire a lock on the object to guard
3318 : * against concurrent DROP and ALTER EXTENSION ADD/DROP operations.
3319 : */
3320 176 : object = get_object_address(stmt->objtype, stmt->object,
3321 : &relation, ShareUpdateExclusiveLock, false);
3322 :
3323 : Assert(object.objectSubId == 0);
3324 176 : if (objAddr)
3325 176 : *objAddr = object;
3326 :
3327 : /* Permission check: must own target object, too */
3328 176 : check_object_ownership(GetUserId(), stmt->objtype, object,
3329 : stmt->object, relation);
3330 :
3331 : /*
3332 : * Check existing extension membership.
3333 : */
3334 176 : oldExtension = getExtensionOfObject(object.classId, object.objectId);
3335 :
3336 176 : if (stmt->action > 0)
3337 : {
3338 : /*
3339 : * ADD, so complain if object is already attached to some extension.
3340 : */
3341 56 : if (OidIsValid(oldExtension))
3342 0 : ereport(ERROR,
3343 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3344 : errmsg("%s is already a member of extension \"%s\"",
3345 : getObjectDescription(&object, false),
3346 : get_extension_name(oldExtension))));
3347 :
3348 : /*
3349 : * Prevent a schema from being added to an extension if the schema
3350 : * contains the extension. That would create a dependency loop.
3351 : */
3352 58 : if (object.classId == NamespaceRelationId &&
3353 2 : object.objectId == get_extension_schema(extension.objectId))
3354 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 :
3361 : /*
3362 : * OK, add the dependency.
3363 : */
3364 56 : recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
3365 :
3366 : /*
3367 : * Also record the initial ACL on the object, if any.
3368 : *
3369 : * Note that this will handle the object's ACLs, as well as any ACLs
3370 : * on object subIds. (In other words, when the object is a table,
3371 : * this will record the table's ACL and the ACLs for the columns on
3372 : * the table, if any).
3373 : */
3374 56 : recordExtObjInitPriv(object.objectId, object.classId);
3375 : }
3376 : else
3377 : {
3378 : /*
3379 : * DROP, so complain if it's not a member.
3380 : */
3381 120 : if (oldExtension != extension.objectId)
3382 0 : ereport(ERROR,
3383 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3384 : errmsg("%s is not a member of extension \"%s\"",
3385 : getObjectDescription(&object, false),
3386 : stmt->extname)));
3387 :
3388 : /*
3389 : * OK, drop the dependency.
3390 : */
3391 120 : if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3392 : ExtensionRelationId,
3393 : DEPENDENCY_EXTENSION) != 1)
3394 0 : elog(ERROR, "unexpected number of extension dependency records");
3395 :
3396 : /*
3397 : * If it's a relation, it might have an entry in the extension's
3398 : * extconfig array, which we must remove.
3399 : */
3400 120 : if (object.classId == RelationRelationId)
3401 32 : extension_config_remove(extension.objectId, object.objectId);
3402 :
3403 : /*
3404 : * Remove all the initial ACLs, if any.
3405 : *
3406 : * Note that this will remove the object's ACLs, as well as any ACLs
3407 : * on object subIds. (In other words, when the object is a table,
3408 : * this will remove the table's ACL and the ACLs for the columns on
3409 : * the table, if any).
3410 : */
3411 120 : removeExtObjInitPriv(object.objectId, object.classId);
3412 : }
3413 :
3414 176 : InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
3415 :
3416 : /*
3417 : * If get_object_address() opened the relation for us, we close it to keep
3418 : * the reference count correct - but we retain any locks acquired by
3419 : * get_object_address() until commit time, to guard against concurrent
3420 : * activity.
3421 : */
3422 176 : if (relation != NULL)
3423 46 : relation_close(relation, NoLock);
3424 :
3425 176 : return extension;
3426 : }
3427 :
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 *
3435 1262 : read_whole_file(const char *filename, int *length)
3436 : {
3437 : char *buf;
3438 : FILE *file;
3439 : size_t bytes_to_read;
3440 : struct stat fst;
3441 :
3442 1262 : if (stat(filename, &fst) < 0)
3443 0 : ereport(ERROR,
3444 : (errcode_for_file_access(),
3445 : errmsg("could not stat file \"%s\": %m", filename)));
3446 :
3447 1262 : if (fst.st_size > (MaxAllocSize - 1))
3448 0 : ereport(ERROR,
3449 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3450 : errmsg("file \"%s\" is too large", filename)));
3451 1262 : bytes_to_read = (size_t) fst.st_size;
3452 :
3453 1262 : if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
3454 0 : ereport(ERROR,
3455 : (errcode_for_file_access(),
3456 : errmsg("could not open file \"%s\" for reading: %m",
3457 : filename)));
3458 :
3459 1262 : buf = (char *) palloc(bytes_to_read + 1);
3460 :
3461 1262 : *length = fread(buf, 1, bytes_to_read, file);
3462 :
3463 1262 : if (ferror(file))
3464 0 : ereport(ERROR,
3465 : (errcode_for_file_access(),
3466 : errmsg("could not read file \"%s\": %m", filename)));
3467 :
3468 1262 : FreeFile(file);
3469 :
3470 1262 : buf[*length] = '\0';
3471 1262 : return buf;
3472 : }
|