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