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