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