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