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