PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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-2024, 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 *value;
75 Size len;
76 text *t;
77
78 value = defGetString(def);
79 len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
80 t = palloc(len + 1);
81 SET_VARSIZE(t, len);
82 sprintf(VARDATA(t), "%s=%s", def->defname, value);
83
84 astate = accumArrayResult(astate, PointerGetDatum(t),
85 false, TEXTOID,
87 }
88
89 if (astate)
91
92 return PointerGetDatum(NULL);
93}
94
95
96/*
97 * Transform a list of DefElem into text array format. This is substantially
98 * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
99 * actions for modifying an existing list of options, which is passed in
100 * Datum form as oldOptions. Also, if fdwvalidator isn't InvalidOid
101 * it specifies a validator function to call on the result.
102 *
103 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
104 * if the list is empty.
105 *
106 * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
107 * FOREIGN TABLE.
108 */
109Datum
111 Datum oldOptions,
112 List *options,
113 Oid fdwvalidator)
114{
115 List *resultOptions = untransformRelOptions(oldOptions);
116 ListCell *optcell;
117 Datum result;
118
119 foreach(optcell, options)
120 {
121 DefElem *od = lfirst(optcell);
122 ListCell *cell;
123
124 /*
125 * Find the element in resultOptions. We need this for validation in
126 * all cases.
127 */
128 foreach(cell, resultOptions)
129 {
130 DefElem *def = lfirst(cell);
131
132 if (strcmp(def->defname, od->defname) == 0)
133 break;
134 }
135
136 /*
137 * It is possible to perform multiple SET/DROP actions on the same
138 * option. The standard permits this, as long as the options to be
139 * added are unique. Note that an unspecified action is taken to be
140 * ADD.
141 */
142 switch (od->defaction)
143 {
144 case DEFELEM_DROP:
145 if (!cell)
147 (errcode(ERRCODE_UNDEFINED_OBJECT),
148 errmsg("option \"%s\" not found",
149 od->defname)));
150 resultOptions = list_delete_cell(resultOptions, cell);
151 break;
152
153 case DEFELEM_SET:
154 if (!cell)
156 (errcode(ERRCODE_UNDEFINED_OBJECT),
157 errmsg("option \"%s\" not found",
158 od->defname)));
159 lfirst(cell) = od;
160 break;
161
162 case DEFELEM_ADD:
163 case DEFELEM_UNSPEC:
164 if (cell)
167 errmsg("option \"%s\" provided more than once",
168 od->defname)));
169 resultOptions = lappend(resultOptions, od);
170 break;
171
172 default:
173 elog(ERROR, "unrecognized action %d on option \"%s\"",
174 (int) od->defaction, od->defname);
175 break;
176 }
177 }
178
179 result = optionListToArray(resultOptions);
180
181 if (OidIsValid(fdwvalidator))
182 {
183 Datum valarg = result;
184
185 /*
186 * Pass a null options list as an empty array, so that validators
187 * don't have to be declared non-strict to handle the case.
188 */
189 if (DatumGetPointer(valarg) == NULL)
190 valarg = PointerGetDatum(construct_empty_array(TEXTOID));
191 OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
192 }
193
194 return result;
195}
196
197
198/*
199 * Internal workhorse for changing a data wrapper's owner.
200 *
201 * Allow this only for superusers; also the new owner must be a
202 * superuser.
203 */
204static void
206{
208 Datum repl_val[Natts_pg_foreign_data_wrapper];
209 bool repl_null[Natts_pg_foreign_data_wrapper];
210 bool repl_repl[Natts_pg_foreign_data_wrapper];
211 Acl *newAcl;
212 Datum aclDatum;
213 bool isNull;
214
216
217 /* Must be a superuser to change a FDW owner */
218 if (!superuser())
220 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
221 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
222 NameStr(form->fdwname)),
223 errhint("Must be superuser to change owner of a foreign-data wrapper.")));
224
225 /* New owner must also be a superuser */
226 if (!superuser_arg(newOwnerId))
228 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
229 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
230 NameStr(form->fdwname)),
231 errhint("The owner of a foreign-data wrapper must be a superuser.")));
232
233 if (form->fdwowner != newOwnerId)
234 {
235 memset(repl_null, false, sizeof(repl_null));
236 memset(repl_repl, false, sizeof(repl_repl));
237
238 repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
239 repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
240
241 aclDatum = heap_getattr(tup,
242 Anum_pg_foreign_data_wrapper_fdwacl,
243 RelationGetDescr(rel),
244 &isNull);
245 /* Null ACLs do not require changes */
246 if (!isNull)
247 {
248 newAcl = aclnewowner(DatumGetAclP(aclDatum),
249 form->fdwowner, newOwnerId);
250 repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
251 repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
252 }
253
254 tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
255 repl_repl);
256
257 CatalogTupleUpdate(rel, &tup->t_self, tup);
258
259 /* Update owner dependency reference */
260 changeDependencyOnOwner(ForeignDataWrapperRelationId,
261 form->oid,
262 newOwnerId);
263 }
264
265 InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
266 form->oid, 0);
267}
268
269/*
270 * Change foreign-data wrapper owner -- by name
271 *
272 * Note restrictions in the "_internal" function, above.
273 */
275AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
276{
277 Oid fdwId;
278 HeapTuple tup;
279 Relation rel;
280 ObjectAddress address;
282
283
284 rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
285
286 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
287
288 if (!HeapTupleIsValid(tup))
290 (errcode(ERRCODE_UNDEFINED_OBJECT),
291 errmsg("foreign-data wrapper \"%s\" does not exist", name)));
292
294 fdwId = form->oid;
295
296 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
297
298 ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
299
300 heap_freetuple(tup);
301
303
304 return address;
305}
306
307/*
308 * Change foreign-data wrapper owner -- by OID
309 *
310 * Note restrictions in the "_internal" function, above.
311 */
312void
314{
315 HeapTuple tup;
316 Relation rel;
317
318 rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
319
320 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
321
322 if (!HeapTupleIsValid(tup))
324 (errcode(ERRCODE_UNDEFINED_OBJECT),
325 errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
326
327 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
328
329 heap_freetuple(tup);
330
332}
333
334/*
335 * Internal workhorse for changing a foreign server's owner
336 */
337static void
339{
341 Datum repl_val[Natts_pg_foreign_server];
342 bool repl_null[Natts_pg_foreign_server];
343 bool repl_repl[Natts_pg_foreign_server];
344 Acl *newAcl;
345 Datum aclDatum;
346 bool isNull;
347
348 form = (Form_pg_foreign_server) GETSTRUCT(tup);
349
350 if (form->srvowner != newOwnerId)
351 {
352 /* Superusers can always do it */
353 if (!superuser())
354 {
355 Oid srvId;
356 AclResult aclresult;
357
358 srvId = form->oid;
359
360 /* Must be owner */
361 if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
363 NameStr(form->srvname));
364
365 /* Must be able to become new owner */
366 check_can_set_role(GetUserId(), newOwnerId);
367
368 /* New owner must have USAGE privilege on foreign-data wrapper */
369 aclresult = object_aclcheck(ForeignDataWrapperRelationId, form->srvfdw, newOwnerId, ACL_USAGE);
370 if (aclresult != ACLCHECK_OK)
371 {
372 ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
373
374 aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
375 }
376 }
377
378 memset(repl_null, false, sizeof(repl_null));
379 memset(repl_repl, false, sizeof(repl_repl));
380
381 repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
382 repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
383
384 aclDatum = heap_getattr(tup,
385 Anum_pg_foreign_server_srvacl,
386 RelationGetDescr(rel),
387 &isNull);
388 /* Null ACLs do not require changes */
389 if (!isNull)
390 {
391 newAcl = aclnewowner(DatumGetAclP(aclDatum),
392 form->srvowner, newOwnerId);
393 repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
394 repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
395 }
396
397 tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
398 repl_repl);
399
400 CatalogTupleUpdate(rel, &tup->t_self, tup);
401
402 /* Update owner dependency reference */
403 changeDependencyOnOwner(ForeignServerRelationId, form->oid,
404 newOwnerId);
405 }
406
407 InvokeObjectPostAlterHook(ForeignServerRelationId,
408 form->oid, 0);
409}
410
411/*
412 * Change foreign server owner -- by name
413 */
415AlterForeignServerOwner(const char *name, Oid newOwnerId)
416{
417 Oid servOid;
418 HeapTuple tup;
419 Relation rel;
420 ObjectAddress address;
422
423 rel = table_open(ForeignServerRelationId, RowExclusiveLock);
424
425 tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
426
427 if (!HeapTupleIsValid(tup))
429 (errcode(ERRCODE_UNDEFINED_OBJECT),
430 errmsg("server \"%s\" does not exist", name)));
431
432 form = (Form_pg_foreign_server) GETSTRUCT(tup);
433 servOid = form->oid;
434
435 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
436
437 ObjectAddressSet(address, ForeignServerRelationId, servOid);
438
439 heap_freetuple(tup);
440
442
443 return address;
444}
445
446/*
447 * Change foreign server owner -- by OID
448 */
449void
451{
452 HeapTuple tup;
453 Relation rel;
454
455 rel = table_open(ForeignServerRelationId, RowExclusiveLock);
456
457 tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
458
459 if (!HeapTupleIsValid(tup))
461 (errcode(ERRCODE_UNDEFINED_OBJECT),
462 errmsg("foreign server with OID %u does not exist", srvId)));
463
464 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
465
466 heap_freetuple(tup);
467
469}
470
471/*
472 * Convert a handler function name passed from the parser to an Oid.
473 */
474static Oid
476{
477 Oid handlerOid;
478
479 if (handler == NULL || handler->arg == NULL)
480 return InvalidOid;
481
482 /* handlers have no arguments */
483 handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
484
485 /* check that handler has correct return type */
486 if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
488 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
489 errmsg("function %s must return type %s",
490 NameListToString((List *) handler->arg), "fdw_handler")));
491
492 return handlerOid;
493}
494
495/*
496 * Convert a validator function name passed from the parser to an Oid.
497 */
498static Oid
500{
501 Oid funcargtypes[2];
502
503 if (validator == NULL || validator->arg == NULL)
504 return InvalidOid;
505
506 /* validators take text[], oid */
507 funcargtypes[0] = TEXTARRAYOID;
508 funcargtypes[1] = OIDOID;
509
510 return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
511 /* validator's return value is ignored, so we don't check the type */
512}
513
514/*
515 * Process function options of CREATE/ALTER FDW
516 */
517static void
518parse_func_options(ParseState *pstate, List *func_options,
519 bool *handler_given, Oid *fdwhandler,
520 bool *validator_given, Oid *fdwvalidator)
521{
522 ListCell *cell;
523
524 *handler_given = false;
525 *validator_given = false;
526 /* return InvalidOid if not given */
527 *fdwhandler = InvalidOid;
528 *fdwvalidator = InvalidOid;
529
530 foreach(cell, func_options)
531 {
532 DefElem *def = (DefElem *) lfirst(cell);
533
534 if (strcmp(def->defname, "handler") == 0)
535 {
536 if (*handler_given)
537 errorConflictingDefElem(def, pstate);
538 *handler_given = true;
539 *fdwhandler = lookup_fdw_handler_func(def);
540 }
541 else if (strcmp(def->defname, "validator") == 0)
542 {
543 if (*validator_given)
544 errorConflictingDefElem(def, pstate);
545 *validator_given = true;
546 *fdwvalidator = lookup_fdw_validator_func(def);
547 }
548 else
549 elog(ERROR, "option \"%s\" not recognized",
550 def->defname);
551 }
552}
553
554/*
555 * Create a foreign-data wrapper
556 */
559{
560 Relation rel;
561 Datum values[Natts_pg_foreign_data_wrapper];
562 bool nulls[Natts_pg_foreign_data_wrapper];
563 HeapTuple tuple;
564 Oid fdwId;
565 bool handler_given;
566 bool validator_given;
567 Oid fdwhandler;
568 Oid fdwvalidator;
569 Datum fdwoptions;
570 Oid ownerId;
571 ObjectAddress myself;
572 ObjectAddress referenced;
573
574 rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
575
576 /* Must be superuser */
577 if (!superuser())
579 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
580 errmsg("permission denied to create foreign-data wrapper \"%s\"",
581 stmt->fdwname),
582 errhint("Must be superuser to create a foreign-data wrapper.")));
583
584 /* For now the owner cannot be specified on create. Use effective user ID. */
585 ownerId = GetUserId();
586
587 /*
588 * Check that there is no other foreign-data wrapper by this name.
589 */
590 if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
593 errmsg("foreign-data wrapper \"%s\" already exists",
594 stmt->fdwname)));
595
596 /*
597 * Insert tuple into pg_foreign_data_wrapper.
598 */
599 memset(values, 0, sizeof(values));
600 memset(nulls, false, sizeof(nulls));
601
602 fdwId = GetNewOidWithIndex(rel, ForeignDataWrapperOidIndexId,
603 Anum_pg_foreign_data_wrapper_oid);
604 values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
605 values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
607 values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
608
609 /* Lookup handler and validator functions, if given */
610 parse_func_options(pstate, stmt->func_options,
611 &handler_given, &fdwhandler,
612 &validator_given, &fdwvalidator);
613
614 values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
615 values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
616
617 nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
618
619 fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
620 PointerGetDatum(NULL),
621 stmt->options,
622 fdwvalidator);
623
624 if (PointerIsValid(DatumGetPointer(fdwoptions)))
625 values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
626 else
627 nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
628
629 tuple = heap_form_tuple(rel->rd_att, values, nulls);
630
631 CatalogTupleInsert(rel, tuple);
632
633 heap_freetuple(tuple);
634
635 /* record dependencies */
636 myself.classId = ForeignDataWrapperRelationId;
637 myself.objectId = fdwId;
638 myself.objectSubId = 0;
639
640 if (OidIsValid(fdwhandler))
641 {
642 referenced.classId = ProcedureRelationId;
643 referenced.objectId = fdwhandler;
644 referenced.objectSubId = 0;
645 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
646 }
647
648 if (OidIsValid(fdwvalidator))
649 {
650 referenced.classId = ProcedureRelationId;
651 referenced.objectId = fdwvalidator;
652 referenced.objectSubId = 0;
653 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
654 }
655
656 recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
657
658 /* dependency on extension */
660
661 /* Post creation hook for new foreign data wrapper */
662 InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
663
665
666 return myself;
667}
668
669
670/*
671 * Alter foreign-data wrapper
672 */
675{
676 Relation rel;
677 HeapTuple tp;
679 Datum repl_val[Natts_pg_foreign_data_wrapper];
680 bool repl_null[Natts_pg_foreign_data_wrapper];
681 bool repl_repl[Natts_pg_foreign_data_wrapper];
682 Oid fdwId;
683 bool isnull;
684 Datum datum;
685 bool handler_given;
686 bool validator_given;
687 Oid fdwhandler;
688 Oid fdwvalidator;
689 ObjectAddress myself;
690
691 rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
692
693 /* Must be superuser */
694 if (!superuser())
696 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
697 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
698 stmt->fdwname),
699 errhint("Must be superuser to alter a foreign-data wrapper.")));
700
701 tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
702 CStringGetDatum(stmt->fdwname));
703
704 if (!HeapTupleIsValid(tp))
706 (errcode(ERRCODE_UNDEFINED_OBJECT),
707 errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
708
710 fdwId = fdwForm->oid;
711
712 memset(repl_val, 0, sizeof(repl_val));
713 memset(repl_null, false, sizeof(repl_null));
714 memset(repl_repl, false, sizeof(repl_repl));
715
716 parse_func_options(pstate, stmt->func_options,
717 &handler_given, &fdwhandler,
718 &validator_given, &fdwvalidator);
719
720 if (handler_given)
721 {
722 repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
723 repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
724
725 /*
726 * It could be that the behavior of accessing foreign table changes
727 * with the new handler. Warn about this.
728 */
730 (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
731 }
732
733 if (validator_given)
734 {
735 repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
736 repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
737
738 /*
739 * It could be that existing options for the FDW or dependent SERVER,
740 * USER MAPPING or FOREIGN TABLE objects are no longer valid according
741 * to the new validator. Warn about this.
742 */
743 if (OidIsValid(fdwvalidator))
745 (errmsg("changing the foreign-data wrapper validator can cause "
746 "the options for dependent objects to become invalid")));
747 }
748 else
749 {
750 /*
751 * Validator is not changed, but we need it for validating options.
752 */
753 fdwvalidator = fdwForm->fdwvalidator;
754 }
755
756 /*
757 * If options specified, validate and update.
758 */
759 if (stmt->options)
760 {
761 /* Extract the current options */
762 datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
763 tp,
764 Anum_pg_foreign_data_wrapper_fdwoptions,
765 &isnull);
766 if (isnull)
767 datum = PointerGetDatum(NULL);
768
769 /* Transform the options */
770 datum = transformGenericOptions(ForeignDataWrapperRelationId,
771 datum,
772 stmt->options,
773 fdwvalidator);
774
776 repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
777 else
778 repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
779
780 repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
781 }
782
783 /* Everything looks good - update the tuple */
785 repl_val, repl_null, repl_repl);
786
787 CatalogTupleUpdate(rel, &tp->t_self, tp);
788
789 heap_freetuple(tp);
790
791 ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
792
793 /* Update function dependencies if we changed them */
794 if (handler_given || validator_given)
795 {
796 ObjectAddress referenced;
797
798 /*
799 * Flush all existing dependency records of this FDW on functions; we
800 * assume there can be none other than the ones we are fixing.
801 */
802 deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
803 fdwId,
804 ProcedureRelationId,
806
807 /* And build new ones. */
808
809 if (OidIsValid(fdwhandler))
810 {
811 referenced.classId = ProcedureRelationId;
812 referenced.objectId = fdwhandler;
813 referenced.objectSubId = 0;
814 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
815 }
816
817 if (OidIsValid(fdwvalidator))
818 {
819 referenced.classId = ProcedureRelationId;
820 referenced.objectId = fdwvalidator;
821 referenced.objectSubId = 0;
822 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
823 }
824 }
825
826 InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
827
829
830 return myself;
831}
832
833
834/*
835 * Create a foreign server
836 */
839{
840 Relation rel;
841 Datum srvoptions;
842 Datum values[Natts_pg_foreign_server];
843 bool nulls[Natts_pg_foreign_server];
844 HeapTuple tuple;
845 Oid srvId;
846 Oid ownerId;
847 AclResult aclresult;
848 ObjectAddress myself;
849 ObjectAddress referenced;
851
852 rel = table_open(ForeignServerRelationId, RowExclusiveLock);
853
854 /* For now the owner cannot be specified on create. Use effective user ID. */
855 ownerId = GetUserId();
856
857 /*
858 * Check that there is no other foreign server by this name. If there is
859 * one, do nothing if IF NOT EXISTS was specified.
860 */
861 srvId = get_foreign_server_oid(stmt->servername, true);
862 if (OidIsValid(srvId))
863 {
864 if (stmt->if_not_exists)
865 {
866 /*
867 * If we are in an extension script, insist that the pre-existing
868 * object be a member of the extension, to avoid security risks.
869 */
870 ObjectAddressSet(myself, ForeignServerRelationId, srvId);
872
873 /* OK to skip */
876 errmsg("server \"%s\" already exists, skipping",
877 stmt->servername)));
880 }
881 else
884 errmsg("server \"%s\" already exists",
885 stmt->servername)));
886 }
887
888 /*
889 * Check that the FDW exists and that we have USAGE on it. Also get the
890 * actual FDW for option validation etc.
891 */
892 fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
893
894 aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdw->fdwid, ownerId, ACL_USAGE);
895 if (aclresult != ACLCHECK_OK)
896 aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
897
898 /*
899 * Insert tuple into pg_foreign_server.
900 */
901 memset(values, 0, sizeof(values));
902 memset(nulls, false, sizeof(nulls));
903
904 srvId = GetNewOidWithIndex(rel, ForeignServerOidIndexId,
905 Anum_pg_foreign_server_oid);
906 values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
907 values[Anum_pg_foreign_server_srvname - 1] =
909 values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
910 values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
911
912 /* Add server type if supplied */
913 if (stmt->servertype)
914 values[Anum_pg_foreign_server_srvtype - 1] =
915 CStringGetTextDatum(stmt->servertype);
916 else
917 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
918
919 /* Add server version if supplied */
920 if (stmt->version)
921 values[Anum_pg_foreign_server_srvversion - 1] =
922 CStringGetTextDatum(stmt->version);
923 else
924 nulls[Anum_pg_foreign_server_srvversion - 1] = true;
925
926 /* Start with a blank acl */
927 nulls[Anum_pg_foreign_server_srvacl - 1] = true;
928
929 /* Add server options */
930 srvoptions = transformGenericOptions(ForeignServerRelationId,
931 PointerGetDatum(NULL),
932 stmt->options,
933 fdw->fdwvalidator);
934
935 if (PointerIsValid(DatumGetPointer(srvoptions)))
936 values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
937 else
938 nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
939
940 tuple = heap_form_tuple(rel->rd_att, values, nulls);
941
942 CatalogTupleInsert(rel, tuple);
943
944 heap_freetuple(tuple);
945
946 /* record dependencies */
947 myself.classId = ForeignServerRelationId;
948 myself.objectId = srvId;
949 myself.objectSubId = 0;
950
951 referenced.classId = ForeignDataWrapperRelationId;
952 referenced.objectId = fdw->fdwid;
953 referenced.objectSubId = 0;
954 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
955
956 recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
957
958 /* dependency on extension */
960
961 /* Post creation hook for new foreign server */
962 InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
963
965
966 return myself;
967}
968
969
970/*
971 * Alter foreign server
972 */
975{
976 Relation rel;
977 HeapTuple tp;
978 Datum repl_val[Natts_pg_foreign_server];
979 bool repl_null[Natts_pg_foreign_server];
980 bool repl_repl[Natts_pg_foreign_server];
981 Oid srvId;
983 ObjectAddress address;
984
985 rel = table_open(ForeignServerRelationId, RowExclusiveLock);
986
987 tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
988 CStringGetDatum(stmt->servername));
989
990 if (!HeapTupleIsValid(tp))
992 (errcode(ERRCODE_UNDEFINED_OBJECT),
993 errmsg("server \"%s\" does not exist", stmt->servername)));
994
995 srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
996 srvId = srvForm->oid;
997
998 /*
999 * Only owner or a superuser can ALTER a SERVER.
1000 */
1001 if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
1003 stmt->servername);
1004
1005 memset(repl_val, 0, sizeof(repl_val));
1006 memset(repl_null, false, sizeof(repl_null));
1007 memset(repl_repl, false, sizeof(repl_repl));
1008
1009 if (stmt->has_version)
1010 {
1011 /*
1012 * Change the server VERSION string.
1013 */
1014 if (stmt->version)
1015 repl_val[Anum_pg_foreign_server_srvversion - 1] =
1016 CStringGetTextDatum(stmt->version);
1017 else
1018 repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1019
1020 repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1021 }
1022
1023 if (stmt->options)
1024 {
1025 ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1026 Datum datum;
1027 bool isnull;
1028
1029 /* Extract the current srvoptions */
1030 datum = SysCacheGetAttr(FOREIGNSERVEROID,
1031 tp,
1032 Anum_pg_foreign_server_srvoptions,
1033 &isnull);
1034 if (isnull)
1035 datum = PointerGetDatum(NULL);
1036
1037 /* Prepare the options array */
1038 datum = transformGenericOptions(ForeignServerRelationId,
1039 datum,
1040 stmt->options,
1041 fdw->fdwvalidator);
1042
1043 if (PointerIsValid(DatumGetPointer(datum)))
1044 repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1045 else
1046 repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1047
1048 repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1049 }
1050
1051 /* Everything looks good - update the tuple */
1052 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1053 repl_val, repl_null, repl_repl);
1054
1055 CatalogTupleUpdate(rel, &tp->t_self, tp);
1056
1057 InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1058
1059 ObjectAddressSet(address, ForeignServerRelationId, srvId);
1060
1061 heap_freetuple(tp);
1062
1064
1065 return address;
1066}
1067
1068
1069/*
1070 * Common routine to check permission for user-mapping-related DDL
1071 * commands. We allow server owners to operate on any mapping, and
1072 * users to operate on their own mapping.
1073 */
1074static void
1075user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1076{
1077 Oid curuserid = GetUserId();
1078
1079 if (!object_ownercheck(ForeignServerRelationId, serverid, curuserid))
1080 {
1081 if (umuserid == curuserid)
1082 {
1083 AclResult aclresult;
1084
1085 aclresult = object_aclcheck(ForeignServerRelationId, serverid, curuserid, ACL_USAGE);
1086 if (aclresult != ACLCHECK_OK)
1087 aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, servername);
1088 }
1089 else
1091 servername);
1092 }
1093}
1094
1095
1096/*
1097 * Create user mapping
1098 */
1101{
1102 Relation rel;
1103 Datum useoptions;
1104 Datum values[Natts_pg_user_mapping];
1105 bool nulls[Natts_pg_user_mapping];
1106 HeapTuple tuple;
1107 Oid useId;
1108 Oid umId;
1109 ObjectAddress myself;
1110 ObjectAddress referenced;
1111 ForeignServer *srv;
1112 ForeignDataWrapper *fdw;
1113 RoleSpec *role = (RoleSpec *) stmt->user;
1114
1115 rel = table_open(UserMappingRelationId, RowExclusiveLock);
1116
1117 if (role->roletype == ROLESPEC_PUBLIC)
1118 useId = ACL_ID_PUBLIC;
1119 else
1120 useId = get_rolespec_oid(stmt->user, false);
1121
1122 /* Check that the server exists. */
1123 srv = GetForeignServerByName(stmt->servername, false);
1124
1125 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1126
1127 /*
1128 * Check that the user mapping is unique within server.
1129 */
1130 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1131 ObjectIdGetDatum(useId),
1133
1134 if (OidIsValid(umId))
1135 {
1136 if (stmt->if_not_exists)
1137 {
1138 /*
1139 * Since user mappings aren't members of extensions (see comments
1140 * below), no need for checkMembershipInCurrentExtension here.
1141 */
1144 errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
1145 MappingUserName(useId),
1146 stmt->servername)));
1147
1149 return InvalidObjectAddress;
1150 }
1151 else
1152 ereport(ERROR,
1154 errmsg("user mapping for \"%s\" already exists for server \"%s\"",
1155 MappingUserName(useId),
1156 stmt->servername)));
1157 }
1158
1159 fdw = GetForeignDataWrapper(srv->fdwid);
1160
1161 /*
1162 * Insert tuple into pg_user_mapping.
1163 */
1164 memset(values, 0, sizeof(values));
1165 memset(nulls, false, sizeof(nulls));
1166
1167 umId = GetNewOidWithIndex(rel, UserMappingOidIndexId,
1168 Anum_pg_user_mapping_oid);
1169 values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
1170 values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1171 values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1172
1173 /* Add user options */
1174 useoptions = transformGenericOptions(UserMappingRelationId,
1175 PointerGetDatum(NULL),
1176 stmt->options,
1177 fdw->fdwvalidator);
1178
1179 if (PointerIsValid(DatumGetPointer(useoptions)))
1180 values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1181 else
1182 nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1183
1184 tuple = heap_form_tuple(rel->rd_att, values, nulls);
1185
1186 CatalogTupleInsert(rel, tuple);
1187
1188 heap_freetuple(tuple);
1189
1190 /* Add dependency on the server */
1191 myself.classId = UserMappingRelationId;
1192 myself.objectId = umId;
1193 myself.objectSubId = 0;
1194
1195 referenced.classId = ForeignServerRelationId;
1196 referenced.objectId = srv->serverid;
1197 referenced.objectSubId = 0;
1198 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1199
1200 if (OidIsValid(useId))
1201 {
1202 /* Record the mapped user dependency */
1203 recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1204 }
1205
1206 /*
1207 * Perhaps someday there should be a recordDependencyOnCurrentExtension
1208 * call here; but since roles aren't members of extensions, it seems like
1209 * user mappings shouldn't be either. Note that the grammar and pg_dump
1210 * would need to be extended too if we change this.
1211 */
1212
1213 /* Post creation hook for new user mapping */
1214 InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1215
1217
1218 return myself;
1219}
1220
1221
1222/*
1223 * Alter user mapping
1224 */
1227{
1228 Relation rel;
1229 HeapTuple tp;
1230 Datum repl_val[Natts_pg_user_mapping];
1231 bool repl_null[Natts_pg_user_mapping];
1232 bool repl_repl[Natts_pg_user_mapping];
1233 Oid useId;
1234 Oid umId;
1235 ForeignServer *srv;
1236 ObjectAddress address;
1237 RoleSpec *role = (RoleSpec *) stmt->user;
1238
1239 rel = table_open(UserMappingRelationId, RowExclusiveLock);
1240
1241 if (role->roletype == ROLESPEC_PUBLIC)
1242 useId = ACL_ID_PUBLIC;
1243 else
1244 useId = get_rolespec_oid(stmt->user, false);
1245
1246 srv = GetForeignServerByName(stmt->servername, false);
1247
1248 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1249 ObjectIdGetDatum(useId),
1251 if (!OidIsValid(umId))
1252 ereport(ERROR,
1253 (errcode(ERRCODE_UNDEFINED_OBJECT),
1254 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1255 MappingUserName(useId), stmt->servername)));
1256
1257 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1258
1259 tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1260
1261 if (!HeapTupleIsValid(tp))
1262 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1263
1264 memset(repl_val, 0, sizeof(repl_val));
1265 memset(repl_null, false, sizeof(repl_null));
1266 memset(repl_repl, false, sizeof(repl_repl));
1267
1268 if (stmt->options)
1269 {
1270 ForeignDataWrapper *fdw;
1271 Datum datum;
1272 bool isnull;
1273
1274 /*
1275 * Process the options.
1276 */
1277
1278 fdw = GetForeignDataWrapper(srv->fdwid);
1279
1280 datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1281 tp,
1282 Anum_pg_user_mapping_umoptions,
1283 &isnull);
1284 if (isnull)
1285 datum = PointerGetDatum(NULL);
1286
1287 /* Prepare the options array */
1288 datum = transformGenericOptions(UserMappingRelationId,
1289 datum,
1290 stmt->options,
1291 fdw->fdwvalidator);
1292
1293 if (PointerIsValid(DatumGetPointer(datum)))
1294 repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1295 else
1296 repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1297
1298 repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1299 }
1300
1301 /* Everything looks good - update the tuple */
1302 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1303 repl_val, repl_null, repl_repl);
1304
1305 CatalogTupleUpdate(rel, &tp->t_self, tp);
1306
1307 InvokeObjectPostAlterHook(UserMappingRelationId,
1308 umId, 0);
1309
1310 ObjectAddressSet(address, UserMappingRelationId, umId);
1311
1312 heap_freetuple(tp);
1313
1315
1316 return address;
1317}
1318
1319
1320/*
1321 * Drop user mapping
1322 */
1323Oid
1325{
1326 ObjectAddress object;
1327 Oid useId;
1328 Oid umId;
1329 ForeignServer *srv;
1330 RoleSpec *role = (RoleSpec *) stmt->user;
1331
1332 if (role->roletype == ROLESPEC_PUBLIC)
1333 useId = ACL_ID_PUBLIC;
1334 else
1335 {
1336 useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1337 if (!OidIsValid(useId))
1338 {
1339 /*
1340 * IF EXISTS specified, role not found and not public. Notice this
1341 * and leave.
1342 */
1343 elog(NOTICE, "role \"%s\" does not exist, skipping",
1344 role->rolename);
1345 return InvalidOid;
1346 }
1347 }
1348
1349 srv = GetForeignServerByName(stmt->servername, true);
1350
1351 if (!srv)
1352 {
1353 if (!stmt->missing_ok)
1354 ereport(ERROR,
1355 (errcode(ERRCODE_UNDEFINED_OBJECT),
1356 errmsg("server \"%s\" does not exist",
1357 stmt->servername)));
1358 /* IF EXISTS, just note it */
1360 (errmsg("server \"%s\" does not exist, skipping",
1361 stmt->servername)));
1362 return InvalidOid;
1363 }
1364
1365 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1366 ObjectIdGetDatum(useId),
1368
1369 if (!OidIsValid(umId))
1370 {
1371 if (!stmt->missing_ok)
1372 ereport(ERROR,
1373 (errcode(ERRCODE_UNDEFINED_OBJECT),
1374 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1375 MappingUserName(useId), stmt->servername)));
1376
1377 /* IF EXISTS specified, just note it */
1379 (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
1380 MappingUserName(useId), stmt->servername)));
1381 return InvalidOid;
1382 }
1383
1385
1386 /*
1387 * Do the deletion
1388 */
1389 object.classId = UserMappingRelationId;
1390 object.objectId = umId;
1391 object.objectSubId = 0;
1392
1393 performDeletion(&object, DROP_CASCADE, 0);
1394
1395 return umId;
1396}
1397
1398
1399/*
1400 * Create a foreign table
1401 * call after DefineRelation().
1402 */
1403void
1405{
1406 Relation ftrel;
1407 Datum ftoptions;
1408 Datum values[Natts_pg_foreign_table];
1409 bool nulls[Natts_pg_foreign_table];
1410 HeapTuple tuple;
1411 AclResult aclresult;
1412 ObjectAddress myself;
1413 ObjectAddress referenced;
1414 Oid ownerId;
1415 ForeignDataWrapper *fdw;
1416 ForeignServer *server;
1417
1418 /*
1419 * Advance command counter to ensure the pg_attribute tuple is visible;
1420 * the tuple might be updated to add constraints in previous step.
1421 */
1423
1424 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
1425
1426 /*
1427 * For now the owner cannot be specified on create. Use effective user ID.
1428 */
1429 ownerId = GetUserId();
1430
1431 /*
1432 * Check that the foreign server exists and that we have USAGE on it. Also
1433 * get the actual FDW for option validation etc.
1434 */
1435 server = GetForeignServerByName(stmt->servername, false);
1436 aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, ownerId, ACL_USAGE);
1437 if (aclresult != ACLCHECK_OK)
1438 aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1439
1440 fdw = GetForeignDataWrapper(server->fdwid);
1441
1442 /*
1443 * Insert tuple into pg_foreign_table.
1444 */
1445 memset(values, 0, sizeof(values));
1446 memset(nulls, false, sizeof(nulls));
1447
1448 values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1449 values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1450 /* Add table generic options */
1451 ftoptions = transformGenericOptions(ForeignTableRelationId,
1452 PointerGetDatum(NULL),
1453 stmt->options,
1454 fdw->fdwvalidator);
1455
1456 if (PointerIsValid(DatumGetPointer(ftoptions)))
1457 values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1458 else
1459 nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1460
1461 tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1462
1463 CatalogTupleInsert(ftrel, tuple);
1464
1465 heap_freetuple(tuple);
1466
1467 /* Add pg_class dependency on the server */
1468 myself.classId = RelationRelationId;
1469 myself.objectId = relid;
1470 myself.objectSubId = 0;
1471
1472 referenced.classId = ForeignServerRelationId;
1473 referenced.objectId = server->serverid;
1474 referenced.objectSubId = 0;
1475 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1476
1478}
1479
1480/*
1481 * Import a foreign schema
1482 */
1483void
1485{
1486 ForeignServer *server;
1487 ForeignDataWrapper *fdw;
1488 FdwRoutine *fdw_routine;
1489 AclResult aclresult;
1490 List *cmd_list;
1491 ListCell *lc;
1492
1493 /* Check that the foreign server exists and that we have USAGE on it */
1494 server = GetForeignServerByName(stmt->server_name, false);
1495 aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, GetUserId(), ACL_USAGE);
1496 if (aclresult != ACLCHECK_OK)
1497 aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1498
1499 /* Check that the schema exists and we have CREATE permissions on it */
1500 (void) LookupCreationNamespace(stmt->local_schema);
1501
1502 /* Get the FDW and check it supports IMPORT */
1503 fdw = GetForeignDataWrapper(server->fdwid);
1504 if (!OidIsValid(fdw->fdwhandler))
1505 ereport(ERROR,
1506 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1507 errmsg("foreign-data wrapper \"%s\" has no handler",
1508 fdw->fdwname)));
1509 fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1510 if (fdw_routine->ImportForeignSchema == NULL)
1511 ereport(ERROR,
1512 (errcode(ERRCODE_FDW_NO_SCHEMAS),
1513 errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1514 fdw->fdwname)));
1515
1516 /* Call FDW to get a list of commands */
1517 cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1518
1519 /* Parse and execute each command */
1520 foreach(lc, cmd_list)
1521 {
1522 char *cmd = (char *) lfirst(lc);
1523 import_error_callback_arg callback_arg;
1524 ErrorContextCallback sqlerrcontext;
1525 List *raw_parsetree_list;
1526 ListCell *lc2;
1527
1528 /*
1529 * Setup error traceback support for ereport(). This is so that any
1530 * error in the generated SQL will be displayed nicely.
1531 */
1532 callback_arg.tablename = NULL; /* not known yet */
1533 callback_arg.cmd = cmd;
1534 sqlerrcontext.callback = import_error_callback;
1535 sqlerrcontext.arg = &callback_arg;
1536 sqlerrcontext.previous = error_context_stack;
1537 error_context_stack = &sqlerrcontext;
1538
1539 /*
1540 * Parse the SQL string into a list of raw parse trees.
1541 */
1542 raw_parsetree_list = pg_parse_query(cmd);
1543
1544 /*
1545 * Process each parse tree (we allow the FDW to put more than one
1546 * command per string, though this isn't really advised).
1547 */
1548 foreach(lc2, raw_parsetree_list)
1549 {
1550 RawStmt *rs = lfirst_node(RawStmt, lc2);
1552 PlannedStmt *pstmt;
1553
1554 /*
1555 * Because we only allow CreateForeignTableStmt, we can skip parse
1556 * analysis, rewrite, and planning steps here.
1557 */
1558 if (!IsA(cstmt, CreateForeignTableStmt))
1559 elog(ERROR,
1560 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1561 fdw->fdwname, (int) nodeTag(cstmt));
1562
1563 /* Ignore commands for tables excluded by filter options */
1565 continue;
1566
1567 /* Enable reporting of current table's name on error */
1568 callback_arg.tablename = cstmt->base.relation->relname;
1569
1570 /* Ensure creation schema is the one given in IMPORT statement */
1571 cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1572
1573 /* No planning needed, just make a wrapper PlannedStmt */
1574 pstmt = makeNode(PlannedStmt);
1575 pstmt->commandType = CMD_UTILITY;
1576 pstmt->canSetTag = false;
1577 pstmt->utilityStmt = (Node *) cstmt;
1578 pstmt->stmt_location = rs->stmt_location;
1579 pstmt->stmt_len = rs->stmt_len;
1580
1581 /* Execute statement */
1582 ProcessUtility(pstmt, cmd, false,
1583 PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1584 None_Receiver, NULL);
1585
1586 /* Be sure to advance the command counter between subcommands */
1588
1589 callback_arg.tablename = NULL;
1590 }
1591
1592 error_context_stack = sqlerrcontext.previous;
1593 }
1594}
1595
1596/*
1597 * error context callback to let us supply the failing SQL statement's text
1598 */
1599static void
1601{
1603 int syntaxerrposition;
1604
1605 /* If it's a syntax error, convert to internal syntax error report */
1606 syntaxerrposition = geterrposition();
1607 if (syntaxerrposition > 0)
1608 {
1609 errposition(0);
1610 internalerrposition(syntaxerrposition);
1611 internalerrquery(callback_arg->cmd);
1612 }
1613
1614 if (callback_arg->tablename)
1615 errcontext("importing foreign table \"%s\"",
1616 callback_arg->tablename);
1617}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1103
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5325
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5588
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:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3810
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4064
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
Definition: arrayfuncs.c:5350
ArrayType * construct_empty_array(Oid elmtype)
Definition: arrayfuncs.c:3580
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
Definition: arrayfuncs.c:5420
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define NameStr(name)
Definition: c.h:700
#define VARHDRSZ
Definition: c.h:646
#define PointerIsValid(pointer)
Definition: c.h:717
#define OidIsValid(objectId)
Definition: c.h:729
size_t Size
Definition: c.h:559
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:419
char * defGetString(DefElem *def)
Definition: define.c:35
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
Definition: define.c:371
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
DestReceiver * None_Receiver
Definition: dest.c:96
int internalerrquery(const char *query)
Definition: elog.c:1482
int internalerrposition(int cursorpos)
Definition: elog.c:1462
ErrorContextCallback * error_context_stack
Definition: elog.c:94
int errhint(const char *fmt,...)
Definition: elog.c:1317
int geterrposition(void)
Definition: elog.c:1595
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
int errposition(int cursorpos)
Definition: elog.c:1446
#define errcontext
Definition: elog.h:196
#define WARNING
Definition: elog.h:36
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
#define OidFunctionCall2(functionId, arg1, arg2)
Definition: fmgr.h:681
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:37
ForeignServer * GetForeignServerByName(const char *srvname, bool missing_ok)
Definition: foreign.c:182
Oid get_foreign_server_oid(const char *servername, bool missing_ok)
Definition: foreign.c:704
ForeignDataWrapper * GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
Definition: foreign.c:96
bool IsImportableForeignTable(const char *tablename, ImportForeignSchemaStmt *stmt)
Definition: foreign.c:482
FdwRoutine * GetFdwRoutine(Oid fdwhandler)
Definition: foreign.c:325
#define MappingUserName(userid)
Definition: foreign.h:20
static void AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
Definition: foreigncmds.c:338
static Oid lookup_fdw_handler_func(DefElem *handler)
Definition: foreigncmds.c:475
ObjectAddress AlterForeignServerOwner(const char *name, Oid newOwnerId)
Definition: foreigncmds.c:415
static void import_error_callback(void *arg)
Definition: foreigncmds.c:1600
void AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
Definition: foreigncmds.c:450
void ImportForeignSchema(ImportForeignSchemaStmt *stmt)
Definition: foreigncmds.c:1484
ObjectAddress AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
Definition: foreigncmds.c:275
ObjectAddress AlterForeignServer(AlterForeignServerStmt *stmt)
Definition: foreigncmds.c:974
static Datum optionListToArray(List *options)
Definition: foreigncmds.c:66
static void AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
Definition: foreigncmds.c:205
static Oid lookup_fdw_validator_func(DefElem *validator)
Definition: foreigncmds.c:499
void AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
Definition: foreigncmds.c:313
static void user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
Definition: foreigncmds.c:1075
ObjectAddress AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
Definition: foreigncmds.c:674
ObjectAddress CreateForeignServer(CreateForeignServerStmt *stmt)
Definition: foreigncmds.c:838
Oid RemoveUserMapping(DropUserMappingStmt *stmt)
Definition: foreigncmds.c:1324
ObjectAddress CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
Definition: foreigncmds.c:558
static void parse_func_options(ParseState *pstate, List *func_options, bool *handler_given, Oid *fdwhandler, bool *validator_given, Oid *fdwvalidator)
Definition: foreigncmds.c:518
ObjectAddress AlterUserMapping(AlterUserMappingStmt *stmt)
Definition: foreigncmds.c:1226
void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
Definition: foreigncmds.c:1404
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:110
ObjectAddress CreateUserMapping(CreateUserMappingStmt *stmt)
Definition: foreigncmds.c:1100
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)
Definition: htup_details.h:792
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
#define stmt
Definition: indent_codes.h:59
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
static struct @161 value
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
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:1655
char * pstrdup(const char *in)
Definition: mcxt.c:1696
void * palloc(Size size)
Definition: mcxt.c:1317
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
Oid GetUserId(void)
Definition: miscinit.c:517
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
char * NameListToString(const List *names)
Definition: namespace.c:3594
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:3428
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define nodeTag(nodeptr)
Definition: nodes.h:133
@ CMD_UTILITY
Definition: nodes.h:270
#define makeNode(_type_)
Definition: nodes.h:155
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2144
@ ROLESPEC_PUBLIC
Definition: parsenodes.h:400
#define ACL_USAGE
Definition: parsenodes.h:84
@ DEFELEM_UNSPEC
Definition: parsenodes.h:807
@ DEFELEM_DROP
Definition: parsenodes.h:810
@ DEFELEM_SET
Definition: parsenodes.h:808
@ DEFELEM_ADD
Definition: parsenodes.h:809
@ DROP_CASCADE
Definition: parsenodes.h:2342
@ OBJECT_FDW
Definition: parsenodes.h:2284
@ OBJECT_FOREIGN_SERVER
Definition: parsenodes.h:2285
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)
Definition: pg_shdepend.c:316
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:168
#define sprintf
Definition: port.h:240
List * pg_parse_query(const char *query_string)
Definition: postgres.c:602
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:531
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1331
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
RangeVar * relation
Definition: parsenodes.h:2675
DefElemAction defaction
Definition: parsenodes.h:820
char * defname
Definition: parsenodes.h:817
Node * arg
Definition: parsenodes.h:818
struct ErrorContextCallback * previous
Definition: elog.h:296
void(* callback)(void *arg)
Definition: elog.h:297
ImportForeignSchema_function ImportForeignSchema
Definition: fdwapi.h:260
char * fdwname
Definition: foreign.h:28
char * servername
Definition: foreign.h:39
Oid serverid
Definition: foreign.h:36
ItemPointerData t_self
Definition: htup.h:65
Definition: pg_list.h:54
Definition: nodes.h:129
char * relname
Definition: primnodes.h:82
char * schemaname
Definition: primnodes.h:79
ParseLoc stmt_location
Definition: parsenodes.h:2023
ParseLoc stmt_len
Definition: parsenodes.h:2024
Node * stmt
Definition: parsenodes.h:2022
TupleDesc rd_att
Definition: rel.h:112
RoleSpecType roletype
Definition: parsenodes.h:406
char * rolename
Definition: parsenodes.h:407
Definition: c.h:641
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:600
#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:499
@ PROCESS_UTILITY_SUBCOMMAND
Definition: utility.h:26
#define VARDATA(PTR)
Definition: varatt.h:278
#define SET_VARSIZE(PTR, len)
Definition: varatt.h:305
const char * name
void CommandCounterIncrement(void)
Definition: xact.c:1099