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