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