* foreigncmds.c
* foreign-data wrapper/server creation/manipulation commands
*
- * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
#include "postgres.h"
#include "access/heapam.h"
-#include "access/xact.h"
+#include "access/htup_details.h"
#include "access/reloptions.h"
-#include "catalog/catalog.h"
+#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
+#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
+#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+typedef struct
+{
+ char *tablename;
+ char *cmd;
+} import_error_callback_arg;
+
+/* Internal functions */
+static void import_error_callback(void *arg);
+
+
/*
* Convert a DefElem list to the text array format that is used in
- * pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
+ * pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
+ * pg_foreign_table.
+ *
* Returns the array in the form of a Datum, or PointerGetDatum(NULL)
* if the list is empty.
*
/*
- * Transform a list of DefElem into text array format. This is substantially
+ * Transform a list of DefElem into text array format. This is substantially
* the same thing as optionListToArray(), except we recognize SET/ADD/DROP
* actions for modifying an existing list of options, which is passed in
* Datum form as oldOptions. Also, if fdwvalidator isn't InvalidOid
* Returns the array in the form of a Datum, or PointerGetDatum(NULL)
* if the list is empty.
*
- * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
+ * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
+ * FOREIGN TABLE.
*/
Datum
transformGenericOptions(Oid catalogId,
/*
* It is possible to perform multiple SET/DROP actions on the same
- * option. The standard permits this, as long as the options to be
+ * option. The standard permits this, as long as the options to be
* added are unique. Note that an unspecified action is taken to be
* ADD.
*/
result = optionListToArray(resultOptions);
- if (fdwvalidator)
- OidFunctionCall2(fdwvalidator, result, ObjectIdGetDatum(catalogId));
-
- return result;
-}
-
-
-/*
- * Convert the user mapping user name to OID
- */
-static Oid
-GetUserOidFromMapping(const char *username, bool missing_ok)
-{
- if (!username)
- /* PUBLIC user mapping */
- return InvalidOid;
+ if (OidIsValid(fdwvalidator))
+ {
+ Datum valarg = result;
- if (strcmp(username, "current_user") == 0)
- /* map to the owner */
- return GetUserId();
+ /*
+ * Pass a null options list as an empty array, so that validators
+ * don't have to be declared non-strict to handle the case.
+ */
+ if (DatumGetPointer(valarg) == NULL)
+ valarg = PointerGetDatum(construct_empty_array(TEXTOID));
+ OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
+ }
- /* map to provided user */
- return get_role_oid(username, missing_ok);
+ return result;
}
/*
- * Change foreign-data wrapper owner.
+ * Internal workhorse for changing a data wrapper's owner.
*
* Allow this only for superusers; also the new owner must be a
* superuser.
*/
-void
-AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
+static void
+AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
- HeapTuple tup;
- Relation rel;
- Oid fdwId;
Form_pg_foreign_data_wrapper form;
+ Datum repl_val[Natts_pg_foreign_data_wrapper];
+ bool repl_null[Natts_pg_foreign_data_wrapper];
+ bool repl_repl[Natts_pg_foreign_data_wrapper];
+ Acl *newAcl;
+ Datum aclDatum;
+ bool isNull;
+
+ form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
/* Must be a superuser to change a FDW owner */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
- name),
+ NameStr(form->fdwname)),
errhint("Must be superuser to change owner of a foreign-data wrapper.")));
/* New owner must also be a superuser */
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
- name),
+ NameStr(form->fdwname)),
errhint("The owner of a foreign-data wrapper must be a superuser.")));
+ if (form->fdwowner != newOwnerId)
+ {
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+
+ repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
+ repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
+
+ aclDatum = heap_getattr(tup,
+ Anum_pg_foreign_data_wrapper_fdwacl,
+ RelationGetDescr(rel),
+ &isNull);
+ /* Null ACLs do not require changes */
+ if (!isNull)
+ {
+ newAcl = aclnewowner(DatumGetAclP(aclDatum),
+ form->fdwowner, newOwnerId);
+ repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
+ repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
+ }
+
+ tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
+ repl_repl);
+
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(ForeignDataWrapperRelationId,
+ HeapTupleGetOid(tup),
+ newOwnerId);
+ }
+
+ InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
+ HeapTupleGetOid(tup), 0);
+}
+
+/*
+ * Change foreign-data wrapper owner -- by name
+ *
+ * Note restrictions in the "_internal" function, above.
+ */
+ObjectAddress
+AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
+{
+ Oid fdwId;
+ HeapTuple tup;
+ Relation rel;
+ ObjectAddress address;
+
rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
errmsg("foreign-data wrapper \"%s\" does not exist", name)));
fdwId = HeapTupleGetOid(tup);
- form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
-
- if (form->fdwowner != newOwnerId)
- {
- form->fdwowner = newOwnerId;
- simple_heap_update(rel, &tup->t_self, tup);
- CatalogUpdateIndexes(rel, tup);
+ AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
- /* Update owner dependency reference */
- changeDependencyOnOwner(ForeignDataWrapperRelationId,
- fdwId,
- newOwnerId);
- }
+ ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
- heap_close(rel, NoLock);
heap_freetuple(tup);
-}
+ heap_close(rel, RowExclusiveLock);
+
+ return address;
+}
/*
- * Change foreign server owner
+ * Change foreign-data wrapper owner -- by OID
+ *
+ * Note restrictions in the "_internal" function, above.
*/
void
-AlterForeignServerOwner(const char *name, Oid newOwnerId)
+AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
- Oid srvId;
- AclResult aclresult;
- Form_pg_foreign_server form;
- rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
- tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
+ tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("server \"%s\" does not exist", name)));
+ errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
+
+ AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
+
+ heap_freetuple(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Internal workhorse for changing a foreign server's owner
+ */
+static void
+AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
+{
+ Form_pg_foreign_server form;
+ Datum repl_val[Natts_pg_foreign_server];
+ bool repl_null[Natts_pg_foreign_server];
+ bool repl_repl[Natts_pg_foreign_server];
+ Acl *newAcl;
+ Datum aclDatum;
+ bool isNull;
- srvId = HeapTupleGetOid(tup);
form = (Form_pg_foreign_server) GETSTRUCT(tup);
if (form->srvowner != newOwnerId)
/* Superusers can always do it */
if (!superuser())
{
+ Oid srvId;
+ AclResult aclresult;
+
+ srvId = HeapTupleGetOid(tup);
+
/* Must be owner */
if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
- name);
+ NameStr(form->srvname));
/* Must be able to become new owner */
check_is_member_of_role(GetUserId(), newOwnerId);
}
}
- form->srvowner = newOwnerId;
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+
+ repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
+ repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
+
+ aclDatum = heap_getattr(tup,
+ Anum_pg_foreign_server_srvacl,
+ RelationGetDescr(rel),
+ &isNull);
+ /* Null ACLs do not require changes */
+ if (!isNull)
+ {
+ newAcl = aclnewowner(DatumGetAclP(aclDatum),
+ form->srvowner, newOwnerId);
+ repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
+ repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
+ }
+
+ tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
+ repl_repl);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
newOwnerId);
}
- heap_close(rel, NoLock);
+ InvokeObjectPostAlterHook(ForeignServerRelationId,
+ HeapTupleGetOid(tup), 0);
+}
+
+/*
+ * Change foreign server owner -- by name
+ */
+ObjectAddress
+AlterForeignServerOwner(const char *name, Oid newOwnerId)
+{
+ Oid servOid;
+ HeapTuple tup;
+ Relation rel;
+ ObjectAddress address;
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist", name)));
+
+ servOid = HeapTupleGetOid(tup);
+
+ AlterForeignServerOwner_internal(rel, tup, newOwnerId);
+
+ ObjectAddressSet(address, ForeignServerRelationId, servOid);
+
heap_freetuple(tup);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return address;
}
+/*
+ * Change foreign server owner -- by OID
+ */
+void
+AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
+{
+ HeapTuple tup;
+ Relation rel;
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign server with OID %u does not exist", srvId)));
+
+ AlterForeignServerOwner_internal(rel, tup, newOwnerId);
+
+ heap_freetuple(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Convert a handler function name passed from the parser to an Oid.
+ */
+static Oid
+lookup_fdw_handler_func(DefElem *handler)
+{
+ Oid handlerOid;
+ Oid funcargtypes[1]; /* dummy */
+
+ if (handler == NULL || handler->arg == NULL)
+ return InvalidOid;
+
+ /* handlers have no arguments */
+ handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false);
+
+ /* check that handler has correct return type */
+ if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type \"%s\"",
+ NameListToString((List *) handler->arg), "fdw_handler")));
+
+ return handlerOid;
+}
/*
* Convert a validator function name passed from the parser to an Oid.
*/
static Oid
-lookup_fdw_validator_func(List *validator)
+lookup_fdw_validator_func(DefElem *validator)
{
Oid funcargtypes[2];
+ if (validator == NULL || validator->arg == NULL)
+ return InvalidOid;
+
+ /* validators take text[], oid */
funcargtypes[0] = TEXTARRAYOID;
funcargtypes[1] = OIDOID;
- return LookupFuncName(validator, 2, funcargtypes, false);
- /* return value is ignored, so we don't check the type */
+
+ return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
+ /* validator's return value is ignored, so we don't check the type */
}
+/*
+ * Process function options of CREATE/ALTER FDW
+ */
+static void
+parse_func_options(List *func_options,
+ bool *handler_given, Oid *fdwhandler,
+ bool *validator_given, Oid *fdwvalidator)
+{
+ ListCell *cell;
+
+ *handler_given = false;
+ *validator_given = false;
+ /* return InvalidOid if not given */
+ *fdwhandler = InvalidOid;
+ *fdwvalidator = InvalidOid;
+
+ foreach(cell, func_options)
+ {
+ DefElem *def = (DefElem *) lfirst(cell);
+
+ if (strcmp(def->defname, "handler") == 0)
+ {
+ if (*handler_given)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ *handler_given = true;
+ *fdwhandler = lookup_fdw_handler_func(def);
+ }
+ else if (strcmp(def->defname, "validator") == 0)
+ {
+ if (*validator_given)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ *validator_given = true;
+ *fdwvalidator = lookup_fdw_validator_func(def);
+ }
+ else
+ elog(ERROR, "option \"%s\" not recognized",
+ def->defname);
+ }
+}
/*
* Create a foreign-data wrapper
*/
-void
+ObjectAddress
CreateForeignDataWrapper(CreateFdwStmt *stmt)
{
Relation rel;
bool nulls[Natts_pg_foreign_data_wrapper];
HeapTuple tuple;
Oid fdwId;
+ bool handler_given;
+ bool validator_given;
+ Oid fdwhandler;
Oid fdwvalidator;
Datum fdwoptions;
Oid ownerId;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
/* Must be super user */
if (!superuser())
/*
* Insert tuple into pg_foreign_data_wrapper.
*/
- rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
-
memset(values, 0, sizeof(values));
memset(nulls, false, sizeof(nulls));
DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
- if (stmt->validator)
- fdwvalidator = lookup_fdw_validator_func(stmt->validator);
- else
- fdwvalidator = InvalidOid;
+ /* Lookup handler and validator functions, if given */
+ parse_func_options(stmt->func_options,
+ &handler_given, &fdwhandler,
+ &validator_given, &fdwvalidator);
- values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator;
+ values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
+ values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
heap_freetuple(tuple);
- if (fdwvalidator)
- {
- ObjectAddress myself;
- ObjectAddress referenced;
+ /* record dependencies */
+ myself.classId = ForeignDataWrapperRelationId;
+ myself.objectId = fdwId;
+ myself.objectSubId = 0;
- myself.classId = ForeignDataWrapperRelationId;
- myself.objectId = fdwId;
- myself.objectSubId = 0;
+ if (OidIsValid(fdwhandler))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = fdwhandler;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ if (OidIsValid(fdwvalidator))
+ {
referenced.classId = ProcedureRelationId;
referenced.objectId = fdwvalidator;
referenced.objectSubId = 0;
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
/* Post creation hook for new foreign data wrapper */
- InvokeObjectAccessHook(OAT_POST_CREATE,
- ForeignDataWrapperRelationId, fdwId, 0);
+ InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
- heap_close(rel, NoLock);
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
}
/*
* Alter foreign-data wrapper
*/
-void
+ObjectAddress
AlterForeignDataWrapper(AlterFdwStmt *stmt)
{
Relation rel;
HeapTuple tp;
+ Form_pg_foreign_data_wrapper fdwForm;
Datum repl_val[Natts_pg_foreign_data_wrapper];
bool repl_null[Natts_pg_foreign_data_wrapper];
bool repl_repl[Natts_pg_foreign_data_wrapper];
Oid fdwId;
bool isnull;
Datum datum;
+ bool handler_given;
+ bool validator_given;
+ Oid fdwhandler;
Oid fdwvalidator;
+ ObjectAddress myself;
+
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
/* Must be super user */
if (!superuser())
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
fdwId = HeapTupleGetOid(tp);
memset(repl_val, 0, sizeof(repl_val));
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
- if (stmt->change_validator)
+ parse_func_options(stmt->func_options,
+ &handler_given, &fdwhandler,
+ &validator_given, &fdwvalidator);
+
+ if (handler_given)
+ {
+ repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
+ repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
+
+ /*
+ * It could be that the behavior of accessing foreign table changes
+ * with the new handler. Warn about this.
+ */
+ ereport(WARNING,
+ (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
+ }
+
+ if (validator_given)
{
- fdwvalidator = stmt->validator ? lookup_fdw_validator_func(stmt->validator) : InvalidOid;
repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
/*
- * It could be that the options for the FDW, SERVER and USER MAPPING
- * are no longer valid with the new validator. Warn about this.
+ * It could be that existing options for the FDW or dependent SERVER,
+ * USER MAPPING or FOREIGN TABLE objects are no longer valid according
+ * to the new validator. Warn about this.
*/
- if (stmt->validator)
+ if (OidIsValid(fdwvalidator))
ereport(WARNING,
(errmsg("changing the foreign-data wrapper validator can cause "
"the options for dependent objects to become invalid")));
/*
* Validator is not changed, but we need it for validating options.
*/
- datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
- tp,
- Anum_pg_foreign_data_wrapper_fdwvalidator,
- &isnull);
- Assert(!isnull);
- fdwvalidator = DatumGetObjectId(datum);
+ fdwvalidator = fdwForm->fdwvalidator;
}
/*
- * Options specified, validate and update.
+ * If options specified, validate and update.
*/
if (stmt->options)
{
}
/* Everything looks good - update the tuple */
-
- rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
-
tp = heap_modify_tuple(tp, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
simple_heap_update(rel, &tp->t_self, tp);
CatalogUpdateIndexes(rel, tp);
- heap_close(rel, RowExclusiveLock);
heap_freetuple(tp);
-}
+ ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
-/*
- * Drop foreign-data wrapper
- */
-void
-RemoveForeignDataWrapper(DropFdwStmt *stmt)
-{
- Oid fdwId;
- ObjectAddress object;
+ /* Update function dependencies if we changed them */
+ if (handler_given || validator_given)
+ {
+ ObjectAddress referenced;
- fdwId = GetForeignDataWrapperOidByName(stmt->fdwname, true);
+ /*
+ * Flush all existing dependency records of this FDW on functions; we
+ * assume there can be none other than the ones we are fixing.
+ */
+ deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
+ fdwId,
+ ProcedureRelationId,
+ DEPENDENCY_NORMAL);
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied to drop foreign-data wrapper \"%s\"",
- stmt->fdwname),
- errhint("Must be superuser to drop a foreign-data wrapper.")));
+ /* And build new ones. */
- if (!OidIsValid(fdwId))
- {
- if (!stmt->missing_ok)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("foreign-data wrapper \"%s\" does not exist",
- stmt->fdwname)));
+ if (OidIsValid(fdwhandler))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = fdwhandler;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- /* IF EXISTS specified, just note it */
- ereport(NOTICE,
- (errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
- stmt->fdwname)));
- return;
+ if (OidIsValid(fdwvalidator))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = fdwvalidator;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
- /*
- * Do the deletion
- */
- object.classId = ForeignDataWrapperRelationId;
- object.objectId = fdwId;
- object.objectSubId = 0;
+ InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
- performDeletion(&object, stmt->behavior);
+ heap_close(rel, RowExclusiveLock);
+
+ return myself;
}
/*
* Create a foreign server
*/
-void
+ObjectAddress
CreateForeignServer(CreateForeignServerStmt *stmt)
{
Relation rel;
ObjectAddress referenced;
ForeignDataWrapper *fdw;
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
/* For now the owner cannot be specified on create. Use effective user ID. */
ownerId = GetUserId();
/*
* Insert tuple into pg_foreign_server.
*/
- rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
-
memset(values, 0, sizeof(values));
memset(nulls, false, sizeof(nulls));
heap_freetuple(tuple);
- /* Add dependency on FDW and owner */
+ /* record dependencies */
myself.classId = ForeignServerRelationId;
myself.objectId = srvId;
myself.objectSubId = 0;
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
/* Post creation hook for new foreign server */
- InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
+ InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
+
+ heap_close(rel, RowExclusiveLock);
- heap_close(rel, NoLock);
+ return myself;
}
/*
* Alter foreign server
*/
-void
+ObjectAddress
AlterForeignServer(AlterForeignServerStmt *stmt)
{
Relation rel;
bool repl_repl[Natts_pg_foreign_server];
Oid srvId;
Form_pg_foreign_server srvForm;
+ ObjectAddress address;
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
CStringGetDatum(stmt->servername));
}
/* Everything looks good - update the tuple */
-
- rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
-
tp = heap_modify_tuple(tp, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
simple_heap_update(rel, &tp->t_self, tp);
CatalogUpdateIndexes(rel, tp);
- heap_close(rel, RowExclusiveLock);
- heap_freetuple(tp);
-}
-
-
-/*
- * Drop foreign server
- */
-void
-RemoveForeignServer(DropForeignServerStmt *stmt)
-{
- Oid srvId;
- ObjectAddress object;
+ InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
- srvId = GetForeignServerOidByName(stmt->servername, true);
+ ObjectAddressSet(address, ForeignServerRelationId, srvId);
- if (!OidIsValid(srvId))
- {
- /* Server not found, complain or notice */
- if (!stmt->missing_ok)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("server \"%s\" does not exist", stmt->servername)));
-
- /* IF EXISTS specified, just note it */
- ereport(NOTICE,
- (errmsg("server \"%s\" does not exist, skipping",
- stmt->servername)));
- return;
- }
-
- /* Only allow DROP if the server is owned by the user. */
- if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
- stmt->servername);
+ heap_freetuple(tp);
- object.classId = ForeignServerRelationId;
- object.objectId = srvId;
- object.objectSubId = 0;
+ heap_close(rel, RowExclusiveLock);
- performDeletion(&object, stmt->behavior);
+ return address;
}
/*
* Create user mapping
*/
-void
+ObjectAddress
CreateUserMapping(CreateUserMappingStmt *stmt)
{
Relation rel;
ObjectAddress referenced;
ForeignServer *srv;
ForeignDataWrapper *fdw;
+ RoleSpec *role = (RoleSpec *) stmt->user;
- useId = GetUserOidFromMapping(stmt->username, false);
+ rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+ if (role->roletype == ROLESPEC_PUBLIC)
+ useId = ACL_ID_PUBLIC;
+ else
+ useId = get_rolespec_oid(stmt->user, false);
/* Check that the server exists. */
srv = GetForeignServerByName(stmt->servername, false);
/*
* Insert tuple into pg_user_mapping.
*/
- rel = heap_open(UserMappingRelationId, RowExclusiveLock);
-
memset(values, 0, sizeof(values));
memset(nulls, false, sizeof(nulls));
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (OidIsValid(useId))
+ {
/* Record the mapped user dependency */
recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
/* Post creation hook for new user mapping */
- InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
+ InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
+
+ heap_close(rel, RowExclusiveLock);
- heap_close(rel, NoLock);
+ return myself;
}
/*
* Alter user mapping
*/
-void
+ObjectAddress
AlterUserMapping(AlterUserMappingStmt *stmt)
{
Relation rel;
Oid useId;
Oid umId;
ForeignServer *srv;
+ ObjectAddress address;
+ RoleSpec *role = (RoleSpec *) stmt->user;
+
+ rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+ if (role->roletype == ROLESPEC_PUBLIC)
+ useId = ACL_ID_PUBLIC;
+ else
+ useId = get_rolespec_oid(stmt->user, false);
- useId = GetUserOidFromMapping(stmt->username, false);
srv = GetForeignServerByName(stmt->servername, false);
umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
}
/* Everything looks good - update the tuple */
-
- rel = heap_open(UserMappingRelationId, RowExclusiveLock);
-
tp = heap_modify_tuple(tp, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
simple_heap_update(rel, &tp->t_self, tp);
CatalogUpdateIndexes(rel, tp);
- heap_close(rel, RowExclusiveLock);
+ ObjectAddressSet(address, UserMappingRelationId, umId);
+
heap_freetuple(tp);
+
+ heap_close(rel, RowExclusiveLock);
+
+ return address;
}
/*
* Drop user mapping
*/
-void
+Oid
RemoveUserMapping(DropUserMappingStmt *stmt)
{
ObjectAddress object;
Oid useId;
Oid umId;
ForeignServer *srv;
+ RoleSpec *role = (RoleSpec *) stmt->user;
- useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
- srv = GetForeignServerByName(stmt->servername, true);
-
- if (stmt->username && !OidIsValid(useId))
+ if (role->roletype == ROLESPEC_PUBLIC)
+ useId = ACL_ID_PUBLIC;
+ else
{
- /*
- * IF EXISTS specified, role not found and not public. Notice this and
- * leave.
- */
- elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
- return;
+ useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
+ if (!OidIsValid(useId))
+ {
+ /*
+ * IF EXISTS specified, role not found and not public. Notice this
+ * and leave.
+ */
+ elog(NOTICE, "role \"%s\" does not exist, skipping",
+ role->rolename);
+ return InvalidOid;
+ }
}
+ srv = GetForeignServerByName(stmt->servername, true);
+
if (!srv)
{
if (!stmt->missing_ok)
stmt->servername)));
/* IF EXISTS, just note it */
ereport(NOTICE, (errmsg("server does not exist, skipping")));
- return;
+ return InvalidOid;
}
umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
ereport(NOTICE,
(errmsg("user mapping \"%s\" does not exist for the server, skipping",
MappingUserName(useId))));
- return;
+ return InvalidOid;
}
user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
object.objectId = umId;
object.objectSubId = 0;
- performDeletion(&object, DROP_CASCADE);
+ performDeletion(&object, DROP_CASCADE, 0);
+
+ return umId;
}
Datum values[Natts_pg_foreign_table];
bool nulls[Natts_pg_foreign_table];
HeapTuple tuple;
- Oid ftId;
AclResult aclresult;
ObjectAddress myself;
ObjectAddress referenced;
ForeignServer *server;
/*
- * Advance command counter to ensure the pg_attribute tuple visible; the
- * tuple might be updated to add constraints in previous step.
+ * Advance command counter to ensure the pg_attribute tuple is visible;
+ * the tuple might be updated to add constraints in previous step.
*/
CommandCounterIncrement();
+ ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
/*
* For now the owner cannot be specified on create. Use effective user ID.
*/
/*
* Insert tuple into pg_foreign_table.
*/
- ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
-
memset(values, 0, sizeof(values));
memset(nulls, false, sizeof(nulls));
tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
- /* pg_foreign_table don't have OID */
- ftId = simple_heap_insert(ftrel, tuple);
-
+ simple_heap_insert(ftrel, tuple);
CatalogUpdateIndexes(ftrel, tuple);
heap_freetuple(tuple);
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- heap_close(ftrel, NoLock);
+ heap_close(ftrel, RowExclusiveLock);
+}
+
+/*
+ * Import a foreign schema
+ */
+void
+ImportForeignSchema(ImportForeignSchemaStmt *stmt)
+{
+ ForeignServer *server;
+ ForeignDataWrapper *fdw;
+ FdwRoutine *fdw_routine;
+ AclResult aclresult;
+ List *cmd_list;
+ ListCell *lc;
+
+ /* Check that the foreign server exists and that we have USAGE on it */
+ server = GetForeignServerByName(stmt->server_name, false);
+ aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+ /* Check that the schema exists and we have CREATE permissions on it */
+ (void) LookupCreationNamespace(stmt->local_schema);
+
+ /* Get the FDW and check it supports IMPORT */
+ fdw = GetForeignDataWrapper(server->fdwid);
+ if (!OidIsValid(fdw->fdwhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("foreign-data wrapper \"%s\" has no handler",
+ fdw->fdwname)));
+ fdw_routine = GetFdwRoutine(fdw->fdwhandler);
+ if (fdw_routine->ImportForeignSchema == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FDW_NO_SCHEMAS),
+ errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
+ fdw->fdwname)));
+
+ /* Call FDW to get a list of commands */
+ cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
+
+ /* Parse and execute each command */
+ foreach(lc, cmd_list)
+ {
+ char *cmd = (char *) lfirst(lc);
+ import_error_callback_arg callback_arg;
+ ErrorContextCallback sqlerrcontext;
+ List *raw_parsetree_list;
+ ListCell *lc2;
+
+ /*
+ * Setup error traceback support for ereport(). This is so that any
+ * error in the generated SQL will be displayed nicely.
+ */
+ callback_arg.tablename = NULL; /* not known yet */
+ callback_arg.cmd = cmd;
+ sqlerrcontext.callback = import_error_callback;
+ sqlerrcontext.arg = (void *) &callback_arg;
+ sqlerrcontext.previous = error_context_stack;
+ error_context_stack = &sqlerrcontext;
+
+ /*
+ * Parse the SQL string into a list of raw parse trees.
+ */
+ raw_parsetree_list = pg_parse_query(cmd);
+
+ /*
+ * Process each parse tree (we allow the FDW to put more than one
+ * command per string, though this isn't really advised).
+ */
+ foreach(lc2, raw_parsetree_list)
+ {
+ CreateForeignTableStmt *cstmt = lfirst(lc2);
+
+ /*
+ * Because we only allow CreateForeignTableStmt, we can skip parse
+ * analysis, rewrite, and planning steps here.
+ */
+ if (!IsA(cstmt, CreateForeignTableStmt))
+ elog(ERROR,
+ "foreign-data wrapper \"%s\" returned incorrect statement type %d",
+ fdw->fdwname, (int) nodeTag(cstmt));
+
+ /* Ignore commands for tables excluded by filter options */
+ if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
+ continue;
+
+ /* Enable reporting of current table's name on error */
+ callback_arg.tablename = cstmt->base.relation->relname;
+
+ /* Ensure creation schema is the one given in IMPORT statement */
+ cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
+
+ /* Execute statement */
+ ProcessUtility((Node *) cstmt,
+ cmd,
+ PROCESS_UTILITY_SUBCOMMAND, NULL,
+ None_Receiver, NULL);
+
+ /* Be sure to advance the command counter between subcommands */
+ CommandCounterIncrement();
+
+ callback_arg.tablename = NULL;
+ }
+
+ error_context_stack = sqlerrcontext.previous;
+ }
+}
+
+/*
+ * error context callback to let us supply the failing SQL statement's text
+ */
+static void
+import_error_callback(void *arg)
+{
+ import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
+ int syntaxerrposition;
+
+ /* If it's a syntax error, convert to internal syntax error report */
+ syntaxerrposition = geterrposition();
+ if (syntaxerrposition > 0)
+ {
+ errposition(0);
+ internalerrposition(syntaxerrposition);
+ internalerrquery(callback_arg->cmd);
+ }
+
+ if (callback_arg->tablename)
+ errcontext("importing foreign table \"%s\"",
+ callback_arg->tablename);
}