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