diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 1383123..0210d35 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -581,6 +581,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 	if (plannedstmt->intoClause != NULL)
 		goto fail;
 
+	/*
+	 * If we're running a SELECT, allow it. This ensures that a
+	 * write rule such as ON INSERT DO SELECT can be executed in
+	 * a read-only session.
+	 */
+	if (plannedstmt->commandType == CMD_SELECT)
+		return;
+
 	/* Fail if write permissions are requested on any non-temp table */
 	foreach(l, plannedstmt->rtable)
 	{
diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out
index fc98f01..f9eda6b 100644
--- a/src/test/regress/expected/transactions.out
+++ b/src/test/regress/expected/transactions.out
@@ -65,6 +65,24 @@ SELECT * FROM writetest, temptest; -- ok
 
 CREATE TABLE test AS SELECT * FROM writetest; -- fail
 ERROR:  transaction is read-only
+BEGIN;
+SET transaction_read_only = false;
+CREATE RULE write2read_rule AS ON INSERT TO writetest DO INSTEAD SELECT new.a;
+COMMIT;
+BEGIN;
+SHOW transaction_read_only;
+ transaction_read_only 
+-----------------------
+ on
+(1 row)
+
+INSERT INTO writetest VALUES (1); -- ok
+ a 
+---
+ 1
+(1 row)
+
+COMMIT;
 START TRANSACTION READ WRITE;
 DROP TABLE writetest; -- ok
 COMMIT;
diff --git a/src/test/regress/sql/transactions.sql b/src/test/regress/sql/transactions.sql
index c670ae1..12a03fa 100644
--- a/src/test/regress/sql/transactions.sql
+++ b/src/test/regress/sql/transactions.sql
@@ -51,6 +51,15 @@ EXECUTE test; -- fail
 SELECT * FROM writetest, temptest; -- ok
 CREATE TABLE test AS SELECT * FROM writetest; -- fail
 
+BEGIN;
+SET transaction_read_only = false;
+CREATE RULE write2read_rule AS ON INSERT TO writetest DO INSTEAD SELECT new.a;
+COMMIT;
+BEGIN;
+SHOW transaction_read_only;
+INSERT INTO writetest VALUES (1); -- ok
+COMMIT;
+
 START TRANSACTION READ WRITE;
 DROP TABLE writetest; -- ok
 COMMIT;
