diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
new file mode 100644
index a331f3e..d64f165
*** a/src/backend/commands/matview.c
--- b/src/backend/commands/matview.c
*************** refresh_matview_datafill(DestReceiver *d
*** 303,309 ****
  
  	/* Lock and rewrite, using a copy to preserve the original query. */
  	copied_query = copyObject(query);
! 	AcquireRewriteLocks(copied_query, false);
  	rewritten = QueryRewrite(copied_query);
  
  	/* SELECT should never rewrite to more or less than one SELECT query */
--- 303,309 ----
  
  	/* Lock and rewrite, using a copy to preserve the original query. */
  	copied_query = copyObject(query);
! 	AcquireRewriteLocks(copied_query, true, false);
  	rewritten = QueryRewrite(copied_query);
  
  	/* SELECT should never rewrite to more or less than one SELECT query */
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
new file mode 100644
index 0b13645..a3ede56
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
*************** typedef struct rewrite_event
*** 37,43 ****
  	CmdType		event;			/* type of rule being fired */
  } rewrite_event;
  
! static bool acquireLocksOnSubLinks(Node *node, void *context);
  static Query *rewriteRuleAction(Query *parsetree,
  				  Query *rule_action,
  				  Node *rule_qual,
--- 37,48 ----
  	CmdType		event;			/* type of rule being fired */
  } rewrite_event;
  
! typedef struct
! {
! 	bool		for_execute;
! } acquireLocksOnSubLinks_context;
! 
! static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context);
  static Query *rewriteRuleAction(Query *parsetree,
  				  Query *rule_action,
  				  Node *rule_qual,
*************** static Bitmapset *adjust_view_column_set
*** 71,79 ****
   *	  These locks will ensure that the relation schemas don't change under us
   *	  while we are rewriting and planning the query.
   *
   * forUpdatePushedDown indicates that a pushed-down FOR [KEY] UPDATE/SHARE applies
   * to the current subquery, requiring all rels to be opened with RowShareLock.
!  * This should always be false at the start of the recursion.
   *
   * A secondary purpose of this routine is to fix up JOIN RTE references to
   * dropped columns (see details below).  Because the RTEs are modified in
--- 76,93 ----
   *	  These locks will ensure that the relation schemas don't change under us
   *	  while we are rewriting and planning the query.
   *
+  * forExecute indicates that the query is about to be executed and that a
+  * RowExclusiveLock should be acquired on the query's resultRelation, and a
+  * RowShareLock should be acquired on any relation accessed FOR [KEY] UPDATE/SHARE.
+  * If forExecute is false, an AccessShareLock is acquired on all relations.
+  * The rewriter and most other callers generally assume that the query is
+  * about to be executed. The query deparsing code in ruleutils.c assumes that
+  * it is not.
+  *
   * forUpdatePushedDown indicates that a pushed-down FOR [KEY] UPDATE/SHARE applies
   * to the current subquery, requiring all rels to be opened with RowShareLock.
!  * This should always be false at the start of the recursion. This flag is
!  * ignored if forExecute is false.
   *
   * A secondary purpose of this routine is to fix up JOIN RTE references to
   * dropped columns (see details below).  Because the RTEs are modified in
*************** static Bitmapset *adjust_view_column_set
*** 101,110 ****
   * construction of a nested join was O(N^2) in the nesting depth.)
   */
  void
! AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
  {
  	ListCell   *l;
  	int			rt_index;
  
  	/*
  	 * First, process RTEs of the current query level.
--- 115,129 ----
   * construction of a nested join was O(N^2) in the nesting depth.)
   */
  void
! AcquireRewriteLocks(Query *parsetree,
! 					bool forExecute,
! 					bool forUpdatePushedDown)
  {
  	ListCell   *l;
  	int			rt_index;
+ 	acquireLocksOnSubLinks_context context;
+ 
+ 	context.for_execute = forExecute;
  
  	/*
  	 * First, process RTEs of the current query level.
*************** AcquireRewriteLocks(Query *parsetree, bo
*** 130,135 ****
--- 149,158 ----
  				 * release it until end of transaction. This protects the
  				 * rewriter and planner against schema changes mid-query.
  				 *
+ 				 * If we are not actually about the execute the query, then we
+ 				 * always just grab an AccessShareLock. Otherwise the type of
+ 				 * lock required varies:
+ 				 *
  				 * If the relation is the query's result relation, then we
  				 * need RowExclusiveLock.  Otherwise, check to see if the
  				 * relation is accessed FOR [KEY] UPDATE/SHARE or not.	We
*************** AcquireRewriteLocks(Query *parsetree, bo
*** 137,143 ****
  				 * would be trying to upgrade the lock, leading to possible
  				 * deadlocks.
  				 */
! 				if (rt_index == parsetree->resultRelation)
  					lockmode = RowExclusiveLock;
  				else if (forUpdatePushedDown ||
  						 get_parse_rowmark(parsetree, rt_index) != NULL)
--- 160,168 ----
  				 * would be trying to upgrade the lock, leading to possible
  				 * deadlocks.
  				 */
! 				if (!forExecute)
! 					lockmode = AccessShareLock;
! 				else if (rt_index == parsetree->resultRelation)
  					lockmode = RowExclusiveLock;
  				else if (forUpdatePushedDown ||
  						 get_parse_rowmark(parsetree, rt_index) != NULL)
*************** AcquireRewriteLocks(Query *parsetree, bo
*** 225,230 ****
--- 250,256 ----
  				 * recurse to process the represented subquery.
  				 */
  				AcquireRewriteLocks(rte->subquery,
+ 									forExecute,
  									(forUpdatePushedDown ||
  							get_parse_rowmark(parsetree, rt_index) != NULL));
  				break;
*************** AcquireRewriteLocks(Query *parsetree, bo
*** 240,246 ****
  	{
  		CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
  
! 		AcquireRewriteLocks((Query *) cte->ctequery, false);
  	}
  
  	/*
--- 266,272 ----
  	{
  		CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
  
! 		AcquireRewriteLocks((Query *) cte->ctequery, forExecute, false);
  	}
  
  	/*
*************** AcquireRewriteLocks(Query *parsetree, bo
*** 248,254 ****
  	 * the rtable and cteList.
  	 */
  	if (parsetree->hasSubLinks)
! 		query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
  						  QTW_IGNORE_RC_SUBQUERIES);
  }
  
--- 274,281 ----
  	 * the rtable and cteList.
  	 */
  	if (parsetree->hasSubLinks)
! 		query_tree_walker(parsetree, acquireLocksOnSubLinks,
! 						  (void *) &context,
  						  QTW_IGNORE_RC_SUBQUERIES);
  }
  
*************** AcquireRewriteLocks(Query *parsetree, bo
*** 256,262 ****
   * Walker to find sublink subqueries for AcquireRewriteLocks
   */
  static bool
! acquireLocksOnSubLinks(Node *node, void *context)
  {
  	if (node == NULL)
  		return false;
--- 283,289 ----
   * Walker to find sublink subqueries for AcquireRewriteLocks
   */
  static bool
! acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
  {
  	if (node == NULL)
  		return false;
*************** acquireLocksOnSubLinks(Node *node, void
*** 265,271 ****
  		SubLink    *sub = (SubLink *) node;
  
  		/* Do what we came for */
! 		AcquireRewriteLocks((Query *) sub->subselect, false);
  		/* Fall through to process lefthand args of SubLink */
  	}
  
--- 292,298 ----
  		SubLink    *sub = (SubLink *) node;
  
  		/* Do what we came for */
! 		AcquireRewriteLocks((Query *) sub->subselect, context->for_execute, false);
  		/* Fall through to process lefthand args of SubLink */
  	}
  
*************** rewriteRuleAction(Query *parsetree,
*** 307,312 ****
--- 334,342 ----
  	int			rt_length;
  	Query	   *sub_action;
  	Query	  **sub_action_ptr;
+ 	acquireLocksOnSubLinks_context context;
+ 
+ 	context.for_execute = true;
  
  	/*
  	 * Make modifiable copies of rule action and qual (what we're passed are
*************** rewriteRuleAction(Query *parsetree,
*** 318,325 ****
  	/*
  	 * Acquire necessary locks and fix any deleted JOIN RTE entries.
  	 */
! 	AcquireRewriteLocks(rule_action, false);
! 	(void) acquireLocksOnSubLinks(rule_qual, NULL);
  
  	current_varno = rt_index;
  	rt_length = list_length(parsetree->rtable);
--- 348,355 ----
  	/*
  	 * Acquire necessary locks and fix any deleted JOIN RTE entries.
  	 */
! 	AcquireRewriteLocks(rule_action, true, false);
! 	(void) acquireLocksOnSubLinks(rule_qual, &context);
  
  	current_varno = rt_index;
  	rt_length = list_length(parsetree->rtable);
*************** ApplyRetrieveRule(Query *parsetree,
*** 1389,1395 ****
  	 */
  	rule_action = copyObject(linitial(rule->actions));
  
! 	AcquireRewriteLocks(rule_action, forUpdatePushedDown);
  
  	/*
  	 * Recursively expand any view references inside the view.
--- 1419,1425 ----
  	 */
  	rule_action = copyObject(linitial(rule->actions));
  
! 	AcquireRewriteLocks(rule_action, true, forUpdatePushedDown);
  
  	/*
  	 * Recursively expand any view references inside the view.
*************** CopyAndAddInvertedQual(Query *parsetree,
*** 1713,1725 ****
  	/* Don't scribble on the passed qual (it's in the relcache!) */
  	Node	   *new_qual = (Node *) copyObject(rule_qual);
  
  	/*
  	 * In case there are subqueries in the qual, acquire necessary locks and
  	 * fix any deleted JOIN RTE entries.  (This is somewhat redundant with
  	 * rewriteRuleAction, but not entirely ... consider restructuring so that
  	 * we only need to process the qual this way once.)
  	 */
! 	(void) acquireLocksOnSubLinks(new_qual, NULL);
  
  	/* Fix references to OLD */
  	ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
--- 1743,1759 ----
  	/* Don't scribble on the passed qual (it's in the relcache!) */
  	Node	   *new_qual = (Node *) copyObject(rule_qual);
  
+ 	acquireLocksOnSubLinks_context context;
+ 
+ 	context.for_execute = true;
+ 
  	/*
  	 * In case there are subqueries in the qual, acquire necessary locks and
  	 * fix any deleted JOIN RTE entries.  (This is somewhat redundant with
  	 * rewriteRuleAction, but not entirely ... consider restructuring so that
  	 * we only need to process the qual this way once.)
  	 */
! 	(void) acquireLocksOnSubLinks(new_qual, &context);
  
  	/* Fix references to OLD */
  	ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
new file mode 100644
index add5cd1..ea574ee
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** make_ruledef(StringInfo buf, HeapTuple r
*** 3966,3972 ****
  		query = getInsertSelectQuery(query, NULL);
  
  		/* Must acquire locks right away; see notes in get_query_def() */
! 		AcquireRewriteLocks(query, false);
  
  		context.buf = buf;
  		context.namespaces = list_make1(&dpns);
--- 3966,3972 ----
  		query = getInsertSelectQuery(query, NULL);
  
  		/* Must acquire locks right away; see notes in get_query_def() */
! 		AcquireRewriteLocks(query, false, false);
  
  		context.buf = buf;
  		context.namespaces = list_make1(&dpns);
*************** get_query_def(Query *query, StringInfo b
*** 4108,4115 ****
  	 * relations, and fix up deleted columns in JOIN RTEs.	This ensures
  	 * consistent results.	Note we assume it's OK to scribble on the passed
  	 * querytree!
  	 */
! 	AcquireRewriteLocks(query, false);
  
  	context.buf = buf;
  	context.namespaces = lcons(&dpns, list_copy(parentnamespace));
--- 4108,4118 ----
  	 * relations, and fix up deleted columns in JOIN RTEs.	This ensures
  	 * consistent results.	Note we assume it's OK to scribble on the passed
  	 * querytree!
+ 	 *
+ 	 * Note that we are only deparsing the query (we are not about the execute
+ 	 * it), so we only need an AccessShareLock on the relations it mentions.
  	 */
! 	AcquireRewriteLocks(query, false, false);
  
  	context.buf = buf;
  	context.namespaces = lcons(&dpns, list_copy(parentnamespace));
diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h
new file mode 100644
index e4027bd..22551ec
*** a/src/include/rewrite/rewriteHandler.h
--- b/src/include/rewrite/rewriteHandler.h
***************
*** 18,24 ****
  #include "nodes/parsenodes.h"
  
  extern List *QueryRewrite(Query *parsetree);
! extern void AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown);
  
  extern Node *build_column_default(Relation rel, int attrno);
  extern Query *get_view_query(Relation view);
--- 18,26 ----
  #include "nodes/parsenodes.h"
  
  extern List *QueryRewrite(Query *parsetree);
! extern void AcquireRewriteLocks(Query *parsetree,
! 					bool forExecute,
! 					bool forUpdatePushedDown);
  
  extern Node *build_column_default(Relation rel, int attrno);
  extern Query *get_view_query(Relation view);
