Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * foreigncmds.c
4 : * foreign-data wrapper/server creation/manipulation commands
5 : *
6 : * Portions Copyright (c) 1996-2023, 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
5224 peter_e 66 CBC 704 : optionListToArray(List *options)
67 : {
5050 bruce 68 704 : ArrayBuildState *astate = NULL;
69 : ListCell *cell;
70 :
5223 tgl 71 1699 : foreach(cell, options)
72 : {
5224 peter_e 73 995 : DefElem *def = lfirst(cell);
74 : const char *value;
75 : Size len;
76 : text *t;
77 :
78 995 : value = defGetString(def);
79 995 : len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
80 995 : t = palloc(len + 1);
81 995 : SET_VARSIZE(t, len);
82 995 : sprintf(VARDATA(t), "%s=%s", def->defname, value);
83 :
84 995 : astate = accumArrayResult(astate, PointerGetDatum(t),
85 : false, TEXTOID,
86 : CurrentMemoryContext);
87 : }
88 :
89 704 : if (astate)
90 463 : return makeArrayResult(astate, CurrentMemoryContext);
91 :
92 241 : 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
4855 heikki.linnakangas 110 716 : transformGenericOptions(Oid catalogId,
111 : Datum oldOptions,
112 : List *options,
113 : Oid fdwvalidator)
114 : {
5050 bruce 115 716 : List *resultOptions = untransformRelOptions(oldOptions);
116 : ListCell *optcell;
117 : Datum result;
118 :
5118 tgl 119 1437 : foreach(optcell, options)
120 : {
5050 bruce 121 733 : 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 1603 : foreach(cell, resultOptions)
129 : {
130 980 : DefElem *def = lfirst(cell);
131 :
5118 tgl 132 980 : if (strcmp(def->defname, od->defname) == 0)
5224 peter_e 133 110 : 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 : */
5118 tgl 142 733 : switch (od->defaction)
143 : {
144 56 : case DEFELEM_DROP:
5224 peter_e 145 56 : if (!cell)
146 3 : ereport(ERROR,
147 : (errcode(ERRCODE_UNDEFINED_OBJECT),
148 : errmsg("option \"%s\" not found",
149 : od->defname)));
1364 tgl 150 53 : resultOptions = list_delete_cell(resultOptions, cell);
5224 peter_e 151 53 : break;
152 :
5118 tgl 153 54 : case DEFELEM_SET:
5224 peter_e 154 54 : if (!cell)
155 3 : ereport(ERROR,
156 : (errcode(ERRCODE_UNDEFINED_OBJECT),
157 : errmsg("option \"%s\" not found",
158 : od->defname)));
5118 tgl 159 51 : lfirst(cell) = od;
5224 peter_e 160 51 : break;
161 :
5118 tgl 162 623 : case DEFELEM_ADD:
163 : case DEFELEM_UNSPEC:
5224 peter_e 164 623 : if (cell)
165 6 : ereport(ERROR,
166 : (errcode(ERRCODE_DUPLICATE_OBJECT),
167 : errmsg("option \"%s\" provided more than once",
168 : od->defname)));
5118 tgl 169 617 : resultOptions = lappend(resultOptions, od);
5224 peter_e 170 617 : break;
171 :
5224 peter_e 172 UBC 0 : default:
173 0 : elog(ERROR, "unrecognized action %d on option \"%s\"",
174 : (int) od->defaction, od->defname);
175 : break;
176 : }
177 : }
178 :
5157 peter_e 179 CBC 704 : result = optionListToArray(resultOptions);
180 :
4296 tgl 181 704 : if (OidIsValid(fdwvalidator))
182 : {
3955 bruce 183 389 : 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 : */
4296 tgl 189 389 : if (DatumGetPointer(valarg) == NULL)
190 57 : valarg = PointerGetDatum(construct_empty_array(TEXTOID));
191 389 : OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
192 : }
193 :
5157 peter_e 194 645 : 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
4065 alvherre 205 10 : 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 10 : form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
216 :
217 : /* Must be a superuser to change a FDW owner */
5224 peter_e 218 10 : if (!superuser())
219 3 : 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 7 : if (!superuser_arg(newOwnerId))
227 3 : 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 4 : if (form->fdwowner != newOwnerId)
234 : {
2999 bruce 235 3 : memset(repl_null, false, sizeof(repl_null));
236 3 : memset(repl_repl, false, sizeof(repl_repl));
237 :
238 3 : repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
239 3 : repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
240 :
241 3 : aclDatum = heap_getattr(tup,
242 : Anum_pg_foreign_data_wrapper_fdwacl,
243 : RelationGetDescr(rel),
244 : &isNull);
245 : /* Null ACLs do not require changes */
246 3 : if (!isNull)
247 : {
2999 bruce 248 UBC 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 :
2999 bruce 254 CBC 3 : tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
255 : repl_repl);
256 :
2259 alvherre 257 3 : CatalogTupleUpdate(rel, &tup->t_self, tup);
258 :
259 : /* Update owner dependency reference */
5224 peter_e 260 3 : changeDependencyOnOwner(ForeignDataWrapperRelationId,
261 : form->oid,
262 : newOwnerId);
263 : }
264 :
3675 rhaas 265 4 : InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
266 : form->oid, 0);
4065 alvherre 267 4 : }
268 :
269 : /*
270 : * Change foreign-data wrapper owner -- by name
271 : *
272 : * Note restrictions in the "_internal" function, above.
273 : */
274 : ObjectAddress
275 10 : 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 :
1539 andres 284 10 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
285 :
4065 alvherre 286 10 : tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
287 :
288 10 : if (!HeapTupleIsValid(tup))
4065 alvherre 289 UBC 0 : ereport(ERROR,
290 : (errcode(ERRCODE_UNDEFINED_OBJECT),
291 : errmsg("foreign-data wrapper \"%s\" does not exist", name)));
292 :
1601 andres 293 CBC 10 : form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
294 10 : fdwId = form->oid;
295 :
4065 alvherre 296 10 : AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
297 :
2959 298 4 : ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
299 :
5224 peter_e 300 4 : heap_freetuple(tup);
301 :
1539 andres 302 4 : table_close(rel, RowExclusiveLock);
303 :
2959 alvherre 304 4 : 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
4065 alvherre 313 UBC 0 : AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
314 : {
315 : HeapTuple tup;
316 : Relation rel;
317 :
1539 andres 318 0 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
319 :
4065 alvherre 320 0 : tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
321 :
5224 peter_e 322 0 : if (!HeapTupleIsValid(tup))
5050 bruce 323 0 : ereport(ERROR,
324 : (errcode(ERRCODE_UNDEFINED_OBJECT),
325 : errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
326 :
4065 alvherre 327 0 : AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
328 :
329 0 : heap_freetuple(tup);
330 :
1539 andres 331 0 : table_close(rel, RowExclusiveLock);
4065 alvherre 332 0 : }
333 :
334 : /*
335 : * Internal workhorse for changing a foreign server's owner
336 : */
337 : static void
4065 alvherre 338 CBC 40 : 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 :
5224 peter_e 348 40 : form = (Form_pg_foreign_server) GETSTRUCT(tup);
349 :
350 40 : if (form->srvowner != newOwnerId)
351 : {
352 : /* Superusers can always do it */
353 36 : if (!superuser())
354 : {
355 : Oid srvId;
356 : AclResult aclresult;
357 :
1601 andres 358 15 : srvId = form->oid;
359 :
360 : /* Must be owner */
147 peter 361 GNC 15 : if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
1954 peter_e 362 CBC 6 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
4065 alvherre 363 6 : NameStr(form->srvname));
364 :
365 : /* Must be able to become new owner */
142 rhaas 366 GNC 9 : check_can_set_role(GetUserId(), newOwnerId);
367 :
368 : /* New owner must have USAGE privilege on foreign-data wrapper */
147 peter 369 6 : aclresult = object_aclcheck(ForeignDataWrapperRelationId, form->srvfdw, newOwnerId, ACL_USAGE);
5224 peter_e 370 CBC 6 : if (aclresult != ACLCHECK_OK)
371 : {
372 3 : ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
373 :
1954 374 3 : aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
375 : }
376 : }
377 :
2999 bruce 378 24 : memset(repl_null, false, sizeof(repl_null));
379 24 : memset(repl_repl, false, sizeof(repl_repl));
380 :
381 24 : repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
382 24 : repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
383 :
384 24 : aclDatum = heap_getattr(tup,
385 : Anum_pg_foreign_server_srvacl,
386 : RelationGetDescr(rel),
387 : &isNull);
388 : /* Null ACLs do not require changes */
389 24 : if (!isNull)
390 : {
391 9 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
392 : form->srvowner, newOwnerId);
393 9 : repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
394 9 : repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
395 : }
396 :
397 24 : tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
398 : repl_repl);
399 :
2259 alvherre 400 24 : CatalogTupleUpdate(rel, &tup->t_self, tup);
401 :
402 : /* Update owner dependency reference */
1601 andres 403 24 : changeDependencyOnOwner(ForeignServerRelationId, form->oid,
404 : newOwnerId);
405 : }
406 :
3675 rhaas 407 28 : InvokeObjectPostAlterHook(ForeignServerRelationId,
408 : form->oid, 0);
4065 alvherre 409 28 : }
410 :
411 : /*
412 : * Change foreign server owner -- by name
413 : */
414 : ObjectAddress
415 34 : 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 :
1539 andres 423 34 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
424 :
4065 alvherre 425 34 : tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
426 :
427 34 : if (!HeapTupleIsValid(tup))
4065 alvherre 428 UBC 0 : ereport(ERROR,
429 : (errcode(ERRCODE_UNDEFINED_OBJECT),
430 : errmsg("server \"%s\" does not exist", name)));
431 :
1601 andres 432 CBC 34 : form = (Form_pg_foreign_server) GETSTRUCT(tup);
433 34 : servOid = form->oid;
434 :
4065 alvherre 435 34 : AlterForeignServerOwner_internal(rel, tup, newOwnerId);
436 :
2959 437 22 : ObjectAddressSet(address, ForeignServerRelationId, servOid);
438 :
5224 peter_e 439 22 : heap_freetuple(tup);
440 :
1539 andres 441 22 : table_close(rel, RowExclusiveLock);
442 :
2959 alvherre 443 22 : return address;
444 : }
445 :
446 : /*
447 : * Change foreign server owner -- by OID
448 : */
449 : void
4065 450 6 : AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
451 : {
452 : HeapTuple tup;
453 : Relation rel;
454 :
1539 andres 455 6 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
456 :
4065 alvherre 457 6 : tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
458 :
459 6 : if (!HeapTupleIsValid(tup))
4065 alvherre 460 UBC 0 : ereport(ERROR,
461 : (errcode(ERRCODE_UNDEFINED_OBJECT),
462 : errmsg("foreign server with OID %u does not exist", srvId)));
463 :
4065 alvherre 464 CBC 6 : AlterForeignServerOwner_internal(rel, tup, newOwnerId);
465 :
466 6 : heap_freetuple(tup);
467 :
1539 andres 468 6 : table_close(rel, RowExclusiveLock);
4065 alvherre 469 6 : }
470 :
471 : /*
472 : * Convert a handler function name passed from the parser to an Oid.
473 : */
474 : static Oid
4432 tgl 475 20 : lookup_fdw_handler_func(DefElem *handler)
476 : {
477 : Oid handlerOid;
478 :
479 20 : if (handler == NULL || handler->arg == NULL)
4432 tgl 480 UBC 0 : return InvalidOid;
481 :
482 : /* handlers have no arguments */
1244 alvherre 483 CBC 20 : handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
484 :
485 : /* check that handler has correct return type */
4432 tgl 486 20 : if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
487 6 : 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 14 : return handlerOid;
493 : }
494 :
495 : /*
496 : * Convert a validator function name passed from the parser to an Oid.
497 : */
498 : static Oid
499 24 : lookup_fdw_validator_func(DefElem *validator)
500 : {
501 : Oid funcargtypes[2];
502 :
503 24 : if (validator == NULL || validator->arg == NULL)
504 3 : return InvalidOid;
505 :
506 : /* validators take text[], oid */
5157 peter_e 507 21 : funcargtypes[0] = TEXTARRAYOID;
508 21 : funcargtypes[1] = OIDOID;
509 :
4432 tgl 510 21 : 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
633 dean.a.rasheed 518 128 : 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 :
4432 tgl 524 128 : *handler_given = false;
525 128 : *validator_given = false;
526 : /* return InvalidOid if not given */
527 128 : *fdwhandler = InvalidOid;
528 128 : *fdwvalidator = InvalidOid;
529 :
530 160 : foreach(cell, func_options)
531 : {
532 50 : DefElem *def = (DefElem *) lfirst(cell);
533 :
534 50 : if (strcmp(def->defname, "handler") == 0)
535 : {
536 26 : if (*handler_given)
633 dean.a.rasheed 537 6 : errorConflictingDefElem(def, pstate);
4432 tgl 538 20 : *handler_given = true;
539 20 : *fdwhandler = lookup_fdw_handler_func(def);
540 : }
541 24 : else if (strcmp(def->defname, "validator") == 0)
542 : {
543 24 : if (*validator_given)
633 dean.a.rasheed 544 UBC 0 : errorConflictingDefElem(def, pstate);
4432 tgl 545 CBC 24 : *validator_given = true;
546 24 : *fdwvalidator = lookup_fdw_validator_func(def);
547 : }
548 : else
4432 tgl 549 UBC 0 : elog(ERROR, "option \"%s\" not recognized",
550 : def->defname);
551 : }
4432 tgl 552 CBC 110 : }
553 :
554 : /*
555 : * Create a foreign-data wrapper
556 : */
557 : ObjectAddress
633 dean.a.rasheed 558 92 : 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 :
1539 andres 574 92 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
575 :
576 : /* Must be superuser */
5224 peter_e 577 92 : if (!superuser())
578 10 : 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 82 : ownerId = GetUserId();
586 :
587 : /*
588 : * Check that there is no other foreign-data wrapper by this name.
589 : */
590 82 : if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
591 3 : 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 : */
5223 tgl 599 79 : memset(values, 0, sizeof(values));
600 79 : memset(nulls, false, sizeof(nulls));
601 :
1601 andres 602 79 : fdwId = GetNewOidWithIndex(rel, ForeignDataWrapperOidIndexId,
603 : Anum_pg_foreign_data_wrapper_oid);
604 79 : values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
5224 peter_e 605 79 : values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
606 79 : DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
607 79 : values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
608 :
609 : /* Lookup handler and validator functions, if given */
633 dean.a.rasheed 610 79 : parse_func_options(pstate, stmt->func_options,
611 : &handler_given, &fdwhandler,
612 : &validator_given, &fdwvalidator);
613 :
4432 tgl 614 70 : values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
615 70 : values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
616 :
5157 peter_e 617 70 : nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
618 :
4855 heikki.linnakangas 619 70 : fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
620 : PointerGetDatum(NULL),
621 : stmt->options,
622 : fdwvalidator);
623 :
5223 624 67 : if (PointerIsValid(DatumGetPointer(fdwoptions)))
5224 peter_e 625 9 : values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
626 : else
627 58 : nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
628 :
629 67 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
630 :
1601 andres 631 67 : CatalogTupleInsert(rel, tuple);
632 :
5224 peter_e 633 67 : heap_freetuple(tuple);
634 :
635 : /* record dependencies */
4443 tgl 636 67 : myself.classId = ForeignDataWrapperRelationId;
637 67 : myself.objectId = fdwId;
638 67 : myself.objectSubId = 0;
639 :
4432 640 67 : if (OidIsValid(fdwhandler))
641 : {
642 5 : referenced.classId = ProcedureRelationId;
643 5 : referenced.objectId = fdwhandler;
644 5 : referenced.objectSubId = 0;
645 5 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
646 : }
647 :
648 67 : if (OidIsValid(fdwvalidator))
649 : {
5157 peter_e 650 12 : referenced.classId = ProcedureRelationId;
651 12 : referenced.objectId = fdwvalidator;
652 12 : referenced.objectSubId = 0;
653 12 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
654 : }
655 :
5224 656 67 : recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
657 :
658 : /* dependency on extension */
4278 tgl 659 67 : recordDependencyOnCurrentExtension(&myself, false);
660 :
661 : /* Post creation hook for new foreign data wrapper */
3686 rhaas 662 67 : InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
663 :
1539 andres 664 67 : table_close(rel, RowExclusiveLock);
665 :
2959 alvherre 666 67 : return myself;
667 : }
668 :
669 :
670 : /*
671 : * Alter foreign-data wrapper
672 : */
673 : ObjectAddress
633 dean.a.rasheed 674 61 : 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 :
1539 andres 691 61 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
692 :
693 : /* Must be superuser */
5224 peter_e 694 61 : if (!superuser())
695 12 : 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 :
4802 rhaas 701 49 : tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
702 : CStringGetDatum(stmt->fdwname));
703 :
5224 peter_e 704 49 : if (!HeapTupleIsValid(tp))
5224 peter_e 705 UBC 0 : ereport(ERROR,
706 : (errcode(ERRCODE_UNDEFINED_OBJECT),
707 : errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
708 :
4432 tgl 709 CBC 49 : fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
1601 andres 710 49 : fdwId = fdwForm->oid;
711 :
5224 peter_e 712 49 : memset(repl_val, 0, sizeof(repl_val));
713 49 : memset(repl_null, false, sizeof(repl_null));
714 49 : memset(repl_repl, false, sizeof(repl_repl));
715 :
633 dean.a.rasheed 716 49 : parse_func_options(pstate, stmt->func_options,
717 : &handler_given, &fdwhandler,
718 : &validator_given, &fdwvalidator);
719 :
4432 tgl 720 40 : if (handler_given)
721 : {
722 3 : repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
723 3 : 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 3 : ereport(WARNING,
730 : (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
731 : }
732 :
733 40 : if (validator_given)
734 : {
5157 peter_e 735 6 : repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
736 6 : 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 : */
4432 tgl 743 6 : if (OidIsValid(fdwvalidator))
5157 peter_e 744 3 : 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 : */
4432 tgl 753 34 : fdwvalidator = fdwForm->fdwvalidator;
754 : }
755 :
756 : /*
757 : * If options specified, validate and update.
758 : */
5224 peter_e 759 40 : if (stmt->options)
760 : {
761 : /* Extract the current options */
762 31 : datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
763 : tp,
764 : Anum_pg_foreign_data_wrapper_fdwoptions,
765 : &isnull);
5223 tgl 766 31 : if (isnull)
767 10 : datum = PointerGetDatum(NULL);
768 :
769 : /* Transform the options */
4855 heikki.linnakangas 770 31 : datum = transformGenericOptions(ForeignDataWrapperRelationId,
771 : datum,
772 : stmt->options,
773 : fdwvalidator);
774 :
5223 775 15 : if (PointerIsValid(DatumGetPointer(datum)))
tgl 776 15 : repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
777 : else
5224 peter_e 778 UBC 0 : repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
779 :
5224 peter_e 780 CBC 15 : repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
781 : }
782 :
783 : /* Everything looks good - update the tuple */
784 24 : tp = heap_modify_tuple(tp, RelationGetDescr(rel),
785 : repl_val, repl_null, repl_repl);
786 :
2259 alvherre 787 24 : CatalogTupleUpdate(rel, &tp->t_self, tp);
788 :
5224 peter_e 789 24 : heap_freetuple(tp);
790 :
2959 alvherre 791 24 : ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
792 :
793 : /* Update function dependencies if we changed them */
4432 tgl 794 24 : 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 9 : deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
803 : fdwId,
804 : ProcedureRelationId,
805 : DEPENDENCY_NORMAL);
806 :
807 : /* And build new ones. */
808 :
809 9 : if (OidIsValid(fdwhandler))
810 : {
811 3 : referenced.classId = ProcedureRelationId;
812 3 : referenced.objectId = fdwhandler;
813 3 : referenced.objectSubId = 0;
814 3 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
815 : }
816 :
817 9 : if (OidIsValid(fdwvalidator))
818 : {
819 3 : referenced.classId = ProcedureRelationId;
820 3 : referenced.objectId = fdwvalidator;
821 3 : referenced.objectSubId = 0;
822 3 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
823 : }
824 : }
825 :
3675 rhaas 826 24 : InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
827 :
1539 andres 828 24 : table_close(rel, RowExclusiveLock);
829 :
2959 alvherre 830 24 : return myself;
831 : }
832 :
833 :
834 : /*
835 : * Create a foreign server
836 : */
837 : ObjectAddress
5224 peter_e 838 133 : 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 :
1539 andres 852 133 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
853 :
854 : /* For now the owner cannot be specified on create. Use effective user ID. */
5224 peter_e 855 133 : 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 : */
244 tgl 861 133 : srvId = get_foreign_server_oid(stmt->servername, true);
862 133 : if (OidIsValid(srvId))
863 : {
2211 andrew 864 8 : 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 : */
244 tgl 870 5 : ObjectAddressSet(myself, ForeignServerRelationId, srvId);
871 5 : checkMembershipInCurrentExtension(&myself);
872 :
873 : /* OK to skip */
2211 andrew 874 4 : ereport(NOTICE,
875 : (errcode(ERRCODE_DUPLICATE_OBJECT),
876 : errmsg("server \"%s\" already exists, skipping",
877 : stmt->servername)));
1539 andres 878 4 : table_close(rel, RowExclusiveLock);
2211 andrew 879 4 : return InvalidObjectAddress;
880 : }
881 : else
882 3 : 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 : */
5224 peter_e 892 125 : fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
893 :
147 peter 894 GNC 122 : aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdw->fdwid, ownerId, ACL_USAGE);
5224 peter_e 895 CBC 122 : if (aclresult != ACLCHECK_OK)
1954 896 13 : aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
897 :
898 : /*
899 : * Insert tuple into pg_foreign_server.
900 : */
5223 tgl 901 109 : memset(values, 0, sizeof(values));
902 109 : memset(nulls, false, sizeof(nulls));
903 :
1601 andres 904 109 : srvId = GetNewOidWithIndex(rel, ForeignServerOidIndexId,
905 : Anum_pg_foreign_server_oid);
906 109 : values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
5224 peter_e 907 109 : values[Anum_pg_foreign_server_srvname - 1] =
908 109 : DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
909 109 : values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
910 109 : values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
911 :
912 : /* Add server type if supplied */
913 109 : if (stmt->servertype)
914 9 : values[Anum_pg_foreign_server_srvtype - 1] =
915 9 : CStringGetTextDatum(stmt->servertype);
916 : else
917 100 : nulls[Anum_pg_foreign_server_srvtype - 1] = true;
918 :
919 : /* Add server version if supplied */
920 109 : if (stmt->version)
921 9 : values[Anum_pg_foreign_server_srvversion - 1] =
922 9 : CStringGetTextDatum(stmt->version);
923 : else
924 100 : nulls[Anum_pg_foreign_server_srvversion - 1] = true;
925 :
926 : /* Start with a blank acl */
927 109 : nulls[Anum_pg_foreign_server_srvacl - 1] = true;
928 :
929 : /* Add server options */
4855 heikki.linnakangas 930 109 : srvoptions = transformGenericOptions(ForeignServerRelationId,
931 : PointerGetDatum(NULL),
932 : stmt->options,
933 : fdw->fdwvalidator);
934 :
5223 935 104 : if (PointerIsValid(DatumGetPointer(srvoptions)))
5224 peter_e 936 22 : values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
937 : else
938 82 : nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
939 :
940 104 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
941 :
1601 andres 942 104 : CatalogTupleInsert(rel, tuple);
943 :
5224 peter_e 944 104 : heap_freetuple(tuple);
945 :
946 : /* record dependencies */
947 104 : myself.classId = ForeignServerRelationId;
948 104 : myself.objectId = srvId;
949 104 : myself.objectSubId = 0;
950 :
951 104 : referenced.classId = ForeignDataWrapperRelationId;
952 104 : referenced.objectId = fdw->fdwid;
953 104 : referenced.objectSubId = 0;
954 104 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
955 :
956 104 : recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
957 :
958 : /* dependency on extension */
4278 tgl 959 104 : recordDependencyOnCurrentExtension(&myself, false);
960 :
961 : /* Post creation hook for new foreign server */
3686 rhaas 962 104 : InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
963 :
1539 andres 964 104 : table_close(rel, RowExclusiveLock);
965 :
2959 alvherre 966 104 : return myself;
967 : }
968 :
969 :
970 : /*
971 : * Alter foreign server
972 : */
973 : ObjectAddress
5224 peter_e 974 107 : 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 :
1539 andres 985 107 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
986 :
4802 rhaas 987 107 : tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
988 : CStringGetDatum(stmt->servername));
989 :
5224 peter_e 990 107 : if (!HeapTupleIsValid(tp))
991 3 : ereport(ERROR,
992 : (errcode(ERRCODE_UNDEFINED_OBJECT),
993 : errmsg("server \"%s\" does not exist", stmt->servername)));
994 :
995 104 : srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
1601 andres 996 104 : srvId = srvForm->oid;
997 :
998 : /*
999 : * Only owner or a superuser can ALTER a SERVER.
1000 : */
147 peter 1001 GNC 104 : if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
1954 peter_e 1002 CBC 12 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
5224 1003 12 : stmt->servername);
1004 :
1005 92 : memset(repl_val, 0, sizeof(repl_val));
1006 92 : memset(repl_null, false, sizeof(repl_null));
1007 92 : memset(repl_repl, false, sizeof(repl_repl));
1008 :
1009 92 : if (stmt->has_version)
1010 : {
1011 : /*
1012 : * Change the server VERSION string.
1013 : */
1014 12 : if (stmt->version)
1015 12 : repl_val[Anum_pg_foreign_server_srvversion - 1] =
1016 12 : CStringGetTextDatum(stmt->version);
1017 : else
5224 peter_e 1018 UBC 0 : repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1019 :
5224 peter_e 1020 CBC 12 : repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1021 : }
1022 :
1023 92 : if (stmt->options)
1024 : {
1025 83 : ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1026 : Datum datum;
1027 : bool isnull;
1028 :
1029 : /* Extract the current srvoptions */
1030 83 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
1031 : tp,
1032 : Anum_pg_foreign_server_srvoptions,
1033 : &isnull);
5223 tgl 1034 83 : if (isnull)
1035 9 : datum = PointerGetDatum(NULL);
1036 :
1037 : /* Prepare the options array */
4855 heikki.linnakangas 1038 83 : datum = transformGenericOptions(ForeignServerRelationId,
1039 : datum,
1040 : stmt->options,
1041 : fdw->fdwvalidator);
1042 :
5223 1043 75 : if (PointerIsValid(DatumGetPointer(datum)))
5224 peter_e 1044 72 : repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1045 : else
1046 3 : repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1047 :
1048 75 : repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1049 : }
1050 :
1051 : /* Everything looks good - update the tuple */
1052 84 : tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1053 : repl_val, repl_null, repl_repl);
1054 :
2259 alvherre 1055 84 : CatalogTupleUpdate(rel, &tp->t_self, tp);
1056 :
3675 rhaas 1057 84 : InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1058 :
2959 alvherre 1059 84 : ObjectAddressSet(address, ForeignServerRelationId, srvId);
1060 :
5224 peter_e 1061 84 : heap_freetuple(tp);
1062 :
1539 andres 1063 84 : table_close(rel, RowExclusiveLock);
1064 :
2959 alvherre 1065 84 : 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
5192 peter_e 1075 198 : user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1076 : {
1077 198 : Oid curuserid = GetUserId();
1078 :
147 peter 1079 GNC 198 : if (!object_ownercheck(ForeignServerRelationId, serverid, curuserid))
1080 : {
5192 peter_e 1081 CBC 36 : if (umuserid == curuserid)
1082 : {
1083 : AclResult aclresult;
1084 :
147 peter 1085 GNC 9 : aclresult = object_aclcheck(ForeignServerRelationId, serverid, curuserid, ACL_USAGE);
5192 peter_e 1086 CBC 9 : if (aclresult != ACLCHECK_OK)
1954 1087 4 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, servername);
1088 : }
1089 : else
1090 27 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
1091 : servername);
1092 : }
5192 1093 167 : }
1094 :
1095 :
1096 : /*
1097 : * Create user mapping
1098 : */
1099 : ObjectAddress
5224 1100 118 : 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;
2953 alvherre 1113 118 : RoleSpec *role = (RoleSpec *) stmt->user;
1114 :
1539 andres 1115 118 : rel = table_open(UserMappingRelationId, RowExclusiveLock);
1116 :
2953 alvherre 1117 118 : if (role->roletype == ROLESPEC_PUBLIC)
1118 33 : useId = ACL_ID_PUBLIC;
1119 : else
1120 85 : useId = get_rolespec_oid(stmt->user, false);
1121 :
1122 : /* Check that the server exists. */
5224 peter_e 1123 114 : srv = GetForeignServerByName(stmt->servername, false);
1124 :
5192 1125 111 : user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1126 :
1127 : /*
1128 : * Check that the user mapping is unique within server.
1129 : */
1601 andres 1130 98 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1131 : ObjectIdGetDatum(useId),
1132 : ObjectIdGetDatum(srv->serverid));
1133 :
5224 peter_e 1134 98 : if (OidIsValid(umId))
1135 : {
2211 andrew 1136 9 : 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 3 : 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 :
1539 andres 1148 3 : table_close(rel, RowExclusiveLock);
2211 andrew 1149 3 : return InvalidObjectAddress;
1150 : }
1151 : else
1152 6 : 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 :
5224 peter_e 1159 89 : fdw = GetForeignDataWrapper(srv->fdwid);
1160 :
1161 : /*
1162 : * Insert tuple into pg_user_mapping.
1163 : */
5223 tgl 1164 89 : memset(values, 0, sizeof(values));
1165 89 : memset(nulls, false, sizeof(nulls));
1166 :
1601 andres 1167 89 : umId = GetNewOidWithIndex(rel, UserMappingOidIndexId,
1168 : Anum_pg_user_mapping_oid);
1169 89 : values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
5224 peter_e 1170 89 : values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1171 89 : values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1172 :
1173 : /* Add user options */
4855 heikki.linnakangas 1174 89 : useoptions = transformGenericOptions(UserMappingRelationId,
1175 : PointerGetDatum(NULL),
1176 : stmt->options,
1177 : fdw->fdwvalidator);
1178 :
5223 1179 83 : if (PointerIsValid(DatumGetPointer(useoptions)))
5224 peter_e 1180 38 : values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1181 : else
1182 45 : nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1183 :
1184 83 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
1185 :
1601 andres 1186 83 : CatalogTupleInsert(rel, tuple);
1187 :
5224 peter_e 1188 83 : heap_freetuple(tuple);
1189 :
1190 : /* Add dependency on the server */
1191 83 : myself.classId = UserMappingRelationId;
1192 83 : myself.objectId = umId;
1193 83 : myself.objectSubId = 0;
1194 :
1195 83 : referenced.classId = ForeignServerRelationId;
1196 83 : referenced.objectId = srv->serverid;
1197 83 : referenced.objectSubId = 0;
1198 83 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1199 :
1200 83 : if (OidIsValid(useId))
1201 : {
1202 : /* Record the mapped user dependency */
1203 62 : 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 */
3686 rhaas 1214 83 : InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1215 :
1539 andres 1216 83 : table_close(rel, RowExclusiveLock);
1217 :
2959 alvherre 1218 83 : return myself;
1219 : }
1220 :
1221 :
1222 : /*
1223 : * Alter user mapping
1224 : */
1225 : ObjectAddress
5224 peter_e 1226 55 : 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;
2953 alvherre 1237 55 : RoleSpec *role = (RoleSpec *) stmt->user;
1238 :
1539 andres 1239 55 : rel = table_open(UserMappingRelationId, RowExclusiveLock);
1240 :
2953 alvherre 1241 55 : if (role->roletype == ROLESPEC_PUBLIC)
1242 14 : useId = ACL_ID_PUBLIC;
1243 : else
1244 41 : useId = get_rolespec_oid(stmt->user, false);
1245 :
5224 peter_e 1246 51 : srv = GetForeignServerByName(stmt->servername, false);
1247 :
1601 andres 1248 48 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1249 : ObjectIdGetDatum(useId),
1250 : ObjectIdGetDatum(srv->serverid));
5224 peter_e 1251 48 : if (!OidIsValid(umId))
1252 3 : 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 :
5192 1257 45 : user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1258 :
4802 rhaas 1259 36 : tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1260 :
5224 peter_e 1261 36 : if (!HeapTupleIsValid(tp))
5224 peter_e 1262 UBC 0 : elog(ERROR, "cache lookup failed for user mapping %u", umId);
1263 :
5224 peter_e 1264 CBC 36 : memset(repl_val, 0, sizeof(repl_val));
1265 36 : memset(repl_null, false, sizeof(repl_null));
1266 36 : memset(repl_repl, false, sizeof(repl_repl));
1267 :
1268 36 : if (stmt->options)
1269 : {
1270 : ForeignDataWrapper *fdw;
1271 : Datum datum;
1272 : bool isnull;
1273 :
1274 : /*
1275 : * Process the options.
1276 : */
1277 :
1278 36 : fdw = GetForeignDataWrapper(srv->fdwid);
1279 :
1280 36 : datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1281 : tp,
1282 : Anum_pg_user_mapping_umoptions,
1283 : &isnull);
5223 tgl 1284 36 : if (isnull)
1285 10 : datum = PointerGetDatum(NULL);
1286 :
1287 : /* Prepare the options array */
4855 heikki.linnakangas 1288 36 : datum = transformGenericOptions(UserMappingRelationId,
1289 : datum,
1290 : stmt->options,
1291 : fdw->fdwvalidator);
1292 :
5223 1293 29 : if (PointerIsValid(DatumGetPointer(datum)))
5224 peter_e 1294 24 : repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1295 : else
1296 5 : repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1297 :
1298 29 : repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1299 : }
1300 :
1301 : /* Everything looks good - update the tuple */
1302 29 : tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1303 : repl_val, repl_null, repl_repl);
1304 :
2259 alvherre 1305 29 : CatalogTupleUpdate(rel, &tp->t_self, tp);
1306 :
1051 michael 1307 29 : InvokeObjectPostAlterHook(UserMappingRelationId,
1308 : umId, 0);
1309 :
2959 alvherre 1310 29 : ObjectAddressSet(address, UserMappingRelationId, umId);
1311 :
5224 peter_e 1312 29 : heap_freetuple(tp);
1313 :
1539 andres 1314 29 : table_close(rel, RowExclusiveLock);
1315 :
2959 alvherre 1316 29 : return address;
1317 : }
1318 :
1319 :
1320 : /*
1321 : * Drop user mapping
1322 : */
1323 : Oid
5224 peter_e 1324 62 : RemoveUserMapping(DropUserMappingStmt *stmt)
1325 : {
1326 : ObjectAddress object;
1327 : Oid useId;
1328 : Oid umId;
1329 : ForeignServer *srv;
2953 alvherre 1330 62 : RoleSpec *role = (RoleSpec *) stmt->user;
1331 :
1332 62 : if (role->roletype == ROLESPEC_PUBLIC)
1333 16 : useId = ACL_ID_PUBLIC;
1334 : else
1335 : {
1336 46 : useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1337 42 : if (!OidIsValid(useId))
1338 : {
1339 : /*
1340 : * IF EXISTS specified, role not found and not public. Notice this
1341 : * and leave.
1342 : */
1343 4 : elog(NOTICE, "role \"%s\" does not exist, skipping",
1344 : role->rolename);
1345 4 : return InvalidOid;
1346 : }
1347 : }
1348 :
1349 54 : srv = GetForeignServerByName(stmt->servername, true);
1350 :
5224 peter_e 1351 54 : if (!srv)
1352 : {
1353 6 : if (!stmt->missing_ok)
1354 3 : ereport(ERROR,
1355 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1356 : errmsg("server \"%s\" does not exist",
1357 : stmt->servername)));
1358 : /* IF EXISTS, just note it */
1432 alvherre 1359 3 : ereport(NOTICE,
1360 : (errmsg("server \"%s\" does not exist, skipping",
1361 : stmt->servername)));
3753 rhaas 1362 3 : return InvalidOid;
1363 : }
1364 :
1601 andres 1365 48 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1366 : ObjectIdGetDatum(useId),
1367 : ObjectIdGetDatum(srv->serverid));
1368 :
5224 peter_e 1369 48 : if (!OidIsValid(umId))
1370 : {
1371 6 : if (!stmt->missing_ok)
1372 3 : 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 3 : ereport(NOTICE,
1379 : (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
1380 : MappingUserName(useId), stmt->servername)));
3753 rhaas 1381 3 : return InvalidOid;
1382 : }
1383 :
5192 peter_e 1384 42 : user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1385 :
1386 : /*
1387 : * Do the deletion
1388 : */
5224 1389 33 : object.classId = UserMappingRelationId;
1390 33 : object.objectId = umId;
1391 33 : object.objectSubId = 0;
1392 :
4091 rhaas 1393 33 : performDeletion(&object, DROP_CASCADE, 0);
1394 :
3753 1395 33 : return umId;
1396 : }
1397 :
1398 :
1399 : /*
1400 : * Create a foreign table
1401 : * call after DefineRelation().
1402 : */
1403 : void
4481 1404 199 : 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 199 : CommandCounterIncrement();
1423 :
1539 andres 1424 199 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
1425 :
1426 : /*
1427 : * For now the owner cannot be specified on create. Use effective user ID.
1428 : */
4481 rhaas 1429 199 : 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 199 : server = GetForeignServerByName(stmt->servername, false);
147 peter 1436 GNC 196 : aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, ownerId, ACL_USAGE);
4481 rhaas 1437 CBC 196 : if (aclresult != ACLCHECK_OK)
1954 peter_e 1438 UBC 0 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1439 :
4481 rhaas 1440 CBC 196 : fdw = GetForeignDataWrapper(server->fdwid);
1441 :
1442 : /*
1443 : * Insert tuple into pg_foreign_table.
1444 : */
1445 196 : memset(values, 0, sizeof(values));
1446 196 : memset(nulls, false, sizeof(nulls));
1447 :
1448 196 : values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1449 196 : values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1450 : /* Add table generic options */
1451 196 : ftoptions = transformGenericOptions(ForeignTableRelationId,
1452 : PointerGetDatum(NULL),
1453 : stmt->options,
1454 : fdw->fdwvalidator);
1455 :
1456 171 : if (PointerIsValid(DatumGetPointer(ftoptions)))
1457 124 : values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1458 : else
1459 47 : nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1460 :
1461 171 : tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1462 :
2259 alvherre 1463 171 : CatalogTupleInsert(ftrel, tuple);
1464 :
4481 rhaas 1465 171 : heap_freetuple(tuple);
1466 :
1467 : /* Add pg_class dependency on the server */
1468 171 : myself.classId = RelationRelationId;
1469 171 : myself.objectId = relid;
1470 171 : myself.objectSubId = 0;
1471 :
1472 171 : referenced.classId = ForeignServerRelationId;
1473 171 : referenced.objectId = server->serverid;
1474 171 : referenced.objectSubId = 0;
1475 171 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1476 :
1539 andres 1477 171 : table_close(ftrel, RowExclusiveLock);
4481 rhaas 1478 171 : }
1479 :
1480 : /*
1481 : * Import a foreign schema
1482 : */
1483 : void
3195 tgl 1484 22 : 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 22 : server = GetForeignServerByName(stmt->server_name, false);
147 peter 1495 GNC 21 : aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, GetUserId(), ACL_USAGE);
3195 tgl 1496 CBC 21 : if (aclresult != ACLCHECK_OK)
1954 peter_e 1497 UBC 0 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1498 :
1499 : /* Check that the schema exists and we have CREATE permissions on it */
3195 tgl 1500 CBC 21 : (void) LookupCreationNamespace(stmt->local_schema);
1501 :
1502 : /* Get the FDW and check it supports IMPORT */
1503 20 : fdw = GetForeignDataWrapper(server->fdwid);
1504 20 : if (!OidIsValid(fdw->fdwhandler))
1505 12 : ereport(ERROR,
1506 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1507 : errmsg("foreign-data wrapper \"%s\" has no handler",
1508 : fdw->fdwname)));
1509 8 : fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1510 8 : if (fdw_routine->ImportForeignSchema == NULL)
3195 tgl 1511 UBC 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 */
3195 tgl 1517 CBC 8 : cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1518 :
1519 : /* Parse and execute each command */
1520 35 : foreach(lc, cmd_list)
1521 : {
1522 30 : 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 30 : callback_arg.tablename = NULL; /* not known yet */
1533 30 : callback_arg.cmd = cmd;
1534 30 : sqlerrcontext.callback = import_error_callback;
1535 30 : sqlerrcontext.arg = (void *) &callback_arg;
1536 30 : sqlerrcontext.previous = error_context_stack;
1537 30 : error_context_stack = &sqlerrcontext;
1538 :
1539 : /*
1540 : * Parse the SQL string into a list of raw parse trees.
1541 : */
1542 30 : 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 58 : foreach(lc2, raw_parsetree_list)
1549 : {
2190 1550 30 : RawStmt *rs = lfirst_node(RawStmt, lc2);
2276 1551 30 : 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 : */
3195 1558 30 : if (!IsA(cstmt, CreateForeignTableStmt))
3195 tgl 1559 UBC 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 */
3195 tgl 1564 CBC 30 : if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
3195 tgl 1565 UBC 0 : continue;
1566 :
1567 : /* Enable reporting of current table's name on error */
3195 tgl 1568 CBC 30 : callback_arg.tablename = cstmt->base.relation->relname;
1569 :
1570 : /* Ensure creation schema is the one given in IMPORT statement */
1571 30 : cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1572 :
1573 : /* No planning needed, just make a wrapper PlannedStmt */
2276 1574 30 : pstmt = makeNode(PlannedStmt);
1575 30 : pstmt->commandType = CMD_UTILITY;
1576 30 : pstmt->canSetTag = false;
1577 30 : pstmt->utilityStmt = (Node *) cstmt;
1578 30 : pstmt->stmt_location = rs->stmt_location;
1579 30 : pstmt->stmt_len = rs->stmt_len;
1580 :
1581 : /* Execute statement */
660 1582 30 : 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 */
3195 1587 28 : CommandCounterIncrement();
1588 :
1589 28 : callback_arg.tablename = NULL;
1590 : }
1591 :
1592 28 : error_context_stack = sqlerrcontext.previous;
1593 : }
1594 5 : }
1595 :
1596 : /*
1597 : * error context callback to let us supply the failing SQL statement's text
1598 : */
1599 : static void
1600 2 : import_error_callback(void *arg)
1601 : {
1602 2 : 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 2 : syntaxerrposition = geterrposition();
1607 2 : if (syntaxerrposition > 0)
1608 : {
1609 1 : errposition(0);
1610 1 : internalerrposition(syntaxerrposition);
1611 1 : internalerrquery(callback_arg->cmd);
1612 : }
1613 :
1614 2 : if (callback_arg->tablename)
1615 2 : errcontext("importing foreign table \"%s\"",
1616 : callback_arg->tablename);
1617 2 : }
|