00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "postgres.h"
00025
00026 #include <dirent.h>
00027 #include <limits.h>
00028 #include <unistd.h>
00029
00030 #include "access/htup_details.h"
00031 #include "access/sysattr.h"
00032 #include "access/xact.h"
00033 #include "catalog/dependency.h"
00034 #include "catalog/indexing.h"
00035 #include "catalog/namespace.h"
00036 #include "catalog/objectaccess.h"
00037 #include "catalog/pg_collation.h"
00038 #include "catalog/pg_depend.h"
00039 #include "catalog/pg_extension.h"
00040 #include "catalog/pg_namespace.h"
00041 #include "catalog/pg_type.h"
00042 #include "commands/alter.h"
00043 #include "commands/comment.h"
00044 #include "commands/extension.h"
00045 #include "commands/schemacmds.h"
00046 #include "funcapi.h"
00047 #include "mb/pg_wchar.h"
00048 #include "miscadmin.h"
00049 #include "storage/fd.h"
00050 #include "tcop/utility.h"
00051 #include "utils/builtins.h"
00052 #include "utils/fmgroids.h"
00053 #include "utils/lsyscache.h"
00054 #include "utils/rel.h"
00055 #include "utils/snapmgr.h"
00056 #include "utils/tqual.h"
00057
00058
00059
00060 bool creating_extension = false;
00061 Oid CurrentExtensionObject = InvalidOid;
00062
00063
00064
00065
00066 typedef struct ExtensionControlFile
00067 {
00068 char *name;
00069 char *directory;
00070 char *default_version;
00071 char *module_pathname;
00072 char *comment;
00073 char *schema;
00074 bool relocatable;
00075 bool superuser;
00076 int encoding;
00077 List *requires;
00078 } ExtensionControlFile;
00079
00080
00081
00082
00083 typedef struct ExtensionVersionInfo
00084 {
00085 char *name;
00086 List *reachable;
00087 bool installable;
00088
00089 bool distance_known;
00090 int distance;
00091 struct ExtensionVersionInfo *previous;
00092 } ExtensionVersionInfo;
00093
00094
00095 static List *find_update_path(List *evi_list,
00096 ExtensionVersionInfo *evi_start,
00097 ExtensionVersionInfo *evi_target,
00098 bool reinitialize);
00099 static void get_available_versions_for_extension(ExtensionControlFile *pcontrol,
00100 Tuplestorestate *tupstore,
00101 TupleDesc tupdesc);
00102 static void ApplyExtensionUpdates(Oid extensionOid,
00103 ExtensionControlFile *pcontrol,
00104 const char *initialVersion,
00105 List *updateVersions);
00106
00107
00108
00109
00110
00111
00112
00113
00114 Oid
00115 get_extension_oid(const char *extname, bool missing_ok)
00116 {
00117 Oid result;
00118 Relation rel;
00119 SysScanDesc scandesc;
00120 HeapTuple tuple;
00121 ScanKeyData entry[1];
00122
00123 rel = heap_open(ExtensionRelationId, AccessShareLock);
00124
00125 ScanKeyInit(&entry[0],
00126 Anum_pg_extension_extname,
00127 BTEqualStrategyNumber, F_NAMEEQ,
00128 CStringGetDatum(extname));
00129
00130 scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
00131 SnapshotNow, 1, entry);
00132
00133 tuple = systable_getnext(scandesc);
00134
00135
00136 if (HeapTupleIsValid(tuple))
00137 result = HeapTupleGetOid(tuple);
00138 else
00139 result = InvalidOid;
00140
00141 systable_endscan(scandesc);
00142
00143 heap_close(rel, AccessShareLock);
00144
00145 if (!OidIsValid(result) && !missing_ok)
00146 ereport(ERROR,
00147 (errcode(ERRCODE_UNDEFINED_OBJECT),
00148 errmsg("extension \"%s\" does not exist",
00149 extname)));
00150
00151 return result;
00152 }
00153
00154
00155
00156
00157
00158
00159 char *
00160 get_extension_name(Oid ext_oid)
00161 {
00162 char *result;
00163 Relation rel;
00164 SysScanDesc scandesc;
00165 HeapTuple tuple;
00166 ScanKeyData entry[1];
00167
00168 rel = heap_open(ExtensionRelationId, AccessShareLock);
00169
00170 ScanKeyInit(&entry[0],
00171 ObjectIdAttributeNumber,
00172 BTEqualStrategyNumber, F_OIDEQ,
00173 ObjectIdGetDatum(ext_oid));
00174
00175 scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
00176 SnapshotNow, 1, entry);
00177
00178 tuple = systable_getnext(scandesc);
00179
00180
00181 if (HeapTupleIsValid(tuple))
00182 result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
00183 else
00184 result = NULL;
00185
00186 systable_endscan(scandesc);
00187
00188 heap_close(rel, AccessShareLock);
00189
00190 return result;
00191 }
00192
00193
00194
00195
00196
00197
00198 static Oid
00199 get_extension_schema(Oid ext_oid)
00200 {
00201 Oid result;
00202 Relation rel;
00203 SysScanDesc scandesc;
00204 HeapTuple tuple;
00205 ScanKeyData entry[1];
00206
00207 rel = heap_open(ExtensionRelationId, AccessShareLock);
00208
00209 ScanKeyInit(&entry[0],
00210 ObjectIdAttributeNumber,
00211 BTEqualStrategyNumber, F_OIDEQ,
00212 ObjectIdGetDatum(ext_oid));
00213
00214 scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
00215 SnapshotNow, 1, entry);
00216
00217 tuple = systable_getnext(scandesc);
00218
00219
00220 if (HeapTupleIsValid(tuple))
00221 result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
00222 else
00223 result = InvalidOid;
00224
00225 systable_endscan(scandesc);
00226
00227 heap_close(rel, AccessShareLock);
00228
00229 return result;
00230 }
00231
00232
00233
00234
00235 static void
00236 check_valid_extension_name(const char *extensionname)
00237 {
00238 int namelen = strlen(extensionname);
00239
00240
00241
00242
00243
00244 if (namelen == 0)
00245 ereport(ERROR,
00246 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00247 errmsg("invalid extension name: \"%s\"", extensionname),
00248 errdetail("Extension names must not be empty.")));
00249
00250
00251
00252
00253 if (strstr(extensionname, "--"))
00254 ereport(ERROR,
00255 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00256 errmsg("invalid extension name: \"%s\"", extensionname),
00257 errdetail("Extension names must not contain \"--\".")));
00258
00259
00260
00261
00262
00263
00264
00265 if (extensionname[0] == '-' || extensionname[namelen - 1] == '-')
00266 ereport(ERROR,
00267 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00268 errmsg("invalid extension name: \"%s\"", extensionname),
00269 errdetail("Extension names must not begin or end with \"-\".")));
00270
00271
00272
00273
00274
00275 if (first_dir_separator(extensionname) != NULL)
00276 ereport(ERROR,
00277 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00278 errmsg("invalid extension name: \"%s\"", extensionname),
00279 errdetail("Extension names must not contain directory separator characters.")));
00280 }
00281
00282 static void
00283 check_valid_version_name(const char *versionname)
00284 {
00285 int namelen = strlen(versionname);
00286
00287
00288
00289
00290
00291 if (namelen == 0)
00292 ereport(ERROR,
00293 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00294 errmsg("invalid extension version name: \"%s\"", versionname),
00295 errdetail("Version names must not be empty.")));
00296
00297
00298
00299
00300 if (strstr(versionname, "--"))
00301 ereport(ERROR,
00302 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00303 errmsg("invalid extension version name: \"%s\"", versionname),
00304 errdetail("Version names must not contain \"--\".")));
00305
00306
00307
00308
00309 if (versionname[0] == '-' || versionname[namelen - 1] == '-')
00310 ereport(ERROR,
00311 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00312 errmsg("invalid extension version name: \"%s\"", versionname),
00313 errdetail("Version names must not begin or end with \"-\".")));
00314
00315
00316
00317
00318
00319 if (first_dir_separator(versionname) != NULL)
00320 ereport(ERROR,
00321 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00322 errmsg("invalid extension version name: \"%s\"", versionname),
00323 errdetail("Version names must not contain directory separator characters.")));
00324 }
00325
00326
00327
00328
00329 static bool
00330 is_extension_control_filename(const char *filename)
00331 {
00332 const char *extension = strrchr(filename, '.');
00333
00334 return (extension != NULL) && (strcmp(extension, ".control") == 0);
00335 }
00336
00337 static bool
00338 is_extension_script_filename(const char *filename)
00339 {
00340 const char *extension = strrchr(filename, '.');
00341
00342 return (extension != NULL) && (strcmp(extension, ".sql") == 0);
00343 }
00344
00345 static char *
00346 get_extension_control_directory(void)
00347 {
00348 char sharepath[MAXPGPATH];
00349 char *result;
00350
00351 get_share_path(my_exec_path, sharepath);
00352 result = (char *) palloc(MAXPGPATH);
00353 snprintf(result, MAXPGPATH, "%s/extension", sharepath);
00354
00355 return result;
00356 }
00357
00358 static char *
00359 get_extension_control_filename(const char *extname)
00360 {
00361 char sharepath[MAXPGPATH];
00362 char *result;
00363
00364 get_share_path(my_exec_path, sharepath);
00365 result = (char *) palloc(MAXPGPATH);
00366 snprintf(result, MAXPGPATH, "%s/extension/%s.control",
00367 sharepath, extname);
00368
00369 return result;
00370 }
00371
00372 static char *
00373 get_extension_script_directory(ExtensionControlFile *control)
00374 {
00375 char sharepath[MAXPGPATH];
00376 char *result;
00377
00378
00379
00380
00381
00382 if (!control->directory)
00383 return get_extension_control_directory();
00384
00385 if (is_absolute_path(control->directory))
00386 return pstrdup(control->directory);
00387
00388 get_share_path(my_exec_path, sharepath);
00389 result = (char *) palloc(MAXPGPATH);
00390 snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
00391
00392 return result;
00393 }
00394
00395 static char *
00396 get_extension_aux_control_filename(ExtensionControlFile *control,
00397 const char *version)
00398 {
00399 char *result;
00400 char *scriptdir;
00401
00402 scriptdir = get_extension_script_directory(control);
00403
00404 result = (char *) palloc(MAXPGPATH);
00405 snprintf(result, MAXPGPATH, "%s/%s--%s.control",
00406 scriptdir, control->name, version);
00407
00408 pfree(scriptdir);
00409
00410 return result;
00411 }
00412
00413 static char *
00414 get_extension_script_filename(ExtensionControlFile *control,
00415 const char *from_version, const char *version)
00416 {
00417 char *result;
00418 char *scriptdir;
00419
00420 scriptdir = get_extension_script_directory(control);
00421
00422 result = (char *) palloc(MAXPGPATH);
00423 if (from_version)
00424 snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
00425 scriptdir, control->name, from_version, version);
00426 else
00427 snprintf(result, MAXPGPATH, "%s/%s--%s.sql",
00428 scriptdir, control->name, version);
00429
00430 pfree(scriptdir);
00431
00432 return result;
00433 }
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445 static void
00446 parse_extension_control_file(ExtensionControlFile *control,
00447 const char *version)
00448 {
00449 char *filename;
00450 FILE *file;
00451 ConfigVariable *item,
00452 *head = NULL,
00453 *tail = NULL;
00454
00455
00456
00457
00458 if (version)
00459 filename = get_extension_aux_control_filename(control, version);
00460 else
00461 filename = get_extension_control_filename(control->name);
00462
00463 if ((file = AllocateFile(filename, "r")) == NULL)
00464 {
00465 if (version && errno == ENOENT)
00466 {
00467
00468 pfree(filename);
00469 return;
00470 }
00471 ereport(ERROR,
00472 (errcode_for_file_access(),
00473 errmsg("could not open extension control file \"%s\": %m",
00474 filename)));
00475 }
00476
00477
00478
00479
00480
00481 (void) ParseConfigFp(file, filename, 0, ERROR, &head, &tail);
00482
00483 FreeFile(file);
00484
00485
00486
00487
00488 for (item = head; item != NULL; item = item->next)
00489 {
00490 if (strcmp(item->name, "directory") == 0)
00491 {
00492 if (version)
00493 ereport(ERROR,
00494 (errcode(ERRCODE_SYNTAX_ERROR),
00495 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
00496 item->name)));
00497
00498 control->directory = pstrdup(item->value);
00499 }
00500 else if (strcmp(item->name, "default_version") == 0)
00501 {
00502 if (version)
00503 ereport(ERROR,
00504 (errcode(ERRCODE_SYNTAX_ERROR),
00505 errmsg("parameter \"%s\" cannot be set in a secondary extension control file",
00506 item->name)));
00507
00508 control->default_version = pstrdup(item->value);
00509 }
00510 else if (strcmp(item->name, "module_pathname") == 0)
00511 {
00512 control->module_pathname = pstrdup(item->value);
00513 }
00514 else if (strcmp(item->name, "comment") == 0)
00515 {
00516 control->comment = pstrdup(item->value);
00517 }
00518 else if (strcmp(item->name, "schema") == 0)
00519 {
00520 control->schema = pstrdup(item->value);
00521 }
00522 else if (strcmp(item->name, "relocatable") == 0)
00523 {
00524 if (!parse_bool(item->value, &control->relocatable))
00525 ereport(ERROR,
00526 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00527 errmsg("parameter \"%s\" requires a Boolean value",
00528 item->name)));
00529 }
00530 else if (strcmp(item->name, "superuser") == 0)
00531 {
00532 if (!parse_bool(item->value, &control->superuser))
00533 ereport(ERROR,
00534 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00535 errmsg("parameter \"%s\" requires a Boolean value",
00536 item->name)));
00537 }
00538 else if (strcmp(item->name, "encoding") == 0)
00539 {
00540 control->encoding = pg_valid_server_encoding(item->value);
00541 if (control->encoding < 0)
00542 ereport(ERROR,
00543 (errcode(ERRCODE_UNDEFINED_OBJECT),
00544 errmsg("\"%s\" is not a valid encoding name",
00545 item->value)));
00546 }
00547 else if (strcmp(item->name, "requires") == 0)
00548 {
00549
00550 char *rawnames = pstrdup(item->value);
00551
00552
00553 if (!SplitIdentifierString(rawnames, ',', &control->requires))
00554 {
00555
00556 ereport(ERROR,
00557 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00558 errmsg("parameter \"%s\" must be a list of extension names",
00559 item->name)));
00560 }
00561 }
00562 else
00563 ereport(ERROR,
00564 (errcode(ERRCODE_SYNTAX_ERROR),
00565 errmsg("unrecognized parameter \"%s\" in file \"%s\"",
00566 item->name, filename)));
00567 }
00568
00569 FreeConfigVariables(head);
00570
00571 if (control->relocatable && control->schema != NULL)
00572 ereport(ERROR,
00573 (errcode(ERRCODE_SYNTAX_ERROR),
00574 errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
00575
00576 pfree(filename);
00577 }
00578
00579
00580
00581
00582 static ExtensionControlFile *
00583 read_extension_control_file(const char *extname)
00584 {
00585 ExtensionControlFile *control;
00586
00587
00588
00589
00590 control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
00591 control->name = pstrdup(extname);
00592 control->relocatable = false;
00593 control->superuser = true;
00594 control->encoding = -1;
00595
00596
00597
00598
00599 parse_extension_control_file(control, NULL);
00600
00601 return control;
00602 }
00603
00604
00605
00606
00607
00608
00609
00610 static ExtensionControlFile *
00611 read_extension_aux_control_file(const ExtensionControlFile *pcontrol,
00612 const char *version)
00613 {
00614 ExtensionControlFile *acontrol;
00615
00616
00617
00618
00619 acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
00620 memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
00621
00622
00623
00624
00625 parse_extension_control_file(acontrol, version);
00626
00627 return acontrol;
00628 }
00629
00630
00631
00632
00633 static char *
00634 read_extension_script_file(const ExtensionControlFile *control,
00635 const char *filename)
00636 {
00637 int src_encoding;
00638 int dest_encoding = GetDatabaseEncoding();
00639 bytea *content;
00640 char *src_str;
00641 char *dest_str;
00642 int len;
00643
00644 content = read_binary_file(filename, 0, -1);
00645
00646
00647 if (control->encoding < 0)
00648 src_encoding = dest_encoding;
00649 else
00650 src_encoding = control->encoding;
00651
00652
00653 len = VARSIZE_ANY_EXHDR(content);
00654 src_str = VARDATA_ANY(content);
00655 pg_verify_mbstr_len(src_encoding, src_str, len, false);
00656
00657
00658 dest_str = (char *) pg_do_encoding_conversion((unsigned char *) src_str,
00659 len,
00660 src_encoding,
00661 dest_encoding);
00662
00663
00664 if (dest_str == src_str)
00665 {
00666 dest_str = (char *) palloc(len + 1);
00667 memcpy(dest_str, src_str, len);
00668 dest_str[len] = '\0';
00669 }
00670
00671 return dest_str;
00672 }
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687 static void
00688 execute_sql_string(const char *sql, const char *filename)
00689 {
00690 List *raw_parsetree_list;
00691 DestReceiver *dest;
00692 ListCell *lc1;
00693
00694
00695
00696
00697 raw_parsetree_list = pg_parse_query(sql);
00698
00699
00700 dest = CreateDestReceiver(DestNone);
00701
00702
00703
00704
00705
00706
00707 foreach(lc1, raw_parsetree_list)
00708 {
00709 Node *parsetree = (Node *) lfirst(lc1);
00710 List *stmt_list;
00711 ListCell *lc2;
00712
00713 stmt_list = pg_analyze_and_rewrite(parsetree,
00714 sql,
00715 NULL,
00716 0);
00717 stmt_list = pg_plan_queries(stmt_list, 0, NULL);
00718
00719 foreach(lc2, stmt_list)
00720 {
00721 Node *stmt = (Node *) lfirst(lc2);
00722
00723 if (IsA(stmt, TransactionStmt))
00724 ereport(ERROR,
00725 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00726 errmsg("transaction control statements are not allowed within an extension script")));
00727
00728 CommandCounterIncrement();
00729
00730 PushActiveSnapshot(GetTransactionSnapshot());
00731
00732 if (IsA(stmt, PlannedStmt) &&
00733 ((PlannedStmt *) stmt)->utilityStmt == NULL)
00734 {
00735 QueryDesc *qdesc;
00736
00737 qdesc = CreateQueryDesc((PlannedStmt *) stmt,
00738 sql,
00739 GetActiveSnapshot(), NULL,
00740 dest, NULL, 0);
00741
00742 ExecutorStart(qdesc, 0);
00743 ExecutorRun(qdesc, ForwardScanDirection, 0);
00744 ExecutorFinish(qdesc);
00745 ExecutorEnd(qdesc);
00746
00747 FreeQueryDesc(qdesc);
00748 }
00749 else
00750 {
00751 ProcessUtility(stmt,
00752 sql,
00753 PROCESS_UTILITY_QUERY,
00754 NULL,
00755 dest,
00756 NULL);
00757 }
00758
00759 PopActiveSnapshot();
00760 }
00761 }
00762
00763
00764 CommandCounterIncrement();
00765 }
00766
00767
00768
00769
00770
00771
00772 static void
00773 execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
00774 const char *from_version,
00775 const char *version,
00776 List *requiredSchemas,
00777 const char *schemaName, Oid schemaOid)
00778 {
00779 char *filename;
00780 int save_nestlevel;
00781 StringInfoData pathbuf;
00782 ListCell *lc;
00783
00784
00785
00786
00787
00788
00789 if (control->superuser && !superuser())
00790 {
00791 if (from_version == NULL)
00792 ereport(ERROR,
00793 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00794 errmsg("permission denied to create extension \"%s\"",
00795 control->name),
00796 errhint("Must be superuser to create this extension.")));
00797 else
00798 ereport(ERROR,
00799 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00800 errmsg("permission denied to update extension \"%s\"",
00801 control->name),
00802 errhint("Must be superuser to update this extension.")));
00803 }
00804
00805 filename = get_extension_script_filename(control, from_version, version);
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816 save_nestlevel = NewGUCNestLevel();
00817
00818 if (client_min_messages < WARNING)
00819 (void) set_config_option("client_min_messages", "warning",
00820 PGC_USERSET, PGC_S_SESSION,
00821 GUC_ACTION_SAVE, true, 0);
00822 if (log_min_messages < WARNING)
00823 (void) set_config_option("log_min_messages", "warning",
00824 PGC_SUSET, PGC_S_SESSION,
00825 GUC_ACTION_SAVE, true, 0);
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837 initStringInfo(&pathbuf);
00838 appendStringInfoString(&pathbuf, quote_identifier(schemaName));
00839 foreach(lc, requiredSchemas)
00840 {
00841 Oid reqschema = lfirst_oid(lc);
00842 char *reqname = get_namespace_name(reqschema);
00843
00844 if (reqname)
00845 appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
00846 }
00847
00848 (void) set_config_option("search_path", pathbuf.data,
00849 PGC_USERSET, PGC_S_SESSION,
00850 GUC_ACTION_SAVE, true, 0);
00851
00852
00853
00854
00855
00856
00857 creating_extension = true;
00858 CurrentExtensionObject = extensionOid;
00859 PG_TRY();
00860 {
00861 char *c_sql = read_extension_script_file(control, filename);
00862 Datum t_sql;
00863
00864
00865 t_sql = CStringGetTextDatum(c_sql);
00866
00867
00868
00869
00870
00871
00872 t_sql = DirectFunctionCall4Coll(textregexreplace,
00873 C_COLLATION_OID,
00874 t_sql,
00875 CStringGetTextDatum("^\\\\echo.*$"),
00876 CStringGetTextDatum(""),
00877 CStringGetTextDatum("ng"));
00878
00879
00880
00881
00882
00883
00884
00885
00886 if (!control->relocatable)
00887 {
00888 const char *qSchemaName = quote_identifier(schemaName);
00889
00890 t_sql = DirectFunctionCall3(replace_text,
00891 t_sql,
00892 CStringGetTextDatum("@extschema@"),
00893 CStringGetTextDatum(qSchemaName));
00894 }
00895
00896
00897
00898
00899
00900 if (control->module_pathname)
00901 {
00902 t_sql = DirectFunctionCall3(replace_text,
00903 t_sql,
00904 CStringGetTextDatum("MODULE_PATHNAME"),
00905 CStringGetTextDatum(control->module_pathname));
00906 }
00907
00908
00909 c_sql = text_to_cstring(DatumGetTextPP(t_sql));
00910
00911 execute_sql_string(c_sql, filename);
00912 }
00913 PG_CATCH();
00914 {
00915 creating_extension = false;
00916 CurrentExtensionObject = InvalidOid;
00917 PG_RE_THROW();
00918 }
00919 PG_END_TRY();
00920
00921 creating_extension = false;
00922 CurrentExtensionObject = InvalidOid;
00923
00924
00925
00926
00927 AtEOXact_GUC(true, save_nestlevel);
00928 }
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938 static ExtensionVersionInfo *
00939 get_ext_ver_info(const char *versionname, List **evi_list)
00940 {
00941 ExtensionVersionInfo *evi;
00942 ListCell *lc;
00943
00944 foreach(lc, *evi_list)
00945 {
00946 evi = (ExtensionVersionInfo *) lfirst(lc);
00947 if (strcmp(evi->name, versionname) == 0)
00948 return evi;
00949 }
00950
00951 evi = (ExtensionVersionInfo *) palloc(sizeof(ExtensionVersionInfo));
00952 evi->name = pstrdup(versionname);
00953 evi->reachable = NIL;
00954 evi->installable = false;
00955
00956 evi->distance_known = false;
00957 evi->distance = INT_MAX;
00958 evi->previous = NULL;
00959
00960 *evi_list = lappend(*evi_list, evi);
00961
00962 return evi;
00963 }
00964
00965
00966
00967
00968
00969
00970
00971 static ExtensionVersionInfo *
00972 get_nearest_unprocessed_vertex(List *evi_list)
00973 {
00974 ExtensionVersionInfo *evi = NULL;
00975 ListCell *lc;
00976
00977 foreach(lc, evi_list)
00978 {
00979 ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
00980
00981
00982 if (evi2->distance_known)
00983 continue;
00984
00985 if (evi == NULL ||
00986 evi->distance > evi2->distance)
00987 evi = evi2;
00988 }
00989
00990 return evi;
00991 }
00992
00993
00994
00995
00996
00997
00998
00999 static List *
01000 get_ext_ver_list(ExtensionControlFile *control)
01001 {
01002 List *evi_list = NIL;
01003 int extnamelen = strlen(control->name);
01004 char *location;
01005 DIR *dir;
01006 struct dirent *de;
01007
01008 location = get_extension_script_directory(control);
01009 dir = AllocateDir(location);
01010 while ((de = ReadDir(dir, location)) != NULL)
01011 {
01012 char *vername;
01013 char *vername2;
01014 ExtensionVersionInfo *evi;
01015 ExtensionVersionInfo *evi2;
01016
01017
01018 if (!is_extension_script_filename(de->d_name))
01019 continue;
01020
01021
01022 if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
01023 de->d_name[extnamelen] != '-' ||
01024 de->d_name[extnamelen + 1] != '-')
01025 continue;
01026
01027
01028 vername = pstrdup(de->d_name + extnamelen + 2);
01029 *strrchr(vername, '.') = '\0';
01030 vername2 = strstr(vername, "--");
01031 if (!vername2)
01032 {
01033
01034 evi = get_ext_ver_info(vername, &evi_list);
01035 evi->installable = true;
01036 continue;
01037 }
01038 *vername2 = '\0';
01039 vername2 += 2;
01040
01041
01042 if (strstr(vername2, "--"))
01043 continue;
01044
01045
01046 evi = get_ext_ver_info(vername, &evi_list);
01047 evi2 = get_ext_ver_info(vername2, &evi_list);
01048 evi->reachable = lappend(evi->reachable, evi2);
01049 }
01050 FreeDir(dir);
01051
01052 return evi_list;
01053 }
01054
01055
01056
01057
01058
01059
01060
01061
01062 static List *
01063 identify_update_path(ExtensionControlFile *control,
01064 const char *oldVersion, const char *newVersion)
01065 {
01066 List *result;
01067 List *evi_list;
01068 ExtensionVersionInfo *evi_start;
01069 ExtensionVersionInfo *evi_target;
01070
01071
01072 evi_list = get_ext_ver_list(control);
01073
01074
01075 evi_start = get_ext_ver_info(oldVersion, &evi_list);
01076 evi_target = get_ext_ver_info(newVersion, &evi_list);
01077
01078
01079 result = find_update_path(evi_list, evi_start, evi_target, false);
01080
01081 if (result == NIL)
01082 ereport(ERROR,
01083 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01084 errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
01085 control->name, oldVersion, newVersion)));
01086
01087 return result;
01088 }
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101 static List *
01102 find_update_path(List *evi_list,
01103 ExtensionVersionInfo *evi_start,
01104 ExtensionVersionInfo *evi_target,
01105 bool reinitialize)
01106 {
01107 List *result;
01108 ExtensionVersionInfo *evi;
01109 ListCell *lc;
01110
01111
01112 Assert(evi_start != evi_target);
01113
01114 if (reinitialize)
01115 {
01116 foreach(lc, evi_list)
01117 {
01118 evi = (ExtensionVersionInfo *) lfirst(lc);
01119 evi->distance_known = false;
01120 evi->distance = INT_MAX;
01121 evi->previous = NULL;
01122 }
01123 }
01124
01125 evi_start->distance = 0;
01126
01127 while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL)
01128 {
01129 if (evi->distance == INT_MAX)
01130 break;
01131 evi->distance_known = true;
01132 if (evi == evi_target)
01133 break;
01134 foreach(lc, evi->reachable)
01135 {
01136 ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
01137 int newdist;
01138
01139 newdist = evi->distance + 1;
01140 if (newdist < evi2->distance)
01141 {
01142 evi2->distance = newdist;
01143 evi2->previous = evi;
01144 }
01145 else if (newdist == evi2->distance &&
01146 evi2->previous != NULL &&
01147 strcmp(evi->name, evi2->previous->name) < 0)
01148 {
01149
01150
01151
01152
01153
01154
01155
01156
01157 evi2->previous = evi;
01158 }
01159 }
01160 }
01161
01162
01163 if (!evi_target->distance_known)
01164 return NIL;
01165
01166
01167 result = NIL;
01168 for (evi = evi_target; evi != evi_start; evi = evi->previous)
01169 result = lcons(evi->name, result);
01170
01171 return result;
01172 }
01173
01174
01175
01176
01177 Oid
01178 CreateExtension(CreateExtensionStmt *stmt)
01179 {
01180 DefElem *d_schema = NULL;
01181 DefElem *d_new_version = NULL;
01182 DefElem *d_old_version = NULL;
01183 char *schemaName;
01184 Oid schemaOid;
01185 char *versionName;
01186 char *oldVersionName;
01187 Oid extowner = GetUserId();
01188 ExtensionControlFile *pcontrol;
01189 ExtensionControlFile *control;
01190 List *updateVersions;
01191 List *requiredExtensions;
01192 List *requiredSchemas;
01193 Oid extensionOid;
01194 ListCell *lc;
01195
01196
01197 check_valid_extension_name(stmt->extname);
01198
01199
01200
01201
01202
01203
01204
01205 if (get_extension_oid(stmt->extname, true) != InvalidOid)
01206 {
01207 if (stmt->if_not_exists)
01208 {
01209 ereport(NOTICE,
01210 (errcode(ERRCODE_DUPLICATE_OBJECT),
01211 errmsg("extension \"%s\" already exists, skipping",
01212 stmt->extname)));
01213 return InvalidOid;
01214 }
01215 else
01216 ereport(ERROR,
01217 (errcode(ERRCODE_DUPLICATE_OBJECT),
01218 errmsg("extension \"%s\" already exists",
01219 stmt->extname)));
01220 }
01221
01222
01223
01224
01225
01226 if (creating_extension)
01227 ereport(ERROR,
01228 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01229 errmsg("nested CREATE EXTENSION is not supported")));
01230
01231
01232
01233
01234
01235
01236 pcontrol = read_extension_control_file(stmt->extname);
01237
01238
01239
01240
01241 foreach(lc, stmt->options)
01242 {
01243 DefElem *defel = (DefElem *) lfirst(lc);
01244
01245 if (strcmp(defel->defname, "schema") == 0)
01246 {
01247 if (d_schema)
01248 ereport(ERROR,
01249 (errcode(ERRCODE_SYNTAX_ERROR),
01250 errmsg("conflicting or redundant options")));
01251 d_schema = defel;
01252 }
01253 else if (strcmp(defel->defname, "new_version") == 0)
01254 {
01255 if (d_new_version)
01256 ereport(ERROR,
01257 (errcode(ERRCODE_SYNTAX_ERROR),
01258 errmsg("conflicting or redundant options")));
01259 d_new_version = defel;
01260 }
01261 else if (strcmp(defel->defname, "old_version") == 0)
01262 {
01263 if (d_old_version)
01264 ereport(ERROR,
01265 (errcode(ERRCODE_SYNTAX_ERROR),
01266 errmsg("conflicting or redundant options")));
01267 d_old_version = defel;
01268 }
01269 else
01270 elog(ERROR, "unrecognized option: %s", defel->defname);
01271 }
01272
01273
01274
01275
01276 if (d_new_version && d_new_version->arg)
01277 versionName = strVal(d_new_version->arg);
01278 else if (pcontrol->default_version)
01279 versionName = pcontrol->default_version;
01280 else
01281 {
01282 ereport(ERROR,
01283 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01284 errmsg("version to install must be specified")));
01285 versionName = NULL;
01286 }
01287 check_valid_version_name(versionName);
01288
01289
01290
01291
01292
01293 if (d_old_version && d_old_version->arg)
01294 {
01295 oldVersionName = strVal(d_old_version->arg);
01296 check_valid_version_name(oldVersionName);
01297
01298 if (strcmp(oldVersionName, versionName) == 0)
01299 ereport(ERROR,
01300 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01301 errmsg("FROM version must be different from installation target version \"%s\"",
01302 versionName)));
01303
01304 updateVersions = identify_update_path(pcontrol,
01305 oldVersionName,
01306 versionName);
01307
01308 if (list_length(updateVersions) == 1)
01309 {
01310
01311
01312
01313
01314 Assert(strcmp((char *) linitial(updateVersions), versionName) == 0);
01315 updateVersions = NIL;
01316 }
01317 else
01318 {
01319
01320
01321
01322
01323
01324 versionName = (char *) linitial(updateVersions);
01325 updateVersions = list_delete_first(updateVersions);
01326 }
01327 }
01328 else
01329 {
01330 oldVersionName = NULL;
01331 updateVersions = NIL;
01332 }
01333
01334
01335
01336
01337 control = read_extension_aux_control_file(pcontrol, versionName);
01338
01339
01340
01341
01342 if (d_schema && d_schema->arg)
01343 {
01344
01345
01346
01347
01348
01349
01350 schemaName = strVal(d_schema->arg);
01351
01352 if (control->schema != NULL &&
01353 strcmp(control->schema, schemaName) != 0)
01354 ereport(ERROR,
01355 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01356 errmsg("extension \"%s\" must be installed in schema \"%s\"",
01357 control->name,
01358 control->schema)));
01359
01360
01361 schemaOid = get_namespace_oid(schemaName, false);
01362 }
01363 else if (control->schema != NULL)
01364 {
01365
01366
01367
01368
01369 schemaName = control->schema;
01370 schemaOid = get_namespace_oid(schemaName, true);
01371
01372 if (schemaOid == InvalidOid)
01373 {
01374 CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
01375
01376 csstmt->schemaname = schemaName;
01377 csstmt->authid = NULL;
01378 csstmt->schemaElts = NIL;
01379 csstmt->if_not_exists = false;
01380 CreateSchemaCommand(csstmt, NULL);
01381
01382
01383
01384
01385
01386 schemaOid = get_namespace_oid(schemaName, false);
01387 }
01388 }
01389 else
01390 {
01391
01392
01393
01394
01395 List *search_path = fetch_search_path(false);
01396
01397 if (search_path == NIL)
01398 elog(ERROR, "there is no default creation target");
01399 schemaOid = linitial_oid(search_path);
01400 schemaName = get_namespace_name(schemaOid);
01401 if (schemaName == NULL)
01402 elog(ERROR, "there is no default creation target");
01403
01404 list_free(search_path);
01405 }
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420 requiredExtensions = NIL;
01421 requiredSchemas = NIL;
01422 foreach(lc, control->requires)
01423 {
01424 char *curreq = (char *) lfirst(lc);
01425 Oid reqext;
01426 Oid reqschema;
01427
01428
01429
01430
01431
01432 reqext = get_extension_oid(curreq, true);
01433 if (!OidIsValid(reqext))
01434 ereport(ERROR,
01435 (errcode(ERRCODE_UNDEFINED_OBJECT),
01436 errmsg("required extension \"%s\" is not installed",
01437 curreq)));
01438 reqschema = get_extension_schema(reqext);
01439 requiredExtensions = lappend_oid(requiredExtensions, reqext);
01440 requiredSchemas = lappend_oid(requiredSchemas, reqschema);
01441 }
01442
01443
01444
01445
01446 extensionOid = InsertExtensionTuple(control->name, extowner,
01447 schemaOid, control->relocatable,
01448 versionName,
01449 PointerGetDatum(NULL),
01450 PointerGetDatum(NULL),
01451 requiredExtensions);
01452
01453
01454
01455
01456 if (control->comment != NULL)
01457 CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
01458
01459
01460
01461
01462 execute_extension_script(extensionOid, control,
01463 oldVersionName, versionName,
01464 requiredSchemas,
01465 schemaName, schemaOid);
01466
01467
01468
01469
01470
01471 ApplyExtensionUpdates(extensionOid, pcontrol,
01472 versionName, updateVersions);
01473
01474 return extensionOid;
01475 }
01476
01477
01478
01479
01480
01481
01482
01483
01484
01485
01486
01487
01488
01489
01490 Oid
01491 InsertExtensionTuple(const char *extName, Oid extOwner,
01492 Oid schemaOid, bool relocatable, const char *extVersion,
01493 Datum extConfig, Datum extCondition,
01494 List *requiredExtensions)
01495 {
01496 Oid extensionOid;
01497 Relation rel;
01498 Datum values[Natts_pg_extension];
01499 bool nulls[Natts_pg_extension];
01500 HeapTuple tuple;
01501 ObjectAddress myself;
01502 ObjectAddress nsp;
01503 ListCell *lc;
01504
01505
01506
01507
01508 rel = heap_open(ExtensionRelationId, RowExclusiveLock);
01509
01510 memset(values, 0, sizeof(values));
01511 memset(nulls, 0, sizeof(nulls));
01512
01513 values[Anum_pg_extension_extname - 1] =
01514 DirectFunctionCall1(namein, CStringGetDatum(extName));
01515 values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner);
01516 values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
01517 values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable);
01518 values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion);
01519
01520 if (extConfig == PointerGetDatum(NULL))
01521 nulls[Anum_pg_extension_extconfig - 1] = true;
01522 else
01523 values[Anum_pg_extension_extconfig - 1] = extConfig;
01524
01525 if (extCondition == PointerGetDatum(NULL))
01526 nulls[Anum_pg_extension_extcondition - 1] = true;
01527 else
01528 values[Anum_pg_extension_extcondition - 1] = extCondition;
01529
01530 tuple = heap_form_tuple(rel->rd_att, values, nulls);
01531
01532 extensionOid = simple_heap_insert(rel, tuple);
01533 CatalogUpdateIndexes(rel, tuple);
01534
01535 heap_freetuple(tuple);
01536 heap_close(rel, RowExclusiveLock);
01537
01538
01539
01540
01541 recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner);
01542
01543 myself.classId = ExtensionRelationId;
01544 myself.objectId = extensionOid;
01545 myself.objectSubId = 0;
01546
01547 nsp.classId = NamespaceRelationId;
01548 nsp.objectId = schemaOid;
01549 nsp.objectSubId = 0;
01550
01551 recordDependencyOn(&myself, &nsp, DEPENDENCY_NORMAL);
01552
01553 foreach(lc, requiredExtensions)
01554 {
01555 Oid reqext = lfirst_oid(lc);
01556 ObjectAddress otherext;
01557
01558 otherext.classId = ExtensionRelationId;
01559 otherext.objectId = reqext;
01560 otherext.objectSubId = 0;
01561
01562 recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
01563 }
01564
01565 InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
01566
01567 return extensionOid;
01568 }
01569
01570
01571
01572
01573
01574
01575
01576 void
01577 RemoveExtensionById(Oid extId)
01578 {
01579 Relation rel;
01580 SysScanDesc scandesc;
01581 HeapTuple tuple;
01582 ScanKeyData entry[1];
01583
01584
01585
01586
01587
01588
01589
01590
01591
01592
01593
01594
01595 if (extId == CurrentExtensionObject)
01596 ereport(ERROR,
01597 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
01598 errmsg("cannot drop extension \"%s\" because it is being modified",
01599 get_extension_name(extId))));
01600
01601 rel = heap_open(ExtensionRelationId, RowExclusiveLock);
01602
01603 ScanKeyInit(&entry[0],
01604 ObjectIdAttributeNumber,
01605 BTEqualStrategyNumber, F_OIDEQ,
01606 ObjectIdGetDatum(extId));
01607 scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
01608 SnapshotNow, 1, entry);
01609
01610 tuple = systable_getnext(scandesc);
01611
01612
01613 if (HeapTupleIsValid(tuple))
01614 simple_heap_delete(rel, &tuple->t_self);
01615
01616 systable_endscan(scandesc);
01617
01618 heap_close(rel, RowExclusiveLock);
01619 }
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630 Datum
01631 pg_available_extensions(PG_FUNCTION_ARGS)
01632 {
01633 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
01634 TupleDesc tupdesc;
01635 Tuplestorestate *tupstore;
01636 MemoryContext per_query_ctx;
01637 MemoryContext oldcontext;
01638 char *location;
01639 DIR *dir;
01640 struct dirent *de;
01641
01642
01643 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
01644 ereport(ERROR,
01645 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01646 errmsg("set-valued function called in context that cannot accept a set")));
01647 if (!(rsinfo->allowedModes & SFRM_Materialize))
01648 ereport(ERROR,
01649 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01650 errmsg("materialize mode required, but it is not " \
01651 "allowed in this context")));
01652
01653
01654 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
01655 elog(ERROR, "return type must be a row type");
01656
01657
01658 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
01659 oldcontext = MemoryContextSwitchTo(per_query_ctx);
01660
01661 tupstore = tuplestore_begin_heap(true, false, work_mem);
01662 rsinfo->returnMode = SFRM_Materialize;
01663 rsinfo->setResult = tupstore;
01664 rsinfo->setDesc = tupdesc;
01665
01666 MemoryContextSwitchTo(oldcontext);
01667
01668 location = get_extension_control_directory();
01669 dir = AllocateDir(location);
01670
01671
01672
01673
01674
01675 if (dir == NULL && errno == ENOENT)
01676 {
01677
01678 }
01679 else
01680 {
01681 while ((de = ReadDir(dir, location)) != NULL)
01682 {
01683 ExtensionControlFile *control;
01684 char *extname;
01685 Datum values[3];
01686 bool nulls[3];
01687
01688 if (!is_extension_control_filename(de->d_name))
01689 continue;
01690
01691
01692 extname = pstrdup(de->d_name);
01693 *strrchr(extname, '.') = '\0';
01694
01695
01696 if (strstr(extname, "--"))
01697 continue;
01698
01699 control = read_extension_control_file(extname);
01700
01701 memset(values, 0, sizeof(values));
01702 memset(nulls, 0, sizeof(nulls));
01703
01704
01705 values[0] = DirectFunctionCall1(namein,
01706 CStringGetDatum(control->name));
01707
01708 if (control->default_version == NULL)
01709 nulls[1] = true;
01710 else
01711 values[1] = CStringGetTextDatum(control->default_version);
01712
01713 if (control->comment == NULL)
01714 nulls[2] = true;
01715 else
01716 values[2] = CStringGetTextDatum(control->comment);
01717
01718 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
01719 }
01720
01721 FreeDir(dir);
01722 }
01723
01724
01725 tuplestore_donestoring(tupstore);
01726
01727 return (Datum) 0;
01728 }
01729
01730
01731
01732
01733
01734
01735
01736
01737
01738
01739 Datum
01740 pg_available_extension_versions(PG_FUNCTION_ARGS)
01741 {
01742 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
01743 TupleDesc tupdesc;
01744 Tuplestorestate *tupstore;
01745 MemoryContext per_query_ctx;
01746 MemoryContext oldcontext;
01747 char *location;
01748 DIR *dir;
01749 struct dirent *de;
01750
01751
01752 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
01753 ereport(ERROR,
01754 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01755 errmsg("set-valued function called in context that cannot accept a set")));
01756 if (!(rsinfo->allowedModes & SFRM_Materialize))
01757 ereport(ERROR,
01758 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01759 errmsg("materialize mode required, but it is not " \
01760 "allowed in this context")));
01761
01762
01763 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
01764 elog(ERROR, "return type must be a row type");
01765
01766
01767 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
01768 oldcontext = MemoryContextSwitchTo(per_query_ctx);
01769
01770 tupstore = tuplestore_begin_heap(true, false, work_mem);
01771 rsinfo->returnMode = SFRM_Materialize;
01772 rsinfo->setResult = tupstore;
01773 rsinfo->setDesc = tupdesc;
01774
01775 MemoryContextSwitchTo(oldcontext);
01776
01777 location = get_extension_control_directory();
01778 dir = AllocateDir(location);
01779
01780
01781
01782
01783
01784 if (dir == NULL && errno == ENOENT)
01785 {
01786
01787 }
01788 else
01789 {
01790 while ((de = ReadDir(dir, location)) != NULL)
01791 {
01792 ExtensionControlFile *control;
01793 char *extname;
01794
01795 if (!is_extension_control_filename(de->d_name))
01796 continue;
01797
01798
01799 extname = pstrdup(de->d_name);
01800 *strrchr(extname, '.') = '\0';
01801
01802
01803 if (strstr(extname, "--"))
01804 continue;
01805
01806
01807 control = read_extension_control_file(extname);
01808
01809
01810 get_available_versions_for_extension(control, tupstore, tupdesc);
01811 }
01812
01813 FreeDir(dir);
01814 }
01815
01816
01817 tuplestore_donestoring(tupstore);
01818
01819 return (Datum) 0;
01820 }
01821
01822
01823
01824
01825
01826 static void
01827 get_available_versions_for_extension(ExtensionControlFile *pcontrol,
01828 Tuplestorestate *tupstore,
01829 TupleDesc tupdesc)
01830 {
01831 int extnamelen = strlen(pcontrol->name);
01832 char *location;
01833 DIR *dir;
01834 struct dirent *de;
01835
01836 location = get_extension_script_directory(pcontrol);
01837 dir = AllocateDir(location);
01838
01839 while ((de = ReadDir(dir, location)) != NULL)
01840 {
01841 ExtensionControlFile *control;
01842 char *vername;
01843 Datum values[7];
01844 bool nulls[7];
01845
01846
01847 if (!is_extension_script_filename(de->d_name))
01848 continue;
01849
01850
01851 if (strncmp(de->d_name, pcontrol->name, extnamelen) != 0 ||
01852 de->d_name[extnamelen] != '-' ||
01853 de->d_name[extnamelen + 1] != '-')
01854 continue;
01855
01856
01857 vername = pstrdup(de->d_name + extnamelen + 2);
01858 *strrchr(vername, '.') = '\0';
01859
01860
01861 if (strstr(vername, "--"))
01862 continue;
01863
01864
01865
01866
01867 control = read_extension_aux_control_file(pcontrol, vername);
01868
01869 memset(values, 0, sizeof(values));
01870 memset(nulls, 0, sizeof(nulls));
01871
01872
01873 values[0] = DirectFunctionCall1(namein,
01874 CStringGetDatum(control->name));
01875
01876 values[1] = CStringGetTextDatum(vername);
01877
01878 values[2] = BoolGetDatum(control->superuser);
01879
01880 values[3] = BoolGetDatum(control->relocatable);
01881
01882 if (control->schema == NULL)
01883 nulls[4] = true;
01884 else
01885 values[4] = DirectFunctionCall1(namein,
01886 CStringGetDatum(control->schema));
01887
01888 if (control->requires == NIL)
01889 nulls[5] = true;
01890 else
01891 {
01892 Datum *datums;
01893 int ndatums;
01894 ArrayType *a;
01895 ListCell *lc;
01896
01897 ndatums = list_length(control->requires);
01898 datums = (Datum *) palloc(ndatums * sizeof(Datum));
01899 ndatums = 0;
01900 foreach(lc, control->requires)
01901 {
01902 char *curreq = (char *) lfirst(lc);
01903
01904 datums[ndatums++] =
01905 DirectFunctionCall1(namein, CStringGetDatum(curreq));
01906 }
01907 a = construct_array(datums, ndatums,
01908 NAMEOID,
01909 NAMEDATALEN, false, 'c');
01910 values[5] = PointerGetDatum(a);
01911 }
01912
01913 if (control->comment == NULL)
01914 nulls[6] = true;
01915 else
01916 values[6] = CStringGetTextDatum(control->comment);
01917
01918 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
01919 }
01920
01921 FreeDir(dir);
01922 }
01923
01924
01925
01926
01927
01928 Datum
01929 pg_extension_update_paths(PG_FUNCTION_ARGS)
01930 {
01931 Name extname = PG_GETARG_NAME(0);
01932 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
01933 TupleDesc tupdesc;
01934 Tuplestorestate *tupstore;
01935 MemoryContext per_query_ctx;
01936 MemoryContext oldcontext;
01937 List *evi_list;
01938 ExtensionControlFile *control;
01939 ListCell *lc1;
01940
01941
01942 check_valid_extension_name(NameStr(*extname));
01943
01944
01945 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
01946 ereport(ERROR,
01947 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01948 errmsg("set-valued function called in context that cannot accept a set")));
01949 if (!(rsinfo->allowedModes & SFRM_Materialize))
01950 ereport(ERROR,
01951 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01952 errmsg("materialize mode required, but it is not " \
01953 "allowed in this context")));
01954
01955
01956 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
01957 elog(ERROR, "return type must be a row type");
01958
01959
01960 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
01961 oldcontext = MemoryContextSwitchTo(per_query_ctx);
01962
01963 tupstore = tuplestore_begin_heap(true, false, work_mem);
01964 rsinfo->returnMode = SFRM_Materialize;
01965 rsinfo->setResult = tupstore;
01966 rsinfo->setDesc = tupdesc;
01967
01968 MemoryContextSwitchTo(oldcontext);
01969
01970
01971 control = read_extension_control_file(NameStr(*extname));
01972
01973
01974 evi_list = get_ext_ver_list(control);
01975
01976
01977 foreach(lc1, evi_list)
01978 {
01979 ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc1);
01980 ListCell *lc2;
01981
01982 foreach(lc2, evi_list)
01983 {
01984 ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
01985 List *path;
01986 Datum values[3];
01987 bool nulls[3];
01988
01989 if (evi1 == evi2)
01990 continue;
01991
01992
01993 path = find_update_path(evi_list, evi1, evi2, true);
01994
01995
01996 memset(values, 0, sizeof(values));
01997 memset(nulls, 0, sizeof(nulls));
01998
01999
02000 values[0] = CStringGetTextDatum(evi1->name);
02001
02002 values[1] = CStringGetTextDatum(evi2->name);
02003
02004 if (path == NIL)
02005 nulls[2] = true;
02006 else
02007 {
02008 StringInfoData pathbuf;
02009 ListCell *lcv;
02010
02011 initStringInfo(&pathbuf);
02012
02013 appendStringInfoString(&pathbuf, evi1->name);
02014 foreach(lcv, path)
02015 {
02016 char *versionName = (char *) lfirst(lcv);
02017
02018 appendStringInfoString(&pathbuf, "--");
02019 appendStringInfoString(&pathbuf, versionName);
02020 }
02021 values[2] = CStringGetTextDatum(pathbuf.data);
02022 pfree(pathbuf.data);
02023 }
02024
02025 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
02026 }
02027 }
02028
02029
02030 tuplestore_donestoring(tupstore);
02031
02032 return (Datum) 0;
02033 }
02034
02035
02036
02037
02038
02039
02040
02041
02042 Datum
02043 pg_extension_config_dump(PG_FUNCTION_ARGS)
02044 {
02045 Oid tableoid = PG_GETARG_OID(0);
02046 text *wherecond = PG_GETARG_TEXT_P(1);
02047 char *tablename;
02048 Relation extRel;
02049 ScanKeyData key[1];
02050 SysScanDesc extScan;
02051 HeapTuple extTup;
02052 Datum arrayDatum;
02053 Datum elementDatum;
02054 int arrayLength;
02055 int arrayIndex;
02056 bool isnull;
02057 Datum repl_val[Natts_pg_extension];
02058 bool repl_null[Natts_pg_extension];
02059 bool repl_repl[Natts_pg_extension];
02060 ArrayType *a;
02061
02062
02063
02064
02065
02066 if (!creating_extension)
02067 ereport(ERROR,
02068 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02069 errmsg("pg_extension_config_dump() can only be called "
02070 "from an SQL script executed by CREATE EXTENSION")));
02071
02072
02073
02074
02075
02076
02077 tablename = get_rel_name(tableoid);
02078 if (tablename == NULL)
02079 ereport(ERROR,
02080 (errcode(ERRCODE_UNDEFINED_TABLE),
02081 errmsg("OID %u does not refer to a table", tableoid)));
02082 if (getExtensionOfObject(RelationRelationId, tableoid) !=
02083 CurrentExtensionObject)
02084 ereport(ERROR,
02085 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
02086 errmsg("table \"%s\" is not a member of the extension being created",
02087 tablename)));
02088
02089
02090
02091
02092
02093
02094
02095
02096
02097
02098 extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
02099
02100 ScanKeyInit(&key[0],
02101 ObjectIdAttributeNumber,
02102 BTEqualStrategyNumber, F_OIDEQ,
02103 ObjectIdGetDatum(CurrentExtensionObject));
02104
02105 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
02106 SnapshotNow, 1, key);
02107
02108 extTup = systable_getnext(extScan);
02109
02110 if (!HeapTupleIsValid(extTup))
02111 elog(ERROR, "extension with oid %u does not exist",
02112 CurrentExtensionObject);
02113
02114 memset(repl_val, 0, sizeof(repl_val));
02115 memset(repl_null, false, sizeof(repl_null));
02116 memset(repl_repl, false, sizeof(repl_repl));
02117
02118
02119 elementDatum = ObjectIdGetDatum(tableoid);
02120
02121 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
02122 RelationGetDescr(extRel), &isnull);
02123 if (isnull)
02124 {
02125
02126 arrayLength = 0;
02127 arrayIndex = 1;
02128
02129 a = construct_array(&elementDatum, 1,
02130 OIDOID,
02131 sizeof(Oid), true, 'i');
02132 }
02133 else
02134 {
02135
02136 Oid *arrayData;
02137 int i;
02138
02139 a = DatumGetArrayTypeP(arrayDatum);
02140
02141 arrayLength = ARR_DIMS(a)[0];
02142 if (ARR_NDIM(a) != 1 ||
02143 ARR_LBOUND(a)[0] != 1 ||
02144 arrayLength < 0 ||
02145 ARR_HASNULL(a) ||
02146 ARR_ELEMTYPE(a) != OIDOID)
02147 elog(ERROR, "extconfig is not a 1-D Oid array");
02148 arrayData = (Oid *) ARR_DATA_PTR(a);
02149
02150 arrayIndex = arrayLength + 1;
02151
02152 for (i = 0; i < arrayLength; i++)
02153 {
02154 if (arrayData[i] == tableoid)
02155 {
02156 arrayIndex = i + 1;
02157 break;
02158 }
02159 }
02160
02161 a = array_set(a, 1, &arrayIndex,
02162 elementDatum,
02163 false,
02164 -1 ,
02165 sizeof(Oid) ,
02166 true ,
02167 'i' );
02168 }
02169 repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
02170 repl_repl[Anum_pg_extension_extconfig - 1] = true;
02171
02172
02173 elementDatum = PointerGetDatum(wherecond);
02174
02175 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
02176 RelationGetDescr(extRel), &isnull);
02177 if (isnull)
02178 {
02179 if (arrayLength != 0)
02180 elog(ERROR, "extconfig and extcondition arrays do not match");
02181
02182 a = construct_array(&elementDatum, 1,
02183 TEXTOID,
02184 -1, false, 'i');
02185 }
02186 else
02187 {
02188 a = DatumGetArrayTypeP(arrayDatum);
02189
02190 if (ARR_NDIM(a) != 1 ||
02191 ARR_LBOUND(a)[0] != 1 ||
02192 ARR_HASNULL(a) ||
02193 ARR_ELEMTYPE(a) != TEXTOID)
02194 elog(ERROR, "extcondition is not a 1-D text array");
02195 if (ARR_DIMS(a)[0] != arrayLength)
02196 elog(ERROR, "extconfig and extcondition arrays do not match");
02197
02198
02199 a = array_set(a, 1, &arrayIndex,
02200 elementDatum,
02201 false,
02202 -1 ,
02203 -1 ,
02204 false ,
02205 'i' );
02206 }
02207 repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
02208 repl_repl[Anum_pg_extension_extcondition - 1] = true;
02209
02210 extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
02211 repl_val, repl_null, repl_repl);
02212
02213 simple_heap_update(extRel, &extTup->t_self, extTup);
02214 CatalogUpdateIndexes(extRel, extTup);
02215
02216 systable_endscan(extScan);
02217
02218 heap_close(extRel, RowExclusiveLock);
02219
02220 PG_RETURN_VOID();
02221 }
02222
02223
02224
02225
02226
02227
02228
02229
02230 static void
02231 extension_config_remove(Oid extensionoid, Oid tableoid)
02232 {
02233 Relation extRel;
02234 ScanKeyData key[1];
02235 SysScanDesc extScan;
02236 HeapTuple extTup;
02237 Datum arrayDatum;
02238 int arrayLength;
02239 int arrayIndex;
02240 bool isnull;
02241 Datum repl_val[Natts_pg_extension];
02242 bool repl_null[Natts_pg_extension];
02243 bool repl_repl[Natts_pg_extension];
02244 ArrayType *a;
02245
02246
02247 extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
02248
02249 ScanKeyInit(&key[0],
02250 ObjectIdAttributeNumber,
02251 BTEqualStrategyNumber, F_OIDEQ,
02252 ObjectIdGetDatum(extensionoid));
02253
02254 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
02255 SnapshotNow, 1, key);
02256
02257 extTup = systable_getnext(extScan);
02258
02259 if (!HeapTupleIsValid(extTup))
02260 elog(ERROR, "extension with oid %u does not exist",
02261 extensionoid);
02262
02263
02264 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
02265 RelationGetDescr(extRel), &isnull);
02266 if (isnull)
02267 {
02268
02269 a = NULL;
02270 arrayLength = 0;
02271 arrayIndex = -1;
02272 }
02273 else
02274 {
02275 Oid *arrayData;
02276 int i;
02277
02278 a = DatumGetArrayTypeP(arrayDatum);
02279
02280 arrayLength = ARR_DIMS(a)[0];
02281 if (ARR_NDIM(a) != 1 ||
02282 ARR_LBOUND(a)[0] != 1 ||
02283 arrayLength < 0 ||
02284 ARR_HASNULL(a) ||
02285 ARR_ELEMTYPE(a) != OIDOID)
02286 elog(ERROR, "extconfig is not a 1-D Oid array");
02287 arrayData = (Oid *) ARR_DATA_PTR(a);
02288
02289 arrayIndex = -1;
02290
02291 for (i = 0; i < arrayLength; i++)
02292 {
02293 if (arrayData[i] == tableoid)
02294 {
02295 arrayIndex = i;
02296 break;
02297 }
02298 }
02299 }
02300
02301
02302 if (arrayIndex < 0)
02303 {
02304 systable_endscan(extScan);
02305 heap_close(extRel, RowExclusiveLock);
02306 return;
02307 }
02308
02309
02310 memset(repl_val, 0, sizeof(repl_val));
02311 memset(repl_null, false, sizeof(repl_null));
02312 memset(repl_repl, false, sizeof(repl_repl));
02313
02314 if (arrayLength <= 1)
02315 {
02316
02317 repl_null[Anum_pg_extension_extconfig - 1] = true;
02318 }
02319 else
02320 {
02321
02322 Datum *dvalues;
02323 bool *dnulls;
02324 int nelems;
02325 int i;
02326
02327 deconstruct_array(a, OIDOID, sizeof(Oid), true, 'i',
02328 &dvalues, &dnulls, &nelems);
02329
02330
02331 for (i = arrayIndex; i < arrayLength - 1; i++)
02332 dvalues[i] = dvalues[i + 1];
02333
02334 a = construct_array(dvalues, arrayLength - 1,
02335 OIDOID, sizeof(Oid), true, 'i');
02336
02337 repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
02338 }
02339 repl_repl[Anum_pg_extension_extconfig - 1] = true;
02340
02341
02342 arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
02343 RelationGetDescr(extRel), &isnull);
02344 if (isnull)
02345 {
02346 elog(ERROR, "extconfig and extcondition arrays do not match");
02347 }
02348 else
02349 {
02350 a = DatumGetArrayTypeP(arrayDatum);
02351
02352 if (ARR_NDIM(a) != 1 ||
02353 ARR_LBOUND(a)[0] != 1 ||
02354 ARR_HASNULL(a) ||
02355 ARR_ELEMTYPE(a) != TEXTOID)
02356 elog(ERROR, "extcondition is not a 1-D text array");
02357 if (ARR_DIMS(a)[0] != arrayLength)
02358 elog(ERROR, "extconfig and extcondition arrays do not match");
02359 }
02360
02361 if (arrayLength <= 1)
02362 {
02363
02364 repl_null[Anum_pg_extension_extcondition - 1] = true;
02365 }
02366 else
02367 {
02368
02369 Datum *dvalues;
02370 bool *dnulls;
02371 int nelems;
02372 int i;
02373
02374 deconstruct_array(a, TEXTOID, -1, false, 'i',
02375 &dvalues, &dnulls, &nelems);
02376
02377
02378 for (i = arrayIndex; i < arrayLength - 1; i++)
02379 dvalues[i] = dvalues[i + 1];
02380
02381 a = construct_array(dvalues, arrayLength - 1,
02382 TEXTOID, -1, false, 'i');
02383
02384 repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
02385 }
02386 repl_repl[Anum_pg_extension_extcondition - 1] = true;
02387
02388 extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
02389 repl_val, repl_null, repl_repl);
02390
02391 simple_heap_update(extRel, &extTup->t_self, extTup);
02392 CatalogUpdateIndexes(extRel, extTup);
02393
02394 systable_endscan(extScan);
02395
02396 heap_close(extRel, RowExclusiveLock);
02397 }
02398
02399
02400
02401
02402 Oid
02403 AlterExtensionNamespace(List *names, const char *newschema)
02404 {
02405 char *extensionName;
02406 Oid extensionOid;
02407 Oid nspOid;
02408 Oid oldNspOid = InvalidOid;
02409 AclResult aclresult;
02410 Relation extRel;
02411 ScanKeyData key[2];
02412 SysScanDesc extScan;
02413 HeapTuple extTup;
02414 Form_pg_extension extForm;
02415 Relation depRel;
02416 SysScanDesc depScan;
02417 HeapTuple depTup;
02418 ObjectAddresses *objsMoved;
02419
02420 if (list_length(names) != 1)
02421 ereport(ERROR,
02422 (errcode(ERRCODE_SYNTAX_ERROR),
02423 errmsg("extension name cannot be qualified")));
02424 extensionName = strVal(linitial(names));
02425
02426 extensionOid = get_extension_oid(extensionName, false);
02427
02428 nspOid = LookupCreationNamespace(newschema);
02429
02430
02431
02432
02433
02434 if (!pg_extension_ownercheck(extensionOid, GetUserId()))
02435 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION,
02436 extensionName);
02437
02438
02439 aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
02440 if (aclresult != ACLCHECK_OK)
02441 aclcheck_error(aclresult, ACL_KIND_NAMESPACE, newschema);
02442
02443
02444
02445
02446
02447 if (getExtensionOfObject(NamespaceRelationId, nspOid) == extensionOid)
02448 ereport(ERROR,
02449 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
02450 errmsg("cannot move extension \"%s\" into schema \"%s\" "
02451 "because the extension contains the schema",
02452 extensionName, newschema)));
02453
02454
02455 extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
02456
02457 ScanKeyInit(&key[0],
02458 ObjectIdAttributeNumber,
02459 BTEqualStrategyNumber, F_OIDEQ,
02460 ObjectIdGetDatum(extensionOid));
02461
02462 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
02463 SnapshotNow, 1, key);
02464
02465 extTup = systable_getnext(extScan);
02466
02467 if (!HeapTupleIsValid(extTup))
02468 elog(ERROR, "extension with oid %u does not exist", extensionOid);
02469
02470
02471 extTup = heap_copytuple(extTup);
02472 extForm = (Form_pg_extension) GETSTRUCT(extTup);
02473
02474 systable_endscan(extScan);
02475
02476
02477
02478
02479
02480 if (extForm->extnamespace == nspOid)
02481 {
02482 heap_close(extRel, RowExclusiveLock);
02483 return InvalidOid;
02484 }
02485
02486
02487 if (!extForm->extrelocatable)
02488 ereport(ERROR,
02489 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02490 errmsg("extension \"%s\" does not support SET SCHEMA",
02491 NameStr(extForm->extname))));
02492
02493 objsMoved = new_object_addresses();
02494
02495
02496
02497
02498
02499 depRel = heap_open(DependRelationId, AccessShareLock);
02500
02501 ScanKeyInit(&key[0],
02502 Anum_pg_depend_refclassid,
02503 BTEqualStrategyNumber, F_OIDEQ,
02504 ObjectIdGetDatum(ExtensionRelationId));
02505 ScanKeyInit(&key[1],
02506 Anum_pg_depend_refobjid,
02507 BTEqualStrategyNumber, F_OIDEQ,
02508 ObjectIdGetDatum(extensionOid));
02509
02510 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
02511 SnapshotNow, 2, key);
02512
02513 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
02514 {
02515 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
02516 ObjectAddress dep;
02517 Oid dep_oldNspOid;
02518
02519
02520
02521
02522
02523
02524 if (pg_depend->deptype != DEPENDENCY_EXTENSION)
02525 continue;
02526
02527 dep.classId = pg_depend->classid;
02528 dep.objectId = pg_depend->objid;
02529 dep.objectSubId = pg_depend->objsubid;
02530
02531 if (dep.objectSubId != 0)
02532 elog(ERROR, "extension should not have a sub-object dependency");
02533
02534
02535 dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
02536 dep.objectId,
02537 nspOid,
02538 objsMoved);
02539
02540
02541
02542
02543 if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid)
02544 oldNspOid = dep_oldNspOid;
02545
02546
02547
02548
02549
02550 if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
02551 ereport(ERROR,
02552 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02553 errmsg("extension \"%s\" does not support SET SCHEMA",
02554 NameStr(extForm->extname)),
02555 errdetail("%s is not in the extension's schema \"%s\"",
02556 getObjectDescription(&dep),
02557 get_namespace_name(oldNspOid))));
02558 }
02559
02560 systable_endscan(depScan);
02561
02562 relation_close(depRel, AccessShareLock);
02563
02564
02565 extForm->extnamespace = nspOid;
02566
02567 simple_heap_update(extRel, &extTup->t_self, extTup);
02568 CatalogUpdateIndexes(extRel, extTup);
02569
02570 heap_close(extRel, RowExclusiveLock);
02571
02572
02573 changeDependencyFor(ExtensionRelationId, extensionOid,
02574 NamespaceRelationId, oldNspOid, nspOid);
02575
02576 InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
02577
02578 return extensionOid;
02579 }
02580
02581
02582
02583
02584 Oid
02585 ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
02586 {
02587 DefElem *d_new_version = NULL;
02588 char *versionName;
02589 char *oldVersionName;
02590 ExtensionControlFile *control;
02591 Oid extensionOid;
02592 Relation extRel;
02593 ScanKeyData key[1];
02594 SysScanDesc extScan;
02595 HeapTuple extTup;
02596 List *updateVersions;
02597 Datum datum;
02598 bool isnull;
02599 ListCell *lc;
02600
02601
02602
02603
02604
02605 if (creating_extension)
02606 ereport(ERROR,
02607 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02608 errmsg("nested ALTER EXTENSION is not supported")));
02609
02610
02611
02612
02613 extRel = heap_open(ExtensionRelationId, AccessShareLock);
02614
02615 ScanKeyInit(&key[0],
02616 Anum_pg_extension_extname,
02617 BTEqualStrategyNumber, F_NAMEEQ,
02618 CStringGetDatum(stmt->extname));
02619
02620 extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
02621 SnapshotNow, 1, key);
02622
02623 extTup = systable_getnext(extScan);
02624
02625 if (!HeapTupleIsValid(extTup))
02626 ereport(ERROR,
02627 (errcode(ERRCODE_UNDEFINED_OBJECT),
02628 errmsg("extension \"%s\" does not exist",
02629 stmt->extname)));
02630
02631 extensionOid = HeapTupleGetOid(extTup);
02632
02633
02634
02635
02636 datum = heap_getattr(extTup, Anum_pg_extension_extversion,
02637 RelationGetDescr(extRel), &isnull);
02638 if (isnull)
02639 elog(ERROR, "extversion is null");
02640 oldVersionName = text_to_cstring(DatumGetTextPP(datum));
02641
02642 systable_endscan(extScan);
02643
02644 heap_close(extRel, AccessShareLock);
02645
02646
02647 if (!pg_extension_ownercheck(extensionOid, GetUserId()))
02648 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION,
02649 stmt->extname);
02650
02651
02652
02653
02654
02655
02656 control = read_extension_control_file(stmt->extname);
02657
02658
02659
02660
02661 foreach(lc, stmt->options)
02662 {
02663 DefElem *defel = (DefElem *) lfirst(lc);
02664
02665 if (strcmp(defel->defname, "new_version") == 0)
02666 {
02667 if (d_new_version)
02668 ereport(ERROR,
02669 (errcode(ERRCODE_SYNTAX_ERROR),
02670 errmsg("conflicting or redundant options")));
02671 d_new_version = defel;
02672 }
02673 else
02674 elog(ERROR, "unrecognized option: %s", defel->defname);
02675 }
02676
02677
02678
02679
02680 if (d_new_version && d_new_version->arg)
02681 versionName = strVal(d_new_version->arg);
02682 else if (control->default_version)
02683 versionName = control->default_version;
02684 else
02685 {
02686 ereport(ERROR,
02687 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
02688 errmsg("version to install must be specified")));
02689 versionName = NULL;
02690 }
02691 check_valid_version_name(versionName);
02692
02693
02694
02695
02696 if (strcmp(oldVersionName, versionName) == 0)
02697 {
02698 ereport(NOTICE,
02699 (errmsg("version \"%s\" of extension \"%s\" is already installed",
02700 versionName, stmt->extname)));
02701 return InvalidOid;
02702 }
02703
02704
02705
02706
02707 updateVersions = identify_update_path(control,
02708 oldVersionName,
02709 versionName);
02710
02711
02712
02713
02714
02715 ApplyExtensionUpdates(extensionOid, control,
02716 oldVersionName, updateVersions);
02717
02718 return extensionOid;
02719 }
02720
02721
02722
02723
02724
02725
02726
02727
02728
02729 static void
02730 ApplyExtensionUpdates(Oid extensionOid,
02731 ExtensionControlFile *pcontrol,
02732 const char *initialVersion,
02733 List *updateVersions)
02734 {
02735 const char *oldVersionName = initialVersion;
02736 ListCell *lcv;
02737
02738 foreach(lcv, updateVersions)
02739 {
02740 char *versionName = (char *) lfirst(lcv);
02741 ExtensionControlFile *control;
02742 char *schemaName;
02743 Oid schemaOid;
02744 List *requiredExtensions;
02745 List *requiredSchemas;
02746 Relation extRel;
02747 ScanKeyData key[1];
02748 SysScanDesc extScan;
02749 HeapTuple extTup;
02750 Form_pg_extension extForm;
02751 Datum values[Natts_pg_extension];
02752 bool nulls[Natts_pg_extension];
02753 bool repl[Natts_pg_extension];
02754 ObjectAddress myself;
02755 ListCell *lc;
02756
02757
02758
02759
02760 control = read_extension_aux_control_file(pcontrol, versionName);
02761
02762
02763 extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
02764
02765 ScanKeyInit(&key[0],
02766 ObjectIdAttributeNumber,
02767 BTEqualStrategyNumber, F_OIDEQ,
02768 ObjectIdGetDatum(extensionOid));
02769
02770 extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
02771 SnapshotNow, 1, key);
02772
02773 extTup = systable_getnext(extScan);
02774
02775 if (!HeapTupleIsValid(extTup))
02776 elog(ERROR, "extension with oid %u does not exist",
02777 extensionOid);
02778
02779 extForm = (Form_pg_extension) GETSTRUCT(extTup);
02780
02781
02782
02783
02784 schemaOid = extForm->extnamespace;
02785 schemaName = get_namespace_name(schemaOid);
02786
02787
02788
02789
02790 memset(values, 0, sizeof(values));
02791 memset(nulls, 0, sizeof(nulls));
02792 memset(repl, 0, sizeof(repl));
02793
02794 values[Anum_pg_extension_extrelocatable - 1] =
02795 BoolGetDatum(control->relocatable);
02796 repl[Anum_pg_extension_extrelocatable - 1] = true;
02797 values[Anum_pg_extension_extversion - 1] =
02798 CStringGetTextDatum(versionName);
02799 repl[Anum_pg_extension_extversion - 1] = true;
02800
02801 extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
02802 values, nulls, repl);
02803
02804 simple_heap_update(extRel, &extTup->t_self, extTup);
02805 CatalogUpdateIndexes(extRel, extTup);
02806
02807 systable_endscan(extScan);
02808
02809 heap_close(extRel, RowExclusiveLock);
02810
02811
02812
02813
02814
02815 requiredExtensions = NIL;
02816 requiredSchemas = NIL;
02817 foreach(lc, control->requires)
02818 {
02819 char *curreq = (char *) lfirst(lc);
02820 Oid reqext;
02821 Oid reqschema;
02822
02823
02824
02825
02826
02827 reqext = get_extension_oid(curreq, true);
02828 if (!OidIsValid(reqext))
02829 ereport(ERROR,
02830 (errcode(ERRCODE_UNDEFINED_OBJECT),
02831 errmsg("required extension \"%s\" is not installed",
02832 curreq)));
02833 reqschema = get_extension_schema(reqext);
02834 requiredExtensions = lappend_oid(requiredExtensions, reqext);
02835 requiredSchemas = lappend_oid(requiredSchemas, reqschema);
02836 }
02837
02838
02839
02840
02841 deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
02842 ExtensionRelationId,
02843 DEPENDENCY_NORMAL);
02844
02845 myself.classId = ExtensionRelationId;
02846 myself.objectId = extensionOid;
02847 myself.objectSubId = 0;
02848
02849 foreach(lc, requiredExtensions)
02850 {
02851 Oid reqext = lfirst_oid(lc);
02852 ObjectAddress otherext;
02853
02854 otherext.classId = ExtensionRelationId;
02855 otherext.objectId = reqext;
02856 otherext.objectSubId = 0;
02857
02858 recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
02859 }
02860
02861 InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0);
02862
02863
02864
02865
02866 execute_extension_script(extensionOid, control,
02867 oldVersionName, versionName,
02868 requiredSchemas,
02869 schemaName, schemaOid);
02870
02871
02872
02873
02874
02875
02876 oldVersionName = versionName;
02877 }
02878 }
02879
02880
02881
02882
02883 Oid
02884 ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
02885 {
02886 ObjectAddress extension;
02887 ObjectAddress object;
02888 Relation relation;
02889 Oid oldExtension;
02890
02891 extension.classId = ExtensionRelationId;
02892 extension.objectId = get_extension_oid(stmt->extname, false);
02893 extension.objectSubId = 0;
02894
02895
02896 if (!pg_extension_ownercheck(extension.objectId, GetUserId()))
02897 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION,
02898 stmt->extname);
02899
02900
02901
02902
02903
02904
02905
02906 object = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
02907 &relation, ShareUpdateExclusiveLock, false);
02908
02909
02910 check_object_ownership(GetUserId(), stmt->objtype, object,
02911 stmt->objname, stmt->objargs, relation);
02912
02913
02914
02915
02916 oldExtension = getExtensionOfObject(object.classId, object.objectId);
02917
02918 if (stmt->action > 0)
02919 {
02920
02921
02922
02923 if (OidIsValid(oldExtension))
02924 ereport(ERROR,
02925 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
02926 errmsg("%s is already a member of extension \"%s\"",
02927 getObjectDescription(&object),
02928 get_extension_name(oldExtension))));
02929
02930
02931
02932
02933
02934 if (object.classId == NamespaceRelationId &&
02935 object.objectId == get_extension_schema(extension.objectId))
02936 ereport(ERROR,
02937 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
02938 errmsg("cannot add schema \"%s\" to extension \"%s\" "
02939 "because the schema contains the extension",
02940 get_namespace_name(object.objectId),
02941 stmt->extname)));
02942
02943
02944
02945
02946 recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
02947 }
02948 else
02949 {
02950
02951
02952
02953 if (oldExtension != extension.objectId)
02954 ereport(ERROR,
02955 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
02956 errmsg("%s is not a member of extension \"%s\"",
02957 getObjectDescription(&object),
02958 stmt->extname)));
02959
02960
02961
02962
02963 if (deleteDependencyRecordsForClass(object.classId, object.objectId,
02964 ExtensionRelationId,
02965 DEPENDENCY_EXTENSION) != 1)
02966 elog(ERROR, "unexpected number of extension dependency records");
02967
02968
02969
02970
02971
02972 if (object.classId == RelationRelationId)
02973 extension_config_remove(extension.objectId, object.objectId);
02974 }
02975
02976 InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
02977
02978
02979
02980
02981
02982
02983
02984 if (relation != NULL)
02985 relation_close(relation, NoLock);
02986
02987 return extension.objectId;
02988 }