PostgreSQL Source Code  git master
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-2020, 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"
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 
43 typedef struct
44 {
45  char *tablename;
46  char *cmd;
48 
49 /* Internal functions */
50 static 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  */
65 static 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)
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  */
109 Datum
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)
146  ereport(ERROR,
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)
155  ereport(ERROR,
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)
165  ereport(ERROR,
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  */
204 static 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())
219  ereport(ERROR,
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))
227  ereport(ERROR,
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  */
275 AlterForeignDataWrapperOwner(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 
287 
288  if (!HeapTupleIsValid(tup))
289  ereport(ERROR,
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  */
312 void
314 {
315  HeapTuple tup;
316  Relation rel;
317 
318  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
319 
321 
322  if (!HeapTupleIsValid(tup))
323  ereport(ERROR,
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  */
337 static 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 */
363  NameStr(form->srvname));
364 
365  /* Must be able to become new owner */
366  check_is_member_of_role(GetUserId(), newOwnerId);
367 
368  /* New owner must have USAGE privilege on foreign-data wrapper */
369  aclresult = pg_foreign_data_wrapper_aclcheck(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  */
415 AlterForeignServerOwner(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 
426 
427  if (!HeapTupleIsValid(tup))
428  ereport(ERROR,
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  */
449 void
451 {
452  HeapTuple tup;
453  Relation rel;
454 
455  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
456 
458 
459  if (!HeapTupleIsValid(tup))
460  ereport(ERROR,
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  */
474 static 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)
487  ereport(ERROR,
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  */
498 static 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  */
517 static void
518 parse_func_options(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  ereport(ERROR,
538  (errcode(ERRCODE_SYNTAX_ERROR),
539  errmsg("conflicting or redundant options")));
540  *handler_given = true;
541  *fdwhandler = lookup_fdw_handler_func(def);
542  }
543  else if (strcmp(def->defname, "validator") == 0)
544  {
545  if (*validator_given)
546  ereport(ERROR,
547  (errcode(ERRCODE_SYNTAX_ERROR),
548  errmsg("conflicting or redundant options")));
549  *validator_given = true;
550  *fdwvalidator = lookup_fdw_validator_func(def);
551  }
552  else
553  elog(ERROR, "option \"%s\" not recognized",
554  def->defname);
555  }
556 }
557 
558 /*
559  * Create a foreign-data wrapper
560  */
563 {
564  Relation rel;
565  Datum values[Natts_pg_foreign_data_wrapper];
566  bool nulls[Natts_pg_foreign_data_wrapper];
567  HeapTuple tuple;
568  Oid fdwId;
569  bool handler_given;
570  bool validator_given;
571  Oid fdwhandler;
572  Oid fdwvalidator;
573  Datum fdwoptions;
574  Oid ownerId;
575  ObjectAddress myself;
576  ObjectAddress referenced;
577 
578  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
579 
580  /* Must be super user */
581  if (!superuser())
582  ereport(ERROR,
583  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
584  errmsg("permission denied to create foreign-data wrapper \"%s\"",
585  stmt->fdwname),
586  errhint("Must be superuser to create a foreign-data wrapper.")));
587 
588  /* For now the owner cannot be specified on create. Use effective user ID. */
589  ownerId = GetUserId();
590 
591  /*
592  * Check that there is no other foreign-data wrapper by this name.
593  */
594  if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
595  ereport(ERROR,
597  errmsg("foreign-data wrapper \"%s\" already exists",
598  stmt->fdwname)));
599 
600  /*
601  * Insert tuple into pg_foreign_data_wrapper.
602  */
603  memset(values, 0, sizeof(values));
604  memset(nulls, false, sizeof(nulls));
605 
607  Anum_pg_foreign_data_wrapper_oid);
608  values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
609  values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
611  values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
612 
613  /* Lookup handler and validator functions, if given */
615  &handler_given, &fdwhandler,
616  &validator_given, &fdwvalidator);
617 
618  values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
619  values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
620 
621  nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
622 
623  fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
624  PointerGetDatum(NULL),
625  stmt->options,
626  fdwvalidator);
627 
628  if (PointerIsValid(DatumGetPointer(fdwoptions)))
629  values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
630  else
631  nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
632 
633  tuple = heap_form_tuple(rel->rd_att, values, nulls);
634 
635  CatalogTupleInsert(rel, tuple);
636 
637  heap_freetuple(tuple);
638 
639  /* record dependencies */
640  myself.classId = ForeignDataWrapperRelationId;
641  myself.objectId = fdwId;
642  myself.objectSubId = 0;
643 
644  if (OidIsValid(fdwhandler))
645  {
646  referenced.classId = ProcedureRelationId;
647  referenced.objectId = fdwhandler;
648  referenced.objectSubId = 0;
649  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
650  }
651 
652  if (OidIsValid(fdwvalidator))
653  {
654  referenced.classId = ProcedureRelationId;
655  referenced.objectId = fdwvalidator;
656  referenced.objectSubId = 0;
657  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
658  }
659 
660  recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
661 
662  /* dependency on extension */
663  recordDependencyOnCurrentExtension(&myself, false);
664 
665  /* Post creation hook for new foreign data wrapper */
666  InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
667 
669 
670  return myself;
671 }
672 
673 
674 /*
675  * Alter foreign-data wrapper
676  */
679 {
680  Relation rel;
681  HeapTuple tp;
683  Datum repl_val[Natts_pg_foreign_data_wrapper];
684  bool repl_null[Natts_pg_foreign_data_wrapper];
685  bool repl_repl[Natts_pg_foreign_data_wrapper];
686  Oid fdwId;
687  bool isnull;
688  Datum datum;
689  bool handler_given;
690  bool validator_given;
691  Oid fdwhandler;
692  Oid fdwvalidator;
693  ObjectAddress myself;
694 
695  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
696 
697  /* Must be super user */
698  if (!superuser())
699  ereport(ERROR,
700  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
701  errmsg("permission denied to alter foreign-data wrapper \"%s\"",
702  stmt->fdwname),
703  errhint("Must be superuser to alter a foreign-data wrapper.")));
704 
706  CStringGetDatum(stmt->fdwname));
707 
708  if (!HeapTupleIsValid(tp))
709  ereport(ERROR,
710  (errcode(ERRCODE_UNDEFINED_OBJECT),
711  errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
712 
713  fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
714  fdwId = fdwForm->oid;
715 
716  memset(repl_val, 0, sizeof(repl_val));
717  memset(repl_null, false, sizeof(repl_null));
718  memset(repl_repl, false, sizeof(repl_repl));
719 
721  &handler_given, &fdwhandler,
722  &validator_given, &fdwvalidator);
723 
724  if (handler_given)
725  {
726  repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
727  repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
728 
729  /*
730  * It could be that the behavior of accessing foreign table changes
731  * with the new handler. Warn about this.
732  */
734  (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
735  }
736 
737  if (validator_given)
738  {
739  repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
740  repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
741 
742  /*
743  * It could be that existing options for the FDW or dependent SERVER,
744  * USER MAPPING or FOREIGN TABLE objects are no longer valid according
745  * to the new validator. Warn about this.
746  */
747  if (OidIsValid(fdwvalidator))
749  (errmsg("changing the foreign-data wrapper validator can cause "
750  "the options for dependent objects to become invalid")));
751  }
752  else
753  {
754  /*
755  * Validator is not changed, but we need it for validating options.
756  */
757  fdwvalidator = fdwForm->fdwvalidator;
758  }
759 
760  /*
761  * If options specified, validate and update.
762  */
763  if (stmt->options)
764  {
765  /* Extract the current options */
767  tp,
768  Anum_pg_foreign_data_wrapper_fdwoptions,
769  &isnull);
770  if (isnull)
771  datum = PointerGetDatum(NULL);
772 
773  /* Transform the options */
774  datum = transformGenericOptions(ForeignDataWrapperRelationId,
775  datum,
776  stmt->options,
777  fdwvalidator);
778 
779  if (PointerIsValid(DatumGetPointer(datum)))
780  repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
781  else
782  repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
783 
784  repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
785  }
786 
787  /* Everything looks good - update the tuple */
788  tp = heap_modify_tuple(tp, RelationGetDescr(rel),
789  repl_val, repl_null, repl_repl);
790 
791  CatalogTupleUpdate(rel, &tp->t_self, tp);
792 
793  heap_freetuple(tp);
794 
795  ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
796 
797  /* Update function dependencies if we changed them */
798  if (handler_given || validator_given)
799  {
800  ObjectAddress referenced;
801 
802  /*
803  * Flush all existing dependency records of this FDW on functions; we
804  * assume there can be none other than the ones we are fixing.
805  */
806  deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
807  fdwId,
808  ProcedureRelationId,
810 
811  /* And build new ones. */
812 
813  if (OidIsValid(fdwhandler))
814  {
815  referenced.classId = ProcedureRelationId;
816  referenced.objectId = fdwhandler;
817  referenced.objectSubId = 0;
818  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
819  }
820 
821  if (OidIsValid(fdwvalidator))
822  {
823  referenced.classId = ProcedureRelationId;
824  referenced.objectId = fdwvalidator;
825  referenced.objectSubId = 0;
826  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
827  }
828  }
829 
830  InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
831 
833 
834  return myself;
835 }
836 
837 
838 /*
839  * Drop foreign-data wrapper by OID
840  */
841 void
843 {
844  HeapTuple tp;
845  Relation rel;
846 
847  rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
848 
850 
851  if (!HeapTupleIsValid(tp))
852  elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
853 
854  CatalogTupleDelete(rel, &tp->t_self);
855 
856  ReleaseSysCache(tp);
857 
859 }
860 
861 
862 /*
863  * Create a foreign server
864  */
867 {
868  Relation rel;
869  Datum srvoptions;
870  Datum values[Natts_pg_foreign_server];
871  bool nulls[Natts_pg_foreign_server];
872  HeapTuple tuple;
873  Oid srvId;
874  Oid ownerId;
875  AclResult aclresult;
876  ObjectAddress myself;
877  ObjectAddress referenced;
878  ForeignDataWrapper *fdw;
879 
880  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
881 
882  /* For now the owner cannot be specified on create. Use effective user ID. */
883  ownerId = GetUserId();
884 
885  /*
886  * Check that there is no other foreign server by this name. Do nothing if
887  * IF NOT EXISTS was enforced.
888  */
889  if (GetForeignServerByName(stmt->servername, true) != NULL)
890  {
891  if (stmt->if_not_exists)
892  {
893  ereport(NOTICE,
895  errmsg("server \"%s\" already exists, skipping",
896  stmt->servername)));
898  return InvalidObjectAddress;
899  }
900  else
901  ereport(ERROR,
903  errmsg("server \"%s\" already exists",
904  stmt->servername)));
905  }
906 
907  /*
908  * Check that the FDW exists and that we have USAGE on it. Also get the
909  * actual FDW for option validation etc.
910  */
911  fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
912 
913  aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
914  if (aclresult != ACLCHECK_OK)
915  aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
916 
917  /*
918  * Insert tuple into pg_foreign_server.
919  */
920  memset(values, 0, sizeof(values));
921  memset(nulls, false, sizeof(nulls));
922 
924  Anum_pg_foreign_server_oid);
925  values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
926  values[Anum_pg_foreign_server_srvname - 1] =
928  values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
929  values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
930 
931  /* Add server type if supplied */
932  if (stmt->servertype)
933  values[Anum_pg_foreign_server_srvtype - 1] =
935  else
936  nulls[Anum_pg_foreign_server_srvtype - 1] = true;
937 
938  /* Add server version if supplied */
939  if (stmt->version)
940  values[Anum_pg_foreign_server_srvversion - 1] =
942  else
943  nulls[Anum_pg_foreign_server_srvversion - 1] = true;
944 
945  /* Start with a blank acl */
946  nulls[Anum_pg_foreign_server_srvacl - 1] = true;
947 
948  /* Add server options */
949  srvoptions = transformGenericOptions(ForeignServerRelationId,
950  PointerGetDatum(NULL),
951  stmt->options,
952  fdw->fdwvalidator);
953 
954  if (PointerIsValid(DatumGetPointer(srvoptions)))
955  values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
956  else
957  nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
958 
959  tuple = heap_form_tuple(rel->rd_att, values, nulls);
960 
961  CatalogTupleInsert(rel, tuple);
962 
963  heap_freetuple(tuple);
964 
965  /* record dependencies */
966  myself.classId = ForeignServerRelationId;
967  myself.objectId = srvId;
968  myself.objectSubId = 0;
969 
970  referenced.classId = ForeignDataWrapperRelationId;
971  referenced.objectId = fdw->fdwid;
972  referenced.objectSubId = 0;
973  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
974 
975  recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
976 
977  /* dependency on extension */
978  recordDependencyOnCurrentExtension(&myself, false);
979 
980  /* Post creation hook for new foreign server */
981  InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
982 
984 
985  return myself;
986 }
987 
988 
989 /*
990  * Alter foreign server
991  */
994 {
995  Relation rel;
996  HeapTuple tp;
997  Datum repl_val[Natts_pg_foreign_server];
998  bool repl_null[Natts_pg_foreign_server];
999  bool repl_repl[Natts_pg_foreign_server];
1000  Oid srvId;
1001  Form_pg_foreign_server srvForm;
1002  ObjectAddress address;
1003 
1004  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
1005 
1007  CStringGetDatum(stmt->servername));
1008 
1009  if (!HeapTupleIsValid(tp))
1010  ereport(ERROR,
1011  (errcode(ERRCODE_UNDEFINED_OBJECT),
1012  errmsg("server \"%s\" does not exist", stmt->servername)));
1013 
1014  srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
1015  srvId = srvForm->oid;
1016 
1017  /*
1018  * Only owner or a superuser can ALTER a SERVER.
1019  */
1020  if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
1022  stmt->servername);
1023 
1024  memset(repl_val, 0, sizeof(repl_val));
1025  memset(repl_null, false, sizeof(repl_null));
1026  memset(repl_repl, false, sizeof(repl_repl));
1027 
1028  if (stmt->has_version)
1029  {
1030  /*
1031  * Change the server VERSION string.
1032  */
1033  if (stmt->version)
1034  repl_val[Anum_pg_foreign_server_srvversion - 1] =
1036  else
1037  repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1038 
1039  repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1040  }
1041 
1042  if (stmt->options)
1043  {
1044  ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1045  Datum datum;
1046  bool isnull;
1047 
1048  /* Extract the current srvoptions */
1050  tp,
1051  Anum_pg_foreign_server_srvoptions,
1052  &isnull);
1053  if (isnull)
1054  datum = PointerGetDatum(NULL);
1055 
1056  /* Prepare the options array */
1057  datum = transformGenericOptions(ForeignServerRelationId,
1058  datum,
1059  stmt->options,
1060  fdw->fdwvalidator);
1061 
1062  if (PointerIsValid(DatumGetPointer(datum)))
1063  repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1064  else
1065  repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1066 
1067  repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1068  }
1069 
1070  /* Everything looks good - update the tuple */
1071  tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1072  repl_val, repl_null, repl_repl);
1073 
1074  CatalogTupleUpdate(rel, &tp->t_self, tp);
1075 
1076  InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1077 
1078  ObjectAddressSet(address, ForeignServerRelationId, srvId);
1079 
1080  heap_freetuple(tp);
1081 
1083 
1084  return address;
1085 }
1086 
1087 
1088 /*
1089  * Drop foreign server by OID
1090  */
1091 void
1093 {
1094  HeapTuple tp;
1095  Relation rel;
1096 
1097  rel = table_open(ForeignServerRelationId, RowExclusiveLock);
1098 
1100 
1101  if (!HeapTupleIsValid(tp))
1102  elog(ERROR, "cache lookup failed for foreign server %u", srvId);
1103 
1104  CatalogTupleDelete(rel, &tp->t_self);
1105 
1106  ReleaseSysCache(tp);
1107 
1109 }
1110 
1111 
1112 /*
1113  * Common routine to check permission for user-mapping-related DDL
1114  * commands. We allow server owners to operate on any mapping, and
1115  * users to operate on their own mapping.
1116  */
1117 static void
1118 user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1119 {
1120  Oid curuserid = GetUserId();
1121 
1122  if (!pg_foreign_server_ownercheck(serverid, curuserid))
1123  {
1124  if (umuserid == curuserid)
1125  {
1126  AclResult aclresult;
1127 
1128  aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
1129  if (aclresult != ACLCHECK_OK)
1130  aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, servername);
1131  }
1132  else
1134  servername);
1135  }
1136 }
1137 
1138 
1139 /*
1140  * Create user mapping
1141  */
1144 {
1145  Relation rel;
1146  Datum useoptions;
1147  Datum values[Natts_pg_user_mapping];
1148  bool nulls[Natts_pg_user_mapping];
1149  HeapTuple tuple;
1150  Oid useId;
1151  Oid umId;
1152  ObjectAddress myself;
1153  ObjectAddress referenced;
1154  ForeignServer *srv;
1155  ForeignDataWrapper *fdw;
1156  RoleSpec *role = (RoleSpec *) stmt->user;
1157 
1158  rel = table_open(UserMappingRelationId, RowExclusiveLock);
1159 
1160  if (role->roletype == ROLESPEC_PUBLIC)
1161  useId = ACL_ID_PUBLIC;
1162  else
1163  useId = get_rolespec_oid(stmt->user, false);
1164 
1165  /* Check that the server exists. */
1166  srv = GetForeignServerByName(stmt->servername, false);
1167 
1168  user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1169 
1170  /*
1171  * Check that the user mapping is unique within server.
1172  */
1173  umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1174  ObjectIdGetDatum(useId),
1175  ObjectIdGetDatum(srv->serverid));
1176 
1177  if (OidIsValid(umId))
1178  {
1179  if (stmt->if_not_exists)
1180  {
1181  ereport(NOTICE,
1183  errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
1184  MappingUserName(useId),
1185  stmt->servername)));
1186 
1188  return InvalidObjectAddress;
1189  }
1190  else
1191  ereport(ERROR,
1193  errmsg("user mapping for \"%s\" already exists for server \"%s\"",
1194  MappingUserName(useId),
1195  stmt->servername)));
1196  }
1197 
1198  fdw = GetForeignDataWrapper(srv->fdwid);
1199 
1200  /*
1201  * Insert tuple into pg_user_mapping.
1202  */
1203  memset(values, 0, sizeof(values));
1204  memset(nulls, false, sizeof(nulls));
1205 
1207  Anum_pg_user_mapping_oid);
1208  values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
1209  values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1210  values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1211 
1212  /* Add user options */
1213  useoptions = transformGenericOptions(UserMappingRelationId,
1214  PointerGetDatum(NULL),
1215  stmt->options,
1216  fdw->fdwvalidator);
1217 
1218  if (PointerIsValid(DatumGetPointer(useoptions)))
1219  values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1220  else
1221  nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1222 
1223  tuple = heap_form_tuple(rel->rd_att, values, nulls);
1224 
1225  CatalogTupleInsert(rel, tuple);
1226 
1227  heap_freetuple(tuple);
1228 
1229  /* Add dependency on the server */
1230  myself.classId = UserMappingRelationId;
1231  myself.objectId = umId;
1232  myself.objectSubId = 0;
1233 
1234  referenced.classId = ForeignServerRelationId;
1235  referenced.objectId = srv->serverid;
1236  referenced.objectSubId = 0;
1237  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1238 
1239  if (OidIsValid(useId))
1240  {
1241  /* Record the mapped user dependency */
1242  recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1243  }
1244 
1245  /*
1246  * Perhaps someday there should be a recordDependencyOnCurrentExtension
1247  * call here; but since roles aren't members of extensions, it seems like
1248  * user mappings shouldn't be either. Note that the grammar and pg_dump
1249  * would need to be extended too if we change this.
1250  */
1251 
1252  /* Post creation hook for new user mapping */
1253  InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1254 
1256 
1257  return myself;
1258 }
1259 
1260 
1261 /*
1262  * Alter user mapping
1263  */
1266 {
1267  Relation rel;
1268  HeapTuple tp;
1269  Datum repl_val[Natts_pg_user_mapping];
1270  bool repl_null[Natts_pg_user_mapping];
1271  bool repl_repl[Natts_pg_user_mapping];
1272  Oid useId;
1273  Oid umId;
1274  ForeignServer *srv;
1275  ObjectAddress address;
1276  RoleSpec *role = (RoleSpec *) stmt->user;
1277 
1278  rel = table_open(UserMappingRelationId, RowExclusiveLock);
1279 
1280  if (role->roletype == ROLESPEC_PUBLIC)
1281  useId = ACL_ID_PUBLIC;
1282  else
1283  useId = get_rolespec_oid(stmt->user, false);
1284 
1285  srv = GetForeignServerByName(stmt->servername, false);
1286 
1287  umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1288  ObjectIdGetDatum(useId),
1289  ObjectIdGetDatum(srv->serverid));
1290  if (!OidIsValid(umId))
1291  ereport(ERROR,
1292  (errcode(ERRCODE_UNDEFINED_OBJECT),
1293  errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1294  MappingUserName(useId), stmt->servername)));
1295 
1296  user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1297 
1299 
1300  if (!HeapTupleIsValid(tp))
1301  elog(ERROR, "cache lookup failed for user mapping %u", umId);
1302 
1303  memset(repl_val, 0, sizeof(repl_val));
1304  memset(repl_null, false, sizeof(repl_null));
1305  memset(repl_repl, false, sizeof(repl_repl));
1306 
1307  if (stmt->options)
1308  {
1309  ForeignDataWrapper *fdw;
1310  Datum datum;
1311  bool isnull;
1312 
1313  /*
1314  * Process the options.
1315  */
1316 
1317  fdw = GetForeignDataWrapper(srv->fdwid);
1318 
1319  datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1320  tp,
1321  Anum_pg_user_mapping_umoptions,
1322  &isnull);
1323  if (isnull)
1324  datum = PointerGetDatum(NULL);
1325 
1326  /* Prepare the options array */
1327  datum = transformGenericOptions(UserMappingRelationId,
1328  datum,
1329  stmt->options,
1330  fdw->fdwvalidator);
1331 
1332  if (PointerIsValid(DatumGetPointer(datum)))
1333  repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1334  else
1335  repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1336 
1337  repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1338  }
1339 
1340  /* Everything looks good - update the tuple */
1341  tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1342  repl_val, repl_null, repl_repl);
1343 
1344  CatalogTupleUpdate(rel, &tp->t_self, tp);
1345 
1346  InvokeObjectPostAlterHook(UserMappingRelationId,
1347  umId, 0);
1348 
1349  ObjectAddressSet(address, UserMappingRelationId, umId);
1350 
1351  heap_freetuple(tp);
1352 
1354 
1355  return address;
1356 }
1357 
1358 
1359 /*
1360  * Drop user mapping
1361  */
1362 Oid
1364 {
1365  ObjectAddress object;
1366  Oid useId;
1367  Oid umId;
1368  ForeignServer *srv;
1369  RoleSpec *role = (RoleSpec *) stmt->user;
1370 
1371  if (role->roletype == ROLESPEC_PUBLIC)
1372  useId = ACL_ID_PUBLIC;
1373  else
1374  {
1375  useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1376  if (!OidIsValid(useId))
1377  {
1378  /*
1379  * IF EXISTS specified, role not found and not public. Notice this
1380  * and leave.
1381  */
1382  elog(NOTICE, "role \"%s\" does not exist, skipping",
1383  role->rolename);
1384  return InvalidOid;
1385  }
1386  }
1387 
1388  srv = GetForeignServerByName(stmt->servername, true);
1389 
1390  if (!srv)
1391  {
1392  if (!stmt->missing_ok)
1393  ereport(ERROR,
1394  (errcode(ERRCODE_UNDEFINED_OBJECT),
1395  errmsg("server \"%s\" does not exist",
1396  stmt->servername)));
1397  /* IF EXISTS, just note it */
1398  ereport(NOTICE,
1399  (errmsg("server \"%s\" does not exist, skipping",
1400  stmt->servername)));
1401  return InvalidOid;
1402  }
1403 
1404  umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1405  ObjectIdGetDatum(useId),
1406  ObjectIdGetDatum(srv->serverid));
1407 
1408  if (!OidIsValid(umId))
1409  {
1410  if (!stmt->missing_ok)
1411  ereport(ERROR,
1412  (errcode(ERRCODE_UNDEFINED_OBJECT),
1413  errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1414  MappingUserName(useId), stmt->servername)));
1415 
1416  /* IF EXISTS specified, just note it */
1417  ereport(NOTICE,
1418  (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
1419  MappingUserName(useId), stmt->servername)));
1420  return InvalidOid;
1421  }
1422 
1423  user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1424 
1425  /*
1426  * Do the deletion
1427  */
1428  object.classId = UserMappingRelationId;
1429  object.objectId = umId;
1430  object.objectSubId = 0;
1431 
1432  performDeletion(&object, DROP_CASCADE, 0);
1433 
1434  return umId;
1435 }
1436 
1437 
1438 /*
1439  * Drop user mapping by OID. This is called to clean up dependencies.
1440  */
1441 void
1443 {
1444  HeapTuple tp;
1445  Relation rel;
1446 
1447  rel = table_open(UserMappingRelationId, RowExclusiveLock);
1448 
1450 
1451  if (!HeapTupleIsValid(tp))
1452  elog(ERROR, "cache lookup failed for user mapping %u", umId);
1453 
1454  CatalogTupleDelete(rel, &tp->t_self);
1455 
1456  ReleaseSysCache(tp);
1457 
1459 }
1460 
1461 /*
1462  * Create a foreign table
1463  * call after DefineRelation().
1464  */
1465 void
1467 {
1468  Relation ftrel;
1469  Datum ftoptions;
1470  Datum values[Natts_pg_foreign_table];
1471  bool nulls[Natts_pg_foreign_table];
1472  HeapTuple tuple;
1473  AclResult aclresult;
1474  ObjectAddress myself;
1475  ObjectAddress referenced;
1476  Oid ownerId;
1477  ForeignDataWrapper *fdw;
1478  ForeignServer *server;
1479 
1480  /*
1481  * Advance command counter to ensure the pg_attribute tuple is visible;
1482  * the tuple might be updated to add constraints in previous step.
1483  */
1485 
1486  ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
1487 
1488  /*
1489  * For now the owner cannot be specified on create. Use effective user ID.
1490  */
1491  ownerId = GetUserId();
1492 
1493  /*
1494  * Check that the foreign server exists and that we have USAGE on it. Also
1495  * get the actual FDW for option validation etc.
1496  */
1497  server = GetForeignServerByName(stmt->servername, false);
1498  aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
1499  if (aclresult != ACLCHECK_OK)
1500  aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1501 
1502  fdw = GetForeignDataWrapper(server->fdwid);
1503 
1504  /*
1505  * Insert tuple into pg_foreign_table.
1506  */
1507  memset(values, 0, sizeof(values));
1508  memset(nulls, false, sizeof(nulls));
1509 
1510  values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1511  values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1512  /* Add table generic options */
1513  ftoptions = transformGenericOptions(ForeignTableRelationId,
1514  PointerGetDatum(NULL),
1515  stmt->options,
1516  fdw->fdwvalidator);
1517 
1518  if (PointerIsValid(DatumGetPointer(ftoptions)))
1519  values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1520  else
1521  nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1522 
1523  tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1524 
1525  CatalogTupleInsert(ftrel, tuple);
1526 
1527  heap_freetuple(tuple);
1528 
1529  /* Add pg_class dependency on the server */
1530  myself.classId = RelationRelationId;
1531  myself.objectId = relid;
1532  myself.objectSubId = 0;
1533 
1534  referenced.classId = ForeignServerRelationId;
1535  referenced.objectId = server->serverid;
1536  referenced.objectSubId = 0;
1537  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1538 
1539  table_close(ftrel, RowExclusiveLock);
1540 }
1541 
1542 /*
1543  * Import a foreign schema
1544  */
1545 void
1547 {
1548  ForeignServer *server;
1549  ForeignDataWrapper *fdw;
1550  FdwRoutine *fdw_routine;
1551  AclResult aclresult;
1552  List *cmd_list;
1553  ListCell *lc;
1554 
1555  /* Check that the foreign server exists and that we have USAGE on it */
1556  server = GetForeignServerByName(stmt->server_name, false);
1557  aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
1558  if (aclresult != ACLCHECK_OK)
1559  aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1560 
1561  /* Check that the schema exists and we have CREATE permissions on it */
1562  (void) LookupCreationNamespace(stmt->local_schema);
1563 
1564  /* Get the FDW and check it supports IMPORT */
1565  fdw = GetForeignDataWrapper(server->fdwid);
1566  if (!OidIsValid(fdw->fdwhandler))
1567  ereport(ERROR,
1568  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1569  errmsg("foreign-data wrapper \"%s\" has no handler",
1570  fdw->fdwname)));
1571  fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1572  if (fdw_routine->ImportForeignSchema == NULL)
1573  ereport(ERROR,
1574  (errcode(ERRCODE_FDW_NO_SCHEMAS),
1575  errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1576  fdw->fdwname)));
1577 
1578  /* Call FDW to get a list of commands */
1579  cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1580 
1581  /* Parse and execute each command */
1582  foreach(lc, cmd_list)
1583  {
1584  char *cmd = (char *) lfirst(lc);
1585  import_error_callback_arg callback_arg;
1586  ErrorContextCallback sqlerrcontext;
1587  List *raw_parsetree_list;
1588  ListCell *lc2;
1589 
1590  /*
1591  * Setup error traceback support for ereport(). This is so that any
1592  * error in the generated SQL will be displayed nicely.
1593  */
1594  callback_arg.tablename = NULL; /* not known yet */
1595  callback_arg.cmd = cmd;
1596  sqlerrcontext.callback = import_error_callback;
1597  sqlerrcontext.arg = (void *) &callback_arg;
1598  sqlerrcontext.previous = error_context_stack;
1599  error_context_stack = &sqlerrcontext;
1600 
1601  /*
1602  * Parse the SQL string into a list of raw parse trees.
1603  */
1604  raw_parsetree_list = pg_parse_query(cmd);
1605 
1606  /*
1607  * Process each parse tree (we allow the FDW to put more than one
1608  * command per string, though this isn't really advised).
1609  */
1610  foreach(lc2, raw_parsetree_list)
1611  {
1612  RawStmt *rs = lfirst_node(RawStmt, lc2);
1614  PlannedStmt *pstmt;
1615 
1616  /*
1617  * Because we only allow CreateForeignTableStmt, we can skip parse
1618  * analysis, rewrite, and planning steps here.
1619  */
1620  if (!IsA(cstmt, CreateForeignTableStmt))
1621  elog(ERROR,
1622  "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1623  fdw->fdwname, (int) nodeTag(cstmt));
1624 
1625  /* Ignore commands for tables excluded by filter options */
1626  if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
1627  continue;
1628 
1629  /* Enable reporting of current table's name on error */
1630  callback_arg.tablename = cstmt->base.relation->relname;
1631 
1632  /* Ensure creation schema is the one given in IMPORT statement */
1633  cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1634 
1635  /* No planning needed, just make a wrapper PlannedStmt */
1636  pstmt = makeNode(PlannedStmt);
1637  pstmt->commandType = CMD_UTILITY;
1638  pstmt->canSetTag = false;
1639  pstmt->utilityStmt = (Node *) cstmt;
1640  pstmt->stmt_location = rs->stmt_location;
1641  pstmt->stmt_len = rs->stmt_len;
1642 
1643  /* Execute statement */
1644  ProcessUtility(pstmt,
1645  cmd,
1646  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1647  None_Receiver, NULL);
1648 
1649  /* Be sure to advance the command counter between subcommands */
1651 
1652  callback_arg.tablename = NULL;
1653  }
1654 
1655  error_context_stack = sqlerrcontext.previous;
1656  }
1657 }
1658 
1659 /*
1660  * error context callback to let us supply the failing SQL statement's text
1661  */
1662 static void
1664 {
1665  import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
1666  int syntaxerrposition;
1667 
1668  /* If it's a syntax error, convert to internal syntax error report */
1669  syntaxerrposition = geterrposition();
1670  if (syntaxerrposition > 0)
1671  {
1672  errposition(0);
1673  internalerrposition(syntaxerrposition);
1674  internalerrquery(callback_arg->cmd);
1675  }
1676 
1677  if (callback_arg->tablename)
1678  errcontext("importing foreign table \"%s\"",
1679  callback_arg->tablename);
1680 }
RangeVar * relation
Definition: parsenodes.h:2072
ObjectAddress AlterForeignServer(AlterForeignServerStmt *stmt)
Definition: foreigncmds.c:993
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:317
void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
Definition: foreigncmds.c:1466
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
int errhint(const char *fmt,...)
Definition: elog.c:1071
#define VARDATA(PTR)
Definition: postgres.h:302
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
static void parse_func_options(List *func_options, bool *handler_given, Oid *fdwhandler, bool *validator_given, Oid *fdwvalidator)
Definition: foreigncmds.c:518
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:151
ObjectAddress CreateForeignDataWrapper(CreateFdwStmt *stmt)
Definition: foreigncmds.c:562
void AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
Definition: foreigncmds.c:313
#define RelationGetDescr(relation)
Definition: rel.h:482
Oid GetUserId(void)
Definition: miscinit.c:448
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:110
char * fdwname
Definition: foreign.h:28
#define DatumGetAclP(X)
Definition: acl.h:120
#define PointerGetDatum(X)
Definition: postgres.h:556
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2936
#define VARHDRSZ
Definition: c.h:561
void RemoveForeignDataWrapperById(Oid fdwId)
Definition: foreigncmds.c:842
char * pstrdup(const char *in)
Definition: mcxt.c:1186
AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4683
bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
Definition: aclchk.c:5084
void AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
Definition: foreigncmds.c:450
DefElemAction defaction
Definition: parsenodes.h:734
Definition: nodes.h:529
int errcode(int sqlerrcode)
Definition: elog.c:610
bool superuser(void)
Definition: superuser.c:46
static void user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
Definition: foreigncmds.c:1118
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:269
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:43
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:624
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:164
ArrayType * construct_empty_array(Oid elmtype)
Definition: arrayfuncs.c:3411
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
DestReceiver * None_Receiver
Definition: dest.c:96
void(* callback)(void *arg)
Definition: elog.h:229
struct ErrorContextCallback * previous
Definition: elog.h:228
#define OidIsValid(objectId)
Definition: c.h:644
void RemoveUserMappingById(Oid umId)
Definition: foreigncmds.c:1442
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1567
char * schemaname
Definition: primnodes.h:67
ErrorContextCallback * error_context_stack
Definition: elog.c:92
static Oid lookup_fdw_validator_func(DefElem *validator)
Definition: foreigncmds.c:499
FdwRoutine * GetFdwRoutine(Oid fdwhandler)
Definition: foreign.c:319
char * relname
Definition: primnodes.h:68
#define sprintf
Definition: port.h:195
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:309
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3327
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:35
List * options
Definition: parsenodes.h:2280
static Oid lookup_fdw_handler_func(DefElem *handler)
Definition: foreigncmds.c:475
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
char * defGetString(DefElem *def)
Definition: define.c:49
List * pg_parse_query(const char *query_string)
Definition: postgres.c:628
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2103
ItemPointerData t_self
Definition: htup.h:65
ObjectAddress AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
Definition: foreigncmds.c:275
#define lfirst_node(type, lc)
Definition: pg_list.h:193
Oid RemoveUserMapping(DropUserMappingStmt *stmt)
Definition: foreigncmds.c:1363
AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4696
ObjectAddress CreateUserMapping(CreateUserMappingStmt *stmt)
Definition: foreigncmds.c:1143
bool IsImportableForeignTable(const char *tablename, ImportForeignSchemaStmt *stmt)
Definition: foreign.c:467
Node * stmt
Definition: parsenodes.h:1508
#define RowExclusiveLock
Definition: lockdefs.h:38
char * fdwname
Definition: parsenodes.h:2278
#define CStringGetDatum(X)
Definition: postgres.h:578
int geterrposition(void)
Definition: elog.c:1331
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:314
char * fdwname
Definition: parsenodes.h:2286
#define ACL_USAGE
Definition: parsenodes.h:82
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
ForeignServer * GetForeignServerByName(const char *srvname, bool missing_ok)
Definition: foreign.c:180
ImportForeignSchema_function ImportForeignSchema
Definition: fdwapi.h:237
ObjectAddress AlterUserMapping(AlterUserMappingStmt *stmt)
Definition: foreigncmds.c:1265
void check_is_member_of_role(Oid member, Oid role)
Definition: acl.c:4938
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
Definition: arrayfuncs.c:5123
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
bool superuser_arg(Oid roleid)
Definition: superuser.c:56
Node * arg
Definition: parsenodes.h:733
List * lappend(List *list, void *datum)
Definition: list.c:321
List * func_options
Definition: parsenodes.h:2287
#define WARNING
Definition: elog.h:40
#define heap_getattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:762
ObjectAddress CreateForeignServer(CreateForeignServerStmt *stmt)
Definition: foreigncmds.c:866
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
char * NameListToString(List *names)
Definition: namespace.c:3102
AclResult
Definition: acl.h:177
uintptr_t Datum
Definition: postgres.h:367
void CommandCounterIncrement(void)
Definition: xact.c:1006
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
RoleSpecType roletype
Definition: parsenodes.h:329
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1377
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5209
int stmt_len
Definition: parsenodes.h:1510
int stmt_location
Definition: parsenodes.h:1509
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1308
TupleDesc rd_att
Definition: rel.h:110
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:240
List * list_delete_cell(List *list, ListCell *cell)
Definition: list.c:767
FormData_pg_foreign_server * Form_pg_foreign_server
void RemoveForeignServerById(Oid srvId)
Definition: foreigncmds.c:1092
#define InvalidOid
Definition: postgres_ext.h:36
static struct @143 value
#define ereport(elevel,...)
Definition: elog.h:144
int internalerrquery(const char *query)
Definition: elog.c:1235
#define NOTICE
Definition: elog.h:37
#define makeNode(_type_)
Definition: nodes.h:577
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
#define ForeignDataWrapperOidIndexId
Definition: indexing.h:291
ObjectAddress AlterForeignServerOwner(const char *name, Oid newOwnerId)
Definition: foreigncmds.c:415
FormData_pg_foreign_data_wrapper * Form_pg_foreign_data_wrapper
ForeignDataWrapper * GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
Definition: foreign.c:94
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition: pg_depend.c:138
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
Definition: syscache.h:194
size_t Size
Definition: c.h:466
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
#define UserMappingOidIndexId
Definition: indexing.h:301
ObjectAddress AlterForeignDataWrapper(AlterFdwStmt *stmt)
Definition: foreigncmds.c:678
static void AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
Definition: foreigncmds.c:338
char * rolename
Definition: parsenodes.h:330
const char * name
Definition: encode.c:555
#define nodeTag(nodeptr)
Definition: nodes.h:534
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define DatumGetPointer(X)
Definition: postgres.h:549
List * options
Definition: parsenodes.h:2288
static Datum values[MAXATTR]
Definition: bootstrap.c:167
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
Definition: arrayfuncs.c:5059
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:174
#define OidFunctionCall2(functionId, arg1, arg2)
Definition: fmgr.h:664
const ObjectAddress InvalidObjectAddress
void * palloc(Size size)
Definition: mcxt.c:949
void ImportForeignSchema(ImportForeignSchemaStmt *stmt)
Definition: foreigncmds.c:1546
int errmsg(const char *fmt,...)
Definition: elog.c:824
char * servername
Definition: foreign.h:39
#define elog(elevel,...)
Definition: elog.h:214
#define errcontext
Definition: elog.h:185
#define NameStr(name)
Definition: c.h:615
#define CStringGetTextDatum(s)
Definition: builtins.h:87
#define ACL_ID_PUBLIC
Definition: acl.h:46
void * arg
static void AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
Definition: foreigncmds.c:205
Definition: c.h:555
char * defname
Definition: parsenodes.h:732
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:329
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1113
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:31
Definition: pg_list.h:50
#define PointerIsValid(pointer)
Definition: c.h:632
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1044
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:501
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:183
Oid serverid
Definition: foreign.h:36
#define MappingUserName(userid)
Definition: foreign.h:20
#define ForeignServerOidIndexId
Definition: indexing.h:296
int errposition(int cursorpos)
Definition: elog.c:1199
List * func_options
Definition: parsenodes.h:2279
static Datum optionListToArray(List *options)
Definition: foreigncmds.c:66
static void import_error_callback(void *arg)
Definition: foreigncmds.c:1663
int internalerrposition(int cursorpos)
Definition: elog.c:1215