Skip to content

Commit 3bf4d8a

Browse files
authored
Merge pull request #155665 from yuzefovich/blathers/backport-release-25.4-155655
release-25.4: sql: don't run EXPLAIN ANALYZE via pausable portals
2 parents e53ea3d + ac2c36f commit 3bf4d8a

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
@@ -1476,7 +1476,13 @@ func (ex *connExecutor) execStmtInOpenStateWithPausablePortal(
14761476
if portal.pauseInfo.execStmtInOpenState.ihWrapper == nil {
14771477
portal.pauseInfo.execStmtInOpenState.ihWrapper = &instrumentationHelperWrapper{
14781478
ctx: ctx,
1479-
ih: *ih,
1479+
// TODO(yuzefovich): we're capturing the instrumentationHelper
1480+
// by value here, meaning that modifications that happen later
1481+
// (notably in makeExecPlan) aren't captured. For example,
1482+
// explainPlan field will remain unset. However, so far we've
1483+
// only observed this impact EXPLAIN ANALYZE which doesn't run
1484+
// through the pausable portal path.
1485+
ih: *ih,
14801486
}
14811487
} else {
14821488
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
@@ -1629,3 +1629,100 @@ ReadyForQuery
16291629
{"Type":"ReadyForQuery","TxStatus":"I"}
16301630

16311631
subtest end
1632+
1633+
subtest explain_analyze
1634+
1635+
# Regression test that EXPLAIN ANALYZE doesn't run through the pausable portal
1636+
# path even when the feature is enabled (#137597).
1637+
1638+
send crdb_only
1639+
Query {"String": "SET multiple_active_portals_enabled = 'true';"}
1640+
----
1641+
1642+
until crdb_only
1643+
ReadyForQuery
1644+
----
1645+
{"Type":"CommandComplete","CommandTag":"SET"}
1646+
{"Type":"ReadyForQuery","TxStatus":"I"}
1647+
1648+
send
1649+
Query {"String": "BEGIN"}
1650+
Parse {"Name": "qea1", "Query": "EXPLAIN ANALYZE SELECT 1;"}
1651+
Bind {"DestinationPortal": "pea1", "PreparedStatement": "qea1"}
1652+
Execute {"Portal": "pea1", "MaxRows": 1}
1653+
Sync
1654+
----
1655+
1656+
until crdb_only ignore=DataRow
1657+
ReadyForQuery
1658+
ReadyForQuery
1659+
----
1660+
{"Type":"CommandComplete","CommandTag":"BEGIN"}
1661+
{"Type":"ReadyForQuery","TxStatus":"T"}
1662+
{"Type":"ParseComplete"}
1663+
{"Type":"BindComplete"}
1664+
{"Type":"PortalSuspended"}
1665+
{"Type":"ReadyForQuery","TxStatus":"T"}
1666+
1667+
send
1668+
Execute {"Portal": "pea1"}
1669+
Sync
1670+
----
1671+
1672+
until crdb_only ignore=DataRow
1673+
ReadyForQuery
1674+
----
1675+
{"Type":"CommandComplete","CommandTag":"EXPLAIN"}
1676+
{"Type":"ReadyForQuery","TxStatus":"T"}
1677+
1678+
send
1679+
Query {"String": "COMMIT"}
1680+
----
1681+
1682+
until
1683+
ReadyForQuery
1684+
----
1685+
{"Type":"CommandComplete","CommandTag":"COMMIT"}
1686+
{"Type":"ReadyForQuery","TxStatus":"I"}
1687+
1688+
send
1689+
Query {"String": "BEGIN"}
1690+
Parse {"Name": "qea2", "Query": "EXPLAIN ANALYZE (DEBUG) SELECT 1;"}
1691+
Bind {"DestinationPortal": "pea2", "PreparedStatement": "qea2"}
1692+
Execute {"Portal": "pea2", "MaxRows": 1}
1693+
Sync
1694+
----
1695+
1696+
until crdb_only ignore=DataRow
1697+
ReadyForQuery
1698+
ReadyForQuery
1699+
----
1700+
{"Type":"CommandComplete","CommandTag":"BEGIN"}
1701+
{"Type":"ReadyForQuery","TxStatus":"T"}
1702+
{"Type":"ParseComplete"}
1703+
{"Type":"BindComplete"}
1704+
{"Type":"PortalSuspended"}
1705+
{"Type":"ReadyForQuery","TxStatus":"T"}
1706+
1707+
send
1708+
Execute {"Portal": "pea2"}
1709+
Sync
1710+
----
1711+
1712+
until crdb_only ignore=DataRow
1713+
ReadyForQuery
1714+
----
1715+
{"Type":"CommandComplete","CommandTag":"EXPLAIN"}
1716+
{"Type":"ReadyForQuery","TxStatus":"T"}
1717+
1718+
send
1719+
Query {"String": "COMMIT"}
1720+
----
1721+
1722+
until
1723+
ReadyForQuery
1724+
----
1725+
{"Type":"CommandComplete","CommandTag":"COMMIT"}
1726+
{"Type":"ReadyForQuery","TxStatus":"I"}
1727+
1728+
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)