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