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