PostgreSQL Source Code git master
Loading...
Searching...
No Matches
foreigncmds.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * foreigncmds.c
4 * foreign-data wrapper/server creation/manipulation commands
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 *
8 *
9 * IDENTIFICATION
10 * src/backend/commands/foreigncmds.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include "access/htup_details.h"
17#include "access/reloptions.h"
18#include "access/table.h"
19#include "access/xact.h"
20#include "catalog/catalog.h"
21#include "catalog/dependency.h"
22#include "catalog/indexing.h"
27#include "catalog/pg_proc.h"
28#include "catalog/pg_type.h"
30#include "commands/defrem.h"
31#include "foreign/fdwapi.h"
32#include "foreign/foreign.h"
33#include "miscadmin.h"
34#include "parser/parse_func.h"
35#include "tcop/utility.h"
36#include "utils/acl.h"
37#include "utils/builtins.h"
38#include "utils/lsyscache.h"
39#include "utils/rel.h"
40#include "utils/syscache.h"
41
42
43typedef struct
44{
45 char *tablename;
46 char *cmd;
48
49/* Internal functions */
50static void import_error_callback(void *arg);
51
52
53/*
54 * Convert a DefElem list to the text array format that is used in
55 * pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
56 * pg_foreign_table.
57 *
58 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
59 * if the list is empty.
60 *
61 * Note: The array is usually stored to database without further
62 * processing, hence any validation should be done before this
63 * conversion.
64 */
65static Datum
67{
68 ArrayBuildState *astate = NULL;
69 ListCell *cell;
70
71 foreach(cell, options)
72 {
73 DefElem *def = lfirst(cell);
74 const char *name;
75 const char *value;
76 Size len;
77 text *t;
78
79 name = def->defname;
80 value = defGetString(def);
81
82 /* Insist that name not contain "=", else "a=b=c" is ambiguous */
83 if (strchr(name, '=') != NULL)
86 errmsg("invalid option name \"%s\": must not contain \"=\"",
87 name)));
88
89 len = VARHDRSZ + strlen(name) + 1 + strlen(value);
90 /* +1 leaves room for sprintf's trailing null */
91 t = palloc(len + 1);
92 SET_VARSIZE(t, len);
93 sprintf(VARDATA(t), "%s=%s", name, value);
94
95 astate = accumArrayResult(astate, PointerGetDatum(t),
96 false, TEXTOID,
98 }
99
100 if (astate)
102
103 return PointerGetDatum(NULL);
104}
105
106
107/*
108 * Transform a list of DefElem into text array format. This is substantially
109 * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
110 * actions for modifying an existing list of options, which is passed in
111 * Datum form as oldOptions. Also, if fdwvalidator isn't InvalidOid
112 * it specifies a validator function to call on the result.
113 *
114 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
115 * if the list is empty.
116 *
117 * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
118 * FOREIGN TABLE.
119 */
120Datum
123 List *options,
124 Oid fdwvalidator)
125{
128 Datum result;
129
130 foreach(optcell, options)
131 {
133 ListCell *cell;
134
135 /*
136 * Find the element in resultOptions. We need this for validation in
137 * all cases.
138 */
139 foreach(cell, resultOptions)
140 {
141 DefElem *def = lfirst(cell);
142
143 if (strcmp(def->defname, od->defname) == 0)
144 break;
145 }
146
147 /*
148 * It is possible to perform multiple SET/DROP actions on the same
149 * option. The standard permits this, as long as the options to be
150 * added are unique. Note that an unspecified action is taken to be
151 * ADD.
152 */
153 switch (od->defaction)
154 {
155 case DEFELEM_DROP:
156 if (!cell)
159 errmsg("option \"%s\" not found",
160 od->defname)));
162 break;
163
164 case DEFELEM_SET:
165 if (!cell)
168 errmsg("option \"%s\" not found",
169 od->defname)));
170 lfirst(cell) = od;
171 break;
172
173 case DEFELEM_ADD:
174 case DEFELEM_UNSPEC:
175 if (cell)
178 errmsg("option \"%s\" provided more than once",
179 od->defname)));
181 break;
182
183 default:
184 elog(ERROR, "unrecognized action %d on option \"%s\"",
185 (int) od->defaction, od->defname);
186 break;
187 }
188 }
189
191
192 if (OidIsValid(fdwvalidator))
193 {
194 Datum valarg = result;
195
196 /*
197 * Pass a null options list as an empty array, so that validators
198 * don't have to be declared non-strict to handle the case.
199 */
202 OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
203 }
204
205 return result;
206}
207
208
209/*
210 * Internal workhorse for changing a data wrapper's owner.
211 *
212 * Allow this only for superusers; also the new owner must be a
213 * superuser.
214 */
215static void
217{
222 Acl *newAcl;
224 bool isNull;
225
227
228 /* Must be a superuser to change a FDW owner */
229 if (!superuser())
232 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
233 NameStr(form->fdwname)),
234 errhint("Must be superuser to change owner of a foreign-data wrapper.")));
235
236 /* New owner must also be a superuser */
240 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
241 NameStr(form->fdwname)),
242 errhint("The owner of a foreign-data wrapper must be a superuser.")));
243
244 if (form->fdwowner != newOwnerId)
245 {
246 memset(repl_null, false, sizeof(repl_null));
247 memset(repl_repl, false, sizeof(repl_repl));
248
251
254 RelationGetDescr(rel),
255 &isNull);
256 /* Null ACLs do not require changes */
257 if (!isNull)
258 {
260 form->fdwowner, newOwnerId);
263 }
264
266 repl_repl);
267
268 CatalogTupleUpdate(rel, &tup->t_self, tup);
269
270 /* Update owner dependency reference */
272 form->oid,
273 newOwnerId);
274 }
275
277 form->oid, 0);
278}
279
280/*
281 * Change foreign-data wrapper owner -- by name
282 *
283 * Note restrictions in the "_internal" function, above.
284 */
317
318/*
319 * Change foreign-data wrapper owner -- by OID
320 *
321 * Note restrictions in the "_internal" function, above.
322 */
323void
344
345/*
346 * Internal workhorse for changing a foreign server's owner
347 */
348static void
350{
355 Acl *newAcl;
357 bool isNull;
358
360
361 if (form->srvowner != newOwnerId)
362 {
363 /* Superusers can always do it */
364 if (!superuser())
365 {
366 Oid srvId;
368
369 srvId = form->oid;
370
371 /* Must be owner */
374 NameStr(form->srvname));
375
376 /* Must be able to become new owner */
378
379 /* New owner must have USAGE privilege on foreign-data wrapper */
381 if (aclresult != ACLCHECK_OK)
382 {
384
386 }
387 }
388
389 memset(repl_null, false, sizeof(repl_null));
390 memset(repl_repl, false, sizeof(repl_repl));
391
394
397 RelationGetDescr(rel),
398 &isNull);
399 /* Null ACLs do not require changes */
400 if (!isNull)
401 {
403 form->srvowner, newOwnerId);
406 }
407
409 repl_repl);
410
411 CatalogTupleUpdate(rel, &tup->t_self, tup);
412
413 /* Update owner dependency reference */
415 newOwnerId);
416 }
417
419 form->oid, 0);
420}
421
422/*
423 * Change foreign server owner -- by name
424 */
456
457/*
458 * Change foreign server owner -- by OID
459 */
460void
481
482/*
483 * Convert a handler function name passed from the parser to an Oid.
484 */
485static Oid
487{
489
490 if (handler == NULL || handler->arg == NULL)
491 return InvalidOid;
492
493 /* handlers have no arguments */
494 handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
495
496 /* check that handler has correct return type */
500 errmsg("function %s must return type %s",
501 NameListToString((List *) handler->arg), "fdw_handler")));
502
503 return handlerOid;
504}
505
506/*
507 * Convert a validator function name passed from the parser to an Oid.
508 */
509static Oid
511{
512 Oid funcargtypes[2];
513
514 if (validator == NULL || validator->arg == NULL)
515 return InvalidOid;
516
517 /* validators take text[], oid */
519 funcargtypes[1] = OIDOID;
520
521 return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
522 /* validator's return value is ignored, so we don't check the type */
523}
524
525/*
526 * Process function options of CREATE/ALTER FDW
527 */
528static void
529parse_func_options(ParseState *pstate, List *func_options,
530 bool *handler_given, Oid *fdwhandler,
531 bool *validator_given, Oid *fdwvalidator)
532{
533 ListCell *cell;
534
535 *handler_given = false;
536 *validator_given = false;
537 /* return InvalidOid if not given */
538 *fdwhandler = InvalidOid;
539 *fdwvalidator = InvalidOid;
540
541 foreach(cell, func_options)
542 {
543 DefElem *def = (DefElem *) lfirst(cell);
544
545 if (strcmp(def->defname, "handler") == 0)
546 {
547 if (*handler_given)
548 errorConflictingDefElem(def, pstate);
549 *handler_given = true;
550 *fdwhandler = lookup_fdw_handler_func(def);
551 }
552 else if (strcmp(def->defname, "validator") == 0)
553 {
554 if (*validator_given)
555 errorConflictingDefElem(def, pstate);
556 *validator_given = true;
557 *fdwvalidator = lookup_fdw_validator_func(def);
558 }
559 else
560 elog(ERROR, "option \"%s\" not recognized",
561 def->defname);
562 }
563}
564
565/*
566 * Create a foreign-data wrapper
567 */
570{
571 Relation rel;
574 HeapTuple tuple;
575 Oid fdwId;
576 bool handler_given;
577 bool validator_given;
578 Oid fdwhandler;
579 Oid fdwvalidator;
580 Datum fdwoptions;
581 Oid ownerId;
584
586
587 /* Must be superuser */
588 if (!superuser())
591 errmsg("permission denied to create foreign-data wrapper \"%s\"",
592 stmt->fdwname),
593 errhint("Must be superuser to create a foreign-data wrapper.")));
594
595 /* For now the owner cannot be specified on create. Use effective user ID. */
596 ownerId = GetUserId();
597
598 /*
599 * Check that there is no other foreign-data wrapper by this name.
600 */
601 if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
604 errmsg("foreign-data wrapper \"%s\" already exists",
605 stmt->fdwname)));
606
607 /*
608 * Insert tuple into pg_foreign_data_wrapper.
609 */
610 memset(values, 0, sizeof(values));
611 memset(nulls, false, sizeof(nulls));
612
619
620 /* Lookup handler and validator functions, if given */
621 parse_func_options(pstate, stmt->func_options,
622 &handler_given, &fdwhandler,
623 &validator_given, &fdwvalidator);
624
627
628 nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
629
632 stmt->options,
633 fdwvalidator);
634
635 if (DatumGetPointer(fdwoptions) != NULL)
637 else
639
640 tuple = heap_form_tuple(rel->rd_att, values, nulls);
641
642 CatalogTupleInsert(rel, tuple);
643
644 heap_freetuple(tuple);
645
646 /* record dependencies */
648 myself.objectId = fdwId;
649 myself.objectSubId = 0;
650
651 if (OidIsValid(fdwhandler))
652 {
654 referenced.objectId = fdwhandler;
655 referenced.objectSubId = 0;
657 }
658
659 if (OidIsValid(fdwvalidator))
660 {
662 referenced.objectId = fdwvalidator;
663 referenced.objectSubId = 0;
665 }
666
668
669 /* dependency on extension */
671
672 /* Post creation hook for new foreign data wrapper */
674
676
677 return myself;
678}
679
680
681/*
682 * Alter foreign-data wrapper
683 */
686{
687 Relation rel;
688 HeapTuple tp;
693 Oid fdwId;
694 bool isnull;
695 Datum datum;
696 bool handler_given;
697 bool validator_given;
698 Oid fdwhandler;
699 Oid fdwvalidator;
701
703
704 /* Must be superuser */
705 if (!superuser())
708 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
709 stmt->fdwname),
710 errhint("Must be superuser to alter a foreign-data wrapper.")));
711
713 CStringGetDatum(stmt->fdwname));
714
715 if (!HeapTupleIsValid(tp))
718 errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
719
721 fdwId = fdwForm->oid;
722
723 memset(repl_val, 0, sizeof(repl_val));
724 memset(repl_null, false, sizeof(repl_null));
725 memset(repl_repl, false, sizeof(repl_repl));
726
727 parse_func_options(pstate, stmt->func_options,
728 &handler_given, &fdwhandler,
729 &validator_given, &fdwvalidator);
730
731 if (handler_given)
732 {
735
736 /*
737 * It could be that the behavior of accessing foreign table changes
738 * with the new handler. Warn about this.
739 */
741 (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
742 }
743
744 if (validator_given)
745 {
748
749 /*
750 * It could be that existing options for the FDW or dependent SERVER,
751 * USER MAPPING or FOREIGN TABLE objects are no longer valid according
752 * to the new validator. Warn about this.
753 */
754 if (OidIsValid(fdwvalidator))
756 (errmsg("changing the foreign-data wrapper validator can cause "
757 "the options for dependent objects to become invalid")));
758 }
759 else
760 {
761 /*
762 * Validator is not changed, but we need it for validating options.
763 */
764 fdwvalidator = fdwForm->fdwvalidator;
765 }
766
767 /*
768 * If options specified, validate and update.
769 */
770 if (stmt->options)
771 {
772 /* Extract the current options */
774 tp,
776 &isnull);
777 if (isnull)
778 datum = PointerGetDatum(NULL);
779
780 /* Transform the options */
782 datum,
783 stmt->options,
784 fdwvalidator);
785
786 if (DatumGetPointer(datum) != NULL)
788 else
790
792 }
793
794 /* Everything looks good - update the tuple */
797
798 CatalogTupleUpdate(rel, &tp->t_self, tp);
799
800 heap_freetuple(tp);
801
803
804 /* Update function dependencies if we changed them */
806 {
808
809 /*
810 * Flush all existing dependency records of this FDW on functions; we
811 * assume there can be none other than the ones we are fixing.
812 */
814 fdwId,
817
818 /* And build new ones. */
819
820 if (OidIsValid(fdwhandler))
821 {
823 referenced.objectId = fdwhandler;
824 referenced.objectSubId = 0;
826 }
827
828 if (OidIsValid(fdwvalidator))
829 {
831 referenced.objectId = fdwvalidator;
832 referenced.objectSubId = 0;
834 }
835 }
836
838
840
841 return myself;
842}
843
844
845/*
846 * Create a foreign server
847 */
850{
851 Relation rel;
852 Datum srvoptions;
854 bool nulls[Natts_pg_foreign_server];
855 HeapTuple tuple;
856 Oid srvId;
857 Oid ownerId;
862
864
865 /* For now the owner cannot be specified on create. Use effective user ID. */
866 ownerId = GetUserId();
867
868 /*
869 * Check that there is no other foreign server by this name. If there is
870 * one, do nothing if IF NOT EXISTS was specified.
871 */
872 srvId = get_foreign_server_oid(stmt->servername, true);
873 if (OidIsValid(srvId))
874 {
875 if (stmt->if_not_exists)
876 {
877 /*
878 * If we are in an extension script, insist that the pre-existing
879 * object be a member of the extension, to avoid security risks.
880 */
883
884 /* OK to skip */
887 errmsg("server \"%s\" already exists, skipping",
888 stmt->servername)));
891 }
892 else
895 errmsg("server \"%s\" already exists",
896 stmt->servername)));
897 }
898
899 /*
900 * Check that the FDW exists and that we have USAGE on it. Also get the
901 * actual FDW for option validation etc.
902 */
903 fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
904
906 if (aclresult != ACLCHECK_OK)
908
909 /*
910 * Insert tuple into pg_foreign_server.
911 */
912 memset(values, 0, sizeof(values));
913 memset(nulls, false, sizeof(nulls));
914
922
923 /* Add server type if supplied */
924 if (stmt->servertype)
926 CStringGetTextDatum(stmt->servertype);
927 else
928 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
929
930 /* Add server version if supplied */
931 if (stmt->version)
933 CStringGetTextDatum(stmt->version);
934 else
935 nulls[Anum_pg_foreign_server_srvversion - 1] = true;
936
937 /* Start with a blank acl */
938 nulls[Anum_pg_foreign_server_srvacl - 1] = true;
939
940 /* Add server options */
943 stmt->options,
944 fdw->fdwvalidator);
945
946 if (DatumGetPointer(srvoptions) != NULL)
948 else
949 nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
950
951 tuple = heap_form_tuple(rel->rd_att, values, nulls);
952
953 CatalogTupleInsert(rel, tuple);
954
955 heap_freetuple(tuple);
956
957 /* record dependencies */
959 myself.objectId = srvId;
960 myself.objectSubId = 0;
961
963 referenced.objectId = fdw->fdwid;
964 referenced.objectSubId = 0;
966
968
969 /* dependency on extension */
971
972 /* Post creation hook for new foreign server */
974
976
977 return myself;
978}
979
980
981/*
982 * Alter foreign server
983 */
986{
987 Relation rel;
988 HeapTuple tp;
992 Oid srvId;
994 ObjectAddress address;
995
997
999 CStringGetDatum(stmt->servername));
1000
1001 if (!HeapTupleIsValid(tp))
1002 ereport(ERROR,
1004 errmsg("server \"%s\" does not exist", stmt->servername)));
1005
1007 srvId = srvForm->oid;
1008
1009 /*
1010 * Only owner or a superuser can ALTER a SERVER.
1011 */
1014 stmt->servername);
1015
1016 memset(repl_val, 0, sizeof(repl_val));
1017 memset(repl_null, false, sizeof(repl_null));
1018 memset(repl_repl, false, sizeof(repl_repl));
1019
1020 if (stmt->has_version)
1021 {
1022 /*
1023 * Change the server VERSION string.
1024 */
1025 if (stmt->version)
1027 CStringGetTextDatum(stmt->version);
1028 else
1030
1032 }
1033
1034 if (stmt->options)
1035 {
1037 Datum datum;
1038 bool isnull;
1039
1040 /* Extract the current srvoptions */
1042 tp,
1044 &isnull);
1045 if (isnull)
1046 datum = PointerGetDatum(NULL);
1047
1048 /* Prepare the options array */
1050 datum,
1051 stmt->options,
1052 fdw->fdwvalidator);
1053
1054 if (DatumGetPointer(datum) != NULL)
1056 else
1058
1060 }
1061
1062 /* Everything looks good - update the tuple */
1063 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1065
1066 CatalogTupleUpdate(rel, &tp->t_self, tp);
1067
1069
1071
1072 heap_freetuple(tp);
1073
1075
1076 return address;
1077}
1078
1079
1080/*
1081 * Common routine to check permission for user-mapping-related DDL
1082 * commands. We allow server owners to operate on any mapping, and
1083 * users to operate on their own mapping.
1084 */
1085static void
1086user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1087{
1089
1091 {
1092 if (umuserid == curuserid)
1093 {
1095
1097 if (aclresult != ACLCHECK_OK)
1099 }
1100 else
1102 servername);
1103 }
1104}
1105
1106
1107/*
1108 * Create user mapping
1109 */
1112{
1113 Relation rel;
1116 bool nulls[Natts_pg_user_mapping];
1117 HeapTuple tuple;
1118 Oid useId;
1119 Oid umId;
1124 RoleSpec *role = (RoleSpec *) stmt->user;
1125
1127
1128 if (role->roletype == ROLESPEC_PUBLIC)
1130 else
1131 useId = get_rolespec_oid(stmt->user, false);
1132
1133 /* Check that the server exists. */
1134 srv = GetForeignServerByName(stmt->servername, false);
1135
1136 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1137
1138 /*
1139 * Check that the user mapping is unique within server.
1140 */
1143 ObjectIdGetDatum(srv->serverid));
1144
1145 if (OidIsValid(umId))
1146 {
1147 if (stmt->if_not_exists)
1148 {
1149 /*
1150 * Since user mappings aren't members of extensions (see comments
1151 * below), no need for checkMembershipInCurrentExtension here.
1152 */
1155 errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
1157 stmt->servername)));
1158
1160 return InvalidObjectAddress;
1161 }
1162 else
1163 ereport(ERROR,
1165 errmsg("user mapping for \"%s\" already exists for server \"%s\"",
1167 stmt->servername)));
1168 }
1169
1170 fdw = GetForeignDataWrapper(srv->fdwid);
1171
1172 /*
1173 * Insert tuple into pg_user_mapping.
1174 */
1175 memset(values, 0, sizeof(values));
1176 memset(nulls, false, sizeof(nulls));
1177
1183
1184 /* Add user options */
1187 stmt->options,
1188 fdw->fdwvalidator);
1189
1192 else
1193 nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1194
1195 tuple = heap_form_tuple(rel->rd_att, values, nulls);
1196
1197 CatalogTupleInsert(rel, tuple);
1198
1199 heap_freetuple(tuple);
1200
1201 /* Add dependency on the server */
1202 myself.classId = UserMappingRelationId;
1203 myself.objectId = umId;
1204 myself.objectSubId = 0;
1205
1207 referenced.objectId = srv->serverid;
1208 referenced.objectSubId = 0;
1210
1211 if (OidIsValid(useId))
1212 {
1213 /* Record the mapped user dependency */
1215 }
1216
1217 /*
1218 * Perhaps someday there should be a recordDependencyOnCurrentExtension
1219 * call here; but since roles aren't members of extensions, it seems like
1220 * user mappings shouldn't be either. Note that the grammar and pg_dump
1221 * would need to be extended too if we change this.
1222 */
1223
1224 /* Post creation hook for new user mapping */
1226
1228
1229 return myself;
1230}
1231
1232
1233/*
1234 * Alter user mapping
1235 */
1238{
1239 Relation rel;
1240 HeapTuple tp;
1244 Oid useId;
1245 Oid umId;
1247 ObjectAddress address;
1248 RoleSpec *role = (RoleSpec *) stmt->user;
1249
1251
1252 if (role->roletype == ROLESPEC_PUBLIC)
1254 else
1255 useId = get_rolespec_oid(stmt->user, false);
1256
1257 srv = GetForeignServerByName(stmt->servername, false);
1258
1261 ObjectIdGetDatum(srv->serverid));
1262 if (!OidIsValid(umId))
1263 ereport(ERROR,
1265 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1266 MappingUserName(useId), stmt->servername)));
1267
1268 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1269
1271
1272 if (!HeapTupleIsValid(tp))
1273 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1274
1275 memset(repl_val, 0, sizeof(repl_val));
1276 memset(repl_null, false, sizeof(repl_null));
1277 memset(repl_repl, false, sizeof(repl_repl));
1278
1279 if (stmt->options)
1280 {
1282 Datum datum;
1283 bool isnull;
1284
1285 /*
1286 * Process the options.
1287 */
1288
1289 fdw = GetForeignDataWrapper(srv->fdwid);
1290
1292 tp,
1294 &isnull);
1295 if (isnull)
1296 datum = PointerGetDatum(NULL);
1297
1298 /* Prepare the options array */
1300 datum,
1301 stmt->options,
1302 fdw->fdwvalidator);
1303
1304 if (DatumGetPointer(datum) != NULL)
1306 else
1308
1310 }
1311
1312 /* Everything looks good - update the tuple */
1313 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1315
1316 CatalogTupleUpdate(rel, &tp->t_self, tp);
1317
1319 umId, 0);
1320
1322
1323 heap_freetuple(tp);
1324
1326
1327 return address;
1328}
1329
1330
1331/*
1332 * Drop user mapping
1333 */
1334Oid
1336{
1337 ObjectAddress object;
1338 Oid useId;
1339 Oid umId;
1341 RoleSpec *role = (RoleSpec *) stmt->user;
1342
1343 if (role->roletype == ROLESPEC_PUBLIC)
1345 else
1346 {
1347 useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1348 if (!OidIsValid(useId))
1349 {
1350 /*
1351 * IF EXISTS specified, role not found and not public. Notice this
1352 * and leave.
1353 */
1354 elog(NOTICE, "role \"%s\" does not exist, skipping",
1355 role->rolename);
1356 return InvalidOid;
1357 }
1358 }
1359
1360 srv = GetForeignServerByName(stmt->servername, true);
1361
1362 if (!srv)
1363 {
1364 if (!stmt->missing_ok)
1365 ereport(ERROR,
1367 errmsg("server \"%s\" does not exist",
1368 stmt->servername)));
1369 /* IF EXISTS, just note it */
1371 (errmsg("server \"%s\" does not exist, skipping",
1372 stmt->servername)));
1373 return InvalidOid;
1374 }
1375
1378 ObjectIdGetDatum(srv->serverid));
1379
1380 if (!OidIsValid(umId))
1381 {
1382 if (!stmt->missing_ok)
1383 ereport(ERROR,
1385 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1386 MappingUserName(useId), stmt->servername)));
1387
1388 /* IF EXISTS specified, just note it */
1390 (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
1391 MappingUserName(useId), stmt->servername)));
1392 return InvalidOid;
1393 }
1394
1395 user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1396
1397 /*
1398 * Do the deletion
1399 */
1400 object.classId = UserMappingRelationId;
1401 object.objectId = umId;
1402 object.objectSubId = 0;
1403
1404 performDeletion(&object, DROP_CASCADE, 0);
1405
1406 return umId;
1407}
1408
1409
1410/*
1411 * Create a foreign table
1412 * call after DefineRelation().
1413 */
1414void
1416{
1420 bool nulls[Natts_pg_foreign_table];
1421 HeapTuple tuple;
1425 Oid ownerId;
1427 ForeignServer *server;
1428
1429 /*
1430 * Advance command counter to ensure the pg_attribute tuple is visible;
1431 * the tuple might be updated to add constraints in previous step.
1432 */
1434
1436
1437 /*
1438 * For now the owner cannot be specified on create. Use effective user ID.
1439 */
1440 ownerId = GetUserId();
1441
1442 /*
1443 * Check that the foreign server exists and that we have USAGE on it. Also
1444 * get the actual FDW for option validation etc.
1445 */
1446 server = GetForeignServerByName(stmt->servername, false);
1448 if (aclresult != ACLCHECK_OK)
1450
1451 fdw = GetForeignDataWrapper(server->fdwid);
1452
1453 /*
1454 * Insert tuple into pg_foreign_table.
1455 */
1456 memset(values, 0, sizeof(values));
1457 memset(nulls, false, sizeof(nulls));
1458
1461 /* Add table generic options */
1464 stmt->options,
1465 fdw->fdwvalidator);
1466
1469 else
1470 nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1471
1472 tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1473
1474 CatalogTupleInsert(ftrel, tuple);
1475
1476 heap_freetuple(tuple);
1477
1478 /* Add pg_class dependency on the server */
1479 myself.classId = RelationRelationId;
1480 myself.objectId = relid;
1481 myself.objectSubId = 0;
1482
1484 referenced.objectId = server->serverid;
1485 referenced.objectSubId = 0;
1487
1489}
1490
1491/*
1492 * Import a foreign schema
1493 */
1494void
1496{
1497 ForeignServer *server;
1501 List *cmd_list;
1502 ListCell *lc;
1503
1504 /* Check that the foreign server exists and that we have USAGE on it */
1505 server = GetForeignServerByName(stmt->server_name, false);
1507 if (aclresult != ACLCHECK_OK)
1509
1510 /* Check that the schema exists and we have CREATE permissions on it */
1511 (void) LookupCreationNamespace(stmt->local_schema);
1512
1513 /* Get the FDW and check it supports IMPORT */
1514 fdw = GetForeignDataWrapper(server->fdwid);
1515 if (!OidIsValid(fdw->fdwhandler))
1516 ereport(ERROR,
1518 errmsg("foreign-data wrapper \"%s\" has no handler",
1519 fdw->fdwname)));
1520 fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1521 if (fdw_routine->ImportForeignSchema == NULL)
1522 ereport(ERROR,
1524 errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1525 fdw->fdwname)));
1526
1527 /* Call FDW to get a list of commands */
1528 cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1529
1530 /* Parse and execute each command */
1531 foreach(lc, cmd_list)
1532 {
1533 char *cmd = (char *) lfirst(lc);
1534 import_error_callback_arg callback_arg;
1537 ListCell *lc2;
1538
1539 /*
1540 * Setup error traceback support for ereport(). This is so that any
1541 * error in the generated SQL will be displayed nicely.
1542 */
1543 callback_arg.tablename = NULL; /* not known yet */
1544 callback_arg.cmd = cmd;
1546 sqlerrcontext.arg = &callback_arg;
1549
1550 /*
1551 * Parse the SQL string into a list of raw parse trees.
1552 */
1554
1555 /*
1556 * Process each parse tree (we allow the FDW to put more than one
1557 * command per string, though this isn't really advised).
1558 */
1559 foreach(lc2, raw_parsetree_list)
1560 {
1563 PlannedStmt *pstmt;
1564
1565 /*
1566 * Because we only allow CreateForeignTableStmt, we can skip parse
1567 * analysis, rewrite, and planning steps here.
1568 */
1570 elog(ERROR,
1571 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1572 fdw->fdwname, (int) nodeTag(cstmt));
1573
1574 /* Ignore commands for tables excluded by filter options */
1575 if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
1576 continue;
1577
1578 /* Enable reporting of current table's name on error */
1579 callback_arg.tablename = cstmt->base.relation->relname;
1580
1581 /* Ensure creation schema is the one given in IMPORT statement */
1582 cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1583
1584 /* No planning needed, just make a wrapper PlannedStmt */
1585 pstmt = makeNode(PlannedStmt);
1586 pstmt->commandType = CMD_UTILITY;
1587 pstmt->canSetTag = false;
1588 pstmt->utilityStmt = (Node *) cstmt;
1589 pstmt->stmt_location = rs->stmt_location;
1590 pstmt->stmt_len = rs->stmt_len;
1592
1593 /* Execute statement */
1594 ProcessUtility(pstmt, cmd, false,
1597
1598 /* Be sure to advance the command counter between subcommands */
1600
1601 callback_arg.tablename = NULL;
1602 }
1603
1605 }
1606}
1607
1608/*
1609 * error context callback to let us supply the failing SQL statement's text
1610 */
1611static void
1613{
1616
1617 /* If it's a syntax error, convert to internal syntax error report */
1619 if (syntaxerrposition > 0)
1620 {
1621 errposition(0);
1623 internalerrquery(callback_arg->cmd);
1624 }
1625
1626 if (callback_arg->tablename)
1627 errcontext("importing foreign table \"%s\"",
1628 callback_arg->tablename);
1629}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition acl.c:1119
void check_can_set_role(Oid member, Oid role)
Definition acl.c:5341
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition acl.c:5586
AclResult
Definition acl.h:182
@ ACLCHECK_OK
Definition acl.h:183
@ ACLCHECK_NOT_OWNER
Definition acl.h:185
#define DatumGetAclP(X)
Definition acl.h:120
#define ACL_ID_PUBLIC
Definition acl.h:46
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2654
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3836
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4090
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
ArrayType * construct_empty_array(Oid elmtype)
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
static Datum values[MAXATTR]
Definition bootstrap.c:155
#define CStringGetTextDatum(s)
Definition builtins.h:97
#define NameStr(name)
Definition c.h:765
#define VARHDRSZ
Definition c.h:711
#define OidIsValid(objectId)
Definition c.h:788
size_t Size
Definition c.h:619
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:448
char * defGetString(DefElem *def)
Definition define.c:34
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
Definition define.c:370
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:274
@ DEPENDENCY_NORMAL
Definition dependency.h:33
DestReceiver * None_Receiver
Definition dest.c:96
int internalerrquery(const char *query)
Definition elog.c:1516
int internalerrposition(int cursorpos)
Definition elog.c:1496
ErrorContextCallback * error_context_stack
Definition elog.c:95
int errhint(const char *fmt,...)
Definition elog.c:1330
int geterrposition(void)
Definition elog.c:1612
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
int errposition(int cursorpos)
Definition elog.c:1480
#define errcontext
Definition elog.h:198
#define WARNING
Definition elog.h:36
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define NOTICE
Definition elog.h:35
#define ereport(elevel,...)
Definition elog.h:150
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:684
#define OidFunctionCall2(functionId, arg1, arg2)
Definition fmgr.h:724
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition foreign.c:38
ForeignServer * GetForeignServerByName(const char *srvname, bool missing_ok)
Definition foreign.c:183
Oid get_foreign_server_oid(const char *servername, bool missing_ok)
Definition foreign.c:705
ForeignDataWrapper * GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
Definition foreign.c:97
bool IsImportableForeignTable(const char *tablename, ImportForeignSchemaStmt *stmt)
Definition foreign.c:483
FdwRoutine * GetFdwRoutine(Oid fdwhandler)
Definition foreign.c:326
#define MappingUserName(userid)
Definition foreign.h:20
static void AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
static Oid lookup_fdw_handler_func(DefElem *handler)
ObjectAddress AlterForeignServerOwner(const char *name, Oid newOwnerId)
static void import_error_callback(void *arg)
void AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
void ImportForeignSchema(ImportForeignSchemaStmt *stmt)
ObjectAddress AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
ObjectAddress AlterForeignServer(AlterForeignServerStmt *stmt)
static Datum optionListToArray(List *options)
Definition foreigncmds.c:66
static void AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
static Oid lookup_fdw_validator_func(DefElem *validator)
void AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
static void user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
ObjectAddress AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
ObjectAddress CreateForeignServer(CreateForeignServerStmt *stmt)
Oid RemoveUserMapping(DropUserMappingStmt *stmt)
ObjectAddress CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
static void parse_func_options(ParseState *pstate, List *func_options, bool *handler_given, Oid *fdwhandler, bool *validator_given, Oid *fdwvalidator)
ObjectAddress AlterUserMapping(AlterUserMappingStmt *stmt)
void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
ObjectAddress CreateUserMapping(CreateUserMappingStmt *stmt)
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1210
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1117
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1435
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
static void * GETSTRUCT(const HeapTupleData *tuple)
#define stmt
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
static struct @172 value
List * lappend(List *list, void *datum)
Definition list.c:339
List * list_delete_cell(List *list, ListCell *cell)
Definition list.c:841
#define RowExclusiveLock
Definition lockdefs.h:38
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1805
char * pstrdup(const char *in)
Definition mcxt.c:1781
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
Oid GetUserId(void)
Definition miscinit.c:469
Datum namein(PG_FUNCTION_ARGS)
Definition name.c:48
char * NameListToString(const List *names)
Definition namespace.c:3664
Oid LookupCreationNamespace(const char *nspname)
Definition namespace.c:3498
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define nodeTag(nodeptr)
Definition nodes.h:139
@ CMD_UTILITY
Definition nodes.h:280
#define makeNode(_type_)
Definition nodes.h:161
#define InvokeObjectPostCreateHook(classId, objectId, subId)
#define InvokeObjectPostAlterHook(classId, objectId, subId)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
@ ROLESPEC_PUBLIC
Definition parsenodes.h:423
#define ACL_USAGE
Definition parsenodes.h:84
@ DEFELEM_UNSPEC
Definition parsenodes.h:834
@ DEFELEM_DROP
Definition parsenodes.h:837
@ DEFELEM_SET
Definition parsenodes.h:835
@ DEFELEM_ADD
Definition parsenodes.h:836
@ DROP_CASCADE
@ OBJECT_FDW
@ OBJECT_FOREIGN_SERVER
void * arg
const void size_t len
void checkMembershipInCurrentExtension(const ObjectAddress *object)
Definition pg_depend.c:258
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:45
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:351
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition pg_depend.c:193
FormData_pg_foreign_data_wrapper * Form_pg_foreign_data_wrapper
FormData_pg_foreign_server * Form_pg_foreign_server
#define lfirst(lc)
Definition pg_list.h:172
#define lfirst_node(type, lc)
Definition pg_list.h:176
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
@ PLAN_STMT_INTERNAL
Definition plannodes.h:40
#define sprintf
Definition port.h:262
List * pg_parse_query(const char *query_string)
Definition postgres.c:604
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:262
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
static Datum CStringGetDatum(const char *X)
Definition postgres.h:380
#define InvalidOid
unsigned int Oid
static int fb(int x)
#define RelationGetDescr(relation)
Definition rel.h:540
List * untransformRelOptions(Datum options)
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
char * defname
Definition parsenodes.h:844
Node * arg
Definition parsenodes.h:845
struct ErrorContextCallback * previous
Definition elog.h:297
char * servername
Definition foreign.h:39
ItemPointerData t_self
Definition htup.h:65
Definition pg_list.h:54
Definition nodes.h:135
bool canSetTag
Definition plannodes.h:86
ParseLoc stmt_len
Definition plannodes.h:165
PlannedStmtOrigin planOrigin
Definition plannodes.h:77
ParseLoc stmt_location
Definition plannodes.h:163
CmdType commandType
Definition plannodes.h:68
Node * utilityStmt
Definition plannodes.h:150
ParseLoc stmt_location
ParseLoc stmt_len
Node * stmt
TupleDesc rd_att
Definition rel.h:112
RoleSpecType roletype
Definition parsenodes.h:429
char * rolename
Definition parsenodes.h:430
Definition c.h:706
bool superuser_arg(Oid roleid)
Definition superuser.c:56
bool superuser(void)
Definition superuser.c:46
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:595
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
Definition syscache.h:111
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition utility.c:501
@ PROCESS_UTILITY_SUBCOMMAND
Definition utility.h:26
static char * VARDATA(const void *PTR)
Definition varatt.h:305
static void SET_VARSIZE(void *PTR, Size len)
Definition varatt.h:432
const char * name
void CommandCounterIncrement(void)
Definition xact.c:1101