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