diff options
Diffstat (limited to 'src/backend')
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 } }; |