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