*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 21,26 ****
--- 21,27 ----
  #include <arpa/inet.h>
  
  #include "access/heapam.h"
+ #include "access/sysattr.h"
  #include "access/xact.h"
  #include "catalog/namespace.h"
  #include "catalog/pg_type.h"
***************
*** 726,733 **** DoCopy(const CopyStmt *stmt, const char *queryString)
  	bool		force_quote_all = false;
  	bool		format_specified = false;
  	AclMode		required_access = (is_from ? ACL_INSERT : ACL_SELECT);
- 	AclMode		relPerms;
- 	AclMode		remainingPerms;
  	ListCell   *option;
  	TupleDesc	tupDesc;
  	int			num_phys_attrs;
--- 727,732 ----
***************
*** 988,993 **** DoCopy(const CopyStmt *stmt, const char *queryString)
--- 987,996 ----
  
  	if (stmt->relation)
  	{
+ 		RangeTblEntry  *rte;
+ 		List		   *attnums;
+ 		ListCell	   *cur;
+ 
  		Assert(!stmt->query);
  		cstate->queryDesc = NULL;
  
***************
*** 997,1025 **** DoCopy(const CopyStmt *stmt, const char *queryString)
  
  		tupDesc = RelationGetDescr(cstate->rel);
  
! 		/* Check relation permissions. */
! 		relPerms = pg_class_aclmask(RelationGetRelid(cstate->rel), GetUserId(),
! 									required_access, ACLMASK_ALL);
! 		remainingPerms = required_access & ~relPerms;
! 		if (remainingPerms != 0)
! 		{
! 			/* We don't have table permissions, check per-column permissions */
! 			List	   *attnums;
! 			ListCell   *cur;
  
! 			attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
! 			foreach(cur, attnums)
! 			{
! 				int			attnum = lfirst_int(cur);
  
! 				if (pg_attribute_aclcheck(RelationGetRelid(cstate->rel),
! 										  attnum,
! 										  GetUserId(),
! 										  remainingPerms) != ACLCHECK_OK)
! 					aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 								   RelationGetRelationName(cstate->rel));
! 			}
  		}
  
  		/* check read-only transaction */
  		if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp)
--- 1000,1026 ----
  
  		tupDesc = RelationGetDescr(cstate->rel);
  
! 		/*
! 		 * check relation permissions.
! 		 * we built an RTE with the relation and columns to be accessed
! 		 * to check for necessary privileges in the common way.
! 		 */
! 		rte = makeNode(RangeTblEntry);
! 		rte->rtekind = RTE_RELATION;
! 		rte->relid = RelationGetRelid(cstate->rel);
! 		rte->requiredPerms = required_access;
  
! 		attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
! 		foreach (cur, attnums)
! 		{
! 			int		attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
  
! 			if (is_from)
! 				rte->modifiedCols = bms_add_member(rte->modifiedCols, attno);
! 			else
! 				rte->selectedCols = bms_add_member(rte->selectedCols, attno);
  		}
+ 		ExecCheckRTPerms(list_make1(rte), true);
  
  		/* check read-only transaction */
  		if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp)
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 75,82 **** static void ExecutePlan(EState *estate, PlanState *planstate,
  			long numberTuples,
  			ScanDirection direction,
  			DestReceiver *dest);
! static void ExecCheckRTPerms(List *rangeTable);
! static void ExecCheckRTEPerms(RangeTblEntry *rte);
  static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
  static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
  				  Plan *planTree);
--- 75,81 ----
  			long numberTuples,
  			ScanDirection direction,
  			DestReceiver *dest);
! static bool ExecCheckRTEPerms(RangeTblEntry *rte, bool ereport_on_violation);
  static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
  static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
  				  Plan *planTree);
***************
*** 410,435 **** ExecutorRewind(QueryDesc *queryDesc)
   * ExecCheckRTPerms
   *		Check access permissions for all relations listed in a range table.
   */
! static void
! ExecCheckRTPerms(List *rangeTable)
  {
  	ListCell   *l;
  
  	foreach(l, rangeTable)
  	{
! 		ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
  	}
  
  	if (ExecutorCheckPerms_hook)
! 		(*ExecutorCheckPerms_hook)(rangeTable);
  }
  
  /*
   * ExecCheckRTEPerms
   *		Check access permissions for a single RTE.
   */
! static void
! ExecCheckRTEPerms(RangeTblEntry *rte)
  {
  	AclMode		requiredPerms;
  	AclMode		relPerms;
--- 409,440 ----
   * ExecCheckRTPerms
   *		Check access permissions for all relations listed in a range table.
   */
! bool
! ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation)
  {
  	ListCell   *l;
+ 	bool		rc = true;
  
  	foreach(l, rangeTable)
  	{
! 		rc = ExecCheckRTEPerms((RangeTblEntry *) lfirst(l),
! 							   ereport_on_violation);
! 		if (!rc)
! 			return false;
  	}
  
  	if (ExecutorCheckPerms_hook)
! 		rc = (*ExecutorCheckPerms_hook)(rangeTable,
! 										ereport_on_violation);
! 	return rc;
  }
  
  /*
   * ExecCheckRTEPerms
   *		Check access permissions for a single RTE.
   */
! static bool
! ExecCheckRTEPerms(RangeTblEntry *rte, bool ereport_on_violation)
  {
  	AclMode		requiredPerms;
  	AclMode		relPerms;
***************
*** 445,458 **** ExecCheckRTEPerms(RangeTblEntry *rte)
  	 * Join, subquery, and special RTEs need no checks.
  	 */
  	if (rte->rtekind != RTE_RELATION)
! 		return;
  
  	/*
  	 * No work if requiredPerms is empty.
  	 */
  	requiredPerms = rte->requiredPerms;
  	if (requiredPerms == 0)
! 		return;
  
  	relOid = rte->relid;
  
--- 450,463 ----
  	 * Join, subquery, and special RTEs need no checks.
  	 */
  	if (rte->rtekind != RTE_RELATION)
! 		return true;
  
  	/*
  	 * No work if requiredPerms is empty.
  	 */
  	requiredPerms = rte->requiredPerms;
  	if (requiredPerms == 0)
! 		return true;
  
  	relOid = rte->relid;
  
***************
*** 480,487 **** ExecCheckRTEPerms(RangeTblEntry *rte)
  		 * we can fail straight away.
  		 */
  		if (remainingPerms & ~(ACL_SELECT | ACL_INSERT | ACL_UPDATE))
! 			aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 						   get_rel_name(relOid));
  
  		/*
  		 * Check to see if we have the needed privileges at column level.
--- 485,496 ----
  		 * we can fail straight away.
  		 */
  		if (remainingPerms & ~(ACL_SELECT | ACL_INSERT | ACL_UPDATE))
! 		{
! 			if (ereport_on_violation)
! 				aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 							   get_rel_name(relOid));
! 			return false;
! 		}
  
  		/*
  		 * Check to see if we have the needed privileges at column level.
***************
*** 501,508 **** ExecCheckRTEPerms(RangeTblEntry *rte)
  			{
  				if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT,
  											  ACLMASK_ANY) != ACLCHECK_OK)
! 					aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 								   get_rel_name(relOid));
  			}
  
  			tmpset = bms_copy(rte->selectedCols);
--- 510,521 ----
  			{
  				if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT,
  											  ACLMASK_ANY) != ACLCHECK_OK)
! 				{
! 					if (ereport_on_violation)
! 						aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 									   get_rel_name(relOid));
! 					return false;
! 				}
  			}
  
  			tmpset = bms_copy(rte->selectedCols);
***************
*** 515,529 **** ExecCheckRTEPerms(RangeTblEntry *rte)
  					/* Whole-row reference, must have priv on all cols */
  					if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT,
  												  ACLMASK_ALL) != ACLCHECK_OK)
! 						aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 									   get_rel_name(relOid));
  				}
  				else
  				{
! 					if (pg_attribute_aclcheck(relOid, col, userid, ACL_SELECT)
! 						!= ACLCHECK_OK)
! 						aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 									   get_rel_name(relOid));
  				}
  			}
  			bms_free(tmpset);
--- 528,550 ----
  					/* Whole-row reference, must have priv on all cols */
  					if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT,
  												  ACLMASK_ALL) != ACLCHECK_OK)
! 					{
! 						if (ereport_on_violation)
! 							aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 										   get_rel_name(relOid));
! 						return false;
! 					}
  				}
  				else
  				{
! 					if (pg_attribute_aclcheck(relOid, col, userid,
! 											  ACL_SELECT) != ACLCHECK_OK)
! 					{
! 						if (ereport_on_violation)
! 							aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 										   get_rel_name(relOid));
! 						return false;
! 					}
  				}
  			}
  			bms_free(tmpset);
***************
*** 546,553 **** ExecCheckRTEPerms(RangeTblEntry *rte)
  			{
  				if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms,
  											  ACLMASK_ANY) != ACLCHECK_OK)
! 					aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 								   get_rel_name(relOid));
  			}
  
  			tmpset = bms_copy(rte->modifiedCols);
--- 567,578 ----
  			{
  				if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms,
  											  ACLMASK_ANY) != ACLCHECK_OK)
! 				{
! 					if (ereport_on_violation)
! 						aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 									   get_rel_name(relOid));
! 					return false;
! 				}
  			}
  
  			tmpset = bms_copy(rte->modifiedCols);
***************
*** 562,576 **** ExecCheckRTEPerms(RangeTblEntry *rte)
  				}
  				else
  				{
! 					if (pg_attribute_aclcheck(relOid, col, userid, remainingPerms)
! 						!= ACLCHECK_OK)
! 						aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 									   get_rel_name(relOid));
  				}
  			}
  			bms_free(tmpset);
  		}
  	}
  }
  
  /*
--- 587,606 ----
  				}
  				else
  				{
! 					if (pg_attribute_aclcheck(relOid, col, userid,
! 											  remainingPerms) != ACLCHECK_OK)
! 					{
! 						if (ereport_on_violation)
! 							aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
! 										   get_rel_name(relOid));
! 						return false;
! 					}
  				}
  			}
  			bms_free(tmpset);
  		}
  	}
+ 	return true;
  }
  
  /*
***************
*** 636,642 **** InitPlan(QueryDesc *queryDesc, int eflags)
  	/*
  	 * Do permissions checks
  	 */
! 	ExecCheckRTPerms(rangeTable);
  
  	/*
  	 * initialize the node's execution state
--- 666,672 ----
  	/*
  	 * Do permissions checks
  	 */
! 	ExecCheckRTPerms(rangeTable, true);
  
  	/*
  	 * initialize the node's execution state
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
***************
*** 31,36 ****
--- 31,37 ----
  #include "postgres.h"
  
  #include "access/xact.h"
+ #include "access/sysattr.h"
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_operator.h"
  #include "catalog/pg_type.h"
***************
*** 2624,2629 **** RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
--- 2625,2632 ----
  	char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
  	char		pkattname[MAX_QUOTED_NAME_LEN + 3];
  	char		fkattname[MAX_QUOTED_NAME_LEN + 3];
+ 	RangeTblEntry  *pkrte;
+ 	RangeTblEntry  *fkrte;
  	const char *sep;
  	int			i;
  	int			old_work_mem;
***************
*** 2632,2649 **** RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
  	SPIPlanPtr	qplan;
  
  	/*
  	 * Check to make sure current user has enough permissions to do the test
  	 * query.  (If not, caller can fall back to the trigger method, which
  	 * works because it changes user IDs on the fly.)
  	 *
  	 * XXX are there any other show-stopper conditions to check?
  	 */
! 	if (pg_class_aclcheck(RelationGetRelid(fk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
! 		return false;
! 	if (pg_class_aclcheck(RelationGetRelid(pk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
! 		return false;
  
! 	ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
  
  	/*----------
  	 * The query string built is:
--- 2635,2674 ----
  	SPIPlanPtr	qplan;
  
  	/*
+ 	 * Set up RI_ConstraintInfo
+ 	 */
+ 	ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
+ 
+ 	/*
  	 * Check to make sure current user has enough permissions to do the test
  	 * query.  (If not, caller can fall back to the trigger method, which
  	 * works because it changes user IDs on the fly.)
  	 *
  	 * XXX are there any other show-stopper conditions to check?
  	 */
! 	pkrte = makeNode(RangeTblEntry);
! 	pkrte->rtekind = RTE_RELATION;
! 	pkrte->relid = RelationGetRelid(pk_rel);
! 	pkrte->requiredPerms = ACL_SELECT;
  
! 	fkrte = makeNode(RangeTblEntry);
! 	fkrte->rtekind = RTE_RELATION;
! 	fkrte->relid = RelationGetRelid(fk_rel);
! 	fkrte->requiredPerms = ACL_SELECT;
! 
! 	for (i = 0; i < riinfo.nkeys; i++)
! 	{
! 		int		attno;
! 
! 		attno = riinfo.pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
! 		pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno);
! 
! 		attno = riinfo.fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
! 		fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno);
! 	}
! 
! 	if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false))
! 		return false;
  
  	/*----------
  	 * The query string built is:
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
***************
*** 75,81 **** typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc);
  extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook;
  
  /* Hook for plugins to get control in ExecCheckRTPerms() */
! typedef void (*ExecutorCheckPerms_hook_type) (List *);
  extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook;
  
  
--- 75,81 ----
  extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook;
  
  /* Hook for plugins to get control in ExecCheckRTPerms() */
! typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool);
  extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook;
  
  
***************
*** 161,166 **** extern void standard_ExecutorRun(QueryDesc *queryDesc,
--- 161,167 ----
  extern void ExecutorEnd(QueryDesc *queryDesc);
  extern void standard_ExecutorEnd(QueryDesc *queryDesc);
  extern void ExecutorRewind(QueryDesc *queryDesc);
+ extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation);
  extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
  				  Relation resultRelationDesc,
  				  Index resultRelationIndex,
