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