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