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 8716 : get_extension_location(ExtensionLocation *loc)
205 : {
206 : /* We only want to show extension paths for superusers. */
207 8716 : if (superuser())
208 : {
209 : /* Return the macro value if present to avoid showing system paths. */
210 8480 : if (loc->macro != NULL)
211 8352 : return loc->macro;
212 : else
213 128 : return loc->loc;
214 : }
215 : else
216 : {
217 : /* Similar to pg_stat_activity for unprivileged users */
218 236 : 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 1580 : get_extension_oid(const char *extname, bool missing_ok)
230 : {
231 : Oid result;
232 :
233 1580 : result = GetSysCacheOid1(EXTENSIONNAME, Anum_pg_extension_oid,
234 : CStringGetDatum(extname));
235 :
236 1580 : 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 1572 : 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 332 : check_valid_extension_name(const char *extensionname)
402 : {
403 332 : int namelen = strlen(extensionname);
404 :
405 : /*
406 : * Disallow empty names (the parser rejects empty identifiers anyway, but
407 : * let's check).
408 : */
409 332 : 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 332 : 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 332 : 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 332 : 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 332 : }
446 :
447 : static void
448 349 : check_valid_version_name(const char *versionname)
449 : {
450 349 : int namelen = strlen(versionname);
451 :
452 : /*
453 : * Disallow empty names (we could possibly allow this, but there seems
454 : * little point).
455 : */
456 349 : 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 349 : 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 349 : 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 349 : 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 349 : }
490 :
491 : /*
492 : * Utility functions to handle extension-related path names
493 : */
494 : static bool
495 27596 : is_extension_control_filename(const char *filename)
496 : {
497 27596 : const char *extension = strrchr(filename, '.');
498 :
499 27596 : return (extension != NULL) && (strcmp(extension, ".control") == 0);
500 : }
501 :
502 : static bool
503 330691 : is_extension_script_filename(const char *filename)
504 : {
505 330691 : const char *extension = strrchr(filename, '.');
506 :
507 330691 : 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 428 : get_extension_control_directories(void)
515 : {
516 : char sharepath[MAXPGPATH];
517 : char *system_dir;
518 : char *ecp;
519 428 : List *paths = NIL;
520 :
521 428 : get_share_path(my_exec_path, sharepath);
522 :
523 428 : system_dir = psprintf("%s/extension", sharepath);
524 :
525 428 : 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 427 : ecp = pstrdup(Extension_control_path);
537 :
538 : for (;;)
539 21 : {
540 : int len;
541 : char *mangled;
542 448 : char *piece = first_path_var_separator(ecp);
543 448 : ExtensionLocation *location = palloc_object(ExtensionLocation);
544 :
545 : /* Get the length of the next path on ecp */
546 448 : if (piece == NULL)
547 427 : len = strlen(ecp);
548 : else
549 21 : len = piece - ecp;
550 :
551 : /* Copy the next path found on ecp */
552 448 : piece = palloc(len + 1);
553 448 : 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 448 : if (strcmp(piece, "$system") == 0)
560 : {
561 427 : location->macro = pstrdup(piece);
562 427 : 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 448 : pfree(piece);
570 :
571 : /* Canonicalize the path based on the OS and add to the list */
572 448 : canonicalize_path(mangled);
573 448 : location->loc = mangled;
574 448 : paths = lappend(paths, location);
575 :
576 : /* Break if ecp is empty or move to the next path on ecp */
577 448 : if (ecp[len] == '\0')
578 427 : break;
579 : else
580 21 : ecp += len + 1;
581 : }
582 : }
583 :
584 428 : 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 353 : find_extension_control_filename(ExtensionControlFile *control)
594 : {
595 : char *basename;
596 : char *result;
597 : List *paths;
598 :
599 : Assert(control->name);
600 :
601 353 : basename = psprintf("%s.control", control->name);
602 :
603 353 : paths = get_extension_control_directories();
604 353 : result = find_in_paths(basename, paths);
605 :
606 353 : if (result)
607 : {
608 : const char *p;
609 :
610 353 : p = strrchr(result, '/');
611 : Assert(p);
612 353 : control->control_dir = pnstrdup(result, p - result);
613 : }
614 :
615 353 : return result;
616 : }
617 :
618 : static char *
619 3798 : 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 3798 : if (!control->directory)
628 3789 : 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 1954 : get_extension_aux_control_filename(ExtensionControlFile *control,
639 : const char *version)
640 : {
641 : char *result;
642 : char *scriptdir;
643 :
644 1954 : scriptdir = get_extension_script_directory(control);
645 :
646 1954 : result = (char *) palloc(MAXPGPATH);
647 1954 : snprintf(result, MAXPGPATH, "%s/%s--%s.control",
648 : scriptdir, control->name, version);
649 :
650 1954 : pfree(scriptdir);
651 :
652 1954 : return result;
653 : }
654 :
655 : static char *
656 937 : get_extension_script_filename(ExtensionControlFile *control,
657 : const char *from_version, const char *version)
658 : {
659 : char *result;
660 : char *scriptdir;
661 :
662 937 : scriptdir = get_extension_script_directory(control);
663 :
664 937 : result = (char *) palloc(MAXPGPATH);
665 937 : if (from_version)
666 288 : snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
667 : scriptdir, control->name, from_version, version);
668 : else
669 649 : snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
670 : scriptdir, control->name, version);
671 :
672 937 : pfree(scriptdir);
673 :
674 937 : 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 11023 : parse_extension_control_file(ExtensionControlFile *control,
692 : const char *version)
693 : {
694 : char *filename;
695 : FILE *file;
696 : ConfigVariable *item,
697 11023 : *head = NULL,
698 11023 : *tail = NULL;
699 :
700 : /*
701 : * Locate the file to read. Auxiliary files are optional.
702 : */
703 11023 : if (version)
704 1954 : 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 9069 : if (control->control_dir)
711 : {
712 8716 : filename = psprintf("%s/%s.control", control->control_dir, control->name);
713 : }
714 : else
715 353 : filename = find_extension_control_filename(control);
716 : }
717 :
718 11023 : 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 22046 : control->basedir = pnstrdup(
731 11023 : control->control_dir,
732 11023 : strlen(control->control_dir) - strlen("/extension"));
733 :
734 11023 : if ((file = AllocateFile(filename, "r")) == NULL)
735 : {
736 : /* no complaint for missing auxiliary file */
737 1954 : if (errno == ENOENT && version)
738 : {
739 1954 : pfree(filename);
740 1954 : 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 9069 : (void) ParseConfigFp(file, filename, CONF_FILE_START_DEPTH, ERROR,
754 : &head, &tail);
755 :
756 9069 : FreeFile(file);
757 :
758 : /*
759 : * Convert the ConfigVariable list into ExtensionControlFile entries.
760 : */
761 48494 : for (item = head; item != NULL; item = item->next)
762 : {
763 39425 : 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 39417 : else if (strcmp(item->name, "default_version") == 0)
774 : {
775 9069 : 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 9069 : control->default_version = pstrdup(item->value);
782 : }
783 30348 : else if (strcmp(item->name, "module_pathname") == 0)
784 : {
785 7419 : control->module_pathname = pstrdup(item->value);
786 : }
787 22929 : else if (strcmp(item->name, "comment") == 0)
788 : {
789 9069 : control->comment = pstrdup(item->value);
790 : }
791 13860 : else if (strcmp(item->name, "schema") == 0)
792 : {
793 912 : control->schema = pstrdup(item->value);
794 : }
795 12948 : else if (strcmp(item->name, "relocatable") == 0)
796 : {
797 8913 : 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 4035 : else if (strcmp(item->name, "superuser") == 0)
804 : {
805 676 : 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 3359 : else if (strcmp(item->name, "trusted") == 0)
812 : {
813 1975 : 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 1384 : 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 1384 : else if (strcmp(item->name, "requires") == 0)
829 : {
830 : /* Need a modifiable copy of string */
831 1305 : char *rawnames = pstrdup(item->value);
832 :
833 : /* Parse string into list of identifiers */
834 1305 : 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 79 : else if (strcmp(item->name, "no_relocate") == 0)
844 : {
845 : /* Need a modifiable copy of string */
846 79 : char *rawnames = pstrdup(item->value);
847 :
848 : /* Parse string into list of identifiers */
849 79 : 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 9069 : FreeConfigVariables(head);
866 :
867 9069 : 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 9069 : pfree(filename);
873 : }
874 :
875 : /*
876 : * Read the primary control file for the specified extension.
877 : */
878 : static ExtensionControlFile *
879 353 : read_extension_control_file(const char *extname)
880 : {
881 353 : ExtensionControlFile *control = new_ExtensionControlFile(extname);
882 :
883 : /*
884 : * Parse the primary control file.
885 : */
886 353 : parse_extension_control_file(control, NULL);
887 :
888 353 : 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 1954 : 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 1954 : acontrol = palloc_object(ExtensionControlFile);
907 1954 : memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
908 :
909 : /*
910 : * Parse the auxiliary control file, overwriting struct fields
911 : */
912 1954 : parse_extension_control_file(acontrol, version);
913 :
914 1954 : return acontrol;
915 : }
916 :
917 : /*
918 : * Read an SQL script file into a string, and convert to database encoding
919 : */
920 : static char *
921 607 : 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 607 : src_str = read_whole_file(filename, &len);
930 :
931 : /* use database encoding if not given */
932 607 : if (control->encoding < 0)
933 607 : 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 607 : (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 607 : dest_str = pg_any_to_server(src_str, len, src_encoding);
946 :
947 607 : return dest_str;
948 : }
949 :
950 : /*
951 : * error context callback for failures in script-file execution
952 : */
953 : static void
954 137 : script_error_callback(void *arg)
955 : {
956 137 : script_error_callback_arg *callback_arg = (script_error_callback_arg *) arg;
957 137 : const char *query = callback_arg->sql;
958 137 : int location = callback_arg->stmt_location;
959 137 : 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 137 : syntaxerrposition = geterrposition();
973 137 : 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 136 : 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 136 : query = CleanQuerytext(query, &location, &len);
1046 136 : 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 137 : lastslash = strrchr(callback_arg->filename, '/');
1055 137 : if (lastslash)
1056 137 : 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 137 : if (location >= 0)
1065 : {
1066 137 : int linenumber = 1;
1067 :
1068 82155 : for (query = callback_arg->sql; *query; query++)
1069 : {
1070 82155 : if (--location < 0)
1071 137 : break;
1072 82018 : if (*query == '\n')
1073 3051 : linenumber++;
1074 : }
1075 137 : errcontext("extension script file \"%s\", near line %d",
1076 : lastslash, linenumber);
1077 : }
1078 : else
1079 0 : errcontext("extension script file \"%s\"", lastslash);
1080 137 : }
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 605 : 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 605 : callback_arg.sql = sql;
1108 605 : callback_arg.filename = filename;
1109 605 : callback_arg.stmt_location = -1;
1110 605 : callback_arg.stmt_len = -1;
1111 :
1112 605 : scripterrcontext.callback = script_error_callback;
1113 605 : scripterrcontext.arg = &callback_arg;
1114 605 : scripterrcontext.previous = error_context_stack;
1115 605 : error_context_stack = &scripterrcontext;
1116 :
1117 : /*
1118 : * Parse the SQL string into a list of raw parse trees.
1119 : */
1120 605 : raw_parsetree_list = pg_parse_query(sql);
1121 :
1122 : /* All output from SELECTs goes to the bit bucket */
1123 604 : 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 7097 : foreach(lc1, raw_parsetree_list)
1131 : {
1132 6505 : 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 6505 : callback_arg.stmt_location = parsetree->stmt_location;
1140 6505 : 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 6505 : AllocSetContextCreate(CurrentMemoryContext,
1148 : "execute_sql_string per-statement context",
1149 : ALLOCSET_DEFAULT_SIZES);
1150 6505 : oldcontext = MemoryContextSwitchTo(per_parsetree_context);
1151 :
1152 : /* Be sure parser can see any DDL done so far */
1153 6505 : CommandCounterIncrement();
1154 :
1155 6505 : stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
1156 : sql,
1157 : NULL,
1158 : 0,
1159 : NULL);
1160 6505 : stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
1161 :
1162 12998 : foreach(lc2, stmt_list)
1163 : {
1164 6505 : PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
1165 :
1166 6505 : CommandCounterIncrement();
1167 :
1168 6505 : PushActiveSnapshot(GetTransactionSnapshot());
1169 :
1170 6505 : 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 6497 : 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 6497 : ProcessUtility(stmt,
1194 : sql,
1195 : false,
1196 : PROCESS_UTILITY_QUERY,
1197 : NULL,
1198 : NULL,
1199 : dest,
1200 : NULL);
1201 : }
1202 :
1203 6493 : PopActiveSnapshot();
1204 : }
1205 :
1206 : /* Clean up per-parsetree context. */
1207 6493 : MemoryContextSwitchTo(oldcontext);
1208 6493 : MemoryContextDelete(per_parsetree_context);
1209 : }
1210 :
1211 592 : error_context_stack = scripterrcontext.previous;
1212 :
1213 : /* Be sure to advance the command counter after the last script command */
1214 592 : CommandCounterIncrement();
1215 592 : }
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 610 : 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 610 : bool switch_to_superuser = false;
1253 : char *filename;
1254 610 : Oid save_userid = 0;
1255 610 : 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 610 : 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 607 : filename = get_extension_script_filename(control, from_version, version);
1289 :
1290 607 : if (from_version == NULL)
1291 319 : elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
1292 : else
1293 288 : 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 607 : 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 607 : save_nestlevel = NewGUCNestLevel();
1320 :
1321 607 : if (client_min_messages < WARNING)
1322 605 : (void) set_config_option("client_min_messages", "warning",
1323 : PGC_USERSET, PGC_S_SESSION,
1324 : GUC_ACTION_SAVE, true, 0, false);
1325 607 : if (log_min_messages[MyBackendType] < WARNING)
1326 11 : (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 607 : if (check_function_bodies)
1336 607 : (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 607 : initStringInfo(&pathbuf);
1349 607 : appendStringInfoString(&pathbuf, quote_identifier(schemaName));
1350 634 : 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 607 : appendStringInfoString(&pathbuf, ", pg_temp");
1359 :
1360 607 : (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 607 : creating_extension = true;
1370 607 : CurrentExtensionObject = extensionOid;
1371 607 : PG_TRY();
1372 : {
1373 607 : 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 607 : const char *quoting_relevant_chars = "\"$'\\";
1385 :
1386 : /* We use various functions that want to operate on text datums */
1387 607 : 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 607 : t_sql = DirectFunctionCall4Coll(textregexreplace,
1395 : C_COLLATION_OID,
1396 : t_sql,
1397 607 : CStringGetTextDatum("^\\\\echo.*$"),
1398 607 : CStringGetTextDatum(""),
1399 607 : CStringGetTextDatum("ng"));
1400 :
1401 : /*
1402 : * If the script uses @extowner@, substitute the calling username.
1403 : */
1404 607 : if (strstr(c_sql, "@extowner@"))
1405 : {
1406 63 : Oid uid = switch_to_superuser ? save_userid : GetUserId();
1407 63 : const char *userName = GetUserNameFromId(uid, false);
1408 63 : const char *qUserName = quote_identifier(userName);
1409 :
1410 63 : t_sql = DirectFunctionCall3Coll(replace_text,
1411 : C_COLLATION_OID,
1412 : t_sql,
1413 63 : CStringGetTextDatum("@extowner@"),
1414 63 : CStringGetTextDatum(qUserName));
1415 63 : 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 607 : if (!control->relocatable)
1430 : {
1431 93 : Datum old = t_sql;
1432 93 : const char *qSchemaName = quote_identifier(schemaName);
1433 :
1434 93 : t_sql = DirectFunctionCall3Coll(replace_text,
1435 : C_COLLATION_OID,
1436 : t_sql,
1437 93 : CStringGetTextDatum("@extschema@"),
1438 93 : CStringGetTextDatum(qSchemaName));
1439 93 : 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 632 : 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 605 : if (control->module_pathname)
1478 : {
1479 557 : t_sql = DirectFunctionCall3Coll(replace_text,
1480 : C_COLLATION_OID,
1481 : t_sql,
1482 557 : CStringGetTextDatum("MODULE_PATHNAME"),
1483 557 : CStringGetTextDatum(control->module_pathname));
1484 : }
1485 :
1486 : /* And now back to C string */
1487 605 : c_sql = text_to_cstring(DatumGetTextPP(t_sql));
1488 :
1489 605 : execute_sql_string(c_sql, filename);
1490 : }
1491 15 : PG_FINALLY();
1492 : {
1493 607 : creating_extension = false;
1494 607 : CurrentExtensionObject = InvalidOid;
1495 : }
1496 607 : PG_END_TRY();
1497 :
1498 : /*
1499 : * Restore the GUC variables we set above.
1500 : */
1501 592 : AtEOXact_GUC(true, save_nestlevel);
1502 :
1503 : /*
1504 : * Restore authentication state if needed.
1505 : */
1506 592 : if (switch_to_superuser)
1507 4 : SetUserIdAndSecContext(save_userid, save_sec_context);
1508 592 : }
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 4019 : get_ext_ver_info(const char *versionname, List **evi_list)
1520 : {
1521 : ExtensionVersionInfo *evi;
1522 : ListCell *lc;
1523 :
1524 16019 : foreach(lc, *evi_list)
1525 : {
1526 13610 : evi = (ExtensionVersionInfo *) lfirst(lc);
1527 13610 : if (strcmp(evi->name, versionname) == 0)
1528 1610 : return evi;
1529 : }
1530 :
1531 2409 : evi = palloc_object(ExtensionVersionInfo);
1532 2409 : evi->name = pstrdup(versionname);
1533 2409 : evi->reachable = NIL;
1534 2409 : evi->installable = false;
1535 : /* initialize for later application of Dijkstra's algorithm */
1536 2409 : evi->distance_known = false;
1537 2409 : evi->distance = INT_MAX;
1538 2409 : evi->previous = NULL;
1539 :
1540 2409 : *evi_list = lappend(*evi_list, evi);
1541 :
1542 2409 : 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 4201 : get_nearest_unprocessed_vertex(List *evi_list)
1553 : {
1554 4201 : ExtensionVersionInfo *evi = NULL;
1555 : ListCell *lc;
1556 :
1557 41456 : foreach(lc, evi_list)
1558 : {
1559 37255 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
1560 :
1561 : /* only vertices whose distance is still uncertain are candidates */
1562 37255 : if (evi2->distance_known)
1563 9594 : continue;
1564 : /* remember the closest such vertex */
1565 27661 : if (evi == NULL ||
1566 23460 : evi->distance > evi2->distance)
1567 6862 : evi = evi2;
1568 : }
1569 :
1570 4201 : 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 907 : get_ext_ver_list(ExtensionControlFile *control)
1581 : {
1582 907 : List *evi_list = NIL;
1583 907 : int extnamelen = strlen(control->name);
1584 : char *location;
1585 : DIR *dir;
1586 : struct dirent *de;
1587 :
1588 907 : location = get_extension_script_directory(control);
1589 907 : dir = AllocateDir(location);
1590 331598 : 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 330691 : if (!is_extension_script_filename(de->d_name))
1599 106336 : continue;
1600 :
1601 : /* ... matching extension name followed by separator */
1602 224355 : if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
1603 2495 : de->d_name[extnamelen] != '-' ||
1604 2409 : de->d_name[extnamelen + 1] != '-')
1605 221946 : continue;
1606 :
1607 : /* extract version name(s) from 'extname--something.sql' filename */
1608 2409 : vername = pstrdup(de->d_name + extnamelen + 2);
1609 2409 : *strrchr(vername, '.') = '\0';
1610 2409 : vername2 = strstr(vername, "--");
1611 2409 : if (!vername2)
1612 : {
1613 : /* It's an install, not update, script; record its version name */
1614 907 : evi = get_ext_ver_info(vername, &evi_list);
1615 907 : evi->installable = true;
1616 907 : continue;
1617 : }
1618 1502 : *vername2 = '\0'; /* terminate first version */
1619 1502 : vername2 += 2; /* and point to second */
1620 :
1621 : /* if there's a third --, it's bogus, ignore it */
1622 1502 : if (strstr(vername2, "--"))
1623 0 : continue;
1624 :
1625 : /* Create ExtensionVersionInfos and link them together */
1626 1502 : evi = get_ext_ver_info(vername, &evi_list);
1627 1502 : evi2 = get_ext_ver_info(vername2, &evi_list);
1628 1502 : evi->reachable = lappend(evi->reachable, evi2);
1629 : }
1630 907 : FreeDir(dir);
1631 :
1632 907 : 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 1020 : 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 1020 : if (reinitialize)
1702 : {
1703 8358 : foreach(lc, evi_list)
1704 : {
1705 7357 : evi = (ExtensionVersionInfo *) lfirst(lc);
1706 7357 : evi->distance_known = false;
1707 7357 : evi->distance = INT_MAX;
1708 7357 : evi->previous = NULL;
1709 : }
1710 : }
1711 :
1712 1020 : evi_start->distance = 0;
1713 :
1714 4201 : while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
1715 : {
1716 4201 : if (evi->distance == INT_MAX)
1717 413 : break; /* all remaining vertices are unreachable */
1718 3788 : evi->distance_known = true;
1719 3788 : if (evi == evi_target)
1720 607 : break; /* found shortest path to target */
1721 5957 : foreach(lc, evi->reachable)
1722 : {
1723 2776 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
1724 : int newdist;
1725 :
1726 : /* if reject_indirect, treat installable versions as unreachable */
1727 2776 : if (reject_indirect && evi2->installable)
1728 0 : continue;
1729 2776 : newdist = evi->distance + 1;
1730 2776 : if (newdist < evi2->distance)
1731 : {
1732 2776 : evi2->distance = newdist;
1733 2776 : 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 1020 : if (!evi_target->distance_known)
1754 413 : return NIL;
1755 :
1756 : /* Build and return list of version names representing the update path */
1757 607 : result = NIL;
1758 2268 : for (evi = evi_target; evi != evi_start; evi = evi->previous)
1759 1661 : result = lcons(evi->name, result);
1760 :
1761 607 : 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 1001 : find_install_path(List *evi_list, ExtensionVersionInfo *evi_target,
1780 : List **best_path)
1781 : {
1782 1001 : ExtensionVersionInfo *evi_start = NULL;
1783 : ListCell *lc;
1784 :
1785 1001 : *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 1001 : if (evi_target->installable)
1792 0 : return evi_target;
1793 :
1794 : /* Consider all installable versions as start points */
1795 8358 : foreach(lc, evi_list)
1796 : {
1797 7357 : ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc);
1798 : List *path;
1799 :
1800 7357 : if (!evi1->installable)
1801 6356 : 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 1001 : path = find_update_path(evi_list, evi1, evi_target, true, true);
1808 1001 : if (path == NIL)
1809 413 : continue;
1810 :
1811 : /* Remember best path */
1812 588 : 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 588 : evi_start = evi1;
1818 588 : *best_path = path;
1819 : }
1820 : }
1821 :
1822 1001 : 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 330 : CreateExtensionInternal(char *extensionName,
1835 : char *schemaName,
1836 : const char *versionName,
1837 : bool cascade,
1838 : List *parents,
1839 : bool is_create)
1840 : {
1841 330 : char *origSchemaName = schemaName;
1842 330 : Oid schemaOid = InvalidOid;
1843 330 : 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 330 : pcontrol = read_extension_control_file(extensionName);
1861 :
1862 : /*
1863 : * Determine the version to install
1864 : */
1865 330 : if (versionName == NULL)
1866 : {
1867 325 : if (pcontrol->default_version)
1868 325 : 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 330 : 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 330 : filename = get_extension_script_filename(pcontrol, NULL, versionName);
1883 330 : if (stat(filename, &fst) == 0)
1884 : {
1885 : /* Easy, no extra scripts */
1886 260 : 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 70 : evi_list = get_ext_ver_list(pcontrol);
1897 :
1898 : /* Identify the target version */
1899 70 : evi_target = get_ext_ver_info(versionName, &evi_list);
1900 :
1901 : /* Identify best path to reach target */
1902 70 : evi_start = find_install_path(evi_list, evi_target,
1903 : &updateVersions);
1904 :
1905 : /* Fail if no path ... */
1906 70 : 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 70 : versionName = evi_start->name;
1914 : }
1915 :
1916 : /*
1917 : * Fetch control parameters for installation target version
1918 : */
1919 330 : control = read_extension_aux_control_file(pcontrol, versionName);
1920 :
1921 : /*
1922 : * Determine the target schema to install the extension into
1923 : */
1924 330 : 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 328 : 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 82 : 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 81 : schemaName = control->schema;
1949 :
1950 : /* Find or create the schema in case it does not exist. */
1951 81 : schemaOid = get_namespace_oid(schemaName, true);
1952 :
1953 81 : if (!OidIsValid(schemaOid))
1954 : {
1955 3 : ParseState *pstate = make_parsestate(NULL);
1956 3 : CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
1957 :
1958 3 : pstate->p_sourcetext = "(generated CREATE SCHEMA command)";
1959 :
1960 3 : csstmt->schemaname = schemaName;
1961 3 : csstmt->authrole = NULL; /* will be created by current user */
1962 3 : csstmt->schemaElts = NIL;
1963 3 : csstmt->if_not_exists = false;
1964 :
1965 3 : CreateSchemaCommand(pstate, csstmt, -1, -1);
1966 :
1967 : /*
1968 : * CreateSchemaCommand includes CommandCounterIncrement, so new
1969 : * schema is now visible.
1970 : */
1971 3 : schemaOid = get_namespace_oid(schemaName, false);
1972 : }
1973 : }
1974 246 : else if (!OidIsValid(schemaOid))
1975 : {
1976 : /*
1977 : * Neither user nor author of the extension specified schema; use the
1978 : * current default creation namespace, which is the first explicit
1979 : * entry in the search_path.
1980 : */
1981 222 : List *search_path = fetch_search_path(false);
1982 :
1983 222 : if (search_path == NIL) /* nothing valid in search_path? */
1984 0 : ereport(ERROR,
1985 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1986 : errmsg("no schema has been selected to create in")));
1987 222 : schemaOid = linitial_oid(search_path);
1988 222 : schemaName = get_namespace_name(schemaOid);
1989 222 : if (schemaName == NULL) /* recently-deleted namespace? */
1990 0 : ereport(ERROR,
1991 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1992 : errmsg("no schema has been selected to create in")));
1993 :
1994 222 : list_free(search_path);
1995 : }
1996 :
1997 : /*
1998 : * Make note if a temporary namespace has been accessed in this
1999 : * transaction.
2000 : */
2001 327 : if (isTempNamespace(schemaOid))
2002 2 : MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
2003 :
2004 : /*
2005 : * We don't check creation rights on the target namespace here. If the
2006 : * extension script actually creates any objects there, it will fail if
2007 : * the user doesn't have such permissions. But there are cases such as
2008 : * procedural languages where it's convenient to set schema = pg_catalog
2009 : * yet we don't want to restrict the command to users with ACL_CREATE for
2010 : * pg_catalog.
2011 : */
2012 :
2013 : /*
2014 : * Look up the prerequisite extensions, install them if necessary, and
2015 : * build lists of their OIDs and the OIDs of their target schemas.
2016 : */
2017 327 : requiredExtensions = NIL;
2018 327 : requiredSchemas = NIL;
2019 354 : foreach(lc, control->requires)
2020 : {
2021 32 : char *curreq = (char *) lfirst(lc);
2022 : Oid reqext;
2023 : Oid reqschema;
2024 :
2025 32 : reqext = get_required_extension(curreq,
2026 : extensionName,
2027 : origSchemaName,
2028 : cascade,
2029 : parents,
2030 : is_create);
2031 27 : reqschema = get_extension_schema(reqext);
2032 27 : requiredExtensions = lappend_oid(requiredExtensions, reqext);
2033 27 : requiredSchemas = lappend_oid(requiredSchemas, reqschema);
2034 : }
2035 :
2036 : /*
2037 : * Insert new tuple into pg_extension, and create dependency entries.
2038 : */
2039 322 : address = InsertExtensionTuple(control->name, extowner,
2040 322 : schemaOid, control->relocatable,
2041 : versionName,
2042 : PointerGetDatum(NULL),
2043 : PointerGetDatum(NULL),
2044 : requiredExtensions);
2045 322 : extensionOid = address.objectId;
2046 :
2047 : /*
2048 : * Apply any control-file comment on extension
2049 : */
2050 322 : if (control->comment != NULL)
2051 322 : CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
2052 :
2053 : /*
2054 : * Execute the installation script file
2055 : */
2056 322 : execute_extension_script(extensionOid, control,
2057 : NULL, versionName,
2058 : requiredSchemas,
2059 : schemaName);
2060 :
2061 : /*
2062 : * If additional update scripts have to be executed, apply the updates as
2063 : * though a series of ALTER EXTENSION UPDATE commands were given
2064 : */
2065 306 : ApplyExtensionUpdates(extensionOid, pcontrol,
2066 : versionName, updateVersions,
2067 : origSchemaName, cascade, is_create);
2068 :
2069 306 : return address;
2070 : }
2071 :
2072 : /*
2073 : * Get the OID of an extension listed in "requires", possibly creating it.
2074 : */
2075 : static Oid
2076 33 : get_required_extension(char *reqExtensionName,
2077 : char *extensionName,
2078 : char *origSchemaName,
2079 : bool cascade,
2080 : List *parents,
2081 : bool is_create)
2082 : {
2083 : Oid reqExtensionOid;
2084 :
2085 33 : reqExtensionOid = get_extension_oid(reqExtensionName, true);
2086 33 : if (!OidIsValid(reqExtensionOid))
2087 : {
2088 22 : if (cascade)
2089 : {
2090 : /* Must install it. */
2091 : ObjectAddress addr;
2092 : List *cascade_parents;
2093 : ListCell *lc;
2094 :
2095 : /* Check extension name validity before trying to cascade. */
2096 20 : check_valid_extension_name(reqExtensionName);
2097 :
2098 : /* Check for cyclic dependency between extensions. */
2099 22 : foreach(lc, parents)
2100 : {
2101 3 : char *pname = (char *) lfirst(lc);
2102 :
2103 3 : if (strcmp(pname, reqExtensionName) == 0)
2104 1 : ereport(ERROR,
2105 : (errcode(ERRCODE_INVALID_RECURSION),
2106 : errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
2107 : reqExtensionName, extensionName)));
2108 : }
2109 :
2110 19 : ereport(NOTICE,
2111 : (errmsg("installing required extension \"%s\"",
2112 : reqExtensionName)));
2113 :
2114 : /* Add current extension to list of parents to pass down. */
2115 19 : cascade_parents = lappend(list_copy(parents), extensionName);
2116 :
2117 : /*
2118 : * Create the required extension. We propagate the SCHEMA option
2119 : * if any, and CASCADE, but no other options.
2120 : */
2121 19 : addr = CreateExtensionInternal(reqExtensionName,
2122 : origSchemaName,
2123 : NULL,
2124 : cascade,
2125 : cascade_parents,
2126 : is_create);
2127 :
2128 : /* Get its newly-assigned OID. */
2129 17 : reqExtensionOid = addr.objectId;
2130 : }
2131 : else
2132 2 : ereport(ERROR,
2133 : (errcode(ERRCODE_UNDEFINED_OBJECT),
2134 : errmsg("required extension \"%s\" is not installed",
2135 : reqExtensionName),
2136 : is_create ?
2137 : errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
2138 : }
2139 :
2140 28 : return reqExtensionOid;
2141 : }
2142 :
2143 : /*
2144 : * CREATE EXTENSION
2145 : */
2146 : ObjectAddress
2147 312 : CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
2148 : {
2149 312 : DefElem *d_schema = NULL;
2150 312 : DefElem *d_new_version = NULL;
2151 312 : DefElem *d_cascade = NULL;
2152 312 : char *schemaName = NULL;
2153 312 : char *versionName = NULL;
2154 312 : bool cascade = false;
2155 : ListCell *lc;
2156 :
2157 : /* Check extension name validity before any filesystem access */
2158 312 : check_valid_extension_name(stmt->extname);
2159 :
2160 : /*
2161 : * Check for duplicate extension name. The unique index on
2162 : * pg_extension.extname would catch this anyway, and serves as a backstop
2163 : * in case of race conditions; but this is a friendlier error message, and
2164 : * besides we need a check to support IF NOT EXISTS.
2165 : */
2166 312 : if (get_extension_oid(stmt->extname, true) != InvalidOid)
2167 : {
2168 1 : if (stmt->if_not_exists)
2169 : {
2170 1 : ereport(NOTICE,
2171 : (errcode(ERRCODE_DUPLICATE_OBJECT),
2172 : errmsg("extension \"%s\" already exists, skipping",
2173 : stmt->extname)));
2174 1 : return InvalidObjectAddress;
2175 : }
2176 : else
2177 0 : ereport(ERROR,
2178 : (errcode(ERRCODE_DUPLICATE_OBJECT),
2179 : errmsg("extension \"%s\" already exists",
2180 : stmt->extname)));
2181 : }
2182 :
2183 : /*
2184 : * We use global variables to track the extension being created, so we can
2185 : * create only one extension at the same time.
2186 : */
2187 311 : if (creating_extension)
2188 0 : ereport(ERROR,
2189 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2190 : errmsg("nested CREATE EXTENSION is not supported")));
2191 :
2192 : /* Deconstruct the statement option list */
2193 359 : foreach(lc, stmt->options)
2194 : {
2195 48 : DefElem *defel = (DefElem *) lfirst(lc);
2196 :
2197 48 : if (strcmp(defel->defname, "schema") == 0)
2198 : {
2199 23 : if (d_schema)
2200 0 : errorConflictingDefElem(defel, pstate);
2201 23 : d_schema = defel;
2202 23 : schemaName = defGetString(d_schema);
2203 : }
2204 25 : else if (strcmp(defel->defname, "new_version") == 0)
2205 : {
2206 5 : if (d_new_version)
2207 0 : errorConflictingDefElem(defel, pstate);
2208 5 : d_new_version = defel;
2209 5 : versionName = defGetString(d_new_version);
2210 : }
2211 20 : else if (strcmp(defel->defname, "cascade") == 0)
2212 : {
2213 20 : if (d_cascade)
2214 0 : errorConflictingDefElem(defel, pstate);
2215 20 : d_cascade = defel;
2216 20 : cascade = defGetBoolean(d_cascade);
2217 : }
2218 : else
2219 0 : elog(ERROR, "unrecognized option: %s", defel->defname);
2220 : }
2221 :
2222 : /* Call CreateExtensionInternal to do the real work. */
2223 311 : return CreateExtensionInternal(stmt->extname,
2224 : schemaName,
2225 : versionName,
2226 : cascade,
2227 : NIL,
2228 : true);
2229 : }
2230 :
2231 : /*
2232 : * InsertExtensionTuple
2233 : *
2234 : * Insert the new pg_extension row, and create extension's dependency entries.
2235 : * Return the OID assigned to the new row.
2236 : *
2237 : * This is exported for the benefit of pg_upgrade, which has to create a
2238 : * pg_extension entry (and the extension-level dependencies) without
2239 : * actually running the extension's script.
2240 : *
2241 : * extConfig and extCondition should be arrays or PointerGetDatum(NULL).
2242 : * We declare them as plain Datum to avoid needing array.h in extension.h.
2243 : */
2244 : ObjectAddress
2245 327 : InsertExtensionTuple(const char *extName, Oid extOwner,
2246 : Oid schemaOid, bool relocatable, const char *extVersion,
2247 : Datum extConfig, Datum extCondition,
2248 : List *requiredExtensions)
2249 : {
2250 : Oid extensionOid;
2251 : Relation rel;
2252 : Datum values[Natts_pg_extension];
2253 : bool nulls[Natts_pg_extension];
2254 : HeapTuple tuple;
2255 : ObjectAddress myself;
2256 : ObjectAddress nsp;
2257 : ObjectAddresses *refobjs;
2258 : ListCell *lc;
2259 :
2260 : /*
2261 : * Build and insert the pg_extension tuple
2262 : */
2263 327 : rel = table_open(ExtensionRelationId, RowExclusiveLock);
2264 :
2265 327 : memset(values, 0, sizeof(values));
2266 327 : memset(nulls, 0, sizeof(nulls));
2267 :
2268 327 : extensionOid = GetNewOidWithIndex(rel, ExtensionOidIndexId,
2269 : Anum_pg_extension_oid);
2270 327 : values[Anum_pg_extension_oid - 1] = ObjectIdGetDatum(extensionOid);
2271 327 : values[Anum_pg_extension_extname - 1] =
2272 327 : DirectFunctionCall1(namein, CStringGetDatum(extName));
2273 327 : values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
2274 327 : values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
2275 327 : values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
2276 327 : values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
2277 :
2278 327 : if (extConfig == PointerGetDatum(NULL))
2279 327 : nulls[Anum_pg_extension_extconfig - 1] = true;
2280 : else
2281 0 : values[Anum_pg_extension_extconfig - 1] = extConfig;
2282 :
2283 327 : if (extCondition == PointerGetDatum(NULL))
2284 327 : nulls[Anum_pg_extension_extcondition - 1] = true;
2285 : else
2286 0 : values[Anum_pg_extension_extcondition - 1] = extCondition;
2287 :
2288 327 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
2289 :
2290 327 : CatalogTupleInsert(rel, tuple);
2291 :
2292 327 : heap_freetuple(tuple);
2293 327 : table_close(rel, RowExclusiveLock);
2294 :
2295 : /*
2296 : * Record dependencies on owner, schema, and prerequisite extensions
2297 : */
2298 327 : recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
2299 :
2300 327 : refobjs = new_object_addresses();
2301 :
2302 327 : ObjectAddressSet(myself, ExtensionRelationId, extensionOid);
2303 :
2304 327 : ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
2305 327 : add_exact_object_address(&nsp, refobjs);
2306 :
2307 353 : foreach(lc, requiredExtensions)
2308 : {
2309 26 : Oid reqext = lfirst_oid(lc);
2310 : ObjectAddress otherext;
2311 :
2312 26 : ObjectAddressSet(otherext, ExtensionRelationId, reqext);
2313 26 : add_exact_object_address(&otherext, refobjs);
2314 : }
2315 :
2316 : /* Record all of them (this includes duplicate elimination) */
2317 327 : record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
2318 327 : free_object_addresses(refobjs);
2319 :
2320 : /* Post creation hook for new extension */
2321 327 : InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
2322 :
2323 327 : return myself;
2324 : }
2325 :
2326 : /*
2327 : * Guts of extension deletion.
2328 : *
2329 : * All we need do here is remove the pg_extension tuple itself. Everything
2330 : * else is taken care of by the dependency infrastructure.
2331 : */
2332 : void
2333 86 : RemoveExtensionById(Oid extId)
2334 : {
2335 : Relation rel;
2336 : SysScanDesc scandesc;
2337 : HeapTuple tuple;
2338 : ScanKeyData entry[1];
2339 :
2340 : /*
2341 : * Disallow deletion of any extension that's currently open for insertion;
2342 : * else subsequent executions of recordDependencyOnCurrentExtension()
2343 : * could create dangling pg_depend records that refer to a no-longer-valid
2344 : * pg_extension OID. This is needed not so much because we think people
2345 : * might write "DROP EXTENSION foo" in foo's own script files, as because
2346 : * errors in dependency management in extension script files could give
2347 : * rise to cases where an extension is dropped as a result of recursing
2348 : * from some contained object. Because of that, we must test for the case
2349 : * here, not at some higher level of the DROP EXTENSION command.
2350 : */
2351 86 : if (extId == CurrentExtensionObject)
2352 0 : ereport(ERROR,
2353 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2354 : errmsg("cannot drop extension \"%s\" because it is being modified",
2355 : get_extension_name(extId))));
2356 :
2357 86 : rel = table_open(ExtensionRelationId, RowExclusiveLock);
2358 :
2359 86 : ScanKeyInit(&entry[0],
2360 : Anum_pg_extension_oid,
2361 : BTEqualStrategyNumber, F_OIDEQ,
2362 : ObjectIdGetDatum(extId));
2363 86 : scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
2364 : NULL, 1, entry);
2365 :
2366 86 : tuple = systable_getnext(scandesc);
2367 :
2368 : /* We assume that there can be at most one matching tuple */
2369 86 : if (HeapTupleIsValid(tuple))
2370 86 : CatalogTupleDelete(rel, &tuple->t_self);
2371 :
2372 86 : systable_endscan(scandesc);
2373 :
2374 86 : table_close(rel, RowExclusiveLock);
2375 86 : }
2376 :
2377 : /*
2378 : * This function lists the available extensions (one row per primary control
2379 : * file in the control directory). We parse each control file and report the
2380 : * interesting fields.
2381 : *
2382 : * The system view pg_available_extensions provides a user interface to this
2383 : * SRF, adding information about whether the extensions are installed in the
2384 : * current DB.
2385 : */
2386 : Datum
2387 68 : pg_available_extensions(PG_FUNCTION_ARGS)
2388 : {
2389 68 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2390 : List *locations;
2391 : DIR *dir;
2392 : struct dirent *de;
2393 68 : List *found_ext = NIL;
2394 :
2395 : /* Build tuplestore to hold the result rows */
2396 68 : InitMaterializedSRF(fcinfo, 0);
2397 :
2398 68 : locations = get_extension_control_directories();
2399 :
2400 214 : foreach_ptr(ExtensionLocation, location, locations)
2401 : {
2402 78 : dir = AllocateDir(location->loc);
2403 :
2404 : /*
2405 : * If the control directory doesn't exist, we want to silently return
2406 : * an empty set. Any other error will be reported by ReadDir.
2407 : */
2408 78 : if (dir == NULL && errno == ENOENT)
2409 : {
2410 : /* do nothing */
2411 : }
2412 : else
2413 : {
2414 25078 : while ((de = ReadDir(dir, location->loc)) != NULL)
2415 : {
2416 : ExtensionControlFile *control;
2417 : char *extname;
2418 : String *extname_str;
2419 : Datum values[4];
2420 : bool nulls[4];
2421 :
2422 25000 : if (!is_extension_control_filename(de->d_name))
2423 17102 : continue;
2424 :
2425 : /* extract extension name from 'name.control' filename */
2426 7902 : extname = pstrdup(de->d_name);
2427 7902 : *strrchr(extname, '.') = '\0';
2428 :
2429 : /* ignore it if it's an auxiliary control file */
2430 7902 : if (strstr(extname, "--"))
2431 0 : continue;
2432 :
2433 : /*
2434 : * Ignore already-found names. They are not reachable by the
2435 : * path search, so don't show them.
2436 : */
2437 7902 : extname_str = makeString(extname);
2438 7902 : if (list_member(found_ext, extname_str))
2439 4 : continue;
2440 : else
2441 7898 : found_ext = lappend(found_ext, extname_str);
2442 :
2443 7898 : control = new_ExtensionControlFile(extname);
2444 7898 : control->control_dir = pstrdup(location->loc);
2445 7898 : parse_extension_control_file(control, NULL);
2446 :
2447 7898 : memset(values, 0, sizeof(values));
2448 7898 : memset(nulls, 0, sizeof(nulls));
2449 :
2450 : /* name */
2451 7898 : values[0] = DirectFunctionCall1(namein,
2452 : CStringGetDatum(control->name));
2453 : /* default_version */
2454 7898 : if (control->default_version == NULL)
2455 0 : nulls[1] = true;
2456 : else
2457 7898 : values[1] = CStringGetTextDatum(control->default_version);
2458 :
2459 : /* location */
2460 7898 : values[2] = CStringGetTextDatum(get_extension_location(location));
2461 :
2462 : /* comment */
2463 7898 : if (control->comment == NULL)
2464 0 : nulls[3] = true;
2465 : else
2466 7898 : values[3] = CStringGetTextDatum(control->comment);
2467 :
2468 7898 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2469 : values, nulls);
2470 : }
2471 :
2472 78 : FreeDir(dir);
2473 : }
2474 : }
2475 :
2476 68 : return (Datum) 0;
2477 : }
2478 :
2479 : /*
2480 : * This function lists the available extension versions (one row per
2481 : * extension installation script). For each version, we parse the related
2482 : * control file(s) and report the interesting fields.
2483 : *
2484 : * The system view pg_available_extension_versions provides a user interface
2485 : * to this SRF, adding information about which versions are installed in the
2486 : * current DB.
2487 : */
2488 : Datum
2489 7 : pg_available_extension_versions(PG_FUNCTION_ARGS)
2490 : {
2491 7 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2492 : List *locations;
2493 : DIR *dir;
2494 : struct dirent *de;
2495 7 : List *found_ext = NIL;
2496 :
2497 : /* Build tuplestore to hold the result rows */
2498 7 : InitMaterializedSRF(fcinfo, 0);
2499 :
2500 7 : locations = get_extension_control_directories();
2501 :
2502 27 : foreach_ptr(ExtensionLocation, location, locations)
2503 : {
2504 13 : dir = AllocateDir(location->loc);
2505 :
2506 : /*
2507 : * If the control directory doesn't exist, we want to silently return
2508 : * an empty set. Any other error will be reported by ReadDir.
2509 : */
2510 13 : if (dir == NULL && errno == ENOENT)
2511 : {
2512 : /* do nothing */
2513 : }
2514 : else
2515 : {
2516 2609 : while ((de = ReadDir(dir, location->loc)) != NULL)
2517 : {
2518 : ExtensionControlFile *control;
2519 : char *extname;
2520 : String *extname_str;
2521 :
2522 2596 : if (!is_extension_control_filename(de->d_name))
2523 1775 : continue;
2524 :
2525 : /* extract extension name from 'name.control' filename */
2526 821 : extname = pstrdup(de->d_name);
2527 821 : *strrchr(extname, '.') = '\0';
2528 :
2529 : /* ignore it if it's an auxiliary control file */
2530 821 : if (strstr(extname, "--"))
2531 0 : continue;
2532 :
2533 : /*
2534 : * Ignore already-found names. They are not reachable by the
2535 : * path search, so don't show them.
2536 : */
2537 821 : extname_str = makeString(extname);
2538 821 : if (list_member(found_ext, extname_str))
2539 3 : continue;
2540 : else
2541 818 : found_ext = lappend(found_ext, extname_str);
2542 :
2543 : /* read the control file */
2544 818 : control = new_ExtensionControlFile(extname);
2545 818 : control->control_dir = pstrdup(location->loc);
2546 818 : parse_extension_control_file(control, NULL);
2547 :
2548 : /* scan extension's script directory for install scripts */
2549 818 : get_available_versions_for_extension(control, rsinfo->setResult,
2550 : rsinfo->setDesc,
2551 : location);
2552 : }
2553 :
2554 13 : FreeDir(dir);
2555 : }
2556 : }
2557 :
2558 7 : return (Datum) 0;
2559 : }
2560 :
2561 : /*
2562 : * Inner loop for pg_available_extension_versions:
2563 : * read versions of one extension, add rows to tupstore
2564 : */
2565 : static void
2566 818 : get_available_versions_for_extension(ExtensionControlFile *pcontrol,
2567 : Tuplestorestate *tupstore,
2568 : TupleDesc tupdesc,
2569 : ExtensionLocation *location)
2570 : {
2571 : List *evi_list;
2572 : ListCell *lc;
2573 :
2574 : /* Extract the version update graph from the script directory */
2575 818 : evi_list = get_ext_ver_list(pcontrol);
2576 :
2577 : /* For each installable version ... */
2578 2567 : foreach(lc, evi_list)
2579 : {
2580 1749 : ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
2581 : ExtensionControlFile *control;
2582 : Datum values[9];
2583 : bool nulls[9];
2584 : ListCell *lc2;
2585 :
2586 1749 : if (!evi->installable)
2587 931 : continue;
2588 :
2589 : /*
2590 : * Fetch parameters for specific version (pcontrol is not changed)
2591 : */
2592 818 : control = read_extension_aux_control_file(pcontrol, evi->name);
2593 :
2594 818 : memset(values, 0, sizeof(values));
2595 818 : memset(nulls, 0, sizeof(nulls));
2596 :
2597 : /* name */
2598 818 : values[0] = DirectFunctionCall1(namein,
2599 : CStringGetDatum(control->name));
2600 : /* version */
2601 818 : values[1] = CStringGetTextDatum(evi->name);
2602 : /* superuser */
2603 818 : values[2] = BoolGetDatum(control->superuser);
2604 : /* trusted */
2605 818 : values[3] = BoolGetDatum(control->trusted);
2606 : /* relocatable */
2607 818 : values[4] = BoolGetDatum(control->relocatable);
2608 : /* schema */
2609 818 : if (control->schema == NULL)
2610 741 : nulls[5] = true;
2611 : else
2612 77 : values[5] = DirectFunctionCall1(namein,
2613 : CStringGetDatum(control->schema));
2614 : /* requires */
2615 818 : if (control->requires == NIL)
2616 699 : nulls[6] = true;
2617 : else
2618 119 : values[6] = convert_requires_to_datum(control->requires);
2619 :
2620 : /* location */
2621 818 : values[7] = CStringGetTextDatum(get_extension_location(location));
2622 :
2623 : /* comment */
2624 818 : if (control->comment == NULL)
2625 0 : nulls[8] = true;
2626 : else
2627 818 : values[8] = CStringGetTextDatum(control->comment);
2628 :
2629 818 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2630 :
2631 : /*
2632 : * Find all non-directly-installable versions that would be installed
2633 : * starting from this version, and report them, inheriting the
2634 : * parameters that aren't changed in updates from this version.
2635 : */
2636 2567 : foreach(lc2, evi_list)
2637 : {
2638 1749 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
2639 : List *best_path;
2640 :
2641 1749 : if (evi2->installable)
2642 818 : continue;
2643 931 : if (find_install_path(evi_list, evi2, &best_path) == evi)
2644 : {
2645 : /*
2646 : * Fetch parameters for this version (pcontrol is not changed)
2647 : */
2648 518 : control = read_extension_aux_control_file(pcontrol, evi2->name);
2649 :
2650 : /* name stays the same */
2651 : /* version */
2652 518 : values[1] = CStringGetTextDatum(evi2->name);
2653 : /* superuser */
2654 518 : values[2] = BoolGetDatum(control->superuser);
2655 : /* trusted */
2656 518 : values[3] = BoolGetDatum(control->trusted);
2657 : /* relocatable */
2658 518 : values[4] = BoolGetDatum(control->relocatable);
2659 : /* schema stays the same */
2660 : /* requires */
2661 518 : if (control->requires == NIL)
2662 511 : nulls[6] = true;
2663 : else
2664 : {
2665 7 : values[6] = convert_requires_to_datum(control->requires);
2666 7 : nulls[6] = false;
2667 : }
2668 : /* comment and location stay the same */
2669 :
2670 518 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2671 : }
2672 : }
2673 : }
2674 818 : }
2675 :
2676 : /*
2677 : * Test whether the given extension exists (not whether it's installed)
2678 : *
2679 : * This checks for the existence of a matching control file in the extension
2680 : * directory. That's not a bulletproof check, since the file might be
2681 : * invalid, but this is only used for hints so it doesn't have to be 100%
2682 : * right.
2683 : */
2684 : bool
2685 0 : extension_file_exists(const char *extensionName)
2686 : {
2687 0 : bool result = false;
2688 : List *locations;
2689 : DIR *dir;
2690 : struct dirent *de;
2691 :
2692 0 : locations = get_extension_control_directories();
2693 :
2694 0 : foreach_ptr(ExtensionLocation, location, locations)
2695 : {
2696 0 : dir = AllocateDir(location->loc);
2697 :
2698 : /*
2699 : * If the control directory doesn't exist, we want to silently return
2700 : * false. Any other error will be reported by ReadDir.
2701 : */
2702 0 : if (dir == NULL && errno == ENOENT)
2703 : {
2704 : /* do nothing */
2705 : }
2706 : else
2707 : {
2708 0 : while ((de = ReadDir(dir, location->loc)) != NULL)
2709 : {
2710 : char *extname;
2711 :
2712 0 : if (!is_extension_control_filename(de->d_name))
2713 0 : continue;
2714 :
2715 : /* extract extension name from 'name.control' filename */
2716 0 : extname = pstrdup(de->d_name);
2717 0 : *strrchr(extname, '.') = '\0';
2718 :
2719 : /* ignore it if it's an auxiliary control file */
2720 0 : if (strstr(extname, "--"))
2721 0 : continue;
2722 :
2723 : /* done if it matches request */
2724 0 : if (strcmp(extname, extensionName) == 0)
2725 : {
2726 0 : result = true;
2727 0 : break;
2728 : }
2729 : }
2730 :
2731 0 : FreeDir(dir);
2732 : }
2733 0 : if (result)
2734 0 : break;
2735 : }
2736 :
2737 0 : return result;
2738 : }
2739 :
2740 : /*
2741 : * Convert a list of extension names to a name[] Datum
2742 : */
2743 : static Datum
2744 126 : convert_requires_to_datum(List *requires)
2745 : {
2746 : Datum *datums;
2747 : int ndatums;
2748 : ArrayType *a;
2749 : ListCell *lc;
2750 :
2751 126 : ndatums = list_length(requires);
2752 126 : datums = (Datum *) palloc(ndatums * sizeof(Datum));
2753 126 : ndatums = 0;
2754 301 : foreach(lc, requires)
2755 : {
2756 175 : char *curreq = (char *) lfirst(lc);
2757 :
2758 175 : datums[ndatums++] =
2759 175 : DirectFunctionCall1(namein, CStringGetDatum(curreq));
2760 : }
2761 126 : a = construct_array_builtin(datums, ndatums, NAMEOID);
2762 126 : return PointerGetDatum(a);
2763 : }
2764 :
2765 : /*
2766 : * This function reports the version update paths that exist for the
2767 : * specified extension.
2768 : */
2769 : Datum
2770 0 : pg_extension_update_paths(PG_FUNCTION_ARGS)
2771 : {
2772 0 : Name extname = PG_GETARG_NAME(0);
2773 0 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2774 : List *evi_list;
2775 : ExtensionControlFile *control;
2776 : ListCell *lc1;
2777 :
2778 : /* Check extension name validity before any filesystem access */
2779 0 : check_valid_extension_name(NameStr(*extname));
2780 :
2781 : /* Build tuplestore to hold the result rows */
2782 0 : InitMaterializedSRF(fcinfo, 0);
2783 :
2784 : /* Read the extension's control file */
2785 0 : control = read_extension_control_file(NameStr(*extname));
2786 :
2787 : /* Extract the version update graph from the script directory */
2788 0 : evi_list = get_ext_ver_list(control);
2789 :
2790 : /* Iterate over all pairs of versions */
2791 0 : foreach(lc1, evi_list)
2792 : {
2793 0 : ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc1);
2794 : ListCell *lc2;
2795 :
2796 0 : foreach(lc2, evi_list)
2797 : {
2798 0 : ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
2799 : List *path;
2800 : Datum values[3];
2801 : bool nulls[3];
2802 :
2803 0 : if (evi1 == evi2)
2804 0 : continue;
2805 :
2806 : /* Find shortest path from evi1 to evi2 */
2807 0 : path = find_update_path(evi_list, evi1, evi2, false, true);
2808 :
2809 : /* Emit result row */
2810 0 : memset(values, 0, sizeof(values));
2811 0 : memset(nulls, 0, sizeof(nulls));
2812 :
2813 : /* source */
2814 0 : values[0] = CStringGetTextDatum(evi1->name);
2815 : /* target */
2816 0 : values[1] = CStringGetTextDatum(evi2->name);
2817 : /* path */
2818 0 : if (path == NIL)
2819 0 : nulls[2] = true;
2820 : else
2821 : {
2822 : StringInfoData pathbuf;
2823 : ListCell *lcv;
2824 :
2825 0 : initStringInfo(&pathbuf);
2826 : /* The path doesn't include start vertex, but show it */
2827 0 : appendStringInfoString(&pathbuf, evi1->name);
2828 0 : foreach(lcv, path)
2829 : {
2830 0 : char *versionName = (char *) lfirst(lcv);
2831 :
2832 0 : appendStringInfoString(&pathbuf, "--");
2833 0 : appendStringInfoString(&pathbuf, versionName);
2834 : }
2835 0 : values[2] = CStringGetTextDatum(pathbuf.data);
2836 0 : pfree(pathbuf.data);
2837 : }
2838 :
2839 0 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2840 : values, nulls);
2841 : }
2842 : }
2843 :
2844 0 : return (Datum) 0;
2845 : }
2846 :
2847 : /*
2848 : * pg_extension_config_dump
2849 : *
2850 : * Record information about a configuration table that belongs to an
2851 : * extension being created, but whose contents should be dumped in whole
2852 : * or in part during pg_dump.
2853 : */
2854 : Datum
2855 6 : pg_extension_config_dump(PG_FUNCTION_ARGS)
2856 : {
2857 6 : Oid tableoid = PG_GETARG_OID(0);
2858 6 : text *wherecond = PG_GETARG_TEXT_PP(1);
2859 : char *tablename;
2860 : Relation extRel;
2861 : ScanKeyData key[1];
2862 : SysScanDesc extScan;
2863 : HeapTuple extTup;
2864 : Datum arrayDatum;
2865 : Datum elementDatum;
2866 : int arrayLength;
2867 : int arrayIndex;
2868 : bool isnull;
2869 : Datum repl_val[Natts_pg_extension];
2870 : bool repl_null[Natts_pg_extension];
2871 : bool repl_repl[Natts_pg_extension];
2872 : ArrayType *a;
2873 :
2874 : /*
2875 : * We only allow this to be called from an extension's SQL script. We
2876 : * shouldn't need any permissions check beyond that.
2877 : */
2878 6 : if (!creating_extension)
2879 0 : ereport(ERROR,
2880 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2881 : errmsg("%s can only be called from an SQL script executed by CREATE EXTENSION",
2882 : "pg_extension_config_dump()")));
2883 :
2884 : /*
2885 : * Check that the table exists and is a member of the extension being
2886 : * created. This ensures that we don't need to register an additional
2887 : * dependency to protect the extconfig entry.
2888 : */
2889 6 : tablename = get_rel_name(tableoid);
2890 6 : if (tablename == NULL)
2891 0 : ereport(ERROR,
2892 : (errcode(ERRCODE_UNDEFINED_TABLE),
2893 : errmsg("OID %u does not refer to a table", tableoid)));
2894 6 : if (getExtensionOfObject(RelationRelationId, tableoid) !=
2895 : CurrentExtensionObject)
2896 0 : ereport(ERROR,
2897 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2898 : errmsg("table \"%s\" is not a member of the extension being created",
2899 : tablename)));
2900 :
2901 : /*
2902 : * Add the table OID and WHERE condition to the extension's extconfig and
2903 : * extcondition arrays.
2904 : *
2905 : * If the table is already in extconfig, treat this as an update of the
2906 : * WHERE condition.
2907 : */
2908 :
2909 : /* Find the pg_extension tuple */
2910 6 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
2911 :
2912 6 : ScanKeyInit(&key[0],
2913 : Anum_pg_extension_oid,
2914 : BTEqualStrategyNumber, F_OIDEQ,
2915 : ObjectIdGetDatum(CurrentExtensionObject));
2916 :
2917 6 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
2918 : NULL, 1, key);
2919 :
2920 6 : extTup = systable_getnext(extScan);
2921 :
2922 6 : if (!HeapTupleIsValid(extTup)) /* should not happen */
2923 0 : elog(ERROR, "could not find tuple for extension %u",
2924 : CurrentExtensionObject);
2925 :
2926 6 : memset(repl_val, 0, sizeof(repl_val));
2927 6 : memset(repl_null, false, sizeof(repl_null));
2928 6 : memset(repl_repl, false, sizeof(repl_repl));
2929 :
2930 : /* Build or modify the extconfig value */
2931 6 : elementDatum = ObjectIdGetDatum(tableoid);
2932 :
2933 6 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
2934 : RelationGetDescr(extRel), &isnull);
2935 6 : if (isnull)
2936 : {
2937 : /* Previously empty extconfig, so build 1-element array */
2938 3 : arrayLength = 0;
2939 3 : arrayIndex = 1;
2940 :
2941 3 : a = construct_array_builtin(&elementDatum, 1, OIDOID);
2942 : }
2943 : else
2944 : {
2945 : /* Modify or extend existing extconfig array */
2946 : Oid *arrayData;
2947 : int i;
2948 :
2949 3 : a = DatumGetArrayTypeP(arrayDatum);
2950 :
2951 3 : arrayLength = ARR_DIMS(a)[0];
2952 3 : if (ARR_NDIM(a) != 1 ||
2953 3 : ARR_LBOUND(a)[0] != 1 ||
2954 3 : arrayLength < 0 ||
2955 3 : ARR_HASNULL(a) ||
2956 3 : ARR_ELEMTYPE(a) != OIDOID)
2957 0 : elog(ERROR, "extconfig is not a 1-D Oid array");
2958 3 : arrayData = (Oid *) ARR_DATA_PTR(a);
2959 :
2960 3 : arrayIndex = arrayLength + 1; /* set up to add after end */
2961 :
2962 6 : for (i = 0; i < arrayLength; i++)
2963 : {
2964 3 : if (arrayData[i] == tableoid)
2965 : {
2966 0 : arrayIndex = i + 1; /* replace this element instead */
2967 0 : break;
2968 : }
2969 : }
2970 :
2971 3 : a = array_set(a, 1, &arrayIndex,
2972 : elementDatum,
2973 : false,
2974 : -1 /* varlena array */ ,
2975 : sizeof(Oid) /* OID's typlen */ ,
2976 : true /* OID's typbyval */ ,
2977 : TYPALIGN_INT /* OID's typalign */ );
2978 : }
2979 6 : repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
2980 6 : repl_repl[Anum_pg_extension_extconfig - 1] = true;
2981 :
2982 : /* Build or modify the extcondition value */
2983 6 : elementDatum = PointerGetDatum(wherecond);
2984 :
2985 6 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
2986 : RelationGetDescr(extRel), &isnull);
2987 6 : if (isnull)
2988 : {
2989 3 : if (arrayLength != 0)
2990 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
2991 :
2992 3 : a = construct_array_builtin(&elementDatum, 1, TEXTOID);
2993 : }
2994 : else
2995 : {
2996 3 : a = DatumGetArrayTypeP(arrayDatum);
2997 :
2998 3 : if (ARR_NDIM(a) != 1 ||
2999 3 : ARR_LBOUND(a)[0] != 1 ||
3000 3 : ARR_HASNULL(a) ||
3001 3 : ARR_ELEMTYPE(a) != TEXTOID)
3002 0 : elog(ERROR, "extcondition is not a 1-D text array");
3003 3 : if (ARR_DIMS(a)[0] != arrayLength)
3004 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
3005 :
3006 : /* Add or replace at same index as in extconfig */
3007 3 : a = array_set(a, 1, &arrayIndex,
3008 : elementDatum,
3009 : false,
3010 : -1 /* varlena array */ ,
3011 : -1 /* TEXT's typlen */ ,
3012 : false /* TEXT's typbyval */ ,
3013 : TYPALIGN_INT /* TEXT's typalign */ );
3014 : }
3015 6 : repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
3016 6 : repl_repl[Anum_pg_extension_extcondition - 1] = true;
3017 :
3018 6 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3019 : repl_val, repl_null, repl_repl);
3020 :
3021 6 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3022 :
3023 6 : systable_endscan(extScan);
3024 :
3025 6 : table_close(extRel, RowExclusiveLock);
3026 :
3027 6 : PG_RETURN_VOID();
3028 : }
3029 :
3030 : /*
3031 : * pg_get_loaded_modules
3032 : *
3033 : * SQL-callable function to get per-loaded-module information. Modules
3034 : * (shared libraries) aren't necessarily one-to-one with extensions, but
3035 : * they're sufficiently closely related to make this file a good home.
3036 : */
3037 : Datum
3038 0 : pg_get_loaded_modules(PG_FUNCTION_ARGS)
3039 : {
3040 0 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
3041 : DynamicFileList *file_scanner;
3042 :
3043 : /* Build tuplestore to hold the result rows */
3044 0 : InitMaterializedSRF(fcinfo, 0);
3045 :
3046 0 : for (file_scanner = get_first_loaded_module(); file_scanner != NULL;
3047 0 : file_scanner = get_next_loaded_module(file_scanner))
3048 : {
3049 : const char *library_path,
3050 : *module_name,
3051 : *module_version;
3052 : const char *sep;
3053 0 : Datum values[3] = {0};
3054 0 : bool nulls[3] = {0};
3055 :
3056 0 : get_loaded_module_details(file_scanner,
3057 : &library_path,
3058 : &module_name,
3059 : &module_version);
3060 :
3061 0 : if (module_name == NULL)
3062 0 : nulls[0] = true;
3063 : else
3064 0 : values[0] = CStringGetTextDatum(module_name);
3065 0 : if (module_version == NULL)
3066 0 : nulls[1] = true;
3067 : else
3068 0 : values[1] = CStringGetTextDatum(module_version);
3069 :
3070 : /* For security reasons, we don't show the directory path */
3071 0 : sep = last_dir_separator(library_path);
3072 0 : if (sep)
3073 0 : library_path = sep + 1;
3074 0 : values[2] = CStringGetTextDatum(library_path);
3075 :
3076 0 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
3077 : values, nulls);
3078 : }
3079 :
3080 0 : return (Datum) 0;
3081 : }
3082 :
3083 : /*
3084 : * extension_config_remove
3085 : *
3086 : * Remove the specified table OID from extension's extconfig, if present.
3087 : * This is not currently exposed as a function, but it could be;
3088 : * for now, we just invoke it from ALTER EXTENSION DROP.
3089 : */
3090 : static void
3091 35 : extension_config_remove(Oid extensionoid, Oid tableoid)
3092 : {
3093 : Relation extRel;
3094 : ScanKeyData key[1];
3095 : SysScanDesc extScan;
3096 : HeapTuple extTup;
3097 : Datum arrayDatum;
3098 : int arrayLength;
3099 : int arrayIndex;
3100 : bool isnull;
3101 : Datum repl_val[Natts_pg_extension];
3102 : bool repl_null[Natts_pg_extension];
3103 : bool repl_repl[Natts_pg_extension];
3104 : ArrayType *a;
3105 :
3106 : /* Find the pg_extension tuple */
3107 35 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3108 :
3109 35 : ScanKeyInit(&key[0],
3110 : Anum_pg_extension_oid,
3111 : BTEqualStrategyNumber, F_OIDEQ,
3112 : ObjectIdGetDatum(extensionoid));
3113 :
3114 35 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3115 : NULL, 1, key);
3116 :
3117 35 : extTup = systable_getnext(extScan);
3118 :
3119 35 : if (!HeapTupleIsValid(extTup)) /* should not happen */
3120 0 : elog(ERROR, "could not find tuple for extension %u",
3121 : extensionoid);
3122 :
3123 : /* Search extconfig for the tableoid */
3124 35 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
3125 : RelationGetDescr(extRel), &isnull);
3126 35 : if (isnull)
3127 : {
3128 : /* nothing to do */
3129 31 : a = NULL;
3130 31 : arrayLength = 0;
3131 31 : arrayIndex = -1;
3132 : }
3133 : else
3134 : {
3135 : Oid *arrayData;
3136 : int i;
3137 :
3138 4 : a = DatumGetArrayTypeP(arrayDatum);
3139 :
3140 4 : arrayLength = ARR_DIMS(a)[0];
3141 4 : if (ARR_NDIM(a) != 1 ||
3142 4 : ARR_LBOUND(a)[0] != 1 ||
3143 4 : arrayLength < 0 ||
3144 4 : ARR_HASNULL(a) ||
3145 4 : ARR_ELEMTYPE(a) != OIDOID)
3146 0 : elog(ERROR, "extconfig is not a 1-D Oid array");
3147 4 : arrayData = (Oid *) ARR_DATA_PTR(a);
3148 :
3149 4 : arrayIndex = -1; /* flag for no deletion needed */
3150 :
3151 12 : for (i = 0; i < arrayLength; i++)
3152 : {
3153 8 : if (arrayData[i] == tableoid)
3154 : {
3155 0 : arrayIndex = i; /* index to remove */
3156 0 : break;
3157 : }
3158 : }
3159 : }
3160 :
3161 : /* If tableoid is not in extconfig, nothing to do */
3162 35 : if (arrayIndex < 0)
3163 : {
3164 35 : systable_endscan(extScan);
3165 35 : table_close(extRel, RowExclusiveLock);
3166 35 : return;
3167 : }
3168 :
3169 : /* Modify or delete the extconfig value */
3170 0 : memset(repl_val, 0, sizeof(repl_val));
3171 0 : memset(repl_null, false, sizeof(repl_null));
3172 0 : memset(repl_repl, false, sizeof(repl_repl));
3173 :
3174 0 : if (arrayLength <= 1)
3175 : {
3176 : /* removing only element, just set array to null */
3177 0 : repl_null[Anum_pg_extension_extconfig - 1] = true;
3178 : }
3179 : else
3180 : {
3181 : /* squeeze out the target element */
3182 : Datum *dvalues;
3183 : int nelems;
3184 : int i;
3185 :
3186 : /* We already checked there are no nulls */
3187 0 : deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
3188 :
3189 0 : for (i = arrayIndex; i < arrayLength - 1; i++)
3190 0 : dvalues[i] = dvalues[i + 1];
3191 :
3192 0 : a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
3193 :
3194 0 : repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
3195 : }
3196 0 : repl_repl[Anum_pg_extension_extconfig - 1] = true;
3197 :
3198 : /* Modify or delete the extcondition value */
3199 0 : arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
3200 : RelationGetDescr(extRel), &isnull);
3201 0 : if (isnull)
3202 : {
3203 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
3204 : }
3205 : else
3206 : {
3207 0 : a = DatumGetArrayTypeP(arrayDatum);
3208 :
3209 0 : if (ARR_NDIM(a) != 1 ||
3210 0 : ARR_LBOUND(a)[0] != 1 ||
3211 0 : ARR_HASNULL(a) ||
3212 0 : ARR_ELEMTYPE(a) != TEXTOID)
3213 0 : elog(ERROR, "extcondition is not a 1-D text array");
3214 0 : if (ARR_DIMS(a)[0] != arrayLength)
3215 0 : elog(ERROR, "extconfig and extcondition arrays do not match");
3216 : }
3217 :
3218 0 : if (arrayLength <= 1)
3219 : {
3220 : /* removing only element, just set array to null */
3221 0 : repl_null[Anum_pg_extension_extcondition - 1] = true;
3222 : }
3223 : else
3224 : {
3225 : /* squeeze out the target element */
3226 : Datum *dvalues;
3227 : int nelems;
3228 : int i;
3229 :
3230 : /* We already checked there are no nulls */
3231 0 : deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
3232 :
3233 0 : for (i = arrayIndex; i < arrayLength - 1; i++)
3234 0 : dvalues[i] = dvalues[i + 1];
3235 :
3236 0 : a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
3237 :
3238 0 : repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
3239 : }
3240 0 : repl_repl[Anum_pg_extension_extcondition - 1] = true;
3241 :
3242 0 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3243 : repl_val, repl_null, repl_repl);
3244 :
3245 0 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3246 :
3247 0 : systable_endscan(extScan);
3248 :
3249 0 : table_close(extRel, RowExclusiveLock);
3250 : }
3251 :
3252 : /*
3253 : * Execute ALTER EXTENSION SET SCHEMA
3254 : */
3255 : ObjectAddress
3256 6 : AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *oldschema)
3257 : {
3258 : Oid extensionOid;
3259 : Oid nspOid;
3260 : Oid oldNspOid;
3261 : AclResult aclresult;
3262 : Relation extRel;
3263 : ScanKeyData key[2];
3264 : SysScanDesc extScan;
3265 : HeapTuple extTup;
3266 : Form_pg_extension extForm;
3267 : Relation depRel;
3268 : SysScanDesc depScan;
3269 : HeapTuple depTup;
3270 : ObjectAddresses *objsMoved;
3271 : ObjectAddress extAddr;
3272 :
3273 6 : extensionOid = get_extension_oid(extensionName, false);
3274 :
3275 6 : nspOid = LookupCreationNamespace(newschema);
3276 :
3277 : /*
3278 : * Permission check: must own extension. Note that we don't bother to
3279 : * check ownership of the individual member objects ...
3280 : */
3281 6 : if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3282 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
3283 : extensionName);
3284 :
3285 : /* Permission check: must have creation rights in target namespace */
3286 6 : aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
3287 6 : if (aclresult != ACLCHECK_OK)
3288 0 : aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
3289 :
3290 : /*
3291 : * If the schema is currently a member of the extension, disallow moving
3292 : * the extension into the schema. That would create a dependency loop.
3293 : */
3294 6 : if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
3295 0 : ereport(ERROR,
3296 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3297 : errmsg("cannot move extension \"%s\" into schema \"%s\" "
3298 : "because the extension contains the schema",
3299 : extensionName, newschema)));
3300 :
3301 : /* Locate the pg_extension tuple */
3302 6 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3303 :
3304 6 : ScanKeyInit(&key[0],
3305 : Anum_pg_extension_oid,
3306 : BTEqualStrategyNumber, F_OIDEQ,
3307 : ObjectIdGetDatum(extensionOid));
3308 :
3309 6 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3310 : NULL, 1, key);
3311 :
3312 6 : extTup = systable_getnext(extScan);
3313 :
3314 6 : if (!HeapTupleIsValid(extTup)) /* should not happen */
3315 0 : elog(ERROR, "could not find tuple for extension %u",
3316 : extensionOid);
3317 :
3318 : /* Copy tuple so we can modify it below */
3319 6 : extTup = heap_copytuple(extTup);
3320 6 : extForm = (Form_pg_extension) GETSTRUCT(extTup);
3321 :
3322 6 : systable_endscan(extScan);
3323 :
3324 : /*
3325 : * If the extension is already in the target schema, just silently do
3326 : * nothing.
3327 : */
3328 6 : if (extForm->extnamespace == nspOid)
3329 : {
3330 0 : table_close(extRel, RowExclusiveLock);
3331 0 : return InvalidObjectAddress;
3332 : }
3333 :
3334 : /* Check extension is supposed to be relocatable */
3335 6 : if (!extForm->extrelocatable)
3336 0 : ereport(ERROR,
3337 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3338 : errmsg("extension \"%s\" does not support SET SCHEMA",
3339 : NameStr(extForm->extname))));
3340 :
3341 6 : objsMoved = new_object_addresses();
3342 :
3343 : /* store the OID of the namespace to-be-changed */
3344 6 : oldNspOid = extForm->extnamespace;
3345 :
3346 : /*
3347 : * Scan pg_depend to find objects that depend directly on the extension,
3348 : * and alter each one's schema.
3349 : */
3350 6 : depRel = table_open(DependRelationId, AccessShareLock);
3351 :
3352 6 : ScanKeyInit(&key[0],
3353 : Anum_pg_depend_refclassid,
3354 : BTEqualStrategyNumber, F_OIDEQ,
3355 : ObjectIdGetDatum(ExtensionRelationId));
3356 6 : ScanKeyInit(&key[1],
3357 : Anum_pg_depend_refobjid,
3358 : BTEqualStrategyNumber, F_OIDEQ,
3359 : ObjectIdGetDatum(extensionOid));
3360 :
3361 6 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
3362 : NULL, 2, key);
3363 :
3364 29 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
3365 : {
3366 25 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
3367 : ObjectAddress dep;
3368 : Oid dep_oldNspOid;
3369 :
3370 : /*
3371 : * If a dependent extension has a no_relocate request for this
3372 : * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
3373 : * the same loop that's actually executing the renames: we may detect
3374 : * the error condition only after having expended a fair amount of
3375 : * work. However, the alternative is to do two scans of pg_depend,
3376 : * which seems like optimizing for failure cases. The rename work
3377 : * will all roll back cleanly enough if we do fail here.)
3378 : */
3379 25 : if (pg_depend->deptype == DEPENDENCY_NORMAL &&
3380 4 : pg_depend->classid == ExtensionRelationId)
3381 : {
3382 4 : char *depextname = get_extension_name(pg_depend->objid);
3383 : ExtensionControlFile *dcontrol;
3384 : ListCell *lc;
3385 :
3386 4 : dcontrol = read_extension_control_file(depextname);
3387 5 : foreach(lc, dcontrol->no_relocate)
3388 : {
3389 2 : char *nrextname = (char *) lfirst(lc);
3390 :
3391 2 : if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
3392 : {
3393 1 : ereport(ERROR,
3394 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3395 : errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
3396 : NameStr(extForm->extname)),
3397 : errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
3398 : depextname,
3399 : NameStr(extForm->extname))));
3400 : }
3401 : }
3402 : }
3403 :
3404 : /*
3405 : * Otherwise, ignore non-membership dependencies. (Currently, the
3406 : * only other case we could see here is a normal dependency from
3407 : * another extension.)
3408 : */
3409 24 : if (pg_depend->deptype != DEPENDENCY_EXTENSION)
3410 3 : continue;
3411 :
3412 21 : dep.classId = pg_depend->classid;
3413 21 : dep.objectId = pg_depend->objid;
3414 21 : dep.objectSubId = pg_depend->objsubid;
3415 :
3416 21 : if (dep.objectSubId != 0) /* should not happen */
3417 0 : elog(ERROR, "extension should not have a sub-object dependency");
3418 :
3419 : /* Relocate the object */
3420 21 : dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
3421 : dep.objectId,
3422 : nspOid,
3423 : objsMoved);
3424 :
3425 : /*
3426 : * If not all the objects had the same old namespace (ignoring any
3427 : * that are not in namespaces or are dependent types), complain.
3428 : */
3429 21 : if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
3430 1 : ereport(ERROR,
3431 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3432 : errmsg("extension \"%s\" does not support SET SCHEMA",
3433 : NameStr(extForm->extname)),
3434 : errdetail("%s is not in the extension's schema \"%s\"",
3435 : getObjectDescription(&dep, false),
3436 : get_namespace_name(oldNspOid))));
3437 : }
3438 :
3439 : /* report old schema, if caller wants it */
3440 4 : if (oldschema)
3441 4 : *oldschema = oldNspOid;
3442 :
3443 4 : systable_endscan(depScan);
3444 :
3445 4 : relation_close(depRel, AccessShareLock);
3446 :
3447 : /* Now adjust pg_extension.extnamespace */
3448 4 : extForm->extnamespace = nspOid;
3449 :
3450 4 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3451 :
3452 4 : table_close(extRel, RowExclusiveLock);
3453 :
3454 : /* update dependency to point to the new schema */
3455 4 : if (changeDependencyFor(ExtensionRelationId, extensionOid,
3456 : NamespaceRelationId, oldNspOid, nspOid) != 1)
3457 0 : elog(ERROR, "could not change schema dependency for extension %s",
3458 : NameStr(extForm->extname));
3459 :
3460 4 : InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3461 :
3462 4 : ObjectAddressSet(extAddr, ExtensionRelationId, extensionOid);
3463 :
3464 4 : return extAddr;
3465 : }
3466 :
3467 : /*
3468 : * Execute ALTER EXTENSION UPDATE
3469 : */
3470 : ObjectAddress
3471 19 : ExecAlterExtensionStmt(ParseState *pstate, AlterExtensionStmt *stmt)
3472 : {
3473 19 : DefElem *d_new_version = NULL;
3474 : char *versionName;
3475 : char *oldVersionName;
3476 : ExtensionControlFile *control;
3477 : Oid extensionOid;
3478 : Relation extRel;
3479 : ScanKeyData key[1];
3480 : SysScanDesc extScan;
3481 : HeapTuple extTup;
3482 : List *updateVersions;
3483 : Datum datum;
3484 : bool isnull;
3485 : ListCell *lc;
3486 : ObjectAddress address;
3487 :
3488 : /*
3489 : * We use global variables to track the extension being created, so we can
3490 : * create/update only one extension at the same time.
3491 : */
3492 19 : if (creating_extension)
3493 0 : ereport(ERROR,
3494 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3495 : errmsg("nested ALTER EXTENSION is not supported")));
3496 :
3497 : /*
3498 : * Look up the extension --- it must already exist in pg_extension
3499 : */
3500 19 : extRel = table_open(ExtensionRelationId, AccessShareLock);
3501 :
3502 19 : ScanKeyInit(&key[0],
3503 : Anum_pg_extension_extname,
3504 : BTEqualStrategyNumber, F_NAMEEQ,
3505 19 : CStringGetDatum(stmt->extname));
3506 :
3507 19 : extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
3508 : NULL, 1, key);
3509 :
3510 19 : extTup = systable_getnext(extScan);
3511 :
3512 19 : if (!HeapTupleIsValid(extTup))
3513 0 : ereport(ERROR,
3514 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3515 : errmsg("extension \"%s\" does not exist",
3516 : stmt->extname)));
3517 :
3518 19 : extensionOid = ((Form_pg_extension) GETSTRUCT(extTup))->oid;
3519 :
3520 : /*
3521 : * Determine the existing version we are updating from
3522 : */
3523 19 : datum = heap_getattr(extTup, Anum_pg_extension_extversion,
3524 : RelationGetDescr(extRel), &isnull);
3525 19 : if (isnull)
3526 0 : elog(ERROR, "extversion is null");
3527 19 : oldVersionName = text_to_cstring(DatumGetTextPP(datum));
3528 :
3529 19 : systable_endscan(extScan);
3530 :
3531 19 : table_close(extRel, AccessShareLock);
3532 :
3533 : /* Permission check: must own extension */
3534 19 : if (!object_ownercheck(ExtensionRelationId, extensionOid, GetUserId()))
3535 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
3536 0 : stmt->extname);
3537 :
3538 : /*
3539 : * Read the primary control file. Note we assume that it does not contain
3540 : * any non-ASCII data, so there is no need to worry about encoding at this
3541 : * point.
3542 : */
3543 19 : control = read_extension_control_file(stmt->extname);
3544 :
3545 : /*
3546 : * Read the statement option list
3547 : */
3548 38 : foreach(lc, stmt->options)
3549 : {
3550 19 : DefElem *defel = (DefElem *) lfirst(lc);
3551 :
3552 19 : if (strcmp(defel->defname, "new_version") == 0)
3553 : {
3554 19 : if (d_new_version)
3555 0 : errorConflictingDefElem(defel, pstate);
3556 19 : d_new_version = defel;
3557 : }
3558 : else
3559 0 : elog(ERROR, "unrecognized option: %s", defel->defname);
3560 : }
3561 :
3562 : /*
3563 : * Determine the version to update to
3564 : */
3565 19 : if (d_new_version && d_new_version->arg)
3566 19 : versionName = strVal(d_new_version->arg);
3567 0 : else if (control->default_version)
3568 0 : versionName = control->default_version;
3569 : else
3570 : {
3571 0 : ereport(ERROR,
3572 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3573 : errmsg("version to install must be specified")));
3574 : versionName = NULL; /* keep compiler quiet */
3575 : }
3576 19 : check_valid_version_name(versionName);
3577 :
3578 : /*
3579 : * If we're already at that version, just say so
3580 : */
3581 19 : if (strcmp(oldVersionName, versionName) == 0)
3582 : {
3583 0 : ereport(NOTICE,
3584 : (errmsg("version \"%s\" of extension \"%s\" is already installed",
3585 : versionName, stmt->extname)));
3586 0 : return InvalidObjectAddress;
3587 : }
3588 :
3589 : /*
3590 : * Identify the series of update script files we need to execute
3591 : */
3592 19 : updateVersions = identify_update_path(control,
3593 : oldVersionName,
3594 : versionName);
3595 :
3596 : /*
3597 : * Update the pg_extension row and execute the update scripts, one at a
3598 : * time
3599 : */
3600 19 : ApplyExtensionUpdates(extensionOid, control,
3601 : oldVersionName, updateVersions,
3602 : NULL, false, false);
3603 :
3604 17 : ObjectAddressSet(address, ExtensionRelationId, extensionOid);
3605 :
3606 17 : return address;
3607 : }
3608 :
3609 : /*
3610 : * Apply a series of update scripts as though individual ALTER EXTENSION
3611 : * UPDATE commands had been given, including altering the pg_extension row
3612 : * and dependencies each time.
3613 : *
3614 : * This might be more work than necessary, but it ensures that old update
3615 : * scripts don't break if newer versions have different control parameters.
3616 : */
3617 : static void
3618 325 : ApplyExtensionUpdates(Oid extensionOid,
3619 : ExtensionControlFile *pcontrol,
3620 : const char *initialVersion,
3621 : List *updateVersions,
3622 : char *origSchemaName,
3623 : bool cascade,
3624 : bool is_create)
3625 : {
3626 325 : const char *oldVersionName = initialVersion;
3627 : ListCell *lcv;
3628 :
3629 611 : foreach(lcv, updateVersions)
3630 : {
3631 288 : char *versionName = (char *) lfirst(lcv);
3632 : ExtensionControlFile *control;
3633 : char *schemaName;
3634 : Oid schemaOid;
3635 : List *requiredExtensions;
3636 : List *requiredSchemas;
3637 : Relation extRel;
3638 : ScanKeyData key[1];
3639 : SysScanDesc extScan;
3640 : HeapTuple extTup;
3641 : Form_pg_extension extForm;
3642 : Datum values[Natts_pg_extension];
3643 : bool nulls[Natts_pg_extension];
3644 : bool repl[Natts_pg_extension];
3645 : ObjectAddress myself;
3646 : ListCell *lc;
3647 :
3648 : /*
3649 : * Fetch parameters for specific version (pcontrol is not changed)
3650 : */
3651 288 : control = read_extension_aux_control_file(pcontrol, versionName);
3652 :
3653 : /* Find the pg_extension tuple */
3654 288 : extRel = table_open(ExtensionRelationId, RowExclusiveLock);
3655 :
3656 288 : ScanKeyInit(&key[0],
3657 : Anum_pg_extension_oid,
3658 : BTEqualStrategyNumber, F_OIDEQ,
3659 : ObjectIdGetDatum(extensionOid));
3660 :
3661 288 : extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
3662 : NULL, 1, key);
3663 :
3664 288 : extTup = systable_getnext(extScan);
3665 :
3666 288 : if (!HeapTupleIsValid(extTup)) /* should not happen */
3667 0 : elog(ERROR, "could not find tuple for extension %u",
3668 : extensionOid);
3669 :
3670 288 : extForm = (Form_pg_extension) GETSTRUCT(extTup);
3671 :
3672 : /*
3673 : * Determine the target schema (set by original install)
3674 : */
3675 288 : schemaOid = extForm->extnamespace;
3676 288 : schemaName = get_namespace_name(schemaOid);
3677 :
3678 : /*
3679 : * Modify extrelocatable and extversion in the pg_extension tuple
3680 : */
3681 288 : memset(values, 0, sizeof(values));
3682 288 : memset(nulls, 0, sizeof(nulls));
3683 288 : memset(repl, 0, sizeof(repl));
3684 :
3685 288 : values[Anum_pg_extension_extrelocatable - 1] =
3686 288 : BoolGetDatum(control->relocatable);
3687 288 : repl[Anum_pg_extension_extrelocatable - 1] = true;
3688 288 : values[Anum_pg_extension_extversion - 1] =
3689 288 : CStringGetTextDatum(versionName);
3690 288 : repl[Anum_pg_extension_extversion - 1] = true;
3691 :
3692 288 : extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
3693 : values, nulls, repl);
3694 :
3695 288 : CatalogTupleUpdate(extRel, &extTup->t_self, extTup);
3696 :
3697 288 : systable_endscan(extScan);
3698 :
3699 288 : table_close(extRel, RowExclusiveLock);
3700 :
3701 : /*
3702 : * Look up the prerequisite extensions for this version, install them
3703 : * if necessary, and build lists of their OIDs and the OIDs of their
3704 : * target schemas.
3705 : */
3706 288 : requiredExtensions = NIL;
3707 288 : requiredSchemas = NIL;
3708 289 : foreach(lc, control->requires)
3709 : {
3710 1 : char *curreq = (char *) lfirst(lc);
3711 : Oid reqext;
3712 : Oid reqschema;
3713 :
3714 1 : reqext = get_required_extension(curreq,
3715 : control->name,
3716 : origSchemaName,
3717 : cascade,
3718 : NIL,
3719 : is_create);
3720 1 : reqschema = get_extension_schema(reqext);
3721 1 : requiredExtensions = lappend_oid(requiredExtensions, reqext);
3722 1 : requiredSchemas = lappend_oid(requiredSchemas, reqschema);
3723 : }
3724 :
3725 : /*
3726 : * Remove and recreate dependencies on prerequisite extensions
3727 : */
3728 288 : deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
3729 : ExtensionRelationId,
3730 : DEPENDENCY_NORMAL);
3731 :
3732 288 : myself.classId = ExtensionRelationId;
3733 288 : myself.objectId = extensionOid;
3734 288 : myself.objectSubId = 0;
3735 :
3736 289 : foreach(lc, requiredExtensions)
3737 : {
3738 1 : Oid reqext = lfirst_oid(lc);
3739 : ObjectAddress otherext;
3740 :
3741 1 : otherext.classId = ExtensionRelationId;
3742 1 : otherext.objectId = reqext;
3743 1 : otherext.objectSubId = 0;
3744 :
3745 1 : recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
3746 : }
3747 :
3748 288 : InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
3749 :
3750 : /*
3751 : * Finally, execute the update script file
3752 : */
3753 288 : execute_extension_script(extensionOid, control,
3754 : oldVersionName, versionName,
3755 : requiredSchemas,
3756 : schemaName);
3757 :
3758 : /*
3759 : * Update prior-version name and loop around. Since
3760 : * execute_sql_string did a final CommandCounterIncrement, we can
3761 : * update the pg_extension row again.
3762 : */
3763 286 : oldVersionName = versionName;
3764 : }
3765 323 : }
3766 :
3767 : /*
3768 : * Execute ALTER EXTENSION ADD/DROP
3769 : *
3770 : * Return value is the address of the altered extension.
3771 : *
3772 : * objAddr is an output argument which, if not NULL, is set to the address of
3773 : * the added/dropped object.
3774 : */
3775 : ObjectAddress
3776 140 : ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
3777 : ObjectAddress *objAddr)
3778 : {
3779 : ObjectAddress extension;
3780 : ObjectAddress object;
3781 : Relation relation;
3782 :
3783 140 : switch (stmt->objtype)
3784 : {
3785 1 : case OBJECT_DATABASE:
3786 : case OBJECT_EXTENSION:
3787 : case OBJECT_INDEX:
3788 : case OBJECT_PUBLICATION:
3789 : case OBJECT_ROLE:
3790 : case OBJECT_STATISTIC_EXT:
3791 : case OBJECT_SUBSCRIPTION:
3792 : case OBJECT_TABLESPACE:
3793 1 : ereport(ERROR,
3794 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3795 : errmsg("cannot add an object of this type to an extension")));
3796 : break;
3797 139 : default:
3798 : /* OK */
3799 139 : break;
3800 : }
3801 :
3802 : /*
3803 : * Find the extension and acquire a lock on it, to ensure it doesn't get
3804 : * dropped concurrently. A sharable lock seems sufficient: there's no
3805 : * reason not to allow other sorts of manipulations, such as add/drop of
3806 : * other objects, to occur concurrently. Concurrently adding/dropping the
3807 : * *same* object would be bad, but we prevent that by using a non-sharable
3808 : * lock on the individual object, below.
3809 : */
3810 139 : extension = get_object_address(OBJECT_EXTENSION,
3811 139 : (Node *) makeString(stmt->extname),
3812 : &relation, AccessShareLock, false);
3813 :
3814 : /* Permission check: must own extension */
3815 139 : if (!object_ownercheck(ExtensionRelationId, extension.objectId, GetUserId()))
3816 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EXTENSION,
3817 0 : stmt->extname);
3818 :
3819 : /*
3820 : * Translate the parser representation that identifies the object into an
3821 : * ObjectAddress. get_object_address() will throw an error if the object
3822 : * does not exist, and will also acquire a lock on the object to guard
3823 : * against concurrent DROP and ALTER EXTENSION ADD/DROP operations.
3824 : */
3825 139 : object = get_object_address(stmt->objtype, stmt->object,
3826 : &relation, ShareUpdateExclusiveLock, false);
3827 :
3828 : Assert(object.objectSubId == 0);
3829 139 : if (objAddr)
3830 139 : *objAddr = object;
3831 :
3832 : /* Permission check: must own target object, too */
3833 139 : check_object_ownership(GetUserId(), stmt->objtype, object,
3834 : stmt->object, relation);
3835 :
3836 : /* Do the update, recursing to any dependent objects */
3837 139 : ExecAlterExtensionContentsRecurse(stmt, extension, object);
3838 :
3839 : /* Finish up */
3840 139 : InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
3841 :
3842 : /*
3843 : * If get_object_address() opened the relation for us, we close it to keep
3844 : * the reference count correct - but we retain any locks acquired by
3845 : * get_object_address() until commit time, to guard against concurrent
3846 : * activity.
3847 : */
3848 139 : if (relation != NULL)
3849 43 : relation_close(relation, NoLock);
3850 :
3851 139 : return extension;
3852 : }
3853 :
3854 : /*
3855 : * ExecAlterExtensionContentsRecurse
3856 : * Subroutine for ExecAlterExtensionContentsStmt
3857 : *
3858 : * Do the bare alteration of object's membership in extension,
3859 : * without permission checks. Recurse to dependent objects, if any.
3860 : */
3861 : static void
3862 231 : ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
3863 : ObjectAddress extension,
3864 : ObjectAddress object)
3865 : {
3866 : Oid oldExtension;
3867 :
3868 : /*
3869 : * Check existing extension membership.
3870 : */
3871 231 : oldExtension = getExtensionOfObject(object.classId, object.objectId);
3872 :
3873 231 : if (stmt->action > 0)
3874 : {
3875 : /*
3876 : * ADD, so complain if object is already attached to some extension.
3877 : */
3878 53 : if (OidIsValid(oldExtension))
3879 0 : ereport(ERROR,
3880 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3881 : errmsg("%s is already a member of extension \"%s\"",
3882 : getObjectDescription(&object, false),
3883 : get_extension_name(oldExtension))));
3884 :
3885 : /*
3886 : * Prevent a schema from being added to an extension if the schema
3887 : * contains the extension. That would create a dependency loop.
3888 : */
3889 54 : if (object.classId == NamespaceRelationId &&
3890 1 : object.objectId == get_extension_schema(extension.objectId))
3891 0 : ereport(ERROR,
3892 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3893 : errmsg("cannot add schema \"%s\" to extension \"%s\" "
3894 : "because the schema contains the extension",
3895 : get_namespace_name(object.objectId),
3896 : stmt->extname)));
3897 :
3898 : /*
3899 : * OK, add the dependency.
3900 : */
3901 53 : recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
3902 :
3903 : /*
3904 : * Also record the initial ACL on the object, if any.
3905 : *
3906 : * Note that this will handle the object's ACLs, as well as any ACLs
3907 : * on object subIds. (In other words, when the object is a table,
3908 : * this will record the table's ACL and the ACLs for the columns on
3909 : * the table, if any).
3910 : */
3911 53 : recordExtObjInitPriv(object.objectId, object.classId);
3912 : }
3913 : else
3914 : {
3915 : /*
3916 : * DROP, so complain if it's not a member.
3917 : */
3918 178 : if (oldExtension != extension.objectId)
3919 0 : ereport(ERROR,
3920 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3921 : errmsg("%s is not a member of extension \"%s\"",
3922 : getObjectDescription(&object, false),
3923 : stmt->extname)));
3924 :
3925 : /*
3926 : * OK, drop the dependency.
3927 : */
3928 178 : if (deleteDependencyRecordsForClass(object.classId, object.objectId,
3929 : ExtensionRelationId,
3930 : DEPENDENCY_EXTENSION) != 1)
3931 0 : elog(ERROR, "unexpected number of extension dependency records");
3932 :
3933 : /*
3934 : * If it's a relation, it might have an entry in the extension's
3935 : * extconfig array, which we must remove.
3936 : */
3937 178 : if (object.classId == RelationRelationId)
3938 35 : extension_config_remove(extension.objectId, object.objectId);
3939 :
3940 : /*
3941 : * Remove all the initial ACLs, if any.
3942 : *
3943 : * Note that this will remove the object's ACLs, as well as any ACLs
3944 : * on object subIds. (In other words, when the object is a table,
3945 : * this will remove the table's ACL and the ACLs for the columns on
3946 : * the table, if any).
3947 : */
3948 178 : removeExtObjInitPriv(object.objectId, object.classId);
3949 : }
3950 :
3951 : /*
3952 : * Recurse to any dependent objects; currently, this includes the array
3953 : * type of a base type, the multirange type associated with a range type,
3954 : * and the rowtype of a table.
3955 : */
3956 231 : if (object.classId == TypeRelationId)
3957 : {
3958 : ObjectAddress depobject;
3959 :
3960 96 : depobject.classId = TypeRelationId;
3961 96 : depobject.objectSubId = 0;
3962 :
3963 : /* If it has an array type, update that too */
3964 96 : depobject.objectId = get_array_type(object.objectId);
3965 96 : if (OidIsValid(depobject.objectId))
3966 48 : ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3967 :
3968 : /* If it is a range type, update the associated multirange too */
3969 96 : if (type_is_range(object.objectId))
3970 : {
3971 2 : depobject.objectId = get_range_multirange(object.objectId);
3972 2 : if (!OidIsValid(depobject.objectId))
3973 0 : ereport(ERROR,
3974 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3975 : errmsg("could not find multirange type for data type %s",
3976 : format_type_be(object.objectId))));
3977 2 : ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3978 : }
3979 : }
3980 231 : if (object.classId == RelationRelationId)
3981 : {
3982 : ObjectAddress depobject;
3983 :
3984 43 : depobject.classId = TypeRelationId;
3985 43 : depobject.objectSubId = 0;
3986 :
3987 : /* It might not have a rowtype, but if it does, update that */
3988 43 : depobject.objectId = get_rel_type_id(object.objectId);
3989 43 : if (OidIsValid(depobject.objectId))
3990 42 : ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
3991 : }
3992 231 : }
3993 :
3994 : /*
3995 : * Read the whole of file into memory.
3996 : *
3997 : * The file contents are returned as a single palloc'd chunk. For convenience
3998 : * of the callers, an extra \0 byte is added to the end. That is not counted
3999 : * in the length returned into *length.
4000 : */
4001 : static char *
4002 607 : read_whole_file(const char *filename, int *length)
4003 : {
4004 : char *buf;
4005 : FILE *file;
4006 : size_t bytes_to_read;
4007 : struct stat fst;
4008 :
4009 607 : if (stat(filename, &fst) < 0)
4010 0 : ereport(ERROR,
4011 : (errcode_for_file_access(),
4012 : errmsg("could not stat file \"%s\": %m", filename)));
4013 :
4014 607 : if (fst.st_size > (MaxAllocSize - 1))
4015 0 : ereport(ERROR,
4016 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4017 : errmsg("file \"%s\" is too large", filename)));
4018 607 : bytes_to_read = (size_t) fst.st_size;
4019 :
4020 607 : if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
4021 0 : ereport(ERROR,
4022 : (errcode_for_file_access(),
4023 : errmsg("could not open file \"%s\" for reading: %m",
4024 : filename)));
4025 :
4026 607 : buf = (char *) palloc(bytes_to_read + 1);
4027 :
4028 607 : bytes_to_read = fread(buf, 1, bytes_to_read, file);
4029 :
4030 607 : if (ferror(file))
4031 0 : ereport(ERROR,
4032 : (errcode_for_file_access(),
4033 : errmsg("could not read file \"%s\": %m", filename)));
4034 :
4035 607 : FreeFile(file);
4036 :
4037 607 : buf[bytes_to_read] = '\0';
4038 :
4039 : /*
4040 : * On Windows, manually convert Windows-style newlines (\r\n) to the Unix
4041 : * convention of \n only. This avoids gotchas due to script files
4042 : * possibly getting converted when being transferred between platforms.
4043 : * Ideally we'd do this by using text mode to read the file, but that also
4044 : * causes control-Z to be treated as end-of-file. Historically we've
4045 : * allowed control-Z in script files, so breaking that seems unwise.
4046 : */
4047 : #ifdef WIN32
4048 : {
4049 : char *s,
4050 : *d;
4051 :
4052 : for (s = d = buf; *s; s++)
4053 : {
4054 : if (!(*s == '\r' && s[1] == '\n'))
4055 : *d++ = *s;
4056 : }
4057 : *d = '\0';
4058 : bytes_to_read = d - buf;
4059 : }
4060 : #endif
4061 :
4062 607 : *length = bytes_to_read;
4063 607 : return buf;
4064 : }
4065 :
4066 : static ExtensionControlFile *
4067 9069 : new_ExtensionControlFile(const char *extname)
4068 : {
4069 : /*
4070 : * Set up default values. Pointer fields are initially null.
4071 : */
4072 9069 : ExtensionControlFile *control = palloc0_object(ExtensionControlFile);
4073 :
4074 9069 : control->name = pstrdup(extname);
4075 9069 : control->relocatable = false;
4076 9069 : control->superuser = true;
4077 9069 : control->trusted = false;
4078 9069 : control->encoding = -1;
4079 :
4080 9069 : return control;
4081 : }
4082 :
4083 : /*
4084 : * Search for the basename in the list of paths.
4085 : *
4086 : * Similar to find_in_path but for simplicity does not support custom error
4087 : * messages and expects that paths already have all macros replaced.
4088 : */
4089 : char *
4090 353 : find_in_paths(const char *basename, List *paths)
4091 : {
4092 : ListCell *cell;
4093 :
4094 356 : foreach(cell, paths)
4095 : {
4096 356 : ExtensionLocation *location = lfirst(cell);
4097 356 : char *path = location->loc;
4098 : char *full;
4099 :
4100 : Assert(path != NULL);
4101 :
4102 356 : path = pstrdup(path);
4103 356 : canonicalize_path(path);
4104 :
4105 : /* only absolute paths */
4106 356 : if (!is_absolute_path(path))
4107 0 : ereport(ERROR,
4108 : errcode(ERRCODE_INVALID_NAME),
4109 : errmsg("component in parameter \"%s\" is not an absolute path", "extension_control_path"));
4110 :
4111 356 : full = psprintf("%s/%s", path, basename);
4112 :
4113 356 : if (pg_file_exists(full))
4114 353 : return full;
4115 :
4116 3 : pfree(path);
4117 3 : pfree(full);
4118 : }
4119 :
4120 0 : return NULL;
4121 : }
|