Skip to content

Commit eb5d062

Browse files
authored
Merge pull request cockroachdb#155666 from yuzefovich/backport25.3-155655
release-25.3: sql: don't run EXPLAIN ANALYZE via pausable portals
2 parents 4086e14 + f549527 commit eb5d062

File tree

3 files changed

+121
-8
lines changed

3 files changed

+121
-8
lines changed

pkg/sql/conn_executor_exec.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,13 @@ func (ex *connExecutor) execStmtInOpenStateWithPausablePortal(
15671567
if portal.pauseInfo.execStmtInOpenState.ihWrapper == nil {
15681568
portal.pauseInfo.execStmtInOpenState.ihWrapper = &instrumentationHelperWrapper{
15691569
ctx: ctx,
1570-
ih: *ih,
1570+
// TODO(yuzefovich): we're capturing the instrumentationHelper
1571+
// by value here, meaning that modifications that happen later
1572+
// (notably in makeExecPlan) aren't captured. For example,
1573+
// explainPlan field will remain unset. However, so far we've
1574+
// only observed this impact EXPLAIN ANALYZE which doesn't run
1575+
// through the pausable portal path.
1576+
ih: *ih,
15711577
}
15721578
} else {
15731579
p.instrumentation = portal.pauseInfo.execStmtInOpenState.ihWrapper.ih

pkg/sql/pgwire/testdata/pgtest/multiple_active_portals

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,3 +1441,100 @@ ReadyForQuery
14411441
{"Type":"ReadyForQuery","TxStatus":"I"}
14421442

14431443
subtest end
1444+
1445+
subtest explain_analyze
1446+
1447+
# Regression test that EXPLAIN ANALYZE doesn't run through the pausable portal
1448+
# path even when the feature is enabled (#137597).
1449+
1450+
send crdb_only
1451+
Query {"String": "SET multiple_active_portals_enabled = 'true';"}
1452+
----
1453+
1454+
until crdb_only
1455+
ReadyForQuery
1456+
----
1457+
{"Type":"CommandComplete","CommandTag":"SET"}
1458+
{"Type":"ReadyForQuery","TxStatus":"I"}
1459+
1460+
send
1461+
Query {"String": "BEGIN"}
1462+
Parse {"Name": "qea1", "Query": "EXPLAIN ANALYZE SELECT 1;"}
1463+
Bind {"DestinationPortal": "pea1", "PreparedStatement": "qea1"}
1464+
Execute {"Portal": "pea1", "MaxRows": 1}
1465+
Sync
1466+
----
1467+
1468+
until crdb_only ignore=DataRow
1469+
ReadyForQuery
1470+
ReadyForQuery
1471+
----
1472+
{"Type":"CommandComplete","CommandTag":"BEGIN"}
1473+
{"Type":"ReadyForQuery","TxStatus":"T"}
1474+
{"Type":"ParseComplete"}
1475+
{"Type":"BindComplete"}
1476+
{"Type":"PortalSuspended"}
1477+
{"Type":"ReadyForQuery","TxStatus":"T"}
1478+
1479+
send
1480+
Execute {"Portal": "pea1"}
1481+
Sync
1482+
----
1483+
1484+
until crdb_only ignore=DataRow
1485+
ReadyForQuery
1486+
----
1487+
{"Type":"CommandComplete","CommandTag":"EXPLAIN"}
1488+
{"Type":"ReadyForQuery","TxStatus":"T"}
1489+
1490+
send
1491+
Query {"String": "COMMIT"}
1492+
----
1493+
1494+
until
1495+
ReadyForQuery
1496+
----
1497+
{"Type":"CommandComplete","CommandTag":"COMMIT"}
1498+
{"Type":"ReadyForQuery","TxStatus":"I"}
1499+
1500+
send
1501+
Query {"String": "BEGIN"}
1502+
Parse {"Name": "qea2", "Query": "EXPLAIN ANALYZE (DEBUG) SELECT 1;"}
1503+
Bind {"DestinationPortal": "pea2", "PreparedStatement": "qea2"}
1504+
Execute {"Portal": "pea2", "MaxRows": 1}
1505+
Sync
1506+
----
1507+
1508+
until crdb_only ignore=DataRow
1509+
ReadyForQuery
1510+
ReadyForQuery
1511+
----
1512+
{"Type":"CommandComplete","CommandTag":"BEGIN"}
1513+
{"Type":"ReadyForQuery","TxStatus":"T"}
1514+
{"Type":"ParseComplete"}
1515+
{"Type":"BindComplete"}
1516+
{"Type":"PortalSuspended"}
1517+
{"Type":"ReadyForQuery","TxStatus":"T"}
1518+
1519+
send
1520+
Execute {"Portal": "pea2"}
1521+
Sync
1522+
----
1523+
1524+
until crdb_only ignore=DataRow
1525+
ReadyForQuery
1526+
----
1527+
{"Type":"CommandComplete","CommandTag":"EXPLAIN"}
1528+
{"Type":"ReadyForQuery","TxStatus":"T"}
1529+
1530+
send
1531+
Query {"String": "COMMIT"}
1532+
----
1533+
1534+
until
1535+
ReadyForQuery
1536+
----
1537+
{"Type":"CommandComplete","CommandTag":"COMMIT"}
1538+
{"Type":"ReadyForQuery","TxStatus":"I"}
1539+
1540+
subtest end

pkg/sql/prepared_stmt.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,23 @@ func (ex *connExecutor) makePreparedPortal(
120120
OutFormats: outFormats,
121121
}
122122

123-
if ex.sessionData().MultipleActivePortalsEnabled && ex.executorType != executorTypeInternal {
124-
telemetry.Inc(sqltelemetry.StmtsTriedWithPausablePortals)
125-
// We will check whether the statement itself is pausable (i.e., that it
126-
// doesn't contain DDL or mutations) when we build the plan.
127-
portal.pauseInfo = &portalPauseInfo{}
128-
portal.pauseInfo.dispatchToExecutionEngine.queryStats = &topLevelQueryStats{}
129-
portal.portalPausablity = PausablePortal
123+
if ex.sessionData().MultipleActivePortalsEnabled {
124+
// Do not even try running EXPLAIN ANALYZE statements via the pausable
125+
// portal path since it doesn't make much sense to do so - the result
126+
// rows can only be produced _after_ the query execution completes, so
127+
// there are no actual pauses during the execution (plus the
128+
// implementation of EXPLAIN ANALYZE in the connExecutor is quite
129+
// special, and it seems hard to make it work with pausable portals
130+
// model).
131+
_, isExplainAnalyze := stmt.AST.(*tree.ExplainAnalyze)
132+
if ex.executorType != executorTypeInternal && !isExplainAnalyze {
133+
telemetry.Inc(sqltelemetry.StmtsTriedWithPausablePortals)
134+
// We will check whether the statement itself is pausable (i.e., that it
135+
// doesn't contain DDL or mutations) when we build the plan.
136+
portal.pauseInfo = &portalPauseInfo{}
137+
portal.pauseInfo.dispatchToExecutionEngine.queryStats = &topLevelQueryStats{}
138+
portal.portalPausablity = PausablePortal
139+
}
130140
}
131141
return portal, portal.accountForCopy(ctx, &ex.extraTxnState.prepStmtsNamespaceMemAcc, name)
132142
}

0 commit comments

Comments
 (0)