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