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 * Convert a connection string function name passed from the parser to an Oid.
527 */
528static Oid
530{
532 Oid funcargtypes[3];
533
534 if (connection == NULL || connection->arg == NULL)
535 return InvalidOid;
536
537 /* connection string functions take user oid, server oid */
538 funcargtypes[0] = OIDOID;
539 funcargtypes[1] = OIDOID;
541
543
544 /* check that connection string function has correct return type */
548 errmsg("function %s must return type %s",
549 NameListToString((List *) connection->arg), "text")));
550
551 return connectionOid;
552}
553
554/*
555 * Process function options of CREATE/ALTER FDW
556 */
557static void
558parse_func_options(ParseState *pstate, List *func_options,
559 bool *handler_given, Oid *fdwhandler,
560 bool *validator_given, Oid *fdwvalidator,
561 bool *connection_given, Oid *fdwconnection)
562{
563 ListCell *cell;
564
565 *handler_given = false;
566 *validator_given = false;
567 *connection_given = false;
568 /* return InvalidOid if not given */
569 *fdwhandler = InvalidOid;
570 *fdwvalidator = InvalidOid;
571 *fdwconnection = InvalidOid;
572
573 foreach(cell, func_options)
574 {
575 DefElem *def = (DefElem *) lfirst(cell);
576
577 if (strcmp(def->defname, "handler") == 0)
578 {
579 if (*handler_given)
580 errorConflictingDefElem(def, pstate);
581 *handler_given = true;
582 *fdwhandler = lookup_fdw_handler_func(def);
583 }
584 else if (strcmp(def->defname, "validator") == 0)
585 {
586 if (*validator_given)
587 errorConflictingDefElem(def, pstate);
588 *validator_given = true;
589 *fdwvalidator = lookup_fdw_validator_func(def);
590 }
591 else if (strcmp(def->defname, "connection") == 0)
592 {
593 if (*connection_given)
594 errorConflictingDefElem(def, pstate);
595 *connection_given = true;
596 *fdwconnection = lookup_fdw_connection_func(def);
597 }
598 else
599 elog(ERROR, "option \"%s\" not recognized",
600 def->defname);
601 }
602}
603
604/*
605 * Create a foreign-data wrapper
606 */
609{
610 Relation rel;
613 HeapTuple tuple;
614 Oid fdwId;
615 bool handler_given;
616 bool validator_given;
617 bool connection_given;
618 Oid fdwhandler;
619 Oid fdwvalidator;
620 Oid fdwconnection;
621 Datum fdwoptions;
622 Oid ownerId;
625
627
628 /* Must be superuser */
629 if (!superuser())
632 errmsg("permission denied to create foreign-data wrapper \"%s\"",
633 stmt->fdwname),
634 errhint("Must be superuser to create a foreign-data wrapper.")));
635
636 /* For now the owner cannot be specified on create. Use effective user ID. */
637 ownerId = GetUserId();
638
639 /*
640 * Check that there is no other foreign-data wrapper by this name.
641 */
642 if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
645 errmsg("foreign-data wrapper \"%s\" already exists",
646 stmt->fdwname)));
647
648 /*
649 * Insert tuple into pg_foreign_data_wrapper.
650 */
651 memset(values, 0, sizeof(values));
652 memset(nulls, false, sizeof(nulls));
653
660
661 /* Lookup handler and validator functions, if given */
662 parse_func_options(pstate, stmt->func_options,
663 &handler_given, &fdwhandler,
664 &validator_given, &fdwvalidator,
665 &connection_given, &fdwconnection);
666
670
671 nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
672
675 stmt->options,
676 fdwvalidator);
677
678 if (DatumGetPointer(fdwoptions) != NULL)
680 else
682
683 tuple = heap_form_tuple(rel->rd_att, values, nulls);
684
685 CatalogTupleInsert(rel, tuple);
686
687 heap_freetuple(tuple);
688
689 /* record dependencies */
691 myself.objectId = fdwId;
692 myself.objectSubId = 0;
693
694 if (OidIsValid(fdwhandler))
695 {
697 referenced.objectId = fdwhandler;
698 referenced.objectSubId = 0;
700 }
701
702 if (OidIsValid(fdwvalidator))
703 {
705 referenced.objectId = fdwvalidator;
706 referenced.objectSubId = 0;
708 }
709
710 if (OidIsValid(fdwconnection))
711 {
713 referenced.objectId = fdwconnection;
714 referenced.objectSubId = 0;
716 }
717
719
720 /* dependency on extension */
722
723 /* Post creation hook for new foreign data wrapper */
725
727
728 return myself;
729}
730
731
732/*
733 * Alter foreign-data wrapper
734 */
737{
738 Relation rel;
739 HeapTuple tp;
744 Oid fdwId;
745 bool isnull;
746 Datum datum;
747 bool handler_given;
748 bool validator_given;
749 bool connection_given;
750 Oid fdwhandler;
751 Oid fdwvalidator;
752 Oid fdwconnection;
754
756
757 /* Must be superuser */
758 if (!superuser())
761 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
762 stmt->fdwname),
763 errhint("Must be superuser to alter a foreign-data wrapper.")));
764
766 CStringGetDatum(stmt->fdwname));
767
768 if (!HeapTupleIsValid(tp))
771 errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
772
774 fdwId = fdwForm->oid;
775
776 memset(repl_val, 0, sizeof(repl_val));
777 memset(repl_null, false, sizeof(repl_null));
778 memset(repl_repl, false, sizeof(repl_repl));
779
780 parse_func_options(pstate, stmt->func_options,
781 &handler_given, &fdwhandler,
782 &validator_given, &fdwvalidator,
783 &connection_given, &fdwconnection);
784
785 if (handler_given)
786 {
789
790 /*
791 * It could be that the behavior of accessing foreign table changes
792 * with the new handler. Warn about this.
793 */
795 (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
796 }
797 else
798 {
799 /* handler unchanged */
800 fdwhandler = fdwForm->fdwhandler;
801 }
802
803 if (validator_given)
804 {
807
808 /*
809 * It could be that existing options for the FDW or dependent SERVER,
810 * USER MAPPING or FOREIGN TABLE objects are no longer valid according
811 * to the new validator. Warn about this.
812 */
813 if (OidIsValid(fdwvalidator))
815 (errmsg("changing the foreign-data wrapper validator can cause "
816 "the options for dependent objects to become invalid")));
817 }
818 else
819 {
820 /*
821 * Validator is not changed, but we need it for validating options.
822 */
823 fdwvalidator = fdwForm->fdwvalidator;
824 }
825
827 {
830
831 /*
832 * If the connection function is changed, behavior of dependent
833 * subscriptions can change. If NO CONNECTION, dependent
834 * subscriptions will fail.
835 */
836 if (OidIsValid(fdwForm->fdwconnection))
837 {
838 if (OidIsValid(fdwconnection))
840 (errmsg("changing the foreign-data wrapper connection function can cause "
841 "the options for dependent objects to become invalid")));
842 else
844 (errmsg("removing the foreign-data wrapper connection function will cause "
845 "dependent subscriptions to fail")));
846 }
847 }
848 else
849 {
850 /* connection function unchanged */
851 fdwconnection = fdwForm->fdwconnection;
852 }
853
854 /*
855 * If options specified, validate and update.
856 */
857 if (stmt->options)
858 {
859 /* Extract the current options */
861 tp,
863 &isnull);
864 if (isnull)
865 datum = PointerGetDatum(NULL);
866
867 /* Transform the options */
869 datum,
870 stmt->options,
871 fdwvalidator);
872
873 if (DatumGetPointer(datum) != NULL)
875 else
877
879 }
880
881 /* Everything looks good - update the tuple */
884
885 CatalogTupleUpdate(rel, &tp->t_self, tp);
886
887 heap_freetuple(tp);
888
890
891 /* Update function dependencies if we changed them */
893 {
895
896 /*
897 * Flush all existing dependency records of this FDW on functions; we
898 * assume there can be none other than the ones we are fixing.
899 */
901 fdwId,
904
905 /* And build new ones. */
906
907 if (OidIsValid(fdwhandler))
908 {
910 referenced.objectId = fdwhandler;
911 referenced.objectSubId = 0;
913 }
914
915 if (OidIsValid(fdwvalidator))
916 {
918 referenced.objectId = fdwvalidator;
919 referenced.objectSubId = 0;
921 }
922
923 if (OidIsValid(fdwconnection))
924 {
926 referenced.objectId = fdwconnection;
927 referenced.objectSubId = 0;
929 }
930 }
931
933
935
936 return myself;
937}
938
939
940/*
941 * Create a foreign server
942 */
945{
946 Relation rel;
947 Datum srvoptions;
949 bool nulls[Natts_pg_foreign_server];
950 HeapTuple tuple;
951 Oid srvId;
952 Oid ownerId;
957
959
960 /* For now the owner cannot be specified on create. Use effective user ID. */
961 ownerId = GetUserId();
962
963 /*
964 * Check that there is no other foreign server by this name. If there is
965 * one, do nothing if IF NOT EXISTS was specified.
966 */
967 srvId = get_foreign_server_oid(stmt->servername, true);
968 if (OidIsValid(srvId))
969 {
970 if (stmt->if_not_exists)
971 {
972 /*
973 * If we are in an extension script, insist that the pre-existing
974 * object be a member of the extension, to avoid security risks.
975 */
978
979 /* OK to skip */
982 errmsg("server \"%s\" already exists, skipping",
983 stmt->servername)));
986 }
987 else
990 errmsg("server \"%s\" already exists",
991 stmt->servername)));
992 }
993
994 /*
995 * Check that the FDW exists and that we have USAGE on it. Also get the
996 * actual FDW for option validation etc.
997 */
998 fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
999
1001 if (aclresult != ACLCHECK_OK)
1003
1004 /*
1005 * Insert tuple into pg_foreign_server.
1006 */
1007 memset(values, 0, sizeof(values));
1008 memset(nulls, false, sizeof(nulls));
1009
1017
1018 /* Add server type if supplied */
1019 if (stmt->servertype)
1021 CStringGetTextDatum(stmt->servertype);
1022 else
1023 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
1024
1025 /* Add server version if supplied */
1026 if (stmt->version)
1028 CStringGetTextDatum(stmt->version);
1029 else
1030 nulls[Anum_pg_foreign_server_srvversion - 1] = true;
1031
1032 /* Start with a blank acl */
1033 nulls[Anum_pg_foreign_server_srvacl - 1] = true;
1034
1035 /* Add server options */
1038 stmt->options,
1039 fdw->fdwvalidator);
1040
1041 if (DatumGetPointer(srvoptions) != NULL)
1042 values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
1043 else
1044 nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
1045
1046 tuple = heap_form_tuple(rel->rd_att, values, nulls);
1047
1048 CatalogTupleInsert(rel, tuple);
1049
1050 heap_freetuple(tuple);
1051
1052 /* record dependencies */
1054 myself.objectId = srvId;
1055 myself.objectSubId = 0;
1056
1058 referenced.objectId = fdw->fdwid;
1059 referenced.objectSubId = 0;
1061
1063
1064 /* dependency on extension */
1066
1067 /* Post creation hook for new foreign server */
1069
1071
1072 return myself;
1073}
1074
1075
1076/*
1077 * Alter foreign server
1078 */
1081{
1082 Relation rel;
1083 HeapTuple tp;
1087 Oid srvId;
1089 ObjectAddress address;
1090
1092
1094 CStringGetDatum(stmt->servername));
1095
1096 if (!HeapTupleIsValid(tp))
1097 ereport(ERROR,
1099 errmsg("server \"%s\" does not exist", stmt->servername)));
1100
1102 srvId = srvForm->oid;
1103
1104 /*
1105 * Only owner or a superuser can ALTER a SERVER.
1106 */
1109 stmt->servername);
1110
1111 memset(repl_val, 0, sizeof(repl_val));
1112 memset(repl_null, false, sizeof(repl_null));
1113 memset(repl_repl, false, sizeof(repl_repl));
1114
1115 if (stmt->has_version)
1116 {
1117 /*
1118 * Change the server VERSION string.
1119 */
1120 if (stmt->version)
1122 CStringGetTextDatum(stmt->version);
1123 else
1125
1127 }
1128
1129 if (stmt->options)
1130 {
1132 Datum datum;
1133 bool isnull;
1134
1135 /* Extract the current srvoptions */
1137 tp,
1139 &isnull);
1140 if (isnull)
1141 datum = PointerGetDatum(NULL);
1142
1143 /* Prepare the options array */
1145 datum,
1146 stmt->options,
1147 fdw->fdwvalidator);
1148
1149 if (DatumGetPointer(datum) != NULL)
1151 else
1153
1155 }
1156
1157 /* Everything looks good - update the tuple */
1158 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1160
1161 CatalogTupleUpdate(rel, &tp->t_self, tp);
1162
1164
1166
1167 heap_freetuple(tp);
1168
1170
1171 return address;
1172}
1173
1174
1175/*
1176 * Common routine to check permission for user-mapping-related DDL
1177 * commands. We allow server owners to operate on any mapping, and
1178 * users to operate on their own mapping.
1179 */
1180static void
1181user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1182{
1184
1186 {
1187 if (umuserid == curuserid)
1188 {
1190
1192 if (aclresult != ACLCHECK_OK)
1194 }
1195 else
1197 servername);
1198 }
1199}
1200
1201
1202/*
1203 * Create user mapping
1204 */
1207{
1208 Relation rel;
1211 bool nulls[Natts_pg_user_mapping];
1212 HeapTuple tuple;
1213 Oid useId;
1214 Oid umId;
1219 RoleSpec *role = (RoleSpec *) stmt->user;
1220
1222
1223 if (role->roletype == ROLESPEC_PUBLIC)
1225 else
1226 useId = get_rolespec_oid(stmt->user, false);
1227
1228 /* Check that the server exists. */
1229 srv = GetForeignServerByName(stmt->servername, false);
1230
1231 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1232
1233 /*
1234 * Check that the user mapping is unique within server.
1235 */
1238 ObjectIdGetDatum(srv->serverid));
1239
1240 if (OidIsValid(umId))
1241 {
1242 if (stmt->if_not_exists)
1243 {
1244 /*
1245 * Since user mappings aren't members of extensions (see comments
1246 * below), no need for checkMembershipInCurrentExtension here.
1247 */
1250 errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
1252 stmt->servername)));
1253
1255 return InvalidObjectAddress;
1256 }
1257 else
1258 ereport(ERROR,
1260 errmsg("user mapping for \"%s\" already exists for server \"%s\"",
1262 stmt->servername)));
1263 }
1264
1265 fdw = GetForeignDataWrapper(srv->fdwid);
1266
1267 /*
1268 * Insert tuple into pg_user_mapping.
1269 */
1270 memset(values, 0, sizeof(values));
1271 memset(nulls, false, sizeof(nulls));
1272
1278
1279 /* Add user options */
1282 stmt->options,
1283 fdw->fdwvalidator);
1284
1287 else
1288 nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1289
1290 tuple = heap_form_tuple(rel->rd_att, values, nulls);
1291
1292 CatalogTupleInsert(rel, tuple);
1293
1294 heap_freetuple(tuple);
1295
1296 /* Add dependency on the server */
1297 myself.classId = UserMappingRelationId;
1298 myself.objectId = umId;
1299 myself.objectSubId = 0;
1300
1302 referenced.objectId = srv->serverid;
1303 referenced.objectSubId = 0;
1305
1306 if (OidIsValid(useId))
1307 {
1308 /* Record the mapped user dependency */
1310 }
1311
1312 /*
1313 * Perhaps someday there should be a recordDependencyOnCurrentExtension
1314 * call here; but since roles aren't members of extensions, it seems like
1315 * user mappings shouldn't be either. Note that the grammar and pg_dump
1316 * would need to be extended too if we change this.
1317 */
1318
1319 /* Post creation hook for new user mapping */
1321
1323
1324 return myself;
1325}
1326
1327
1328/*
1329 * Alter user mapping
1330 */
1333{
1334 Relation rel;
1335 HeapTuple tp;
1339 Oid useId;
1340 Oid umId;
1342 ObjectAddress address;
1343 RoleSpec *role = (RoleSpec *) stmt->user;
1344
1346
1347 if (role->roletype == ROLESPEC_PUBLIC)
1349 else
1350 useId = get_rolespec_oid(stmt->user, false);
1351
1352 srv = GetForeignServerByName(stmt->servername, false);
1353
1356 ObjectIdGetDatum(srv->serverid));
1357 if (!OidIsValid(umId))
1358 ereport(ERROR,
1360 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1361 MappingUserName(useId), stmt->servername)));
1362
1363 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1364
1366
1367 if (!HeapTupleIsValid(tp))
1368 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1369
1370 memset(repl_val, 0, sizeof(repl_val));
1371 memset(repl_null, false, sizeof(repl_null));
1372 memset(repl_repl, false, sizeof(repl_repl));
1373
1374 if (stmt->options)
1375 {
1377 Datum datum;
1378 bool isnull;
1379
1380 /*
1381 * Process the options.
1382 */
1383
1384 fdw = GetForeignDataWrapper(srv->fdwid);
1385
1387 tp,
1389 &isnull);
1390 if (isnull)
1391 datum = PointerGetDatum(NULL);
1392
1393 /* Prepare the options array */
1395 datum,
1396 stmt->options,
1397 fdw->fdwvalidator);
1398
1399 if (DatumGetPointer(datum) != NULL)
1401 else
1403
1405 }
1406
1407 /* Everything looks good - update the tuple */
1408 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1410
1411 CatalogTupleUpdate(rel, &tp->t_self, tp);
1412
1414 umId, 0);
1415
1417
1418 heap_freetuple(tp);
1419
1421
1422 return address;
1423}
1424
1425
1426/*
1427 * Drop user mapping
1428 */
1429Oid
1431{
1432 ObjectAddress object;
1433 Oid useId;
1434 Oid umId;
1436 RoleSpec *role = (RoleSpec *) stmt->user;
1437
1438 if (role->roletype == ROLESPEC_PUBLIC)
1440 else
1441 {
1442 useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1443 if (!OidIsValid(useId))
1444 {
1445 /*
1446 * IF EXISTS specified, role not found and not public. Notice this
1447 * and leave.
1448 */
1449 elog(NOTICE, "role \"%s\" does not exist, skipping",
1450 role->rolename);
1451 return InvalidOid;
1452 }
1453 }
1454
1455 srv = GetForeignServerByName(stmt->servername, true);
1456
1457 if (!srv)
1458 {
1459 if (!stmt->missing_ok)
1460 ereport(ERROR,
1462 errmsg("server \"%s\" does not exist",
1463 stmt->servername)));
1464 /* IF EXISTS, just note it */
1466 (errmsg("server \"%s\" does not exist, skipping",
1467 stmt->servername)));
1468 return InvalidOid;
1469 }
1470
1473 ObjectIdGetDatum(srv->serverid));
1474
1475 if (!OidIsValid(umId))
1476 {
1477 if (!stmt->missing_ok)
1478 ereport(ERROR,
1480 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1481 MappingUserName(useId), stmt->servername)));
1482
1483 /* IF EXISTS specified, just note it */
1485 (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
1486 MappingUserName(useId), stmt->servername)));
1487 return InvalidOid;
1488 }
1489
1490 user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1491
1492 /*
1493 * Do the deletion
1494 */
1495 object.classId = UserMappingRelationId;
1496 object.objectId = umId;
1497 object.objectSubId = 0;
1498
1499 performDeletion(&object, DROP_CASCADE, 0);
1500
1501 return umId;
1502}
1503
1504
1505/*
1506 * Create a foreign table
1507 * call after DefineRelation().
1508 */
1509void
1511{
1515 bool nulls[Natts_pg_foreign_table];
1516 HeapTuple tuple;
1520 Oid ownerId;
1522 ForeignServer *server;
1523
1524 /*
1525 * Advance command counter to ensure the pg_attribute tuple is visible;
1526 * the tuple might be updated to add constraints in previous step.
1527 */
1529
1531
1532 /*
1533 * For now the owner cannot be specified on create. Use effective user ID.
1534 */
1535 ownerId = GetUserId();
1536
1537 /*
1538 * Check that the foreign server exists and that we have USAGE on it. Also
1539 * get the actual FDW for option validation etc.
1540 */
1541 server = GetForeignServerByName(stmt->servername, false);
1543 if (aclresult != ACLCHECK_OK)
1545
1546 fdw = GetForeignDataWrapper(server->fdwid);
1547
1548 /*
1549 * Insert tuple into pg_foreign_table.
1550 */
1551 memset(values, 0, sizeof(values));
1552 memset(nulls, false, sizeof(nulls));
1553
1556 /* Add table generic options */
1559 stmt->options,
1560 fdw->fdwvalidator);
1561
1564 else
1565 nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1566
1567 tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1568
1569 CatalogTupleInsert(ftrel, tuple);
1570
1571 heap_freetuple(tuple);
1572
1573 /* Add pg_class dependency on the server */
1574 myself.classId = RelationRelationId;
1575 myself.objectId = relid;
1576 myself.objectSubId = 0;
1577
1579 referenced.objectId = server->serverid;
1580 referenced.objectSubId = 0;
1582
1584}
1585
1586/*
1587 * Import a foreign schema
1588 */
1589void
1591{
1592 ForeignServer *server;
1596 List *cmd_list;
1597 ListCell *lc;
1598
1599 /* Check that the foreign server exists and that we have USAGE on it */
1600 server = GetForeignServerByName(stmt->server_name, false);
1602 if (aclresult != ACLCHECK_OK)
1604
1605 /* Check that the schema exists and we have CREATE permissions on it */
1606 (void) LookupCreationNamespace(stmt->local_schema);
1607
1608 /* Get the FDW and check it supports IMPORT */
1609 fdw = GetForeignDataWrapper(server->fdwid);
1610 if (!OidIsValid(fdw->fdwhandler))
1611 ereport(ERROR,
1613 errmsg("foreign-data wrapper \"%s\" has no handler",
1614 fdw->fdwname)));
1615 fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1616 if (fdw_routine->ImportForeignSchema == NULL)
1617 ereport(ERROR,
1619 errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1620 fdw->fdwname)));
1621
1622 /* Call FDW to get a list of commands */
1623 cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1624
1625 /* Parse and execute each command */
1626 foreach(lc, cmd_list)
1627 {
1628 char *cmd = (char *) lfirst(lc);
1629 import_error_callback_arg callback_arg;
1632 ListCell *lc2;
1633
1634 /*
1635 * Setup error traceback support for ereport(). This is so that any
1636 * error in the generated SQL will be displayed nicely.
1637 */
1638 callback_arg.tablename = NULL; /* not known yet */
1639 callback_arg.cmd = cmd;
1641 sqlerrcontext.arg = &callback_arg;
1644
1645 /*
1646 * Parse the SQL string into a list of raw parse trees.
1647 */
1649
1650 /*
1651 * Process each parse tree (we allow the FDW to put more than one
1652 * command per string, though this isn't really advised).
1653 */
1654 foreach(lc2, raw_parsetree_list)
1655 {
1658 PlannedStmt *pstmt;
1659
1660 /*
1661 * Because we only allow CreateForeignTableStmt, we can skip parse
1662 * analysis, rewrite, and planning steps here.
1663 */
1665 elog(ERROR,
1666 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1667 fdw->fdwname, (int) nodeTag(cstmt));
1668
1669 /* Ignore commands for tables excluded by filter options */
1670 if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
1671 continue;
1672
1673 /* Enable reporting of current table's name on error */
1674 callback_arg.tablename = cstmt->base.relation->relname;
1675
1676 /* Ensure creation schema is the one given in IMPORT statement */
1677 cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1678
1679 /* No planning needed, just make a wrapper PlannedStmt */
1680 pstmt = makeNode(PlannedStmt);
1681 pstmt->commandType = CMD_UTILITY;
1682 pstmt->canSetTag = false;
1683 pstmt->utilityStmt = (Node *) cstmt;
1684 pstmt->stmt_location = rs->stmt_location;
1685 pstmt->stmt_len = rs->stmt_len;
1687
1688 /* Execute statement */
1689 ProcessUtility(pstmt, cmd, false,
1692
1693 /* Be sure to advance the command counter between subcommands */
1695
1696 callback_arg.tablename = NULL;
1697 }
1698
1700 }
1701}
1702
1703/*
1704 * error context callback to let us supply the failing SQL statement's text
1705 */
1706static void
1708{
1711
1712 /* If it's a syntax error, convert to internal syntax error report */
1714 if (syntaxerrposition > 0)
1715 {
1716 errposition(0);
1718 internalerrquery(callback_arg->cmd);
1719 }
1720
1721 if (callback_arg->tablename)
1722 errcontext("importing foreign table \"%s\"",
1723 callback_arg->tablename);
1724}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition acl.c:1147
void check_can_set_role(Oid member, Oid role)
Definition acl.c:5371
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition acl.c:5639
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
@ ACLCHECK_NOT_OWNER
Definition acl.h:186
#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:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3879
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4133
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:188
#define CStringGetTextDatum(s)
Definition builtins.h:98
#define NameStr(name)
Definition c.h:837
#define VARHDRSZ
Definition c.h:783
#define OidIsValid(objectId)
Definition c.h:860
size_t Size
Definition c.h:691
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:279
@ DEPENDENCY_NORMAL
Definition dependency.h:33
DestReceiver * None_Receiver
Definition dest.c:96
Datum arg
Definition elog.c:1322
ErrorContextCallback * error_context_stack
Definition elog.c:99
int errcode(int sqlerrcode)
Definition elog.c:874
#define errcontext
Definition elog.h:198
int errhint(const char *fmt,...) pg_attribute_printf(1
int internalerrquery(const char *query)
int internalerrposition(int cursorpos)
#define WARNING
Definition elog.h:36
#define ERROR
Definition elog.h:39
int geterrposition(void)
#define elog(elevel,...)
Definition elog.h:226
#define NOTICE
Definition elog.h:35
int errposition(int cursorpos)
#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:39
ForeignServer * GetForeignServerByName(const char *srvname, bool missing_ok)
Definition foreign.c:210
Oid get_foreign_server_oid(const char *servername, bool missing_ok)
Definition foreign.c:793
ForeignDataWrapper * GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
Definition foreign.c:99
bool IsImportableForeignTable(const char *tablename, ImportForeignSchemaStmt *stmt)
Definition foreign.c:571
FdwRoutine * GetFdwRoutine(Oid fdwhandler)
Definition foreign.c:414
#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)
static Oid lookup_fdw_connection_func(DefElem *connection)
ObjectAddress AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
ObjectAddress CreateForeignServer(CreateForeignServerStmt *stmt)
Oid RemoveUserMapping(DropUserMappingStmt *stmt)
ObjectAddress CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
ObjectAddress AlterUserMapping(AlterUserMappingStmt *stmt)
static void parse_func_options(ParseState *pstate, List *func_options, bool *handler_given, Oid *fdwhandler, bool *validator_given, Oid *fdwvalidator, bool *connection_given, Oid *fdwconnection)
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:1130
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1037
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1384
#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 @174 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:1875
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:470
Datum namein(PG_FUNCTION_ARGS)
Definition name.c:48
char * NameListToString(const List *names)
Definition namespace.c:3666
Oid LookupCreationNamespace(const char *nspname)
Definition namespace.c:3500
#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
static char * errmsg
#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:847
@ DEFELEM_DROP
Definition parsenodes.h:850
@ DEFELEM_SET
Definition parsenodes.h:848
@ DEFELEM_ADD
Definition parsenodes.h:849
@ DROP_CASCADE
@ OBJECT_FDW
@ OBJECT_FOREIGN_SERVER
const void size_t len
void checkMembershipInCurrentExtension(const ObjectAddress *object)
Definition pg_depend.c:260
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:47
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:353
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition pg_depend.c:195
END_CATALOG_STRUCT typedef FormData_pg_foreign_data_wrapper * Form_pg_foreign_data_wrapper
END_CATALOG_STRUCT typedef 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:38
#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:342
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static Datum CStringGetDatum(const char *X)
Definition postgres.h:370
#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:857
Node * arg
Definition parsenodes.h:858
struct ErrorContextCallback * previous
Definition elog.h:297
char * servername
Definition foreign.h:40
ItemPointerData t_self
Definition htup.h:65
Definition pg_list.h:54
Definition nodes.h:135
bool canSetTag
Definition plannodes.h:84
ParseLoc stmt_len
Definition plannodes.h:169
PlannedStmtOrigin planOrigin
Definition plannodes.h:75
ParseLoc stmt_location
Definition plannodes.h:167
CmdType commandType
Definition plannodes.h:66
Node * utilityStmt
Definition plannodes.h:151
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:778
bool superuser_arg(Oid roleid)
Definition superuser.c:57
bool superuser(void)
Definition superuser.c:47
Datum SysCacheGetAttr(SysCacheIdentifier 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:504
@ 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:1102