diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 7a34add7105b27e69dfe08511434ad01971f1a86..e0856a3d8f2bcc93fb029da301db2b8ceb275890 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.172 2007/03/15 23:12:06 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.173 2007/03/17 03:15:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -964,6 +964,30 @@ SPI_cursor_open(const char *name, SPIPlanPtr plan,
 	else
 		portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
 
+	/*
+	 * If told to be read-only, we'd better check for read-only queries.
+	 * This can't be done earlier because we need to look at the finished,
+	 * planned queries.  (In particular, we don't want to do it between
+	 * RevalidateCachedPlan and PortalDefineQuery, because throwing an error
+	 * between those steps would result in leaking our plancache refcount.)
+	 */
+	if (read_only)
+	{
+		ListCell   *lc;
+
+		foreach(lc, stmt_list)
+		{
+			Node   *pstmt = (Node *) lfirst(lc);
+
+			if (!CommandIsReadOnly(pstmt))
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 /* translator: %s is a SQL statement name */
+						 errmsg("%s is not allowed in a non-volatile function",
+								CreateCommandTag(pstmt))));
+		}
+	}
+
 	/*
 	 * Set up the snapshot to use.	(PortalStart will do CopySnapshot, so we
 	 * skip that here.)