aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/Makefile4
-rw-r--r--src/backend/catalog/Makefile3
-rw-r--r--src/backend/catalog/aclchk.c472
-rw-r--r--src/backend/catalog/dependency.c74
-rw-r--r--src/backend/catalog/information_schema.sql257
-rw-r--r--src/backend/catalog/sql_features.txt25
-rw-r--r--src/backend/catalog/system_views.sql24
-rw-r--r--src/backend/commands/Makefile4
-rw-r--r--src/backend/commands/alter.c11
-rw-r--r--src/backend/commands/foreigncmds.c1102
-rw-r--r--src/backend/foreign/Makefile25
-rw-r--r--src/backend/foreign/dummy/Makefile27
-rw-r--r--src/backend/foreign/dummy/dummy_fdw.c24
-rw-r--r--src/backend/foreign/foreign.c389
-rw-r--r--src/backend/foreign/postgresql/Makefile27
-rw-r--r--src/backend/foreign/postgresql/postgresql_fdw.c123
-rw-r--r--src/backend/nodes/copyfuncs.c154
-rw-r--r--src/backend/nodes/equalfuncs.c134
-rw-r--r--src/backend/nodes/makefuncs.c15
-rw-r--r--src/backend/parser/gram.y383
-rw-r--r--src/backend/parser/keywords.c6
-rw-r--r--src/backend/tcop/utility.c101
-rw-r--r--src/backend/utils/adt/acl.c309
-rw-r--r--src/backend/utils/cache/syscache.c77
24 files changed, 3728 insertions, 42 deletions
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 7fd613f6c9f..1903e554cf9 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -5,7 +5,7 @@
# Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
-# $PostgreSQL: pgsql/src/backend/Makefile,v 1.130 2008/08/29 13:02:32 petere Exp $
+# $PostgreSQL: pgsql/src/backend/Makefile,v 1.131 2008/12/19 16:25:16 petere Exp $
#
#-------------------------------------------------------------------------
@@ -14,7 +14,7 @@ subdir = src/backend
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
-SUBDIRS = access bootstrap catalog parser commands executor lib libpq \
+SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
main nodes optimizer port postmaster regex rewrite \
storage tcop tsearch utils $(top_builddir)/src/timezone
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index bb4c42135c5..58973c9a889 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.67 2008/11/19 10:34:51 heikki Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.68 2008/12/19 16:25:16 petere Exp $
#
#-------------------------------------------------------------------------
@@ -36,6 +36,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
+ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index f25c7608e41..cecfe84e7a3 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.149 2008/11/02 01:45:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.150 2008/12/19 16:25:16 petere Exp $
*
* NOTES
* See acl.h.
@@ -27,6 +27,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
@@ -38,6 +40,7 @@
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "commands/dbcommands.h"
+#include "foreign/foreign.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "utils/acl.h"
@@ -50,6 +53,8 @@
static void ExecGrant_Relation(InternalGrant *grantStmt);
static void ExecGrant_Database(InternalGrant *grantStmt);
+static void ExecGrant_Fdw(InternalGrant *grantStmt);
+static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
static void ExecGrant_Function(InternalGrant *grantStmt);
static void ExecGrant_Language(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
@@ -188,6 +193,12 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case ACL_KIND_TABLESPACE:
whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
break;
+ case ACL_KIND_FDW:
+ whole_mask = ACL_ALL_RIGHTS_FDW;
+ break;
+ case ACL_KIND_FOREIGN_SERVER:
+ whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+ break;
default:
elog(ERROR, "unrecognized object kind: %d", objkind);
/* not reached, but keep compiler quiet */
@@ -323,6 +334,14 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
errormsg = gettext_noop("invalid privilege type %s for tablespace");
break;
+ case ACL_OBJECT_FDW:
+ all_privileges = ACL_ALL_RIGHTS_FDW;
+ errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+ errormsg = gettext_noop("invalid privilege type %s for foreign server");
+ break;
default:
/* keep compiler quiet */
all_privileges = ACL_NO_RIGHTS;
@@ -380,6 +399,12 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case ACL_OBJECT_DATABASE:
ExecGrant_Database(istmt);
break;
+ case ACL_OBJECT_FDW:
+ ExecGrant_Fdw(istmt);
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ ExecGrant_ForeignServer(istmt);
+ break;
case ACL_OBJECT_FUNCTION:
ExecGrant_Function(istmt);
break;
@@ -520,6 +545,24 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
heap_close(relation, AccessShareLock);
}
break;
+ case ACL_OBJECT_FDW:
+ foreach(cell, objnames)
+ {
+ char *fdwname = strVal(lfirst(cell));
+ Oid fdwid = GetForeignDataWrapperOidByName(fdwname, false);
+
+ objects = lappend_oid(objects, fdwid);
+ }
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ foreach(cell, objnames)
+ {
+ char *srvname = strVal(lfirst(cell));
+ Oid srvid = GetForeignServerOidByName(srvname, false);
+
+ objects = lappend_oid(objects, srvid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -841,6 +884,239 @@ ExecGrant_Database(InternalGrant *istmt)
}
static void
+ExecGrant_Fdw(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_FDW;
+
+ relation = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid fdwid = lfirst_oid(cell);
+ Form_pg_foreign_data_wrapper pg_fdw_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_foreign_data_wrapper];
+ bool nulls[Natts_pg_foreign_data_wrapper];
+ bool replaces[Natts_pg_foreign_data_wrapper];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
+
+ pg_fdw_tuple = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_fdw_tuple->fdwowner;
+ aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
+ Anum_pg_foreign_data_wrapper_fdwacl,
+ &isNull);
+ if (isNull)
+ old_acl = acldefault(ACL_OBJECT_FDW, ownerId);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ fdwid, grantorId, ACL_KIND_FDW,
+ NameStr(pg_fdw_tuple->fdwname));
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
+ values[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ForeignDataWrapperRelationId, HeapTupleGetOid(tuple),
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+static void ExecGrant_ForeignServer(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+
+ relation = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid srvid = lfirst_oid(cell);
+ Form_pg_foreign_server pg_server_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_foreign_server];
+ bool nulls[Natts_pg_foreign_server];
+ bool replaces[Natts_pg_foreign_server];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srvid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign server %u", srvid);
+
+ pg_server_tuple = (Form_pg_foreign_server) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_server_tuple->srvowner;
+ aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
+ Anum_pg_foreign_server_srvacl,
+ &isNull);
+ if (isNull)
+ old_acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
+ NameStr(pg_server_tuple->srvname));
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_foreign_server_srvacl - 1] = true;
+ values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ForeignServerRelationId, HeapTupleGetOid(tuple),
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+static void
ExecGrant_Function(InternalGrant *istmt)
{
Relation relation;
@@ -1428,7 +1704,11 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
/* ACL_KIND_TSDICTIONARY */
gettext_noop("permission denied for text search dictionary %s"),
/* ACL_KIND_TSCONFIGURATION */
- gettext_noop("permission denied for text search configuration %s")
+ gettext_noop("permission denied for text search configuration %s"),
+ /* ACL_KIND_FDW */
+ gettext_noop("permission denied for foreign-data wrapper %s"),
+ /* ACL_KIND_FOREIGN_SERVER */
+ gettext_noop("permission denied for foreign server %s")
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
@@ -1460,7 +1740,11 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
/* ACL_KIND_TSDICTIONARY */
gettext_noop("must be owner of text search dictionary %s"),
/* ACL_KIND_TSCONFIGURATION */
- gettext_noop("must be owner of text search configuration %s")
+ gettext_noop("must be owner of text search configuration %s"),
+ /* ACL_KIND_FDW */
+ gettext_noop("must be owner of foreign-data wrapper %s"),
+ /* ACL_KIND_FOREIGN_SERVER */
+ gettext_noop("must be owner of foreign server %s")
};
@@ -1534,6 +1818,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid,
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_TABLESPACE:
return pg_tablespace_aclmask(table_oid, roleid, mask, how);
+ case ACL_KIND_FDW:
+ return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
+ case ACL_KIND_FOREIGN_SERVER:
+ return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objkind: %d",
(int) objkind);
@@ -1962,6 +2250,131 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a foreign
+ * data wrapper
+ */
+AclMode
+pg_foreign_data_wrapper_aclmask(Oid fdw_oid, Oid roleid,
+ AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_foreign_data_wrapper fdwForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the FDW's tuple from pg_foreign_data_wrapper
+ */
+ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdw_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errmsg("foreign-data wrapper with OID %u does not exist",
+ fdw_oid)));
+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+
+ /*
+ * Normal case: get the FDW's ACL from pg_foreign_data_wrapper
+ */
+ ownerId = fdwForm->fdwowner;
+
+ aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
+ Anum_pg_foreign_data_wrapper_fdwacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_FDW, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+/*
+ * Exported routine for examining a user's privileges for a foreign
+ * server.
+ */
+AclMode
+pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
+ AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_foreign_server srvForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the FDW's tuple from pg_foreign_data_wrapper
+ */
+ tuple = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srv_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errmsg("foreign server with OID %u does not exist",
+ srv_oid)));
+ srvForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
+
+ /*
+ * Normal case: get the foreign server's ACL from pg_foreign_server
+ */
+ ownerId = srvForm->srvowner;
+
+ aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
+ Anum_pg_foreign_server_srvacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
/*
* Exported routine for checking a user's access privileges to a table
@@ -2039,6 +2452,31 @@ pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a foreign
+ * data wrapper
+ */
+AclResult
+pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode)
+{
+ if (pg_foreign_data_wrapper_aclmask(fdw_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a foreign
+ * server
+ */
+AclResult
+pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode)
+{
+ if (pg_foreign_server_aclmask(srv_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
/*
* Ownership check for a relation (specified by OID).
@@ -2364,6 +2802,34 @@ pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a foreign server (specified by OID).
+ */
+bool
+pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srv_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign server with OID %u does not exist",
+ srv_oid)));
+
+ ownerId = ((Form_pg_foreign_server) GETSTRUCT(tuple))->srvowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
/*
* Ownership check for a database (specified by OID).
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 8cd1abdf1b9..2cbc19f5a06 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.82 2008/11/10 21:49:16 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.83 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -33,6 +33,8 @@
#include "catalog/pg_conversion_fn.h"
#include "catalog/pg_database.h"
#include "catalog/pg_depend.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
@@ -47,6 +49,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
@@ -55,6 +58,7 @@
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
+#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
@@ -1105,6 +1109,18 @@ doDeletion(const ObjectAddress *object)
RemoveTSConfigurationById(object->objectId);
break;
+ case OCLASS_USER_MAPPING:
+ RemoveUserMappingById(object->objectId);
+ break;
+
+ case OCLASS_FOREIGN_SERVER:
+ RemoveForeignServerById(object->objectId);
+ break;
+
+ case OCLASS_FDW:
+ RemoveForeignDataWrapperById(object->objectId);
+ break;
+
/* OCLASS_ROLE, OCLASS_DATABASE, OCLASS_TBLSPACE not handled */
default:
@@ -2005,6 +2021,18 @@ getObjectClass(const ObjectAddress *object)
case TableSpaceRelationId:
Assert(object->objectSubId == 0);
return OCLASS_TBLSPACE;
+
+ case ForeignDataWrapperRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_FDW;
+
+ case ForeignServerRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_FOREIGN_SERVER;
+
+ case UserMappingRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_USER_MAPPING;
}
/* shouldn't get here */
@@ -2501,6 +2529,50 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_FDW:
+ {
+ ForeignDataWrapper *fdw;
+
+ fdw = GetForeignDataWrapper(object->objectId);
+ appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
+ break;
+ }
+
+ case OCLASS_FOREIGN_SERVER:
+ {
+ ForeignServer *srv;
+
+ srv = GetForeignServer(object->objectId);
+ appendStringInfo(&buffer, _("server %s"), srv->servername);
+ break;
+ }
+
+ case OCLASS_USER_MAPPING:
+ {
+ HeapTuple tup;
+ Oid useid;
+ char *usename;
+
+ tup = SearchSysCache(USERMAPPINGOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for user mapping %u",
+ object->objectId);
+
+ useid = ((Form_pg_user_mapping) GETSTRUCT(tup))->umuser;
+
+ ReleaseSysCache(tup);
+
+ if (OidIsValid(useid))
+ usename = GetUserNameFromId(useid);
+ else
+ usename = "public";
+
+ appendStringInfo(&buffer, _("user mapping for %s"), usename);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 970b48b7dff..2d9cdb8e224 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -4,7 +4,7 @@
*
* Copyright (c) 2003-2008, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.46 2008/09/08 00:47:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.47 2008/12/19 16:25:17 petere Exp $
*/
/*
@@ -1244,19 +1244,53 @@ GRANT SELECT ON role_table_grants TO PUBLIC;
* ROLE_USAGE_GRANTS view
*/
--- See USAGE_PRIVILEGES.
-
CREATE VIEW role_usage_grants AS
- SELECT CAST(null AS sql_identifier) AS grantor,
- CAST(null AS sql_identifier) AS grantee,
+
+ /* foreign-data wrappers */
+ SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+ CAST(g_grantee.rolname AS sql_identifier) AS grantee,
CAST(current_database() AS sql_identifier) AS object_catalog,
- CAST(null AS sql_identifier) AS object_schema,
- CAST(null AS sql_identifier) AS object_name,
- CAST(null AS character_data) AS object_type,
+ CAST('' AS sql_identifier) AS object_schema,
+ CAST(fdw.fdwname AS sql_identifier) AS object_name,
+ CAST('FOREIGN DATA WRAPPER' AS character_data) AS object_type,
CAST('USAGE' AS character_data) AS privilege_type,
- CAST(null AS character_data) AS is_grantable
+ CAST(
+ CASE WHEN aclcontains(fdw.fdwacl,
+ makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', true))
+ THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
- WHERE false;
+ FROM pg_foreign_data_wrapper fdw,
+ pg_authid u_grantor,
+ pg_authid g_grantee
+
+ WHERE aclcontains(fdw.fdwacl,
+ makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', false))
+ AND (u_grantor.rolname IN (SELECT role_name FROM enabled_roles)
+ OR g_grantee.rolname IN (SELECT role_name FROM enabled_roles))
+
+ UNION ALL
+
+ /* foreign server */
+ SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+ CAST(g_grantee.rolname AS sql_identifier) AS grantee,
+ CAST(current_database() AS sql_identifier) AS object_catalog,
+ CAST('' AS sql_identifier) AS object_schema,
+ CAST(srv.srvname AS sql_identifier) AS object_name,
+ CAST('FOREIGN SERVER' AS character_data) AS object_type,
+ CAST('USAGE' AS character_data) AS privilege_type,
+ CAST(
+ CASE WHEN aclcontains(srv.srvacl,
+ makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', true))
+ THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
+
+ FROM pg_foreign_server srv,
+ pg_authid u_grantor,
+ pg_authid g_grantee
+
+ WHERE aclcontains(srv.srvacl,
+ makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', false))
+ AND (u_grantor.rolname IN (SELECT role_name FROM enabled_roles)
+ OR g_grantee.rolname IN (SELECT role_name FROM enabled_roles));
GRANT SELECT ON role_usage_grants TO PUBLIC;
@@ -2007,11 +2041,10 @@ GRANT SELECT ON triggers TO PUBLIC;
* USAGE_PRIVILEGES view
*/
--- Of the things currently implemented in PostgreSQL, usage privileges
--- apply only to domains. Since domains have no real privileges, we
--- represent all domains with implicit usage privilege here.
-
CREATE VIEW usage_privileges AS
+
+ /* domains */
+ -- Domains have no real privileges, so we represent all domains with implicit usage privilege here.
SELECT CAST(u.rolname AS sql_identifier) AS grantor,
CAST('PUBLIC' AS sql_identifier) AS grantee,
CAST(current_database() AS sql_identifier) AS object_catalog,
@@ -2027,7 +2060,65 @@ CREATE VIEW usage_privileges AS
WHERE u.oid = t.typowner
AND t.typnamespace = n.oid
- AND t.typtype = 'd';
+ AND t.typtype = 'd'
+
+ UNION ALL
+
+ /* foreign-data wrappers */
+ SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+ CAST(grantee.rolname AS sql_identifier) AS grantee,
+ CAST(current_database() AS sql_identifier) AS object_catalog,
+ CAST('' AS sql_identifier) AS object_schema,
+ CAST(fdw.fdwname AS sql_identifier) AS object_name,
+ CAST('FOREIGN DATA WRAPPER' AS character_data) AS object_type,
+ CAST('USAGE' AS character_data) AS privilege_type,
+ CAST(
+ CASE WHEN aclcontains(fdw.fdwacl,
+ makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', true))
+ THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
+
+ FROM pg_foreign_data_wrapper fdw,
+ pg_authid u_grantor,
+ (
+ SELECT oid, rolname FROM pg_authid
+ UNION ALL
+ SELECT 0::oid, 'PUBLIC'
+ ) AS grantee (oid, rolname)
+
+ WHERE aclcontains(fdw.fdwacl,
+ makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', false))
+ AND (pg_has_role(u_grantor.oid, 'USAGE')
+ OR pg_has_role(grantee.oid, 'USAGE')
+ OR grantee.rolname = 'PUBLIC')
+
+ UNION ALL
+
+ /* foreign servers */
+ SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+ CAST(grantee.rolname AS sql_identifier) AS grantee,
+ CAST(current_database() AS sql_identifier) AS object_catalog,
+ CAST('' AS sql_identifier) AS object_schema,
+ CAST(srv.srvname AS sql_identifier) AS object_name,
+ CAST('FOREIGN SERVER' AS character_data) AS object_type,
+ CAST('USAGE' AS character_data) AS privilege_type,
+ CAST(
+ CASE WHEN aclcontains(srv.srvacl,
+ makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', true))
+ THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
+
+ FROM pg_foreign_server srv,
+ pg_authid u_grantor,
+ (
+ SELECT oid, rolname FROM pg_authid
+ UNION ALL
+ SELECT 0::oid, 'PUBLIC'
+ ) AS grantee (oid, rolname)
+
+ WHERE aclcontains(srv.srvacl,
+ makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', false))
+ AND (pg_has_role(u_grantor.oid, 'USAGE')
+ OR pg_has_role(grantee.oid, 'USAGE')
+ OR grantee.rolname = 'PUBLIC');
GRANT SELECT ON usage_privileges TO PUBLIC;
@@ -2313,3 +2404,139 @@ CREATE VIEW element_types AS
FROM data_type_privileges );
GRANT SELECT ON element_types TO PUBLIC;
+
+
+-- SQL/MED views; these use section numbers from part 9 of the standard.
+
+/* Base view for foreign-data wrappers */
+CREATE VIEW _pg_foreign_data_wrappers AS
+ SELECT w.oid,
+ w.fdwowner,
+ w.fdwoptions,
+ CAST(current_database() AS sql_identifier) AS foreign_data_wrapper_catalog,
+ CAST(fdwname AS sql_identifier) AS foreign_data_wrapper_name,
+ CAST(u.rolname AS sql_identifier) AS authorization_identifier,
+ CAST(fdwlibrary AS character_data) AS library_name,
+ CAST('c' AS character_data) AS foreign_data_wrapper_language
+ FROM pg_foreign_data_wrapper w, pg_authid u
+ WHERE u.oid = w.fdwowner
+ AND (pg_has_role(fdwowner, 'USAGE')
+ OR has_foreign_data_wrapper_privilege(w.oid, 'USAGE'));
+
+
+/*
+ * 24.4
+ * FOREIGN_DATA_WRAPPER_OPTIONS view
+ */
+CREATE VIEW foreign_data_wrapper_options AS
+ SELECT foreign_data_wrapper_catalog,
+ foreign_data_wrapper_name,
+ CAST((pg_options_to_table(w.fdwoptions)).option_name AS sql_identifier) AS option_name,
+ CAST((pg_options_to_table(w.fdwoptions)).option_value AS character_data) AS option_value
+ FROM _pg_foreign_data_wrappers w;
+
+GRANT SELECT ON foreign_data_wrapper_options TO PUBLIC;
+
+
+/*
+ * 24.5
+ * FOREIGN_DATA_WRAPPERS view
+ */
+CREATE VIEW foreign_data_wrappers AS
+ SELECT foreign_data_wrapper_catalog,
+ foreign_data_wrapper_name,
+ authorization_identifier,
+ library_name,
+ foreign_data_wrapper_language
+ FROM _pg_foreign_data_wrappers w;
+
+GRANT SELECT ON foreign_data_wrappers TO PUBLIC;
+
+
+/* Base view for foreign servers */
+CREATE VIEW _pg_foreign_servers AS
+ SELECT s.oid,
+ s.srvoptions,
+ CAST(current_database() AS sql_identifier) AS foreign_server_catalog,
+ CAST(srvname AS sql_identifier) AS foreign_server_name,
+ w.foreign_data_wrapper_catalog,
+ w.foreign_data_wrapper_name,
+ CAST(srvtype AS character_data) AS foreign_server_type,
+ CAST(srvversion AS character_data) AS foreign_server_version,
+ CAST(u.rolname AS sql_identifier) AS authorization_identifier
+ FROM pg_foreign_server s, _pg_foreign_data_wrappers w, pg_authid u
+ WHERE w.oid = s.srvfdw
+ AND u.oid = s.srvowner
+ AND (pg_has_role(s.srvowner, 'USAGE')
+ OR has_server_privilege(s.oid, 'USAGE'));
+
+
+/*
+ * 24.6
+ * FOREIGN_SERVER_OPTIONS view
+ */
+CREATE VIEW foreign_server_options AS
+ SELECT foreign_server_catalog,
+ foreign_server_name,
+ CAST((pg_options_to_table(s.srvoptions)).option_name AS sql_identifier) AS option_name,
+ CAST((pg_options_to_table(s.srvoptions)).option_value AS character_data) AS option_value
+ FROM _pg_foreign_servers s;
+
+GRANT SELECT ON TABLE foreign_server_options TO PUBLIC;
+
+
+/*
+ * 24.7
+ * FOREIGN_SERVERS view
+ */
+CREATE VIEW foreign_servers AS
+ SELECT foreign_server_catalog,
+ foreign_server_name,
+ foreign_data_wrapper_catalog,
+ foreign_data_wrapper_name,
+ foreign_server_type,
+ foreign_server_version,
+ authorization_identifier
+ FROM _pg_foreign_servers;
+
+GRANT SELECT ON foreign_servers TO PUBLIC;
+
+
+/* Base view for user mappings */
+CREATE VIEW _pg_user_mappings AS
+ SELECT um.oid,
+ um.umoptions,
+ CAST(COALESCE(u.rolname,'PUBLIC') AS sql_identifier ) AS authorization_identifier,
+ s.foreign_server_catalog,
+ s.foreign_server_name
+ FROM pg_user_mapping um LEFT JOIN pg_authid u ON (u.oid = um.umuser),
+ _pg_foreign_servers s
+ WHERE s.oid = um.umserver;
+
+
+/*
+ * 24.12
+ * USER_MAPPING_OPTIONS view
+ */
+CREATE VIEW user_mapping_options AS
+ SELECT authorization_identifier,
+ foreign_server_catalog,
+ foreign_server_name,
+ CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
+ CAST((pg_options_to_table(um.umoptions)).option_value AS character_data) AS option_value
+ FROM _pg_user_mappings um;
+
+GRANT SELECT ON user_mapping_options TO PUBLIC;
+
+
+/*
+ * 24.13
+ * USER_MAPPINGS view
+ */
+CREATE VIEW user_mappings AS
+ SELECT authorization_identifier,
+ foreign_server_catalog,
+ foreign_server_name
+ FROM _pg_user_mappings;
+
+GRANT SELECT ON user_mappings TO PUBLIC;
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 402bc5e3919..5b387456fd3 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -487,6 +487,31 @@ T652 SQL-dynamic statements in SQL routines NO
T653 SQL-schema statements in external routines NO
T654 SQL-dynamic statements in external routines NO
T655 Cyclically dependent routines NO
+M001 Datalinks NO
+M002 Datalinks via SQL/CLI NO
+M003 Datalinks via Embedded SQL NO
+M004 Foreign data support NO
+M005 Foreign schema support NO
+M006 GetSQLString routine NO
+M007 TransmitRequest NO
+M009 GetOpts and GetStatistics routines NO
+M010 Foreign data wrapper support NO
+M011 Datalinks via Ada NO
+M012 Datalinks via C NO
+M013 Datalinks via COBOL NO
+M014 Datalinks via Fortran NO
+M015 Datalinks via M NO
+M016 Datalinks via Pascal NO
+M017 Datalinks via PL/I NO
+M018 Foreign data wrapper interface routines in Ada NO
+M019 Foreign data wrapper interface routines in C NO
+M020 Foreign data wrapper interface routines in COBOL NO
+M021 Foreign data wrapper interface routines in Fortran NO
+M022 Foreign data wrapper interface routines in MUMPS NO
+M023 Foreign data wrapper interface routines in Pascal NO
+M024 Foreign data wrapper interface routines in PL/I NO
+M030 SQL-server foreign data support NO
+M031 Foreign data wrapper general routines NO
X010 XML type YES
X011 Arrays of XML type YES
X012 Multisets of XML type NO
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 07c0809609d..4223e5d1940 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -3,7 +3,7 @@
*
* Copyright (c) 1996-2008, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.56 2008/11/09 21:24:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.57 2008/12/19 16:25:17 petere Exp $
*/
CREATE VIEW pg_roles AS
@@ -381,6 +381,28 @@ CREATE VIEW pg_stat_bgwriter AS
pg_stat_get_buf_written_backend() AS buffers_backend,
pg_stat_get_buf_alloc() AS buffers_alloc;
+CREATE VIEW pg_user_mappings AS
+ SELECT
+ U.oid AS umid,
+ S.oid AS srvid,
+ S.srvname AS srvname,
+ U.umuser AS umuser,
+ CASE WHEN U.umuser = 0 THEN
+ 'public'
+ ELSE
+ A.rolname
+ END AS usename,
+ CASE WHEN pg_has_role(S.srvowner, 'USAGE') OR has_server_privilege(S.oid, 'USAGE') THEN
+ U.umoptions
+ ELSE
+ NULL
+ END AS umoptions
+ FROM pg_user_mapping U
+ LEFT JOIN pg_authid A ON (A.oid = U.umuser) JOIN
+ pg_foreign_server S ON (U.umserver = S.oid);
+
+REVOKE ALL on pg_user_mapping FROM public;
+
-- Tsearch debug function. Defined here because it'd be pretty unwieldy
-- to put it into pg_proc.h
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 49aefde4164..e455e62c9aa 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -4,7 +4,7 @@
# Makefile for backend/commands
#
# IDENTIFICATION
-# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.38 2008/02/19 10:30:07 petere Exp $
+# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.39 2008/12/19 16:25:17 petere Exp $
#
#-------------------------------------------------------------------------
@@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
conversioncmds.o copy.o \
- dbcommands.o define.o discard.o explain.o functioncmds.o \
+ dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index fce328bcc40..3772638766c 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.29 2008/06/15 01:25:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.30 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -270,6 +270,15 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
AlterTSConfigurationOwner(stmt->object, newowner);
break;
+ case OBJECT_FDW:
+ AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)),
+ newowner);
+ break;
+
+ case OBJECT_FOREIGN_SERVER:
+ AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner);
+ break;
+
default:
elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
(int) stmt->objectType);
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
new file mode 100644
index 00000000000..224a8f004e6
--- /dev/null
+++ b/src/backend/commands/foreigncmds.c
@@ -0,0 +1,1102 @@
+/*-------------------------------------------------------------------------
+ *
+ * foreigncmds.c
+ * foreign-data wrapper/server creation/manipulation commands
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/commands/foreigncmds.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/reloptions.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
+#include "commands/defrem.h"
+#include "foreign/foreign.h"
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Convert a DefElem list to the text array format that is used in
+ * pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
+ *
+ * Note: The array is usually stored to database without further
+ * processing, hence any validation should be done before this
+ * conversion.
+ */
+static Datum
+optionListToArray(List *options)
+{
+ ArrayBuildState *astate = NULL;
+ ListCell *cell;
+ text *t;
+
+ foreach (cell, options)
+ {
+ DefElem *def = lfirst(cell);
+ const char *value = "";
+ Size len;
+
+ value = defGetString(def);
+ len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
+ t = palloc(len + 1);
+ SET_VARSIZE(t, len);
+ sprintf(VARDATA(t), "%s=%s", def->defname, value);
+
+ astate = accumArrayResult(astate, PointerGetDatum(t),
+ false, TEXTOID,
+ CurrentMemoryContext);
+ }
+
+ if (astate)
+ return makeArrayResult(astate, CurrentMemoryContext);
+
+ return PointerGetDatum(NULL);
+}
+
+
+/*
+ * Transform the list of OptionDefElem into list of generic options.
+ * The result is converted to array of text suitable for storing in
+ * options.
+ *
+ * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER
+ * MAPPING. In the ALTER cases, oldOptions is the current text array
+ * of options.
+ */
+static Datum
+transformGenericOptions(Datum oldOptions,
+ List *optionDefList,
+ GenericOptionFlags flags,
+ ForeignDataWrapper *fdw,
+ OptionListValidatorFunc validateOptionList)
+{
+ List *resultOptions = untransformRelOptions(oldOptions);
+ ListCell *optcell;
+
+ foreach (optcell, optionDefList)
+ {
+ ListCell *cell;
+ ListCell *prev = NULL;
+ OptionDefElem *od = lfirst(optcell);
+
+ /*
+ * Find the element in resultOptions. We need this for
+ * validation in all cases.
+ */
+ foreach (cell, resultOptions)
+ {
+ DefElem *def = lfirst(cell);
+
+ if (strcmp(def->defname, od->def->defname) == 0)
+ break;
+ else
+ prev = cell;
+ }
+
+ /*
+ * It is possible to perform multiple SET/DROP actions on the
+ * same option. The standard permits this, as long as the
+ * options to be added are unique.
+ */
+
+ switch (od->alter_op)
+ {
+ case ALTER_OPT_DROP:
+ if (!cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("option \"%s\" not found",
+ od->def->defname)));
+ resultOptions = list_delete_cell(resultOptions, cell, prev);
+ break;
+
+ case ALTER_OPT_SET:
+ if (!cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("option \"%s\" not found",
+ od->def->defname)));
+ lfirst(cell) = od->def;
+ break;
+
+ case ALTER_OPT_ADD:
+ if (cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("option \"%s\" provided more than once",
+ od->def->defname)));
+ resultOptions = lappend(resultOptions, od->def);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized action %d on option \"%s\"",
+ od->alter_op, od->def->defname);
+ break;
+ }
+ }
+
+ if (validateOptionList)
+ validateOptionList(fdw, flags, resultOptions);
+
+ return optionListToArray(resultOptions);
+}
+
+
+/*
+ * 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 (strcmp(username, "current_user") == 0)
+ /* map to the owner */
+ return GetUserId();
+
+ /* map to provided user */
+ return missing_ok ? get_roleid(username) : get_roleid_checked(username);
+}
+
+
+/*
+ * Change foreign-data wrapper owner.
+ *
+ * Allow this only for superusers; also the new owner must be a
+ * superuser.
+ */
+void
+AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
+{
+ HeapTuple tup;
+ Relation rel;
+ Oid fdwId;
+
+ Form_pg_foreign_data_wrapper form;
+
+ /* 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),
+ errhint("Must be superuser to change owner of a foreign-data wrapper.")));
+
+ /* New owner must also be a superuser */
+ if (!superuser_arg(newOwnerId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
+ name),
+ errhint("The owner of a foreign-data wrapper must be a superuser.")));
+
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ tup = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME,
+ CStringGetDatum(name),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ 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);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(ForeignDataWrapperRelationId,
+ fdwId,
+ newOwnerId);
+ }
+
+ heap_close(rel, NoLock);
+ heap_freetuple(tup);
+}
+
+
+/*
+ * Change foreign server owner
+ */
+void
+AlterForeignServerOwner(const char *name, Oid newOwnerId)
+{
+ HeapTuple tup;
+ Relation rel;
+ Oid srvId;
+ AclResult aclresult;
+ Form_pg_foreign_server form;
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ tup = SearchSysCacheCopy(FOREIGNSERVERNAME,
+ CStringGetDatum(name),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist", name)));
+
+ srvId = HeapTupleGetOid(tup);
+ form = (Form_pg_foreign_server) GETSTRUCT(tup);
+
+ if (form->srvowner != newOwnerId)
+ {
+ /* Superusers can always do it */
+ if (!superuser())
+ {
+ /* Must be owner */
+ if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ name);
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have USAGE privilege on foreign-data wrapper */
+ aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ {
+ ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
+
+ aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+ }
+ }
+
+ form->srvowner = newOwnerId;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
+ newOwnerId);
+ }
+
+ heap_close(rel, NoLock);
+ heap_freetuple(tup);
+}
+
+
+/*
+ * Create a foreign-data wrapper
+ */
+void
+CreateForeignDataWrapper(CreateFdwStmt *stmt)
+{
+ Relation rel;
+ Datum values[Natts_pg_foreign_data_wrapper];
+ bool nulls[Natts_pg_foreign_data_wrapper];
+ HeapTuple tuple;
+ Oid fdwId;
+ Datum fdwoptions = InvalidOid;
+ Oid ownerId;
+ ForeignDataWrapperLibrary *fdwlib;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create foreign-data wrapper \"%s\"",
+ stmt->fdwname),
+ errhint("Must be superuser to create a foreign-data wrapper.")));
+
+ /* For now the owner cannot be specified on create. Use effective user ID. */
+ ownerId = GetUserId();
+
+ /*
+ * Check that there is no other foreign-data wrapper by this name.
+ */
+ if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("foreign-data wrapper \"%s\" already exists",
+ stmt->fdwname)));
+
+ /*
+ * Insert tuple into pg_foreign_data_wrapper.
+ */
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ MemSet(nulls, false, Natts_pg_foreign_data_wrapper);
+
+ values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
+ values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
+ values[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = CStringGetTextDatum(stmt->library);
+ nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
+
+ /*
+ * See if the FDW library loads at all. We also might want to use it
+ * later for validating the options.
+ */
+ fdwlib = GetForeignDataWrapperLibrary(stmt->library);
+
+ fdwoptions = transformGenericOptions(0, stmt->options, FdwOpt, NULL,
+ fdwlib->validateOptionList);
+
+ if (OidIsValid(fdwoptions))
+ values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
+ else
+ nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+
+ tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+ fdwId = simple_heap_insert(rel, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
+
+ heap_close(rel, NoLock);
+}
+
+
+/*
+ * Alter foreign-data wrapper
+ */
+void
+AlterForeignDataWrapper(AlterFdwStmt *stmt)
+{
+ Relation rel;
+ HeapTuple tp;
+ 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;
+ ForeignDataWrapperLibrary *fdwlib;
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to alter foreign-data wrapper \"%s\"",
+ stmt->fdwname),
+ errhint("Must be superuser to alter a foreign-data wrapper.")));
+
+ tp = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME,
+ CStringGetDatum(stmt->fdwname),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
+
+ 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->library)
+ {
+ /*
+ * New library specified -- load to see if valid.
+ */
+ fdwlib = GetForeignDataWrapperLibrary(stmt->library);
+
+ repl_val[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = CStringGetTextDatum(stmt->library);
+ repl_repl[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = true;
+
+ /*
+ * It could be that the options for the FDW, SERVER and USER MAPPING
+ * are no longer valid with the new library. Warn about this.
+ */
+ ereport(WARNING,
+ (errmsg("changing the foreign-data wrapper library can cause "
+ "the options for dependent objects to become invalid")));
+ }
+ else
+ {
+ /*
+ * No LIBRARY clause specified, but we need to load it for validating
+ * options.
+ */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_foreign_data_wrapper_fdwlibrary,
+ &isnull);
+ fdwlib = GetForeignDataWrapperLibrary(TextDatumGetCString(datum));
+ }
+
+ /*
+ * Options specified, validate and update.
+ */
+ if (stmt->options)
+ {
+ /* Extract the current options */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_foreign_data_wrapper_fdwoptions,
+ &isnull);
+
+ /* Transform the options */
+ datum = transformGenericOptions(datum, stmt->options, FdwOpt,
+ NULL, fdwlib->validateOptionList);
+
+ if (OidIsValid(datum))
+ repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = ObjectIdGetDatum(datum);
+ else
+ repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+
+ repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+ }
+
+ /* 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);
+}
+
+
+/*
+ * Drop foreign-data wrapper
+ */
+void
+RemoveForeignDataWrapper(DropFdwStmt *stmt)
+{
+ Oid fdwId;
+ ObjectAddress object;
+
+ fdwId = GetForeignDataWrapperOidByName(stmt->fdwname, true);
+
+ 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.")));
+
+ if (!OidIsValid(fdwId))
+ {
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign-data wrapper \"%s\" does not exist",
+ stmt->fdwname)));
+
+ /* IF EXISTS specified, just note it */
+ ereport(NOTICE,
+ (errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
+ stmt->fdwname)));
+ return;
+ }
+
+ /*
+ * Do the deletion
+ */
+ object.classId = ForeignDataWrapperRelationId;
+ object.objectId = fdwId;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+}
+
+
+/*
+ * Drop foreign-data wrapper by OID
+ */
+void
+RemoveForeignDataWrapperById(Oid fdwId)
+{
+ HeapTuple tp;
+ Relation rel;
+
+ rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+ tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
+
+ simple_heap_delete(rel, &tp->t_self);
+
+ ReleaseSysCache(tp);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * Create a foreign server
+ */
+void
+CreateForeignServer(CreateForeignServerStmt *stmt)
+{
+ Relation rel;
+ Datum srvoptions = InvalidOid;
+ Datum values[Natts_pg_foreign_server];
+ bool nulls[Natts_pg_foreign_server];
+ HeapTuple tuple;
+ Oid srvId;
+ Oid ownerId;
+ AclResult aclresult;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ ForeignDataWrapper *fdw;
+
+ /* For now the owner cannot be specified on create. Use effective user ID. */
+ ownerId = GetUserId();
+
+ /*
+ * Check that there is no other foreign server by this name.
+ */
+ if (GetForeignServerByName(stmt->servername, true) != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("server \"%s\" already exists",
+ stmt->servername)));
+
+ /*
+ * Check that the FDW exists and that we have USAGE on it.
+ * Also get the actual FDW for option validation etc.
+ */
+ fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
+
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+
+ /*
+ * Insert tuple into pg_foreign_server.
+ */
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ MemSet(nulls, false, Natts_pg_foreign_server);
+
+ values[Anum_pg_foreign_server_srvname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
+ values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
+ values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
+
+ /* Add server type if supplied */
+ if (stmt->servertype)
+ values[Anum_pg_foreign_server_srvtype - 1] =
+ CStringGetTextDatum(stmt->servertype);
+ else
+ nulls[Anum_pg_foreign_server_srvtype - 1] = true;
+
+ /* Add server version if supplied */
+ if (stmt->version)
+ values[Anum_pg_foreign_server_srvversion - 1] =
+ CStringGetTextDatum(stmt->version);
+ else
+ nulls[Anum_pg_foreign_server_srvversion - 1] = true;
+
+ /* Start with a blank acl */
+ nulls[Anum_pg_foreign_server_srvacl - 1] = true;
+
+ /* Add server options */
+ srvoptions = transformGenericOptions(0, stmt->options, ServerOpt, fdw,
+ fdw->lib->validateOptionList);
+
+ if (OidIsValid(srvoptions))
+ values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
+ else
+ nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
+
+ tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+ srvId = simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ /* Add dependency on FDW and owner */
+ myself.classId = ForeignServerRelationId;
+ myself.objectId = srvId;
+ myself.objectSubId = 0;
+
+ referenced.classId = ForeignDataWrapperRelationId;
+ referenced.objectId = fdw->fdwid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+
+ heap_close(rel, NoLock);
+}
+
+
+/*
+ * Alter foreign server
+ */
+void
+AlterForeignServer(AlterForeignServerStmt *stmt)
+{
+ Relation rel;
+ HeapTuple tp;
+ Datum repl_val[Natts_pg_foreign_server];
+ bool repl_null[Natts_pg_foreign_server];
+ bool repl_repl[Natts_pg_foreign_server];
+ Oid srvId;
+ Form_pg_foreign_server srvForm;
+
+ tp = SearchSysCacheCopy(FOREIGNSERVERNAME,
+ CStringGetDatum(stmt->servername),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist", stmt->servername)));
+
+ srvId = HeapTupleGetOid(tp);
+ srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
+
+ /*
+ * Only owner or a superuser can ALTER a SERVER.
+ */
+ if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ stmt->servername);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+
+ if (stmt->has_version)
+ {
+ /*
+ * Change the server VERSION string.
+ */
+ if (stmt->version)
+ repl_val[Anum_pg_foreign_server_srvversion - 1] =
+ CStringGetTextDatum(stmt->version);
+ else
+ repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
+
+ repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
+ }
+
+ if (stmt->options)
+ {
+ ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
+ Datum datum;
+ bool isnull;
+
+ /* Extract the current srvoptions */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvoptions,
+ &isnull);
+
+ /* Prepare the options array */
+ datum = transformGenericOptions(datum, stmt->options, ServerOpt,
+ fdw, fdw->lib->validateOptionList);
+
+ if (OidIsValid(datum))
+ repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
+ else
+ repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
+
+ repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
+ }
+
+ /* 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;
+
+ srvId = GetForeignServerOidByName(stmt->servername, true);
+
+ 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);
+
+ object.classId = ForeignServerRelationId;
+ object.objectId = srvId;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+}
+
+
+/*
+ * Drop foreign server by OID
+ */
+void
+RemoveForeignServerById(Oid srvId)
+{
+ HeapTuple tp;
+ Relation rel;
+
+ rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+ tp = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(srvId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign server %u", srvId);
+
+ simple_heap_delete(rel, &tp->t_self);
+
+ ReleaseSysCache(tp);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * Create user mapping
+ */
+void
+CreateUserMapping(CreateUserMappingStmt *stmt)
+{
+ Relation rel;
+ Datum useoptions = InvalidOid;
+ Datum values[Natts_pg_user_mapping];
+ bool nulls[Natts_pg_user_mapping];
+ HeapTuple tuple;
+ Oid useId;
+ Oid umId;
+ Oid ownerId;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ ForeignServer *srv;
+ ForeignDataWrapper *fdw;
+
+ ownerId = GetUserId();
+
+ useId = GetUserOidFromMapping(stmt->username, false);
+
+ /*
+ * Check that the server exists and that the we own it.
+ */
+ srv = GetForeignServerByName(stmt->servername, false);
+
+ if (!pg_foreign_server_ownercheck(srv->serverid, ownerId))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ stmt->servername);
+
+ /*
+ * Check that the user mapping is unique within server.
+ */
+ umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(useId),
+ ObjectIdGetDatum(srv->serverid),
+ 0, 0);
+ if (OidIsValid(umId))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("user mapping \"%s\" already exists for server %s",
+ MappingUserName(useId),
+ stmt->servername)));
+
+ fdw = GetForeignDataWrapper(srv->fdwid);
+
+ /*
+ * Insert tuple into pg_user_mapping.
+ */
+ rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+ MemSet(nulls, false, Natts_pg_user_mapping);
+
+ values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
+ values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
+
+ /* Add user options */
+ useoptions = transformGenericOptions(0, stmt->options, UserMappingOpt,
+ fdw, fdw->lib->validateOptionList);
+
+ if (OidIsValid(useoptions))
+ values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
+ else
+ nulls[Anum_pg_user_mapping_umoptions - 1] = true;
+
+ tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+ umId = simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ /* Add dependency on the server */
+ myself.classId = UserMappingRelationId;
+ myself.objectId = umId;
+ myself.objectSubId = 0;
+
+ referenced.classId = ForeignServerRelationId;
+ referenced.objectId = srv->serverid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(useId))
+ /* Record the mapped user dependency */
+ recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+
+ heap_close(rel, NoLock);
+}
+
+
+/*
+ * Alter user mapping
+ */
+void
+AlterUserMapping(AlterUserMappingStmt *stmt)
+{
+ Relation rel;
+ HeapTuple tp;
+ Datum repl_val[Natts_pg_user_mapping];
+ bool repl_null[Natts_pg_user_mapping];
+ bool repl_repl[Natts_pg_user_mapping];
+ Oid useId;
+ Oid umId;
+ ForeignServer *srv;
+
+ useId = GetUserOidFromMapping(stmt->username, false);
+ srv = GetForeignServerByName(stmt->servername, false);
+
+ umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(useId),
+ ObjectIdGetDatum(srv->serverid),
+ 0, 0);
+ if (!OidIsValid(umId))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping \"%s\" does not exist for the server",
+ MappingUserName(useId))));
+ }
+
+ /*
+ * Must be owner of the server to alter user mapping.
+ */
+ if (!pg_foreign_server_ownercheck(srv->serverid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ stmt->servername);
+
+ tp = SearchSysCacheCopy(USERMAPPINGOID,
+ ObjectIdGetDatum(umId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for user mapping %u", umId);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+
+ if (stmt->options)
+ {
+ ForeignDataWrapper *fdw;
+ Datum datum;
+ bool isnull;
+
+ /*
+ * Process the options.
+ */
+
+ fdw = GetForeignDataWrapper(srv->fdwid);
+
+ datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
+ tp,
+ Anum_pg_user_mapping_umoptions,
+ &isnull);
+
+ /* Prepare the options array */
+ datum = transformGenericOptions(datum, stmt->options, UserMappingOpt,
+ fdw, fdw->lib->validateOptionList);
+
+ if (OidIsValid(datum))
+ repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
+ else
+ repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
+
+ repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
+ }
+
+ /* 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);
+ heap_freetuple(tp);
+}
+
+
+/*
+ * Drop user mapping
+ */
+void
+RemoveUserMapping(DropUserMappingStmt *stmt)
+{
+ ObjectAddress object;
+ Oid useId;
+ Oid umId;
+ ForeignServer *srv;
+
+ useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
+ srv = GetForeignServerByName(stmt->servername, true);
+
+ if (stmt->username && !OidIsValid(useId))
+ {
+ /*
+ * IF EXISTS specified, role not found and not public.
+ * Notice this and leave.
+ */
+ elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
+ return;
+ }
+
+ if (!srv)
+ {
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist",
+ stmt->servername)));
+ /* IF EXISTS, just note it */
+ ereport(NOTICE, (errmsg("server does not exist, skipping")));
+ return;
+ }
+
+ umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(useId),
+ ObjectIdGetDatum(srv->serverid),
+ 0, 0);
+
+ if (!OidIsValid(umId))
+ {
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping \"%s\" does not exist for the server",
+ MappingUserName(useId))));
+
+ /* IF EXISTS specified, just note it */
+ ereport(NOTICE,
+ (errmsg("user mapping \"%s\" does not exist for the server, skipping",
+ MappingUserName(useId))));
+ return;
+ }
+
+ /*
+ * Only allow DROP if we own the server.
+ */
+ if (!pg_foreign_server_ownercheck(srv->serverid, GetUserId()))
+ {
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+ srv->servername);
+ }
+
+ /*
+ * Do the deletion
+ */
+ object.classId = UserMappingRelationId;
+ object.objectId = umId;
+ object.objectSubId = 0;
+
+ performDeletion(&object, DROP_CASCADE);
+}
+
+
+/*
+ * Drop user mapping by OID. This is called to clean up dependencies.
+ */
+void
+RemoveUserMappingById(Oid umId)
+{
+ HeapTuple tp;
+ Relation rel;
+
+ rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+ tp = SearchSysCache(USERMAPPINGOID,
+ ObjectIdGetDatum(umId),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for user mapping %u", umId);
+
+ simple_heap_delete(rel, &tp->t_self);
+
+ ReleaseSysCache(tp);
+
+ heap_close(rel, RowExclusiveLock);
+}
diff --git a/src/backend/foreign/Makefile b/src/backend/foreign/Makefile
new file mode 100644
index 00000000000..b3cc209c161
--- /dev/null
+++ b/src/backend/foreign/Makefile
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+# Makefile for foreign
+#
+# IDENTIFICATION
+# $PostgreSQL: pgsql/src/backend/foreign/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/foreign
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS= foreign.o
+
+include $(top_srcdir)/src/backend/common.mk
+
+FDW = dummy postgresql
+
+$(addsuffix -fdw,all install installdirs uninstall distprep):
+ for dir in $(FDW); do $(MAKE) -C $$dir `echo $@ | sed 's/-fdw$$//'` || exit; done
+
+clean distclean maintainer-clean:
+ for dir in $(FDW); do $(MAKE) -C $$dir $@ || exit; done
diff --git a/src/backend/foreign/dummy/Makefile b/src/backend/foreign/dummy/Makefile
new file mode 100644
index 00000000000..8a05ada0197
--- /dev/null
+++ b/src/backend/foreign/dummy/Makefile
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+# Makefile for dummy foreign-data wrapper
+#
+# IDENTIFICATION
+# $PostgreSQL: pgsql/src/backend/foreign/dummy/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/foreign/dummy
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+NAME = dummy_fdw
+OBJS = dummy_fdw.o
+
+include $(top_srcdir)/src/Makefile.shlib
+
+all: all-shared-lib
+
+install: all install-lib
+
+installdirs: installdirs-lib
+
+clean distclean maintainer-clean: clean-lib
+ rm -f $(OBJS)
diff --git a/src/backend/foreign/dummy/dummy_fdw.c b/src/backend/foreign/dummy/dummy_fdw.c
new file mode 100644
index 00000000000..2017c847136
--- /dev/null
+++ b/src/backend/foreign/dummy/dummy_fdw.c
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * dummy_fdw.c
+ * "dummy" foreign-data wrapper
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/foreign/dummy/dummy_fdw.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "foreign/foreign.h"
+
+PG_MODULE_MAGIC;
+
+/*
+ * This looks like a complete waste right now, but it is useful for
+ * testing, and will become more interesting as more parts of the
+ * interface are implemented.
+ */
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
new file mode 100644
index 00000000000..2fdb84ac1b4
--- /dev/null
+++ b/src/backend/foreign/foreign.c
@@ -0,0 +1,389 @@
+/*-------------------------------------------------------------------------
+ *
+ * foreign.c
+ * support for foreign-data wrappers, servers and user mappings.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/foreign/foreign.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
+#include "foreign/foreign.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/parsenodes.h"
+#include "utils/acl.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+
+
+extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
+
+
+/* list of currently loaded foreign-data wrapper interfaces */
+static List *loaded_fdw_interfaces = NIL;
+
+
+/*
+ * GetForeignDataWrapperLibrary - return the named FDW library. If it
+ * is already loaded, use that. Otherwise allocate, initialize, and
+ * store in cache.
+ */
+ForeignDataWrapperLibrary *
+GetForeignDataWrapperLibrary(const char *libname)
+{
+ MemoryContext oldcontext;
+ void *libhandle = NULL;
+ ForeignDataWrapperLibrary *fdwl = NULL;
+ ListCell *cell;
+
+ /* See if we have the FDW library is already loaded */
+ foreach (cell, loaded_fdw_interfaces)
+ {
+ fdwl = lfirst(cell);
+ if (strcmp(fdwl->libname, libname) == 0)
+ return fdwl;
+ }
+
+ /*
+ * We don't have it yet, so load and add. Attempt a load_file()
+ * first to filter out any missing or unloadable libraries.
+ */
+ load_file(libname, false);
+
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+ fdwl = palloc(sizeof(*fdwl));
+ fdwl->libname = pstrdup(libname);
+ loaded_fdw_interfaces = lappend(loaded_fdw_interfaces, fdwl);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * Now look up the foreign data wrapper functions.
+ */
+#define LOOKUP_FUNCTION(name) \
+ (void *)(libhandle ? \
+ lookup_external_function(libhandle, name) \
+ : load_external_function(fdwl->libname, name, false, &libhandle))
+
+ fdwl->validateOptionList = LOOKUP_FUNCTION("_pg_validateOptionList");
+
+ return fdwl;
+}
+
+
+/*
+ * GetForeignDataWrapper - look up the foreign-data wrapper by OID.
+ *
+ * Here we also deal with loading the FDW library and looking up the
+ * actual functions.
+ */
+ForeignDataWrapper *
+GetForeignDataWrapper(Oid fdwid)
+{
+ Form_pg_foreign_data_wrapper fdwform;
+ ForeignDataWrapper *fdw;
+ Datum datum;
+ HeapTuple tp;
+ bool isnull;
+
+ tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
+
+ fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
+
+ fdw = palloc(sizeof(ForeignDataWrapper));
+ fdw->fdwid = fdwid;
+ fdw->owner = fdwform->fdwowner;
+ fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
+
+ /* Extract library name */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_foreign_data_wrapper_fdwlibrary,
+ &isnull);
+ fdw->fdwlibrary = pstrdup(TextDatumGetCString(datum));
+
+ fdw->lib = GetForeignDataWrapperLibrary(fdw->fdwlibrary);
+
+ /* Extract the options */
+ datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+ tp,
+ Anum_pg_foreign_data_wrapper_fdwoptions,
+ &isnull);
+ fdw->options = untransformRelOptions(datum);
+
+ ReleaseSysCache(tp);
+
+ return fdw;
+}
+
+
+/*
+ * GetForeignDataWrapperOidByName - look up the foreign-data wrapper
+ * OID by name.
+ */
+Oid
+GetForeignDataWrapperOidByName(const char *fdwname, bool missing_ok)
+{
+ Oid fdwId;
+
+ fdwId = GetSysCacheOid(FOREIGNDATAWRAPPERNAME,
+ CStringGetDatum(fdwname),
+ 0, 0, 0);
+
+ if (!OidIsValid(fdwId) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign-data wrapper \"%s\" does not exist", fdwname)));
+
+ return fdwId;
+}
+
+
+/*
+ * GetForeignDataWrapperByName - look up the foreign-data wrapper
+ * definition by name.
+ */
+ForeignDataWrapper *
+GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
+{
+ Oid fdwId = GetForeignDataWrapperOidByName(fdwname, missing_ok);
+
+ if (!OidIsValid(fdwId) && missing_ok)
+ return NULL;
+
+ return GetForeignDataWrapper(fdwId);
+}
+
+
+/*
+ * GetForeignServer - look up the foreign server definition.
+ */
+ForeignServer *
+GetForeignServer(Oid serverid)
+{
+ Form_pg_foreign_server serverform;
+ ForeignServer *server;
+ HeapTuple tp;
+ Datum datum;
+ bool isnull;
+
+ tp = SearchSysCache(FOREIGNSERVEROID,
+ ObjectIdGetDatum(serverid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign server %u", serverid);
+
+ serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
+
+ server = palloc(sizeof(ForeignServer));
+ server->serverid = serverid;
+ server->servername = pstrdup(NameStr(serverform->srvname));
+ server->owner = serverform->srvowner;
+ server->fdwid = serverform->srvfdw;
+
+ /* Extract server type */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvtype,
+ &isnull);
+ server->servertype = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
+
+ /* Extract server version */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvversion,
+ &isnull);
+ server->serverversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
+
+ /* Extract the srvoptions */
+ datum = SysCacheGetAttr(FOREIGNSERVEROID,
+ tp,
+ Anum_pg_foreign_server_srvoptions,
+ &isnull);
+
+ /* untransformRelOptions does exactly what we want - avoid duplication */
+ server->options = untransformRelOptions(datum);
+
+ ReleaseSysCache(tp);
+
+ return server;
+}
+
+
+/*
+ * GetForeignServerByName - look up the foreign server oid by name.
+ */
+Oid
+GetForeignServerOidByName(const char *srvname, bool missing_ok)
+{
+ Oid serverid;
+
+ serverid = GetSysCacheOid(FOREIGNSERVERNAME,
+ CStringGetDatum(srvname),
+ 0, 0, 0);
+
+ if (!OidIsValid(serverid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist", srvname)));
+
+ return serverid;
+}
+
+
+/*
+ * GetForeignServerByName - look up the foreign server definition by name.
+ */
+ForeignServer *
+GetForeignServerByName(const char *srvname, bool missing_ok)
+{
+ Oid serverid = GetForeignServerOidByName(srvname, missing_ok);
+
+ if (!OidIsValid(serverid) && missing_ok)
+ return NULL;
+
+ return GetForeignServer(serverid);
+}
+
+
+/*
+ * GetUserMapping - look up the user mapping.
+ *
+ * If no mapping is found for the supplied user, we also look for
+ * PUBLIC mappings (userid == InvalidOid).
+ */
+UserMapping *
+GetUserMapping(Oid userid, Oid serverid)
+{
+ Form_pg_user_mapping umform;
+ Datum datum;
+ HeapTuple tp;
+ bool isnull;
+ UserMapping *um;
+
+ tp = SearchSysCache(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(userid),
+ ObjectIdGetDatum(serverid),
+ 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ {
+ /* Not found for the specific user -- try PUBLIC */
+ tp = SearchSysCache(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(serverid),
+ 0, 0);
+ }
+
+ if (!HeapTupleIsValid(tp))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping not found for \"%s\"",
+ MappingUserName(userid))));
+
+ umform = (Form_pg_user_mapping) GETSTRUCT(tp);
+
+ /* Extract the umoptions */
+ datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
+ tp,
+ Anum_pg_user_mapping_umoptions,
+ &isnull);
+
+ um = palloc(sizeof(UserMapping));
+ um->userid = userid;
+ um->serverid = serverid;
+ um->options = untransformRelOptions(datum);
+
+ ReleaseSysCache(tp);
+
+ return um;
+}
+
+
+/*
+ * deflist_to_tuplestore - Helper function to convert DefElem list to
+ * tuplestore usable in SRF.
+ */
+static void
+deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
+{
+ ListCell *cell;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ Datum values[2];
+ bool nulls[2] = { 0 };
+ MemoryContext per_query_ctx;
+ MemoryContext oldcontext;
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not allowed in this context")));
+
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+ /*
+ * Now prepare the result set.
+ */
+ tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ foreach (cell, options)
+ {
+ DefElem *def = lfirst(cell);
+
+ values[0] = CStringGetTextDatum(def->defname);
+ values[1] = CStringGetTextDatum(((Value *)def->arg)->val.str);
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ }
+
+ /* clean up and return the tuplestore */
+ tuplestore_donestoring(tupstore);
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+
+/*
+ * Convert options array to name/value table. Useful for information
+ * schema and pg_dump.
+ */
+Datum
+pg_options_to_table(PG_FUNCTION_ARGS)
+{
+ Datum array = PG_GETARG_DATUM(0);
+
+ deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, untransformRelOptions(array));
+
+ return (Datum) 0;
+}
diff --git a/src/backend/foreign/postgresql/Makefile b/src/backend/foreign/postgresql/Makefile
new file mode 100644
index 00000000000..40ed90f8d70
--- /dev/null
+++ b/src/backend/foreign/postgresql/Makefile
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+# Makefile for postgresql foreign-data wrapper
+#
+# IDENTIFICATION
+# $PostgreSQL: pgsql/src/backend/foreign/postgresql/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/foreign/postgresql
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+NAME = postgresql_fdw
+OBJS = postgresql_fdw.o
+
+include $(top_srcdir)/src/Makefile.shlib
+
+all: all-shared-lib
+
+install: all install-lib
+
+installdirs: installdirs-lib
+
+clean distclean maintainer-clean: clean-lib
+ rm -f $(OBJS)
diff --git a/src/backend/foreign/postgresql/postgresql_fdw.c b/src/backend/foreign/postgresql/postgresql_fdw.c
new file mode 100644
index 00000000000..f1c881e3976
--- /dev/null
+++ b/src/backend/foreign/postgresql/postgresql_fdw.c
@@ -0,0 +1,123 @@
+/*-------------------------------------------------------------------------
+ *
+ * postgresql_fdw.c
+ * foreign-data wrapper for postgresql (libpq) connections.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/foreign/postgresql/postgresql_fdw.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "nodes/value.h"
+#include "nodes/parsenodes.h"
+#include "nodes/makefuncs.h"
+#include "foreign/foreign.h"
+
+PG_MODULE_MAGIC;
+
+
+/*
+ * Describes the valid options for postgresql FDW, server and user mapping.
+ */
+typedef struct ConnectionOptions {
+ const char *optname; /* Option name */
+ GenericOptionFlags optflags; /* Option usage bitmap */
+} ConnectionOptions;
+
+/*
+ * Copied from fe-connect.c PQconninfoOptions.
+ *
+ * The list is small - don't bother with bsearch if it stays so.
+ */
+static ConnectionOptions libpq_conninfo_options[] = {
+ { "authtype", ServerOpt },
+ { "service", ServerOpt },
+ { "user", UserMappingOpt },
+ { "password", UserMappingOpt },
+ { "connect_timeout", ServerOpt },
+ { "dbname", ServerOpt },
+ { "host", ServerOpt },
+ { "hostaddr", ServerOpt },
+ { "port", ServerOpt },
+ { "tty", ServerOpt },
+ { "options", ServerOpt },
+ { "requiressl", ServerOpt },
+ { "sslmode", ServerOpt },
+ { "gsslib", ServerOpt },
+ { NULL, InvalidOpt }
+};
+
+void _PG_fini(void);
+
+
+/*
+ * Check if the provided option is one of libpq conninfo options.
+ * We look at only options with matching flags.
+ */
+static bool
+is_conninfo_option(const char *option, GenericOptionFlags flags)
+{
+ ConnectionOptions *opt;
+
+ for (opt = libpq_conninfo_options; opt->optname != NULL; opt++)
+ if (flags & opt->optflags && strcmp(opt->optname, option) == 0)
+ return true;
+ return false;
+}
+
+/*
+ * Validate the generic option given to SERVER or USER MAPPING.
+ * Raise an ERROR if the option or its value is considered
+ * invalid.
+ *
+ * Valid server options are all libpq conninfo options except
+ * user and password -- these may only appear in USER MAPPING options.
+ */
+void
+_pg_validateOptionList(ForeignDataWrapper *fdw, GenericOptionFlags flags,
+ List *options)
+{
+ ListCell *cell;
+
+ foreach (cell, options)
+ {
+ DefElem *def = lfirst(cell);
+
+ if (!is_conninfo_option(def->defname, flags))
+ {
+ ConnectionOptions *opt;
+ StringInfoData buf;
+ const char *objtype;
+
+ /*
+ * Unknown option specified, complain about it. Provide a hint
+ * with list of valid options for the object.
+ */
+ initStringInfo(&buf);
+ for (opt = libpq_conninfo_options; opt->optname != NULL; opt++)
+ if (flags & opt->optflags)
+ appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
+ opt->optname);
+
+ if (flags & ServerOpt)
+ objtype = "server";
+ else if (flags & UserMappingOpt)
+ objtype = "user mapping";
+ else if (flags & FdwOpt)
+ objtype = "foreign-data wrapper";
+ else
+ objtype = "???";
+
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid option \"%s\" to %s", def->defname, objtype),
+ errhint("valid %s options are: %s", objtype, buf.data)));
+ }
+ }
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6a0c3374ce8..86f555a03a6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.415 2008/12/04 17:51:26 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.416 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2033,6 +2033,17 @@ _copyDefElem(DefElem *from)
return newnode;
}
+static OptionDefElem *
+_copyOptionDefElem(OptionDefElem *from)
+{
+ OptionDefElem *newnode = makeNode(OptionDefElem);
+
+ COPY_SCALAR_FIELD(alter_op);
+ COPY_NODE_FIELD(def);
+
+ return newnode;
+}
+
static LockingClause *
_copyLockingClause(LockingClause *from)
{
@@ -2869,6 +2880,117 @@ _copyDropTableSpaceStmt(DropTableSpaceStmt *from)
return newnode;
}
+static CreateFdwStmt *
+_copyCreateFdwStmt(CreateFdwStmt *from)
+{
+ CreateFdwStmt *newnode = makeNode(CreateFdwStmt);
+
+ COPY_STRING_FIELD(fdwname);
+ COPY_STRING_FIELD(library);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+static AlterFdwStmt *
+_copyAlterFdwStmt(AlterFdwStmt *from)
+{
+ AlterFdwStmt *newnode = makeNode(AlterFdwStmt);
+
+ COPY_STRING_FIELD(fdwname);
+ COPY_STRING_FIELD(library);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+static DropFdwStmt *
+_copyDropFdwStmt(DropFdwStmt *from)
+{
+ DropFdwStmt *newnode = makeNode(DropFdwStmt);
+
+ COPY_STRING_FIELD(fdwname);
+ COPY_SCALAR_FIELD(missing_ok);
+ COPY_SCALAR_FIELD(behavior);
+
+ return newnode;
+}
+
+static CreateForeignServerStmt *
+_copyCreateForeignServerStmt(CreateForeignServerStmt *from)
+{
+ CreateForeignServerStmt *newnode = makeNode(CreateForeignServerStmt);
+
+ COPY_STRING_FIELD(servername);
+ COPY_STRING_FIELD(servertype);
+ COPY_STRING_FIELD(version);
+ COPY_STRING_FIELD(fdwname);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+static AlterForeignServerStmt *
+_copyAlterForeignServerStmt(AlterForeignServerStmt *from)
+{
+ AlterForeignServerStmt *newnode = makeNode(AlterForeignServerStmt);
+
+ COPY_STRING_FIELD(servername);
+ COPY_STRING_FIELD(version);
+ COPY_NODE_FIELD(options);
+ COPY_SCALAR_FIELD(has_version);
+
+ return newnode;
+}
+
+static DropForeignServerStmt *
+_copyDropForeignServerStmt(DropForeignServerStmt *from)
+{
+ DropForeignServerStmt *newnode = makeNode(DropForeignServerStmt);
+
+ COPY_STRING_FIELD(servername);
+ COPY_SCALAR_FIELD(missing_ok);
+ COPY_SCALAR_FIELD(behavior);
+
+ return newnode;
+}
+
+static CreateUserMappingStmt *
+_copyCreateUserMappingStmt(CreateUserMappingStmt *from)
+{
+ CreateUserMappingStmt *newnode = makeNode(CreateUserMappingStmt);
+
+ COPY_STRING_FIELD(username);
+ COPY_STRING_FIELD(servername);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+static AlterUserMappingStmt *
+_copyAlterUserMappingStmt(AlterUserMappingStmt *from)
+{
+ AlterUserMappingStmt *newnode = makeNode(AlterUserMappingStmt);
+
+ COPY_STRING_FIELD(username);
+ COPY_STRING_FIELD(servername);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
+static DropUserMappingStmt *
+_copyDropUserMappingStmt(DropUserMappingStmt *from)
+{
+ DropUserMappingStmt *newnode = makeNode(DropUserMappingStmt);
+
+ COPY_STRING_FIELD(username);
+ COPY_STRING_FIELD(servername);
+ COPY_SCALAR_FIELD(missing_ok);
+
+ return newnode;
+}
+
static CreateTrigStmt *
_copyCreateTrigStmt(CreateTrigStmt *from)
{
@@ -3696,6 +3818,33 @@ copyObject(void *from)
case T_DropTableSpaceStmt:
retval = _copyDropTableSpaceStmt(from);
break;
+ case T_CreateFdwStmt:
+ retval = _copyCreateFdwStmt(from);
+ break;
+ case T_AlterFdwStmt:
+ retval = _copyAlterFdwStmt(from);
+ break;
+ case T_DropFdwStmt:
+ retval = _copyDropFdwStmt(from);
+ break;
+ case T_CreateForeignServerStmt:
+ retval = _copyCreateForeignServerStmt(from);
+ break;
+ case T_AlterForeignServerStmt:
+ retval = _copyAlterForeignServerStmt(from);
+ break;
+ case T_DropForeignServerStmt:
+ retval = _copyDropForeignServerStmt(from);
+ break;
+ case T_CreateUserMappingStmt:
+ retval = _copyCreateUserMappingStmt(from);
+ break;
+ case T_AlterUserMappingStmt:
+ retval = _copyAlterUserMappingStmt(from);
+ break;
+ case T_DropUserMappingStmt:
+ retval = _copyDropUserMappingStmt(from);
+ break;
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
@@ -3823,6 +3972,9 @@ copyObject(void *from)
case T_DefElem:
retval = _copyDefElem(from);
break;
+ case T_OptionDefElem:
+ retval = _copyOptionDefElem(from);
+ break;
case T_LockingClause:
retval = _copyLockingClause(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index ebf8cea2f0d..e5e2bc44226 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.340 2008/12/04 17:51:26 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.341 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1519,6 +1519,99 @@ _equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b)
}
static bool
+_equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b)
+{
+ COMPARE_STRING_FIELD(fdwname);
+ COMPARE_STRING_FIELD(library);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
+static bool
+_equalAlterFdwStmt(AlterFdwStmt *a, AlterFdwStmt *b)
+{
+ COMPARE_STRING_FIELD(fdwname);
+ COMPARE_STRING_FIELD(library);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
+static bool
+_equalDropFdwStmt(DropFdwStmt *a, DropFdwStmt *b)
+{
+ COMPARE_STRING_FIELD(fdwname);
+ COMPARE_SCALAR_FIELD(missing_ok);
+ COMPARE_SCALAR_FIELD(behavior);
+
+ return true;
+}
+
+static bool
+_equalCreateForeignServerStmt(CreateForeignServerStmt *a, CreateForeignServerStmt *b)
+{
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_STRING_FIELD(servertype);
+ COMPARE_STRING_FIELD(version);
+ COMPARE_STRING_FIELD(fdwname);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
+static bool
+_equalAlterForeignServerStmt(AlterForeignServerStmt *a, AlterForeignServerStmt *b)
+{
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_STRING_FIELD(version);
+ COMPARE_NODE_FIELD(options);
+ COMPARE_SCALAR_FIELD(has_version);
+
+ return true;
+}
+
+static bool
+_equalDropForeignServerStmt(DropForeignServerStmt *a, DropForeignServerStmt *b)
+{
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_SCALAR_FIELD(missing_ok);
+ COMPARE_SCALAR_FIELD(behavior);
+
+ return true;
+}
+
+static bool
+_equalCreateUserMappingStmt(CreateUserMappingStmt *a, CreateUserMappingStmt *b)
+{
+ COMPARE_STRING_FIELD(username);
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
+static bool
+_equalAlterUserMappingStmt(AlterUserMappingStmt *a, AlterUserMappingStmt *b)
+{
+ COMPARE_STRING_FIELD(username);
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
+static bool
+_equalDropUserMappingStmt(DropUserMappingStmt *a, DropUserMappingStmt *b)
+{
+ COMPARE_STRING_FIELD(username);
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_SCALAR_FIELD(missing_ok);
+
+ return true;
+}
+
+static bool
_equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
{
COMPARE_STRING_FIELD(trigname);
@@ -1957,6 +2050,15 @@ _equalDefElem(DefElem *a, DefElem *b)
}
static bool
+_equalOptionDefElem(OptionDefElem *a, OptionDefElem *b)
+{
+ COMPARE_SCALAR_FIELD(alter_op);
+ COMPARE_NODE_FIELD(def);
+
+ return true;
+}
+
+static bool
_equalLockingClause(LockingClause *a, LockingClause *b)
{
COMPARE_NODE_FIELD(lockedRels);
@@ -2534,6 +2636,33 @@ equal(void *a, void *b)
case T_DropTableSpaceStmt:
retval = _equalDropTableSpaceStmt(a, b);
break;
+ case T_CreateFdwStmt:
+ retval = _equalCreateFdwStmt(a, b);
+ break;
+ case T_AlterFdwStmt:
+ retval = _equalAlterFdwStmt(a, b);
+ break;
+ case T_DropFdwStmt:
+ retval = _equalDropFdwStmt(a, b);
+ break;
+ case T_CreateForeignServerStmt:
+ retval = _equalCreateForeignServerStmt(a, b);
+ break;
+ case T_AlterForeignServerStmt:
+ retval = _equalAlterForeignServerStmt(a, b);
+ break;
+ case T_DropForeignServerStmt:
+ retval = _equalDropForeignServerStmt(a, b);
+ break;
+ case T_CreateUserMappingStmt:
+ retval = _equalCreateUserMappingStmt(a, b);
+ break;
+ case T_AlterUserMappingStmt:
+ retval = _equalAlterUserMappingStmt(a, b);
+ break;
+ case T_DropUserMappingStmt:
+ retval = _equalDropUserMappingStmt(a, b);
+ break;
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
@@ -2661,6 +2790,9 @@ equal(void *a, void *b)
case T_DefElem:
retval = _equalDefElem(a, b);
break;
+ case T_OptionDefElem:
+ retval = _equalOptionDefElem(a, b);
+ break;
case T_LockingClause:
retval = _equalLockingClause(a, b);
break;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 42539f6f97b..cb4f09be5a1 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.60 2008/09/01 20:42:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.61 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -361,3 +361,16 @@ makeDefElem(char *name, Node *arg)
res->arg = arg;
return res;
}
+
+/*
+ * makeOptionDefElem -
+ * build an OptionDefElem node
+ */
+OptionDefElem *
+makeOptionDefElem(int op, DefElem *def)
+{
+ OptionDefElem *res = makeNode(OptionDefElem);
+ res->alter_op = op;
+ res->def = def;
+ return res;
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 337af633272..cc81e656461 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.645 2008/12/18 18:20:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.646 2008/12/19 16:25:17 petere Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -156,6 +156,7 @@ static TypeName *TableFuncTypeName(List *columns);
FunctionParameterMode fun_param_mode;
FuncWithArgs *funwithargs;
DefElem *defelt;
+ OptionDefElem *optdef;
SortBy *sortby;
JoinExpr *jexpr;
IndexElem *ielem;
@@ -172,19 +173,22 @@ static TypeName *TableFuncTypeName(List *columns);
}
%type <node> stmt schema_stmt
- AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt
+ AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
+ AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
- AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
+ AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
- CreateAssertStmt CreateTrigStmt CreateUserStmt CreateRoleStmt
+ CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
+ CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
- DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt
+ DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
+ DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
@@ -222,6 +226,10 @@ static TypeName *TableFuncTypeName(List *columns);
%type <list> OptRoleList
%type <defelt> OptRoleElem
+%type <str> opt_type
+%type <str> foreign_server_version opt_foreign_server_version
+%type <str> auth_ident
+
%type <str> OptSchemaName
%type <list> OptSchemaEltList
@@ -274,6 +282,7 @@ static TypeName *TableFuncTypeName(List *columns);
prep_type_clause
execute_param_clause using_clause returning_clause
enum_val_list table_func_column_list
+ create_generic_options alter_generic_options
%type <range> OptTempTableName
%type <into> into_clause create_as_target
@@ -342,6 +351,12 @@ static TypeName *TableFuncTypeName(List *columns);
%type <range> relation_expr_opt_alias
%type <target> target_el single_set_clause set_target insert_column_item
+%type <str> generic_option_name
+%type <node> generic_option_arg
+%type <defelt> generic_option_elem
+%type <optdef> alter_generic_option_elem
+%type <list> generic_option_list alter_generic_option_list
+
%type <typnam> Typename SimpleTypename ConstTypename
GenericType Numeric opt_float
Character ConstCharacter
@@ -436,7 +451,7 @@ static TypeName *TableFuncTypeName(List *columns);
KEY
LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEAST LEFT LEVEL
- LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
+ LIBRARY LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
LOCK_P LOGIN_P
MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -445,7 +460,7 @@ static TypeName *TableFuncTypeName(List *columns);
NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC
- OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR
+ OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER
PARSER PARTIAL PASSWORD PLACING PLANS POSITION
@@ -459,7 +474,7 @@ static TypeName *TableFuncTypeName(List *columns);
REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
- SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
+ SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
SYMMETRIC SYSID SYSTEM_P
@@ -474,7 +489,7 @@ static TypeName *TableFuncTypeName(List *columns);
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
- WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
+ WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRAPPER WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
@@ -562,6 +577,8 @@ stmt :
AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDomainStmt
+ | AlterFdwStmt
+ | AlterForeignServerStmt
| AlterFunctionStmt
| AlterGroupStmt
| AlterObjectSchemaStmt
@@ -572,6 +589,7 @@ stmt :
| AlterRoleStmt
| AlterTSConfigurationStmt
| AlterTSDictionaryStmt
+ | AlterUserMappingStmt
| AlterUserSetStmt
| AlterUserStmt
| AnalyzeStmt
@@ -586,6 +604,8 @@ stmt :
| CreateCastStmt
| CreateConversionStmt
| CreateDomainStmt
+ | CreateFdwStmt
+ | CreateForeignServerStmt
| CreateFunctionStmt
| CreateGroupStmt
| CreateOpClassStmt
@@ -599,6 +619,7 @@ stmt :
| CreateTrigStmt
| CreateRoleStmt
| CreateUserStmt
+ | CreateUserMappingStmt
| CreatedbStmt
| DeallocateStmt
| DeclareCursorStmt
@@ -607,6 +628,8 @@ stmt :
| DiscardStmt
| DropAssertStmt
| DropCastStmt
+ | DropFdwStmt
+ | DropForeignServerStmt
| DropGroupStmt
| DropOpClassStmt
| DropOpFamilyStmt
@@ -618,6 +641,7 @@ stmt :
| DropTrigStmt
| DropRoleStmt
| DropUserStmt
+ | DropUserMappingStmt
| DropdbStmt
| ExecuteStmt
| ExplainStmt
@@ -2717,6 +2741,313 @@ DropTableSpaceStmt: DROP TABLESPACE name
/*****************************************************************************
*
+ * QUERY:
+ * CREATE FOREIGN DATA WRAPPER name LIBRARY 'library_name' LANGUAGE C
+ *
+ *****************************************************************************/
+
+CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER name LIBRARY Sconst LANGUAGE ColId create_generic_options
+ {
+ CreateFdwStmt *n = makeNode(CreateFdwStmt);
+ n->fdwname = $5;
+ n->library = $7;
+ n->options = $10;
+ $$ = (Node *) n;
+
+ if (pg_strcasecmp($9, "C") != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("language for foreign-data wrapper must be C"),
+ scanner_errposition(@9)));
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * DROP FOREIGN DATA WRAPPER name
+ *
+ ****************************************************************************/
+
+DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior
+ {
+ DropFdwStmt *n = makeNode(DropFdwStmt);
+ n->fdwname = $5;
+ n->missing_ok = false;
+ n->behavior = $6;
+ $$ = (Node *) n;
+ }
+ | DROP FOREIGN DATA_P WRAPPER IF_P EXISTS name opt_drop_behavior
+ {
+ DropFdwStmt *n = makeNode(DropFdwStmt);
+ n->fdwname = $7;
+ n->missing_ok = true;
+ n->behavior = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * ALTER FOREIGN DATA WRAPPER name
+ *
+ ****************************************************************************/
+
+AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER name LIBRARY Sconst alter_generic_options
+ {
+ AlterFdwStmt *n = makeNode(AlterFdwStmt);
+ n->fdwname = $5;
+ n->library = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ | ALTER FOREIGN DATA_P WRAPPER name LIBRARY Sconst
+ {
+ AlterFdwStmt *n = makeNode(AlterFdwStmt);
+ n->fdwname = $5;
+ n->library = $7;
+ $$ = (Node *) n;
+ }
+ | ALTER FOREIGN DATA_P WRAPPER name alter_generic_options
+ {
+ AlterFdwStmt *n = makeNode(AlterFdwStmt);
+ n->fdwname = $5;
+ n->options = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+/* Options definition for CREATE FDW, SERVER and USER MAPPING */
+create_generic_options:
+ OPTIONS '(' generic_option_list ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+generic_option_list: generic_option_elem
+ {
+ $$ = list_make1(makeOptionDefElem(ALTER_OPT_ADD, $1));
+ }
+ | generic_option_list ',' generic_option_elem
+ {
+ $$ = lappend($1, makeOptionDefElem(ALTER_OPT_ADD, $3));
+ }
+ ;
+
+/* Options definition for ALTER FDW, SERVER and USER MAPPING */
+alter_generic_options:
+ OPTIONS '(' alter_generic_option_list ')' { $$ = $3; }
+ ;
+
+alter_generic_option_list:
+ alter_generic_option_elem
+ {
+ $$ = list_make1($1);
+ }
+ | generic_option_elem
+ {
+ $$ = list_make1(makeOptionDefElem(ALTER_OPT_ADD, $1));
+ }
+ | alter_generic_option_list ',' alter_generic_option_elem
+ {
+ $$ = lappend($1, $3);
+ }
+ | alter_generic_option_list ',' generic_option_elem
+ {
+ $$ = lappend($1, makeOptionDefElem(ALTER_OPT_ADD, $3));
+ }
+ ;
+
+alter_generic_option_elem:
+ ADD_P generic_option_elem
+ {
+ $$ = makeOptionDefElem(ALTER_OPT_ADD, $2);
+ }
+ | SET generic_option_elem
+ {
+ $$ = makeOptionDefElem(ALTER_OPT_SET, $2);
+ }
+ | DROP generic_option_name
+ {
+ $$ = makeOptionDefElem(ALTER_OPT_DROP,
+ makeDefElem($2, NULL));
+ }
+ ;
+
+generic_option_elem:
+ generic_option_name generic_option_arg { $$ = makeDefElem($1, $2); }
+ ;
+
+generic_option_name:
+ attr_name { $$ = $1; }
+ ;
+
+generic_option_arg:
+ Sconst { $$ = (Node *)makeString($1); }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * CREATE SERVER name [TYPE] [VERSION] [OPTIONS]
+ *
+ *****************************************************************************/
+
+CreateForeignServerStmt: CREATE SERVER name opt_type opt_foreign_server_version
+ FOREIGN DATA_P WRAPPER name create_generic_options
+ {
+ CreateForeignServerStmt *n = makeNode(CreateForeignServerStmt);
+ n->servername = $3;
+ n->servertype = $4;
+ n->version = $5;
+ n->fdwname = $9;
+ n->options = $10;
+ $$ = (Node *) n;
+ }
+ ;
+
+opt_type:
+ TYPE_P Sconst { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+
+foreign_server_version:
+ VERSION_P Sconst { $$ = $2; }
+ | VERSION_P NULL_P { $$ = NULL; }
+ ;
+
+opt_foreign_server_version:
+ foreign_server_version { $$ = $1; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * DROP SERVER name
+ *
+ ****************************************************************************/
+
+DropForeignServerStmt: DROP SERVER name opt_drop_behavior
+ {
+ DropForeignServerStmt *n = makeNode(DropForeignServerStmt);
+ n->servername = $3;
+ n->missing_ok = false;
+ n->behavior = $4;
+ $$ = (Node *) n;
+ }
+ | DROP SERVER IF_P EXISTS name opt_drop_behavior
+ {
+ DropForeignServerStmt *n = makeNode(DropForeignServerStmt);
+ n->servername = $5;
+ n->missing_ok = true;
+ n->behavior = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * ALTER SERVER name [VERSION] [OPTIONS]
+ *
+ ****************************************************************************/
+
+AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_options
+ {
+ AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt);
+ n->servername = $3;
+ n->version = $4;
+ n->options = $5;
+ n->has_version = true;
+ $$ = (Node *) n;
+ }
+ | ALTER SERVER name foreign_server_version
+ {
+ AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt);
+ n->servername = $3;
+ n->version = $4;
+ n->has_version = true;
+ $$ = (Node *) n;
+ }
+ | ALTER SERVER name alter_generic_options
+ {
+ AlterForeignServerStmt *n = makeNode(AlterForeignServerStmt);
+ n->servername = $3;
+ n->options = $4;
+ $$ = (Node *) n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * CREATE USER MAPPING FOR auth_ident SERVER name [OPTIONS]
+ *
+ *****************************************************************************/
+
+CreateUserMappingStmt: CREATE USER MAPPING FOR auth_ident SERVER name create_generic_options
+ {
+ CreateUserMappingStmt *n = makeNode(CreateUserMappingStmt);
+ n->username = $5;
+ n->servername = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+/* User mapping authorization identifier */
+auth_ident:
+ CURRENT_USER { $$ = "current_user"; }
+ | USER { $$ = "current_user"; }
+ | RoleId { $$ = (strcmp($1, "public") == 0) ? NULL : $1 }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * DROP USER MAPPING FOR auth_ident SERVER name
+ *
+ ****************************************************************************/
+
+DropUserMappingStmt: DROP USER MAPPING FOR auth_ident SERVER name
+ {
+ DropUserMappingStmt *n = makeNode(DropUserMappingStmt);
+ n->username = $5;
+ n->servername = $7;
+ n->missing_ok = false;
+ $$ = (Node *) n;
+ }
+ | DROP USER MAPPING IF_P EXISTS FOR auth_ident SERVER name
+ {
+ DropUserMappingStmt *n = makeNode(DropUserMappingStmt);
+ n->username = $7;
+ n->servername = $9;
+ n->missing_ok = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ * ALTER USER MAPPING FOR auth_ident SERVER name OPTIONS
+ *
+ ****************************************************************************/
+
+AlterUserMappingStmt: ALTER USER MAPPING FOR auth_ident SERVER name alter_generic_options
+ {
+ AlterUserMappingStmt *n = makeNode(AlterUserMappingStmt);
+ n->username = $5;
+ n->servername = $7;
+ n->options = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+/*****************************************************************************
+ *
* QUERIES :
* CREATE TRIGGER ...
* DROP TRIGGER ...
@@ -3912,6 +4243,20 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | FOREIGN DATA_P WRAPPER name_list
+ {
+ PrivTarget *n = makeNode(PrivTarget);
+ n->objtype = ACL_OBJECT_FDW;
+ n->objs = $4;
+ $$ = n;
+ }
+ | FOREIGN SERVER name_list
+ {
+ PrivTarget *n = makeNode(PrivTarget);
+ n->objtype = ACL_OBJECT_FOREIGN_SERVER;
+ n->objs = $3;
+ $$ = n;
+ }
| FUNCTION function_with_argtypes_list
{
PrivTarget *n = makeNode(PrivTarget);
@@ -5123,6 +5468,22 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
n->newowner = $8;
$$ = (Node *)n;
}
+ | ALTER FOREIGN DATA_P WRAPPER name OWNER TO RoleId
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_FDW;
+ n->object = list_make1(makeString($5));
+ n->newowner = $8;
+ $$ = (Node *)n;
+ }
+ | ALTER SERVER name OWNER TO RoleId
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_FOREIGN_SERVER;
+ n->object = list_make1(makeString($3));
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -9556,6 +9917,7 @@ unreserved_keyword:
| INVOKER
| ISOLATION
| KEY
+ | LIBRARY
| LANCOMPILER
| LANGUAGE
| LARGE_P
@@ -9594,6 +9956,7 @@ unreserved_keyword:
| OIDS
| OPERATOR
| OPTION
+ | OPTIONS
| OWNED
| OWNER
| PARSER
@@ -9629,6 +9992,7 @@ unreserved_keyword:
| ROWS
| RULE
| SAVEPOINT
+ | SERVER
| SCHEMA
| SCROLL
| SEARCH
@@ -9681,6 +10045,7 @@ unreserved_keyword:
| WHITESPACE_P
| WITHOUT
| WORK
+ | WRAPPER
| WRITE
| XML_P
| YEAR_P
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 9eb6d082269..bf7b1f6ad2e 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.205 2008/10/27 09:37:47 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.206 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -229,6 +229,7 @@ const ScanKeyword ScanKeywords[] = {
{"least", LEAST, COL_NAME_KEYWORD},
{"left", LEFT, TYPE_FUNC_NAME_KEYWORD},
{"level", LEVEL, UNRESERVED_KEYWORD},
+ {"library", LIBRARY, UNRESERVED_KEYWORD},
{"like", LIKE, TYPE_FUNC_NAME_KEYWORD},
{"limit", LIMIT, RESERVED_KEYWORD},
{"listen", LISTEN, UNRESERVED_KEYWORD},
@@ -281,6 +282,7 @@ const ScanKeyword ScanKeywords[] = {
{"only", ONLY, RESERVED_KEYWORD},
{"operator", OPERATOR, UNRESERVED_KEYWORD},
{"option", OPTION, UNRESERVED_KEYWORD},
+ {"options", OPTIONS, UNRESERVED_KEYWORD},
{"or", OR, RESERVED_KEYWORD},
{"order", ORDER, RESERVED_KEYWORD},
{"out", OUT_P, COL_NAME_KEYWORD},
@@ -339,6 +341,7 @@ const ScanKeyword ScanKeywords[] = {
{"select", SELECT, RESERVED_KEYWORD},
{"sequence", SEQUENCE, UNRESERVED_KEYWORD},
{"serializable", SERIALIZABLE, UNRESERVED_KEYWORD},
+ {"server", SERVER, UNRESERVED_KEYWORD},
{"session", SESSION, UNRESERVED_KEYWORD},
{"session_user", SESSION_USER, RESERVED_KEYWORD},
{"set", SET, UNRESERVED_KEYWORD},
@@ -411,6 +414,7 @@ const ScanKeyword ScanKeywords[] = {
{"with", WITH, RESERVED_KEYWORD},
{"without", WITHOUT, UNRESERVED_KEYWORD},
{"work", WORK, UNRESERVED_KEYWORD},
+ {"wrapper", WRAPPER, UNRESERVED_KEYWORD},
{"write", WRITE, UNRESERVED_KEYWORD},
{"xml", XML_P, UNRESERVED_KEYWORD},
{"xmlattributes", XMLATTRIBUTES, COL_NAME_KEYWORD},
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f222b9cd75e..555e8730dd9 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.302 2008/12/04 17:51:26 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.303 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -203,6 +203,15 @@ check_xact_readonly(Node *parsetree)
case T_ReassignOwnedStmt:
case T_AlterTSDictionaryStmt:
case T_AlterTSConfigurationStmt:
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_DropFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_DropForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("transaction is read-only")));
@@ -452,6 +461,42 @@ ProcessUtility(Node *parsetree,
DropTableSpace((DropTableSpaceStmt *) parsetree);
break;
+ case T_CreateFdwStmt:
+ CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
+ break;
+
+ case T_AlterFdwStmt:
+ AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
+ break;
+
+ case T_DropFdwStmt:
+ RemoveForeignDataWrapper((DropFdwStmt *) parsetree);
+ break;
+
+ case T_CreateForeignServerStmt:
+ CreateForeignServer((CreateForeignServerStmt *) parsetree);
+ break;
+
+ case T_AlterForeignServerStmt:
+ AlterForeignServer((AlterForeignServerStmt *) parsetree);
+ break;
+
+ case T_DropForeignServerStmt:
+ RemoveForeignServer((DropForeignServerStmt *) parsetree);
+ break;
+
+ case T_CreateUserMappingStmt:
+ CreateUserMapping((CreateUserMappingStmt *) parsetree);
+ break;
+
+ case T_AlterUserMappingStmt:
+ AlterUserMapping((AlterUserMappingStmt *) parsetree);
+ break;
+
+ case T_DropUserMappingStmt:
+ RemoveUserMapping((DropUserMappingStmt *) parsetree);
+ break;
+
case T_DropStmt:
{
DropStmt *stmt = (DropStmt *) parsetree;
@@ -1310,6 +1355,42 @@ CreateCommandTag(Node *parsetree)
tag = "DROP TABLESPACE";
break;
+ case T_CreateFdwStmt:
+ tag = "CREATE FOREIGN DATA WRAPPER";
+ break;
+
+ case T_AlterFdwStmt:
+ tag = "ALTER FOREIGN DATA WRAPPER";
+ break;
+
+ case T_DropFdwStmt:
+ tag = "DROP FOREIGN DATA WRAPPER";
+ break;
+
+ case T_CreateForeignServerStmt:
+ tag = "CREATE SERVER";
+ break;
+
+ case T_AlterForeignServerStmt:
+ tag = "ALTER SERVER";
+ break;
+
+ case T_DropForeignServerStmt:
+ tag = "DROP SERVER";
+ break;
+
+ case T_CreateUserMappingStmt:
+ tag = "CREATE USER MAPPING";
+ break;
+
+ case T_AlterUserMappingStmt:
+ tag = "ALTER USER MAPPING";
+ break;
+
+ case T_DropUserMappingStmt:
+ tag = "DROP USER MAPPING";
+ break;
+
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
@@ -1523,6 +1604,12 @@ CreateCommandTag(Node *parsetree)
case OBJECT_TSDICTIONARY:
tag = "ALTER TEXT SEARCH DICTIONARY";
break;
+ case OBJECT_FDW:
+ tag = "ALTER FOREIGN DATA WRAPPER";
+ break;
+ case OBJECT_FOREIGN_SERVER:
+ tag = "ALTER SERVER";
+ break;
default:
tag = "???";
break;
@@ -2037,6 +2124,18 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_DropFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_DropForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_DropStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 963112ef74a..4641aecd59f 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.143 2008/12/15 18:09:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.144 2008/12/19 16:25:17 petere Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,6 +22,7 @@
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/tablespace.h"
+#include "foreign/foreign.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -577,6 +578,14 @@ acldefault(GrantObjectType objtype, Oid ownerId)
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_TABLESPACE;
break;
+ case ACL_OBJECT_FDW:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_FDW;
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1824,6 +1833,156 @@ convert_database_priv_string(text *priv_type_text)
/*
+ * has_foreign_data_wrapper_privilege variants
+ * These are all named "has_foreign_data_wrapper_privilege" at the SQL level.
+ * They take various combinations of foreign-data wrapper name,
+ * fdw OID, user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not. The variants that take an OID return
+ * NULL if the OID doesn't exist.
+ */
+
+/*
+ * has_foreign_data_wrapper_privilege
+ * Check user privileges on a foreign-data wrapper.
+ */
+static Datum
+has_foreign_data_wrapper_privilege(Oid roleid, Oid fdwid, text *priv_type_text)
+{
+ AclResult aclresult;
+ AclMode mode = ACL_NO_RIGHTS;
+ char *priv_type = text_to_cstring(priv_type_text);
+
+ if (pg_strcasecmp(priv_type, "USAGE") == 0)
+ mode = ACL_USAGE;
+ else if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
+ mode = ACL_GRANT_OPTION_FOR(ACL_USAGE);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized privilege type: \"%s\"", priv_type)));
+
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_foreign_data_wrapper_privilege_name_name
+ * Check user privileges on a foreign-data wrapper given
+ * name username, text fdwname, and text priv name.
+ */
+Datum
+has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ char *fdwname = text_to_cstring(PG_GETARG_TEXT_P(1));
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ return has_foreign_data_wrapper_privilege(get_roleid_checked(NameStr(*username)),
+ GetForeignDataWrapperOidByName(fdwname, false),
+ priv_type_text);
+}
+
+/*
+ * has_foreign_data_wrapper_privilege_name
+ * Check user privileges on a foreign-data wrapper given
+ * text fdwname and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS)
+{
+ char *fdwname = text_to_cstring(PG_GETARG_TEXT_P(0));
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+
+ return has_foreign_data_wrapper_privilege(GetUserId(),
+ GetForeignDataWrapperOidByName(fdwname, false),
+ priv_type_text);
+}
+
+/*
+ * has_foreign_data_wrapper_privilege_name_id
+ * Check user privileges on a foreign-data wrapper given
+ * name usename, foreign-data wrapper oid, and text priv name.
+ */
+Datum
+has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid fdwid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ if (!SearchSysCacheExists(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ return has_foreign_data_wrapper_privilege(get_roleid_checked(NameStr(*username)),
+ fdwid, priv_type_text);
+}
+
+/*
+ * has_foreign_data_wrapper_privilege_id
+ * Check user privileges on a foreign-data wrapper given
+ * foreign-data wrapper oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid fdwid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+
+ if (!SearchSysCacheExists(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ return has_foreign_data_wrapper_privilege(GetUserId(), fdwid,
+ priv_type_text);
+}
+
+/*
+ * has_foreign_data_wrapper_privilege_id_name
+ * Check user privileges on a foreign-data wrapper given
+ * roleid, text fdwname, and text priv name.
+ */
+Datum
+has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ char *fdwname = text_to_cstring(PG_GETARG_TEXT_P(1));
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ return has_foreign_data_wrapper_privilege(roleid,
+ GetForeignDataWrapperOidByName(fdwname, false),
+ priv_type_text);
+}
+
+/*
+ * has_foreign_data_wrapper_privilege_id_id
+ * Check user privileges on a foreign-data wrapper given
+ * roleid, fdw oid, and text priv name.
+ */
+Datum
+has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid fdwid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ if (!SearchSysCacheExists(FOREIGNDATAWRAPPEROID,
+ ObjectIdGetDatum(fdwid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ return has_foreign_data_wrapper_privilege(roleid, fdwid, priv_type_text);
+}
+
+
+/*
* has_function_privilege variants
* These are all named "has_function_privilege" at the SQL level.
* They take various combinations of function name, function OID,
@@ -2467,6 +2626,154 @@ convert_schema_priv_string(text *priv_type_text)
}
/*
+ * has_server_privilege variants
+ * These are all named "has_server_privilege" at the SQL level.
+ * They take various combinations of foreign server name,
+ * server OID, user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not.
+ */
+
+/*
+ * has_server_privilege
+ * Check user privileges on a foreign server.
+ */
+static Datum
+has_server_privilege(Oid roleid, Oid serverid, text *priv_type_text)
+{
+ AclResult aclresult;
+ AclMode mode = ACL_NO_RIGHTS;
+ char *priv_type = text_to_cstring(priv_type_text);
+
+ if (pg_strcasecmp(priv_type, "USAGE") == 0)
+ mode = ACL_USAGE;
+ else if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
+ mode = ACL_GRANT_OPTION_FOR(ACL_USAGE);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized privilege type: \"%s\"", priv_type)));
+
+ aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_server_privilege_name_name
+ * Check user privileges on a foreign server given
+ * name username, text servername, and text priv name.
+ */
+Datum
+has_server_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ char *servername = text_to_cstring(PG_GETARG_TEXT_P(1));
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ return has_server_privilege(get_roleid_checked(NameStr(*username)),
+ GetForeignServerOidByName(servername, false),
+ priv_type_text);
+}
+
+/*
+ * has_server_privilege_name
+ * Check user privileges on a foreign server given
+ * text servername and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_server_privilege_name(PG_FUNCTION_ARGS)
+{
+ char *servername = text_to_cstring(PG_GETARG_TEXT_P(0));
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+
+ return has_server_privilege(GetUserId(),
+ GetForeignServerOidByName(servername, false),
+ priv_type_text);
+}
+
+/*
+ * has_server_privilege_name_id
+ * Check user privileges on a foreign server given
+ * name usename, foreign server oid, and text priv name.
+ */
+Datum
+has_server_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid serverid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ if (!SearchSysCacheExists(FOREIGNSERVEROID,
+ ObjectIdGetDatum(serverid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ return has_server_privilege(get_roleid_checked(NameStr(*username)), serverid,
+ priv_type_text);
+}
+
+/*
+ * has_server_privilege_id
+ * Check user privileges on a foreign server given
+ * server oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_server_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid serverid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+
+ if (!SearchSysCacheExists(FOREIGNSERVEROID,
+ ObjectIdGetDatum(serverid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ return has_server_privilege(GetUserId(), serverid, priv_type_text);
+}
+
+/*
+ * has_server_privilege_id_name
+ * Check user privileges on a foreign server given
+ * roleid, text servername, and text priv name.
+ */
+Datum
+has_server_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ char *servername = text_to_cstring(PG_GETARG_TEXT_P(1));
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ return has_server_privilege(roleid,
+ GetForeignServerOidByName(servername, false),
+ priv_type_text);
+}
+
+/*
+ * has_server_privilege_id_id
+ * Check user privileges on a foreign server given
+ * roleid, server oid, and text priv name.
+ */
+Datum
+has_server_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid serverid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+
+ if (!SearchSysCacheExists(FOREIGNSERVEROID,
+ ObjectIdGetDatum(serverid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ return has_server_privilege(roleid, serverid, priv_type_text);
+}
+
+
+/*
* has_tablespace_privilege variants
* These are all named "has_tablespace_privilege" at the SQL level.
* They take various combinations of tablespace name, tablespace OID,
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 1ccf7b8d69b..35022456879 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.117 2008/06/19 00:46:05 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.118 2008/12/19 16:25:17 petere Exp $
*
* NOTES
* These routines allow the parser/planner/executor to perform
@@ -32,6 +32,8 @@
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_enum.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
@@ -46,6 +48,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
#include "utils/rel.h"
#include "utils/syscache.h"
@@ -365,6 +368,54 @@ static const struct cachedesc cacheinfo[] = {
},
256
},
+ {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPERNAME */
+ ForeignDataWrapperNameIndexId,
+ 0,
+ 1,
+ {
+ Anum_pg_foreign_data_wrapper_fdwname,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPEROID */
+ ForeignDataWrapperOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
+ {ForeignServerRelationId, /* FOREIGNSERVERNAME */
+ ForeignServerNameIndexId,
+ 0,
+ 1,
+ {
+ Anum_pg_foreign_server_srvname,
+ 0,
+ 0,
+ 0
+ },
+ 32
+ },
+ {ForeignServerRelationId, /* FOREIGNSERVEROID */
+ ForeignServerOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 32
+ },
{IndexRelationId, /* INDEXRELID */
IndexRelidIndexId,
Anum_pg_index_indrelid,
@@ -676,6 +727,30 @@ static const struct cachedesc cacheinfo[] = {
0
},
1024
+ },
+ {UserMappingRelationId, /* USERMAPPINGOID */
+ UserMappingOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 128
+ },
+ {UserMappingRelationId, /* USERMAPPINGUSERSERVER */
+ UserMappingUserServerIndexId,
+ 0,
+ 2,
+ {
+ Anum_pg_user_mapping_umuser,
+ Anum_pg_user_mapping_umserver,
+ 0,
+ 0
+ },
+ 128
}
};