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