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