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