Age Owner Branch data TLA 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
5595 peter_e@gmx.net 66 :CBC 720 : optionListToArray(List *options)
67 : : {
5421 bruce@momjian.us 68 : 720 : ArrayBuildState *astate = NULL;
69 : : ListCell *cell;
70 : :
5594 tgl@sss.pgh.pa.us 71 [ + + + + : 1746 : foreach(cell, options)
+ + ]
72 : : {
5595 peter_e@gmx.net 73 : 1026 : DefElem *def = lfirst(cell);
74 : : const char *value;
75 : : Size len;
76 : : text *t;
77 : :
78 : 1026 : value = defGetString(def);
79 : 1026 : len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
80 : 1026 : t = palloc(len + 1);
81 : 1026 : SET_VARSIZE(t, len);
82 : 1026 : sprintf(VARDATA(t), "%s=%s", def->defname, value);
83 : :
84 : 1026 : astate = accumArrayResult(astate, PointerGetDatum(t),
85 : : false, TEXTOID,
86 : : CurrentMemoryContext);
87 : : }
88 : :
89 [ + + ]: 720 : if (astate)
90 : 475 : return makeArrayResult(astate, CurrentMemoryContext);
91 : :
92 : 245 : 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
5226 heikki.linnakangas@i 110 : 732 : transformGenericOptions(Oid catalogId,
111 : : Datum oldOptions,
112 : : List *options,
113 : : Oid fdwvalidator)
114 : : {
5421 bruce@momjian.us 115 : 732 : List *resultOptions = untransformRelOptions(oldOptions);
116 : : ListCell *optcell;
117 : : Datum result;
118 : :
5489 tgl@sss.pgh.pa.us 119 [ + + + + : 1472 : foreach(optcell, options)
+ + ]
120 : : {
5421 bruce@momjian.us 121 : 752 : 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 [ + + + + : 1668 : foreach(cell, resultOptions)
+ + ]
129 : : {
130 : 1028 : DefElem *def = lfirst(cell);
131 : :
5489 tgl@sss.pgh.pa.us 132 [ + + ]: 1028 : if (strcmp(def->defname, od->defname) == 0)
5595 peter_e@gmx.net 133 : 112 : 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 : : */
5489 tgl@sss.pgh.pa.us 142 [ + + + - ]: 752 : switch (od->defaction)
143 : : {
144 : 57 : case DEFELEM_DROP:
5595 peter_e@gmx.net 145 [ + + ]: 57 : if (!cell)
146 [ + - ]: 3 : ereport(ERROR,
147 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
148 : : errmsg("option \"%s\" not found",
149 : : od->defname)));
1735 tgl@sss.pgh.pa.us 150 : 54 : resultOptions = list_delete_cell(resultOptions, cell);
5595 peter_e@gmx.net 151 : 54 : break;
152 : :
5489 tgl@sss.pgh.pa.us 153 : 55 : case DEFELEM_SET:
5595 peter_e@gmx.net 154 [ + + ]: 55 : if (!cell)
155 [ + - ]: 3 : ereport(ERROR,
156 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
157 : : errmsg("option \"%s\" not found",
158 : : od->defname)));
5489 tgl@sss.pgh.pa.us 159 : 52 : lfirst(cell) = od;
5595 peter_e@gmx.net 160 : 52 : break;
161 : :
5489 tgl@sss.pgh.pa.us 162 : 640 : case DEFELEM_ADD:
163 : : case DEFELEM_UNSPEC:
5595 peter_e@gmx.net 164 [ + + ]: 640 : if (cell)
165 [ + - ]: 6 : ereport(ERROR,
166 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
167 : : errmsg("option \"%s\" provided more than once",
168 : : od->defname)));
5489 tgl@sss.pgh.pa.us 169 : 634 : resultOptions = lappend(resultOptions, od);
5595 peter_e@gmx.net 170 : 634 : break;
171 : :
5595 peter_e@gmx.net 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 : :
5528 peter_e@gmx.net 179 :CBC 720 : result = optionListToArray(resultOptions);
180 : :
4667 tgl@sss.pgh.pa.us 181 [ + + ]: 720 : if (OidIsValid(fdwvalidator))
182 : : {
4326 bruce@momjian.us 183 : 403 : 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 : : */
4667 tgl@sss.pgh.pa.us 189 [ + + ]: 403 : if (DatumGetPointer(valarg) == NULL)
190 : 59 : valarg = PointerGetDatum(construct_empty_array(TEXTOID));
191 : 403 : OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
192 : : }
193 : :
5528 peter_e@gmx.net 194 : 661 : 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
4436 alvherre@alvh.no-ip. 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 */
5595 peter_e@gmx.net 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 : : {
3370 bruce@momjian.us 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 : : {
3370 bruce@momjian.us 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 : :
3370 bruce@momjian.us 254 :CBC 3 : tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
255 : : repl_repl);
256 : :
2630 alvherre@alvh.no-ip. 257 : 3 : CatalogTupleUpdate(rel, &tup->t_self, tup);
258 : :
259 : : /* Update owner dependency reference */
5595 peter_e@gmx.net 260 : 3 : changeDependencyOnOwner(ForeignDataWrapperRelationId,
261 : : form->oid,
262 : : newOwnerId);
263 : : }
264 : :
4046 rhaas@postgresql.org 265 [ - + ]: 4 : InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
266 : : form->oid, 0);
4436 alvherre@alvh.no-ip. 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 : :
1910 andres@anarazel.de 284 : 10 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
285 : :
4436 alvherre@alvh.no-ip. 286 : 10 : tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
287 : :
288 [ - + ]: 10 : if (!HeapTupleIsValid(tup))
4436 alvherre@alvh.no-ip. 289 [ # # ]:UBC 0 : ereport(ERROR,
290 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
291 : : errmsg("foreign-data wrapper \"%s\" does not exist", name)));
292 : :
1972 andres@anarazel.de 293 :CBC 10 : form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
294 : 10 : fdwId = form->oid;
295 : :
4436 alvherre@alvh.no-ip. 296 : 10 : AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
297 : :
3330 298 : 4 : ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
299 : :
5595 peter_e@gmx.net 300 : 4 : heap_freetuple(tup);
301 : :
1910 andres@anarazel.de 302 : 4 : table_close(rel, RowExclusiveLock);
303 : :
3330 alvherre@alvh.no-ip. 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
4436 alvherre@alvh.no-ip. 313 :UBC 0 : AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
314 : : {
315 : : HeapTuple tup;
316 : : Relation rel;
317 : :
1910 andres@anarazel.de 318 : 0 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
319 : :
4436 alvherre@alvh.no-ip. 320 : 0 : tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
321 : :
5595 peter_e@gmx.net 322 [ # # ]: 0 : if (!HeapTupleIsValid(tup))
5421 bruce@momjian.us 323 [ # # ]: 0 : ereport(ERROR,
324 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
325 : : errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
326 : :
4436 alvherre@alvh.no-ip. 327 : 0 : AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
328 : :
329 : 0 : heap_freetuple(tup);
330 : :
1910 andres@anarazel.de 331 : 0 : table_close(rel, RowExclusiveLock);
4436 alvherre@alvh.no-ip. 332 : 0 : }
333 : :
334 : : /*
335 : : * Internal workhorse for changing a foreign server's owner
336 : : */
337 : : static void
4436 alvherre@alvh.no-ip. 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 : :
5595 peter_e@gmx.net 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 : :
1972 andres@anarazel.de 358 : 15 : srvId = form->oid;
359 : :
360 : : /* Must be owner */
518 peter@eisentraut.org 361 [ + + ]: 15 : if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
2325 peter_e@gmx.net 362 : 6 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
4436 alvherre@alvh.no-ip. 363 : 6 : NameStr(form->srvname));
364 : :
365 : : /* Must be able to become new owner */
513 rhaas@postgresql.org 366 : 9 : check_can_set_role(GetUserId(), newOwnerId);
367 : :
368 : : /* New owner must have USAGE privilege on foreign-data wrapper */
518 peter@eisentraut.org 369 : 6 : aclresult = object_aclcheck(ForeignDataWrapperRelationId, form->srvfdw, newOwnerId, ACL_USAGE);
5595 peter_e@gmx.net 370 [ + + ]: 6 : if (aclresult != ACLCHECK_OK)
371 : : {
372 : 3 : ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
373 : :
2325 374 : 3 : aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
375 : : }
376 : : }
377 : :
3370 bruce@momjian.us 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 : :
2630 alvherre@alvh.no-ip. 400 : 24 : CatalogTupleUpdate(rel, &tup->t_self, tup);
401 : :
402 : : /* Update owner dependency reference */
1972 andres@anarazel.de 403 : 24 : changeDependencyOnOwner(ForeignServerRelationId, form->oid,
404 : : newOwnerId);
405 : : }
406 : :
4046 rhaas@postgresql.org 407 [ - + ]: 28 : InvokeObjectPostAlterHook(ForeignServerRelationId,
408 : : form->oid, 0);
4436 alvherre@alvh.no-ip. 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 : :
1910 andres@anarazel.de 423 : 34 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
424 : :
4436 alvherre@alvh.no-ip. 425 : 34 : tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
426 : :
427 [ - + ]: 34 : if (!HeapTupleIsValid(tup))
4436 alvherre@alvh.no-ip. 428 [ # # ]:UBC 0 : ereport(ERROR,
429 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
430 : : errmsg("server \"%s\" does not exist", name)));
431 : :
1972 andres@anarazel.de 432 :CBC 34 : form = (Form_pg_foreign_server) GETSTRUCT(tup);
433 : 34 : servOid = form->oid;
434 : :
4436 alvherre@alvh.no-ip. 435 : 34 : AlterForeignServerOwner_internal(rel, tup, newOwnerId);
436 : :
3330 437 : 22 : ObjectAddressSet(address, ForeignServerRelationId, servOid);
438 : :
5595 peter_e@gmx.net 439 : 22 : heap_freetuple(tup);
440 : :
1910 andres@anarazel.de 441 : 22 : table_close(rel, RowExclusiveLock);
442 : :
3330 alvherre@alvh.no-ip. 443 : 22 : return address;
444 : : }
445 : :
446 : : /*
447 : : * Change foreign server owner -- by OID
448 : : */
449 : : void
4436 450 : 6 : AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
451 : : {
452 : : HeapTuple tup;
453 : : Relation rel;
454 : :
1910 andres@anarazel.de 455 : 6 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
456 : :
4436 alvherre@alvh.no-ip. 457 : 6 : tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
458 : :
459 [ - + ]: 6 : if (!HeapTupleIsValid(tup))
4436 alvherre@alvh.no-ip. 460 [ # # ]:UBC 0 : ereport(ERROR,
461 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
462 : : errmsg("foreign server with OID %u does not exist", srvId)));
463 : :
4436 alvherre@alvh.no-ip. 464 :CBC 6 : AlterForeignServerOwner_internal(rel, tup, newOwnerId);
465 : :
466 : 6 : heap_freetuple(tup);
467 : :
1910 andres@anarazel.de 468 : 6 : table_close(rel, RowExclusiveLock);
4436 alvherre@alvh.no-ip. 469 : 6 : }
470 : :
471 : : /*
472 : : * Convert a handler function name passed from the parser to an Oid.
473 : : */
474 : : static Oid
4803 tgl@sss.pgh.pa.us 475 : 21 : lookup_fdw_handler_func(DefElem *handler)
476 : : {
477 : : Oid handlerOid;
478 : :
479 [ + - - + ]: 21 : if (handler == NULL || handler->arg == NULL)
4803 tgl@sss.pgh.pa.us 480 :UBC 0 : return InvalidOid;
481 : :
482 : : /* handlers have no arguments */
1615 alvherre@alvh.no-ip. 483 :CBC 21 : handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
484 : :
485 : : /* check that handler has correct return type */
4803 tgl@sss.pgh.pa.us 486 [ + + ]: 21 : 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 : 15 : return handlerOid;
493 : : }
494 : :
495 : : /*
496 : : * Convert a validator function name passed from the parser to an Oid.
497 : : */
498 : : static Oid
499 : 26 : lookup_fdw_validator_func(DefElem *validator)
500 : : {
501 : : Oid funcargtypes[2];
502 : :
503 [ + - + + ]: 26 : if (validator == NULL || validator->arg == NULL)
504 : 3 : return InvalidOid;
505 : :
506 : : /* validators take text[], oid */
5528 peter_e@gmx.net 507 : 23 : funcargtypes[0] = TEXTARRAYOID;
508 : 23 : funcargtypes[1] = OIDOID;
509 : :
4803 tgl@sss.pgh.pa.us 510 : 23 : 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
1004 dean.a.rasheed@gmail 518 : 131 : 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 : :
4803 tgl@sss.pgh.pa.us 524 : 131 : *handler_given = false;
525 : 131 : *validator_given = false;
526 : : /* return InvalidOid if not given */
527 : 131 : *fdwhandler = InvalidOid;
528 : 131 : *fdwvalidator = InvalidOid;
529 : :
530 [ + + + + : 166 : foreach(cell, func_options)
+ + ]
531 : : {
532 : 53 : DefElem *def = (DefElem *) lfirst(cell);
533 : :
534 [ + + ]: 53 : if (strcmp(def->defname, "handler") == 0)
535 : : {
536 [ + + ]: 27 : if (*handler_given)
1004 dean.a.rasheed@gmail 537 : 6 : errorConflictingDefElem(def, pstate);
4803 tgl@sss.pgh.pa.us 538 : 21 : *handler_given = true;
539 : 21 : *fdwhandler = lookup_fdw_handler_func(def);
540 : : }
541 [ + - ]: 26 : else if (strcmp(def->defname, "validator") == 0)
542 : : {
543 [ - + ]: 26 : if (*validator_given)
1004 dean.a.rasheed@gmail 544 :UBC 0 : errorConflictingDefElem(def, pstate);
4803 tgl@sss.pgh.pa.us 545 :CBC 26 : *validator_given = true;
546 : 26 : *fdwvalidator = lookup_fdw_validator_func(def);
547 : : }
548 : : else
4803 tgl@sss.pgh.pa.us 549 [ # # ]:UBC 0 : elog(ERROR, "option \"%s\" not recognized",
550 : : def->defname);
551 : : }
4803 tgl@sss.pgh.pa.us 552 :CBC 113 : }
553 : :
554 : : /*
555 : : * Create a foreign-data wrapper
556 : : */
557 : : ObjectAddress
1004 dean.a.rasheed@gmail 558 : 95 : 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 : :
1910 andres@anarazel.de 574 : 95 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
575 : :
576 : : /* Must be superuser */
5595 peter_e@gmx.net 577 [ + + ]: 95 : 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 : 85 : ownerId = GetUserId();
586 : :
587 : : /*
588 : : * Check that there is no other foreign-data wrapper by this name.
589 : : */
590 [ + + ]: 85 : 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 : : */
5594 tgl@sss.pgh.pa.us 599 : 82 : memset(values, 0, sizeof(values));
600 : 82 : memset(nulls, false, sizeof(nulls));
601 : :
1972 andres@anarazel.de 602 : 82 : fdwId = GetNewOidWithIndex(rel, ForeignDataWrapperOidIndexId,
603 : : Anum_pg_foreign_data_wrapper_oid);
604 : 82 : values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
5595 peter_e@gmx.net 605 : 82 : values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
606 : 82 : DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
607 : 82 : values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
608 : :
609 : : /* Lookup handler and validator functions, if given */
1004 dean.a.rasheed@gmail 610 : 82 : parse_func_options(pstate, stmt->func_options,
611 : : &handler_given, &fdwhandler,
612 : : &validator_given, &fdwvalidator);
613 : :
4803 tgl@sss.pgh.pa.us 614 : 73 : values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
615 : 73 : values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
616 : :
5528 peter_e@gmx.net 617 : 73 : nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
618 : :
5226 heikki.linnakangas@i 619 : 73 : fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
620 : : PointerGetDatum(NULL),
621 : : stmt->options,
622 : : fdwvalidator);
623 : :
5594 624 [ + + ]: 70 : if (PointerIsValid(DatumGetPointer(fdwoptions)))
5595 peter_e@gmx.net 625 : 9 : values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
626 : : else
627 : 61 : nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
628 : :
629 : 70 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
630 : :
1972 andres@anarazel.de 631 : 70 : CatalogTupleInsert(rel, tuple);
632 : :
5595 peter_e@gmx.net 633 : 70 : heap_freetuple(tuple);
634 : :
635 : : /* record dependencies */
4814 tgl@sss.pgh.pa.us 636 : 70 : myself.classId = ForeignDataWrapperRelationId;
637 : 70 : myself.objectId = fdwId;
638 : 70 : myself.objectSubId = 0;
639 : :
4803 640 [ + + ]: 70 : if (OidIsValid(fdwhandler))
641 : : {
642 : 6 : referenced.classId = ProcedureRelationId;
643 : 6 : referenced.objectId = fdwhandler;
644 : 6 : referenced.objectSubId = 0;
645 : 6 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
646 : : }
647 : :
648 [ + + ]: 70 : if (OidIsValid(fdwvalidator))
649 : : {
5528 peter_e@gmx.net 650 : 14 : referenced.classId = ProcedureRelationId;
651 : 14 : referenced.objectId = fdwvalidator;
652 : 14 : referenced.objectSubId = 0;
653 : 14 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
654 : : }
655 : :
5595 656 : 70 : recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
657 : :
658 : : /* dependency on extension */
4649 tgl@sss.pgh.pa.us 659 : 70 : recordDependencyOnCurrentExtension(&myself, false);
660 : :
661 : : /* Post creation hook for new foreign data wrapper */
4057 rhaas@postgresql.org 662 [ - + ]: 70 : InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
663 : :
1910 andres@anarazel.de 664 : 70 : table_close(rel, RowExclusiveLock);
665 : :
3330 alvherre@alvh.no-ip. 666 : 70 : return myself;
667 : : }
668 : :
669 : :
670 : : /*
671 : : * Alter foreign-data wrapper
672 : : */
673 : : ObjectAddress
1004 dean.a.rasheed@gmail 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 : :
1910 andres@anarazel.de 691 : 61 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
692 : :
693 : : /* Must be superuser */
5595 peter_e@gmx.net 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 : :
5173 rhaas@postgresql.org 701 : 49 : tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
702 : : CStringGetDatum(stmt->fdwname));
703 : :
5595 peter_e@gmx.net 704 [ - + ]: 49 : if (!HeapTupleIsValid(tp))
5595 peter_e@gmx.net 705 [ # # ]:UBC 0 : ereport(ERROR,
706 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
707 : : errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
708 : :
4803 tgl@sss.pgh.pa.us 709 :CBC 49 : fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
1972 andres@anarazel.de 710 : 49 : fdwId = fdwForm->oid;
711 : :
5595 peter_e@gmx.net 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 : :
1004 dean.a.rasheed@gmail 716 : 49 : parse_func_options(pstate, stmt->func_options,
717 : : &handler_given, &fdwhandler,
718 : : &validator_given, &fdwvalidator);
719 : :
4803 tgl@sss.pgh.pa.us 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 : : {
5528 peter_e@gmx.net 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 : : */
4803 tgl@sss.pgh.pa.us 743 [ + + ]: 6 : if (OidIsValid(fdwvalidator))
5528 peter_e@gmx.net 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 : : */
4803 tgl@sss.pgh.pa.us 753 : 34 : fdwvalidator = fdwForm->fdwvalidator;
754 : : }
755 : :
756 : : /*
757 : : * If options specified, validate and update.
758 : : */
5595 peter_e@gmx.net 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);
5594 tgl@sss.pgh.pa.us 766 [ + + ]: 31 : if (isnull)
767 : 10 : datum = PointerGetDatum(NULL);
768 : :
769 : : /* Transform the options */
5226 heikki.linnakangas@i 770 : 31 : datum = transformGenericOptions(ForeignDataWrapperRelationId,
771 : : datum,
772 : : stmt->options,
773 : : fdwvalidator);
774 : :
5594 775 [ + - ]: 15 : if (PointerIsValid(DatumGetPointer(datum)))
tgl@sss.pgh.pa.us 776 : 15 : repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
777 : : else
5595 peter_e@gmx.net 778 :UBC 0 : repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
779 : :
5595 peter_e@gmx.net 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 : :
2630 alvherre@alvh.no-ip. 787 : 24 : CatalogTupleUpdate(rel, &tp->t_self, tp);
788 : :
5595 peter_e@gmx.net 789 : 24 : heap_freetuple(tp);
790 : :
3330 alvherre@alvh.no-ip. 791 : 24 : ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
792 : :
793 : : /* Update function dependencies if we changed them */
4803 tgl@sss.pgh.pa.us 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 : :
4046 rhaas@postgresql.org 826 [ - + ]: 24 : InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
827 : :
1910 andres@anarazel.de 828 : 24 : table_close(rel, RowExclusiveLock);
829 : :
3330 alvherre@alvh.no-ip. 830 : 24 : return myself;
831 : : }
832 : :
833 : :
834 : : /*
835 : : * Create a foreign server
836 : : */
837 : : ObjectAddress
5595 peter_e@gmx.net 838 : 136 : 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 : :
1910 andres@anarazel.de 852 : 136 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
853 : :
854 : : /* For now the owner cannot be specified on create. Use effective user ID. */
5595 peter_e@gmx.net 855 : 136 : 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 : : */
615 tgl@sss.pgh.pa.us 861 : 136 : srvId = get_foreign_server_oid(stmt->servername, true);
862 [ + + ]: 136 : if (OidIsValid(srvId))
863 : : {
2582 andrew@dunslane.net 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 : : */
615 tgl@sss.pgh.pa.us 870 : 5 : ObjectAddressSet(myself, ForeignServerRelationId, srvId);
871 : 5 : checkMembershipInCurrentExtension(&myself);
872 : :
873 : : /* OK to skip */
2582 andrew@dunslane.net 874 [ + + ]: 4 : ereport(NOTICE,
875 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
876 : : errmsg("server \"%s\" already exists, skipping",
877 : : stmt->servername)));
1910 andres@anarazel.de 878 : 4 : table_close(rel, RowExclusiveLock);
2582 andrew@dunslane.net 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 : : */
5595 peter_e@gmx.net 892 : 128 : fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
893 : :
518 peter@eisentraut.org 894 : 125 : aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdw->fdwid, ownerId, ACL_USAGE);
5595 peter_e@gmx.net 895 [ + + ]: 125 : if (aclresult != ACLCHECK_OK)
2325 896 : 13 : aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
897 : :
898 : : /*
899 : : * Insert tuple into pg_foreign_server.
900 : : */
5594 tgl@sss.pgh.pa.us 901 : 112 : memset(values, 0, sizeof(values));
902 : 112 : memset(nulls, false, sizeof(nulls));
903 : :
1972 andres@anarazel.de 904 : 112 : srvId = GetNewOidWithIndex(rel, ForeignServerOidIndexId,
905 : : Anum_pg_foreign_server_oid);
906 : 112 : values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
5595 peter_e@gmx.net 907 : 112 : values[Anum_pg_foreign_server_srvname - 1] =
908 : 112 : DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
909 : 112 : values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
910 : 112 : values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
911 : :
912 : : /* Add server type if supplied */
913 [ + + ]: 112 : if (stmt->servertype)
914 : 9 : values[Anum_pg_foreign_server_srvtype - 1] =
915 : 9 : CStringGetTextDatum(stmt->servertype);
916 : : else
917 : 103 : nulls[Anum_pg_foreign_server_srvtype - 1] = true;
918 : :
919 : : /* Add server version if supplied */
920 [ + + ]: 112 : if (stmt->version)
921 : 9 : values[Anum_pg_foreign_server_srvversion - 1] =
922 : 9 : CStringGetTextDatum(stmt->version);
923 : : else
924 : 103 : nulls[Anum_pg_foreign_server_srvversion - 1] = true;
925 : :
926 : : /* Start with a blank acl */
927 : 112 : nulls[Anum_pg_foreign_server_srvacl - 1] = true;
928 : :
929 : : /* Add server options */
5226 heikki.linnakangas@i 930 : 112 : srvoptions = transformGenericOptions(ForeignServerRelationId,
931 : : PointerGetDatum(NULL),
932 : : stmt->options,
933 : : fdw->fdwvalidator);
934 : :
5594 935 [ + + ]: 107 : if (PointerIsValid(DatumGetPointer(srvoptions)))
5595 peter_e@gmx.net 936 : 24 : values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
937 : : else
938 : 83 : nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
939 : :
940 : 107 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
941 : :
1972 andres@anarazel.de 942 : 107 : CatalogTupleInsert(rel, tuple);
943 : :
5595 peter_e@gmx.net 944 : 107 : heap_freetuple(tuple);
945 : :
946 : : /* record dependencies */
947 : 107 : myself.classId = ForeignServerRelationId;
948 : 107 : myself.objectId = srvId;
949 : 107 : myself.objectSubId = 0;
950 : :
951 : 107 : referenced.classId = ForeignDataWrapperRelationId;
952 : 107 : referenced.objectId = fdw->fdwid;
953 : 107 : referenced.objectSubId = 0;
954 : 107 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
955 : :
956 : 107 : recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
957 : :
958 : : /* dependency on extension */
4649 tgl@sss.pgh.pa.us 959 : 107 : recordDependencyOnCurrentExtension(&myself, false);
960 : :
961 : : /* Post creation hook for new foreign server */
4057 rhaas@postgresql.org 962 [ - + ]: 107 : InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
963 : :
1910 andres@anarazel.de 964 : 107 : table_close(rel, RowExclusiveLock);
965 : :
3330 alvherre@alvh.no-ip. 966 : 107 : return myself;
967 : : }
968 : :
969 : :
970 : : /*
971 : : * Alter foreign server
972 : : */
973 : : ObjectAddress
5595 peter_e@gmx.net 974 : 109 : 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 : :
1910 andres@anarazel.de 985 : 109 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
986 : :
5173 rhaas@postgresql.org 987 : 109 : tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
988 : : CStringGetDatum(stmt->servername));
989 : :
5595 peter_e@gmx.net 990 [ + + ]: 109 : if (!HeapTupleIsValid(tp))
991 [ + - ]: 3 : ereport(ERROR,
992 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
993 : : errmsg("server \"%s\" does not exist", stmt->servername)));
994 : :
995 : 106 : srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
1972 andres@anarazel.de 996 : 106 : srvId = srvForm->oid;
997 : :
998 : : /*
999 : : * Only owner or a superuser can ALTER a SERVER.
1000 : : */
518 peter@eisentraut.org 1001 [ + + ]: 106 : if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
2325 peter_e@gmx.net 1002 : 12 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
5595 1003 : 12 : stmt->servername);
1004 : :
1005 : 94 : memset(repl_val, 0, sizeof(repl_val));
1006 : 94 : memset(repl_null, false, sizeof(repl_null));
1007 : 94 : memset(repl_repl, false, sizeof(repl_repl));
1008 : :
1009 [ + + ]: 94 : 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
5595 peter_e@gmx.net 1018 :UBC 0 : repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1019 : :
5595 peter_e@gmx.net 1020 :CBC 12 : repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1021 : : }
1022 : :
1023 [ + + ]: 94 : if (stmt->options)
1024 : : {
1025 : 85 : ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1026 : : Datum datum;
1027 : : bool isnull;
1028 : :
1029 : : /* Extract the current srvoptions */
1030 : 85 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
1031 : : tp,
1032 : : Anum_pg_foreign_server_srvoptions,
1033 : : &isnull);
5594 tgl@sss.pgh.pa.us 1034 [ + + ]: 85 : if (isnull)
1035 : 9 : datum = PointerGetDatum(NULL);
1036 : :
1037 : : /* Prepare the options array */
5226 heikki.linnakangas@i 1038 : 85 : datum = transformGenericOptions(ForeignServerRelationId,
1039 : : datum,
1040 : : stmt->options,
1041 : : fdw->fdwvalidator);
1042 : :
5594 1043 [ + + ]: 77 : if (PointerIsValid(DatumGetPointer(datum)))
5595 peter_e@gmx.net 1044 : 74 : repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1045 : : else
1046 : 3 : repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1047 : :
1048 : 77 : repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1049 : : }
1050 : :
1051 : : /* Everything looks good - update the tuple */
1052 : 86 : tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1053 : : repl_val, repl_null, repl_repl);
1054 : :
2630 alvherre@alvh.no-ip. 1055 : 86 : CatalogTupleUpdate(rel, &tp->t_self, tp);
1056 : :
4046 rhaas@postgresql.org 1057 [ - + ]: 86 : InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1058 : :
3330 alvherre@alvh.no-ip. 1059 : 86 : ObjectAddressSet(address, ForeignServerRelationId, srvId);
1060 : :
5595 peter_e@gmx.net 1061 : 86 : heap_freetuple(tp);
1062 : :
1910 andres@anarazel.de 1063 : 86 : table_close(rel, RowExclusiveLock);
1064 : :
3330 alvherre@alvh.no-ip. 1065 : 86 : 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
5563 peter_e@gmx.net 1075 : 202 : user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1076 : : {
1077 : 202 : Oid curuserid = GetUserId();
1078 : :
518 peter@eisentraut.org 1079 [ + + ]: 202 : if (!object_ownercheck(ForeignServerRelationId, serverid, curuserid))
1080 : : {
5563 peter_e@gmx.net 1081 [ + + ]: 36 : if (umuserid == curuserid)
1082 : : {
1083 : : AclResult aclresult;
1084 : :
518 peter@eisentraut.org 1085 : 9 : aclresult = object_aclcheck(ForeignServerRelationId, serverid, curuserid, ACL_USAGE);
5563 peter_e@gmx.net 1086 [ + + ]: 9 : if (aclresult != ACLCHECK_OK)
2325 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 : : }
5563 1093 : 171 : }
1094 : :
1095 : :
1096 : : /*
1097 : : * Create user mapping
1098 : : */
1099 : : ObjectAddress
5595 1100 : 121 : 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;
3324 alvherre@alvh.no-ip. 1113 : 121 : RoleSpec *role = (RoleSpec *) stmt->user;
1114 : :
1910 andres@anarazel.de 1115 : 121 : rel = table_open(UserMappingRelationId, RowExclusiveLock);
1116 : :
3324 alvherre@alvh.no-ip. 1117 [ + + ]: 121 : if (role->roletype == ROLESPEC_PUBLIC)
1118 : 33 : useId = ACL_ID_PUBLIC;
1119 : : else
1120 : 88 : useId = get_rolespec_oid(stmt->user, false);
1121 : :
1122 : : /* Check that the server exists. */
5595 peter_e@gmx.net 1123 : 117 : srv = GetForeignServerByName(stmt->servername, false);
1124 : :
5563 1125 : 114 : user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1126 : :
1127 : : /*
1128 : : * Check that the user mapping is unique within server.
1129 : : */
1972 andres@anarazel.de 1130 : 101 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1131 : : ObjectIdGetDatum(useId),
1132 : : ObjectIdGetDatum(srv->serverid));
1133 : :
5595 peter_e@gmx.net 1134 [ + + ]: 101 : if (OidIsValid(umId))
1135 : : {
2582 andrew@dunslane.net 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 : :
1910 andres@anarazel.de 1148 : 3 : table_close(rel, RowExclusiveLock);
2582 andrew@dunslane.net 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 : :
5595 peter_e@gmx.net 1159 : 92 : fdw = GetForeignDataWrapper(srv->fdwid);
1160 : :
1161 : : /*
1162 : : * Insert tuple into pg_user_mapping.
1163 : : */
5594 tgl@sss.pgh.pa.us 1164 : 92 : memset(values, 0, sizeof(values));
1165 : 92 : memset(nulls, false, sizeof(nulls));
1166 : :
1972 andres@anarazel.de 1167 : 92 : umId = GetNewOidWithIndex(rel, UserMappingOidIndexId,
1168 : : Anum_pg_user_mapping_oid);
1169 : 92 : values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
5595 peter_e@gmx.net 1170 : 92 : values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1171 : 92 : values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1172 : :
1173 : : /* Add user options */
5226 heikki.linnakangas@i 1174 : 92 : useoptions = transformGenericOptions(UserMappingRelationId,
1175 : : PointerGetDatum(NULL),
1176 : : stmt->options,
1177 : : fdw->fdwvalidator);
1178 : :
5594 1179 [ + + ]: 86 : if (PointerIsValid(DatumGetPointer(useoptions)))
5595 peter_e@gmx.net 1180 : 41 : values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1181 : : else
1182 : 45 : nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1183 : :
1184 : 86 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
1185 : :
1972 andres@anarazel.de 1186 : 86 : CatalogTupleInsert(rel, tuple);
1187 : :
5595 peter_e@gmx.net 1188 : 86 : heap_freetuple(tuple);
1189 : :
1190 : : /* Add dependency on the server */
1191 : 86 : myself.classId = UserMappingRelationId;
1192 : 86 : myself.objectId = umId;
1193 : 86 : myself.objectSubId = 0;
1194 : :
1195 : 86 : referenced.classId = ForeignServerRelationId;
1196 : 86 : referenced.objectId = srv->serverid;
1197 : 86 : referenced.objectSubId = 0;
1198 : 86 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1199 : :
1200 [ + + ]: 86 : if (OidIsValid(useId))
1201 : : {
1202 : : /* Record the mapped user dependency */
1203 : 65 : 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 */
4057 rhaas@postgresql.org 1214 [ - + ]: 86 : InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1215 : :
1910 andres@anarazel.de 1216 : 86 : table_close(rel, RowExclusiveLock);
1217 : :
3330 alvherre@alvh.no-ip. 1218 : 86 : return myself;
1219 : : }
1220 : :
1221 : :
1222 : : /*
1223 : : * Alter user mapping
1224 : : */
1225 : : ObjectAddress
5595 peter_e@gmx.net 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;
3324 alvherre@alvh.no-ip. 1237 : 55 : RoleSpec *role = (RoleSpec *) stmt->user;
1238 : :
1910 andres@anarazel.de 1239 : 55 : rel = table_open(UserMappingRelationId, RowExclusiveLock);
1240 : :
3324 alvherre@alvh.no-ip. 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 : :
5595 peter_e@gmx.net 1246 : 51 : srv = GetForeignServerByName(stmt->servername, false);
1247 : :
1972 andres@anarazel.de 1248 : 48 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1249 : : ObjectIdGetDatum(useId),
1250 : : ObjectIdGetDatum(srv->serverid));
5595 peter_e@gmx.net 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 : :
5563 1257 : 45 : user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1258 : :
5173 rhaas@postgresql.org 1259 : 36 : tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1260 : :
5595 peter_e@gmx.net 1261 [ - + ]: 36 : if (!HeapTupleIsValid(tp))
5595 peter_e@gmx.net 1262 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for user mapping %u", umId);
1263 : :
5595 peter_e@gmx.net 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);
5594 tgl@sss.pgh.pa.us 1284 [ + + ]: 36 : if (isnull)
1285 : 10 : datum = PointerGetDatum(NULL);
1286 : :
1287 : : /* Prepare the options array */
5226 heikki.linnakangas@i 1288 : 36 : datum = transformGenericOptions(UserMappingRelationId,
1289 : : datum,
1290 : : stmt->options,
1291 : : fdw->fdwvalidator);
1292 : :
5594 1293 [ + + ]: 29 : if (PointerIsValid(DatumGetPointer(datum)))
5595 peter_e@gmx.net 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 : :
2630 alvherre@alvh.no-ip. 1305 : 29 : CatalogTupleUpdate(rel, &tp->t_self, tp);
1306 : :
1422 michael@paquier.xyz 1307 [ - + ]: 29 : InvokeObjectPostAlterHook(UserMappingRelationId,
1308 : : umId, 0);
1309 : :
3330 alvherre@alvh.no-ip. 1310 : 29 : ObjectAddressSet(address, UserMappingRelationId, umId);
1311 : :
5595 peter_e@gmx.net 1312 : 29 : heap_freetuple(tp);
1313 : :
1910 andres@anarazel.de 1314 : 29 : table_close(rel, RowExclusiveLock);
1315 : :
3330 alvherre@alvh.no-ip. 1316 : 29 : return address;
1317 : : }
1318 : :
1319 : :
1320 : : /*
1321 : : * Drop user mapping
1322 : : */
1323 : : Oid
5595 peter_e@gmx.net 1324 : 63 : RemoveUserMapping(DropUserMappingStmt *stmt)
1325 : : {
1326 : : ObjectAddress object;
1327 : : Oid useId;
1328 : : Oid umId;
1329 : : ForeignServer *srv;
3324 alvherre@alvh.no-ip. 1330 : 63 : RoleSpec *role = (RoleSpec *) stmt->user;
1331 : :
1332 [ + + ]: 63 : if (role->roletype == ROLESPEC_PUBLIC)
1333 : 16 : useId = ACL_ID_PUBLIC;
1334 : : else
1335 : : {
1336 : 47 : useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1337 [ + + ]: 43 : 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 : 55 : srv = GetForeignServerByName(stmt->servername, true);
1350 : :
5595 peter_e@gmx.net 1351 [ + + ]: 55 : 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 */
1803 alvherre@alvh.no-ip. 1359 [ + - ]: 3 : ereport(NOTICE,
1360 : : (errmsg("server \"%s\" does not exist, skipping",
1361 : : stmt->servername)));
4124 rhaas@postgresql.org 1362 : 3 : return InvalidOid;
1363 : : }
1364 : :
1972 andres@anarazel.de 1365 : 49 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1366 : : ObjectIdGetDatum(useId),
1367 : : ObjectIdGetDatum(srv->serverid));
1368 : :
5595 peter_e@gmx.net 1369 [ + + ]: 49 : 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)));
4124 rhaas@postgresql.org 1381 : 3 : return InvalidOid;
1382 : : }
1383 : :
5563 peter_e@gmx.net 1384 : 43 : user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1385 : :
1386 : : /*
1387 : : * Do the deletion
1388 : : */
5595 1389 : 34 : object.classId = UserMappingRelationId;
1390 : 34 : object.objectId = umId;
1391 : 34 : object.objectSubId = 0;
1392 : :
4462 rhaas@postgresql.org 1393 : 34 : performDeletion(&object, DROP_CASCADE, 0);
1394 : :
4124 1395 : 34 : return umId;
1396 : : }
1397 : :
1398 : :
1399 : : /*
1400 : : * Create a foreign table
1401 : : * call after DefineRelation().
1402 : : */
1403 : : void
4852 1404 : 202 : 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 : 202 : CommandCounterIncrement();
1423 : :
1910 andres@anarazel.de 1424 : 202 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
1425 : :
1426 : : /*
1427 : : * For now the owner cannot be specified on create. Use effective user ID.
1428 : : */
4852 rhaas@postgresql.org 1429 : 202 : 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 : 202 : server = GetForeignServerByName(stmt->servername, false);
518 peter@eisentraut.org 1436 : 199 : aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, ownerId, ACL_USAGE);
4852 rhaas@postgresql.org 1437 [ - + ]: 199 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 1438 :UBC 0 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1439 : :
4852 rhaas@postgresql.org 1440 :CBC 199 : fdw = GetForeignDataWrapper(server->fdwid);
1441 : :
1442 : : /*
1443 : : * Insert tuple into pg_foreign_table.
1444 : : */
1445 : 199 : memset(values, 0, sizeof(values));
1446 : 199 : memset(nulls, false, sizeof(nulls));
1447 : :
1448 : 199 : values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1449 : 199 : values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1450 : : /* Add table generic options */
1451 : 199 : ftoptions = transformGenericOptions(ForeignTableRelationId,
1452 : : PointerGetDatum(NULL),
1453 : : stmt->options,
1454 : : fdw->fdwvalidator);
1455 : :
1456 [ + + ]: 174 : if (PointerIsValid(DatumGetPointer(ftoptions)))
1457 : 127 : values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1458 : : else
1459 : 47 : nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1460 : :
1461 : 174 : tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1462 : :
2630 alvherre@alvh.no-ip. 1463 : 174 : CatalogTupleInsert(ftrel, tuple);
1464 : :
4852 rhaas@postgresql.org 1465 : 174 : heap_freetuple(tuple);
1466 : :
1467 : : /* Add pg_class dependency on the server */
1468 : 174 : myself.classId = RelationRelationId;
1469 : 174 : myself.objectId = relid;
1470 : 174 : myself.objectSubId = 0;
1471 : :
1472 : 174 : referenced.classId = ForeignServerRelationId;
1473 : 174 : referenced.objectId = server->serverid;
1474 : 174 : referenced.objectSubId = 0;
1475 : 174 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1476 : :
1910 andres@anarazel.de 1477 : 174 : table_close(ftrel, RowExclusiveLock);
4852 rhaas@postgresql.org 1478 : 174 : }
1479 : :
1480 : : /*
1481 : : * Import a foreign schema
1482 : : */
1483 : : void
3566 tgl@sss.pgh.pa.us 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);
518 peter@eisentraut.org 1495 : 21 : aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, GetUserId(), ACL_USAGE);
3566 tgl@sss.pgh.pa.us 1496 [ - + ]: 21 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 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 */
3566 tgl@sss.pgh.pa.us 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)
3566 tgl@sss.pgh.pa.us 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 */
3566 tgl@sss.pgh.pa.us 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 : : {
2561 1550 : 30 : RawStmt *rs = lfirst_node(RawStmt, lc2);
2647 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 : : */
3566 1558 [ - + ]: 30 : if (!IsA(cstmt, CreateForeignTableStmt))
3566 tgl@sss.pgh.pa.us 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 */
3566 tgl@sss.pgh.pa.us 1564 [ - + ]:CBC 30 : if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
3566 tgl@sss.pgh.pa.us 1565 :UBC 0 : continue;
1566 : :
1567 : : /* Enable reporting of current table's name on error */
3566 tgl@sss.pgh.pa.us 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 */
2647 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 */
1031 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 */
3566 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 : }
|