diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index f57299bcc7573e04a738013c34c11cc55c2a8c35..98cf744f89cd94edfb64f40670e4d26a74ad7db8 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1942,7 +1942,9 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 			 * Parameter datatypes are driven by parserSetup hook if provided,
 			 * otherwise we use the fixed parameter list.
 			 */
-			if (plan->parserSetup != NULL)
+			if (parsetree == NULL)
+				stmt_list = NIL;
+			else if (plan->parserSetup != NULL)
 			{
 				Assert(plan->nargs == 0);
 				stmt_list = pg_analyze_and_rewrite_params(parsetree,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index f7f1081258afeb63f61d4b7c804b7971039229dc..5bbd3134830e8bba0a78ebb2e22cc21b7578a62d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -287,17 +287,13 @@ transformStmt(ParseState *pstate, Node *parseTree)
  *		Returns true if a snapshot must be set before doing parse analysis
  *		on the given raw parse tree.
  *
- * Classification here should match transformStmt(); but we also have to
- * allow a NULL input (for Parse/Bind of an empty query string).
+ * Classification here should match transformStmt().
  */
 bool
 analyze_requires_snapshot(Node *parseTree)
 {
 	bool		result;
 
-	if (parseTree == NULL)
-		return false;
-
 	switch (nodeTag(parseTree))
 	{
 			/*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 906e7ef1d7ee0b1929b5b0423c8c4da9b253182b..6a0d7996e903003efa99d9a82d249684be9ea20a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1547,7 +1547,9 @@ exec_bind_message(StringInfo input_message)
 	 * snapshot active till we're done, so that plancache.c doesn't have to
 	 * take new ones.
 	 */
-	if (numParams > 0 || analyze_requires_snapshot(psrc->raw_parse_tree))
+	if (numParams > 0 ||
+		(psrc->raw_parse_tree &&
+		 analyze_requires_snapshot(psrc->raw_parse_tree)))
 	{
 		PushActiveSnapshot(GetTransactionSnapshot());
 		snapshot_set = true;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 81e832506afd8b87b7f613343c9c0b5801cc3ed7..d41c692bcbe982e9bdcda391598b108ec1f23e64 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -2184,9 +2184,6 @@ GetCommandLogLevel(Node *parsetree)
 {
 	LogStmtLevel lev;
 
-	if (parsetree == NULL)
-		return LOGSTMT_ALL;
-
 	switch (nodeTag(parsetree))
 	{
 			/* raw plannable queries */
@@ -2290,7 +2287,7 @@ GetCommandLogLevel(Node *parsetree)
 
 				/* Look through an EXECUTE to the referenced stmt */
 				ps = FetchPreparedStatement(stmt->name, false);
-				if (ps)
+				if (ps && ps->plansource->raw_parse_tree)
 					lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
 				else
 					lev = LOGSTMT_ALL;
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 4af2138ce1f388ac6c22a5c1eeae7316fe05bb13..09079e22f274e1eebbcd5f10950719f8247727e7 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -136,7 +136,7 @@ InitPlanCache(void)
  * Once constructed, the cached plan can be made longer-lived, if needed,
  * by calling SaveCachedPlan.
  *
- * raw_parse_tree: output of raw_parser()
+ * raw_parse_tree: output of raw_parser(), or NULL if empty query
  * query_string: original query text
  * commandTag: compile-time-constant tag for query, or NULL if empty query
  */
@@ -218,7 +218,7 @@ CreateCachedPlan(Node *raw_parse_tree,
  * invalidation, so plan use must be completed in the current transaction,
  * and DDL that might invalidate the querytree_list must be avoided as well.
  *
- * raw_parse_tree: output of raw_parser()
+ * raw_parse_tree: output of raw_parser(), or NULL if empty query
  * query_string: original query text
  * commandTag: compile-time-constant tag for query, or NULL if empty query
  */
@@ -644,7 +644,9 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
 	 * the cache.
 	 */
 	rawtree = copyObject(plansource->raw_parse_tree);
-	if (plansource->parserSetup != NULL)
+	if (rawtree == NULL)
+		tlist = NIL;
+	else if (plansource->parserSetup != NULL)
 		tlist = pg_analyze_and_rewrite_params(rawtree,
 											  plansource->query_string,
 											  plansource->parserSetup,
@@ -883,6 +885,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	 */
 	snapshot_set = false;
 	if (!ActiveSnapshotSet() &&
+		plansource->raw_parse_tree &&
 		analyze_requires_snapshot(plansource->raw_parse_tree))
 	{
 		PushActiveSnapshot(GetTransactionSnapshot());
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 382870c81859437ea562eaab85188045036afbb7..f44dd49534bf9e1b46c4e03bb9085703495bcf20 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -76,7 +76,7 @@
 typedef struct CachedPlanSource
 {
 	int			magic;			/* should equal CACHEDPLANSOURCE_MAGIC */
-	Node	   *raw_parse_tree; /* output of raw_parser() */
+	Node	   *raw_parse_tree; /* output of raw_parser(), or NULL */
 	const char *query_string;	/* source text of query */
 	const char *commandTag;		/* command tag (a constant!), or NULL */
 	Oid		   *param_types;	/* array of parameter type OIDs, or NULL */