Skip to content

Commit

Permalink
Allow EXPLAIN in read-only mode
Browse files Browse the repository at this point in the history
Using `EXPLAIN` when `transaction_read_only` is set to `on` fails with
an error. This is fixed by explicitly setting the `check_read_only`
flag.
  • Loading branch information
mkindahl committed Jan 31, 2025
1 parent 65b8151 commit 87947d8
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .unreleased/pr_7637
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixes: #7637 Allow EXPLAIN in read-only mode
Thanks: @ikalafat for reporting the error
1 change: 1 addition & 0 deletions src/process_utility.c
Original file line number Diff line number Diff line change
Expand Up @@ -4859,6 +4859,7 @@ process_ddl_command_start(ProcessUtilityArgs *args)
handler = preprocess_execute;
break;
case T_ExplainStmt:
check_read_only = false;
handler = process_explain_start;
break;

Expand Down
77 changes: 77 additions & 0 deletions tsl/test/expected/read_only.out
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,84 @@
-- create_hypertable()
--
CREATE TABLE test_table(time bigint NOT NULL, device int);
-- EXPLAIN should work in read-only mode, when enabling in transaction.
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (COSTS OFF) SELECT * FROM test_table;
QUERY PLAN
------------------------
Seq Scan on test_table
(1 row)

ROLLBACK;
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) SELECT * FROM test_table;
QUERY PLAN
------------------------------------------------
Seq Scan on test_table (actual rows=0 loops=1)
(1 row)

ROLLBACK;
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (COSTS OFF) INSERT INTO test_table VALUES (1, 1);
QUERY PLAN
----------------------
Insert on test_table
-> Result
(2 rows)

ROLLBACK;
-- This should give an error since we are using ANALYZE and a DML. The
-- read-only is checked when executing the actual INSERT statement,
-- not inside our code.
\set ON_ERROR_STOP 0
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) INSERT INTO test_table VALUES (1, 1);
ERROR: cannot execute INSERT in a read-only transaction
ROLLBACK;
\set ON_ERROR_STOP 1
SET default_transaction_read_only TO on;
-- EXPLAIN should work in read-only mode, even when using the default.
START TRANSACTION;
EXPLAIN (COSTS OFF) SELECT * FROM test_table;
QUERY PLAN
------------------------
Seq Scan on test_table
(1 row)

ROLLBACK;
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) SELECT * FROM test_table;
QUERY PLAN
------------------------------------------------
Seq Scan on test_table (actual rows=0 loops=1)
(1 row)

ROLLBACK;
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (COSTS OFF) INSERT INTO test_table VALUES (1, 1);
QUERY PLAN
----------------------
Insert on test_table
-> Result
(2 rows)

ROLLBACK;
-- This should give an error since we are using ANALYZE and a DML. The
-- read-only is checked when executing the actual INSERT statement,
-- not inside our code.
\set ON_ERROR_STOP 0
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) INSERT INTO test_table VALUES (1, 1);
ERROR: cannot execute INSERT in a read-only transaction
ROLLBACK;
\set ON_ERROR_STOP 1
\set ON_ERROR_STOP 0
SELECT * FROM create_hypertable('test_table', 'time');
ERROR: cannot execute create_hypertable() in a read-only transaction
Expand Down
52 changes: 52 additions & 0 deletions tsl/test/sql/read_only.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,59 @@
--
CREATE TABLE test_table(time bigint NOT NULL, device int);

-- EXPLAIN should work in read-only mode, when enabling in transaction.
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (COSTS OFF) SELECT * FROM test_table;
ROLLBACK;

START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) SELECT * FROM test_table;
ROLLBACK;

START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (COSTS OFF) INSERT INTO test_table VALUES (1, 1);
ROLLBACK;

-- This should give an error since we are using ANALYZE and a DML. The
-- read-only is checked when executing the actual INSERT statement,
-- not inside our code.
\set ON_ERROR_STOP 0
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) INSERT INTO test_table VALUES (1, 1);
ROLLBACK;
\set ON_ERROR_STOP 1

SET default_transaction_read_only TO on;

-- EXPLAIN should work in read-only mode, even when using the default.
START TRANSACTION;
EXPLAIN (COSTS OFF) SELECT * FROM test_table;
ROLLBACK;

START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) SELECT * FROM test_table;
ROLLBACK;

START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (COSTS OFF) INSERT INTO test_table VALUES (1, 1);
ROLLBACK;

-- This should give an error since we are using ANALYZE and a DML. The
-- read-only is checked when executing the actual INSERT statement,
-- not inside our code.
\set ON_ERROR_STOP 0
START TRANSACTION;
SET transaction_read_only TO on;
EXPLAIN (ANALYZE ON, COSTS OFF, TIMING OFF, SUMMARY OFF) INSERT INTO test_table VALUES (1, 1);
ROLLBACK;
\set ON_ERROR_STOP 1

\set ON_ERROR_STOP 0
SELECT * FROM create_hypertable('test_table', 'time');
\set ON_ERROR_STOP 1
Expand Down Expand Up @@ -213,3 +264,4 @@ SELECT remove_retention_policy('test_table');
SELECT add_job('now','12h');
SELECT alter_job(1,scheduled:=false);
SELECT delete_job(1);

0 comments on commit 87947d8

Please sign in to comment.