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