diff --git a/pkg/sql/opt/optbuilder/join.go b/pkg/sql/opt/optbuilder/join.go index 9b48fc5ecaa2..1c68ff1380a4 100644 --- a/pkg/sql/opt/optbuilder/join.go +++ b/pkg/sql/opt/optbuilder/join.go @@ -35,14 +35,12 @@ func (b *Builder) buildJoin( ) (outScope *scope) { leftScope := b.buildDataSource(join.Left, nil /* indexFlags */, locking, inScope) - isLateral := false inScopeRight := inScope - // If this is a lateral join, use leftScope as inScope for the right side. - // The right side scope of a LATERAL join includes the columns produced by - // the left side. - if t, ok := join.Right.(*tree.AliasedTableExpr); ok && t.Lateral { - telemetry.Inc(sqltelemetry.LateralJoinUseCounter) - isLateral = true + isLateral := b.exprIsLateral(join.Right) + if isLateral { + // If this is a lateral join, use leftScope as inScope for the right side. + // The right side scope of a LATERAL join includes the columns produced by + // the left side. inScopeRight = leftScope inScopeRight.context = exprKindLateralJoin } diff --git a/pkg/sql/opt/optbuilder/select.go b/pkg/sql/opt/optbuilder/select.go index e9f5e047650f..c7c2524edce5 100644 --- a/pkg/sql/opt/optbuilder/select.go +++ b/pkg/sql/opt/optbuilder/select.go @@ -1205,6 +1205,7 @@ func (b *Builder) exprIsLateral(t tree.TableExpr) bool { } // Expressions which explicitly use the LATERAL keyword are lateral. if ate.Lateral { + telemetry.Inc(sqltelemetry.LateralJoinUseCounter) return true } // SRFs are always lateral. diff --git a/pkg/sql/opt/optbuilder/testdata/srfs b/pkg/sql/opt/optbuilder/testdata/srfs index b71779e180b8..88fb3b3be849 100644 --- a/pkg/sql/opt/optbuilder/testdata/srfs +++ b/pkg/sql/opt/optbuilder/testdata/srfs @@ -672,9 +672,9 @@ SELECT a.*, b.*, c.* FROM upper('abc') a JOIN ROWS FROM (upper('def'), generate_series(1, 3)) b ON true JOIN generate_series(1, 4) c ON true ---- -inner-join (cross) +inner-join-apply ├── columns: a:1 upper:2 generate_series:3 c:4 - ├── inner-join (cross) + ├── inner-join-apply │ ├── columns: upper:1 upper:2 generate_series:3 │ ├── project-set │ │ ├── columns: upper:1 @@ -894,3 +894,74 @@ project-set │ ├── columns: tab31755.a:1 rowid:2!null crdb_internal_mvcc_timestamp:3 tableoid:4 │ └── limit hint: 1.00 └── 1 + +# Regression test for #70342. +exec-ddl +CREATE TABLE t70342 (i INT PRIMARY KEY); +---- + +# Verify that an SRF is implicitly lateral. +build +WITH data AS (SELECT row_to_json(t70342) AS row FROM t70342) +SELECT count(*) +FROM data, json_each_text(data.row); +---- +with &1 (data) + ├── columns: count:8!null + ├── project + │ ├── columns: row:4 + │ ├── scan t70342 + │ │ └── columns: i:1!null crdb_internal_mvcc_timestamp:2 tableoid:3 + │ └── projections + │ └── row_to_json(((i:1,) AS i)) [as=row:4] + └── scalar-group-by + ├── columns: count_rows:8!null + ├── project + │ └── inner-join-apply + │ ├── columns: row:5 key:6 value:7 + │ ├── with-scan &1 (data) + │ │ ├── columns: row:5 + │ │ └── mapping: + │ │ └── row:4 => row:5 + │ ├── project-set + │ │ ├── columns: key:6 value:7 + │ │ ├── values + │ │ │ └── () + │ │ └── zip + │ │ └── json_each_text(row:5) + │ └── filters (true) + └── aggregations + └── count-rows [as=count_rows:8] + +# Verify that an SRF is implicitly lateral when using the JOIN syntax. +build +WITH data AS (SELECT row_to_json(t70342) AS row FROM t70342) +SELECT count(*) +FROM data CROSS JOIN json_each_text(data.row); +---- +with &1 (data) + ├── columns: count:8!null + ├── project + │ ├── columns: row:4 + │ ├── scan t70342 + │ │ └── columns: i:1!null crdb_internal_mvcc_timestamp:2 tableoid:3 + │ └── projections + │ └── row_to_json(((i:1,) AS i)) [as=row:4] + └── scalar-group-by + ├── columns: count_rows:8!null + ├── project + │ └── inner-join-apply + │ ├── columns: row:5 key:6 value:7 + │ ├── with-scan &1 (data) + │ │ ├── columns: row:5 + │ │ └── mapping: + │ │ └── row:4 => row:5 + │ ├── project-set + │ │ ├── columns: key:6 value:7 + │ │ ├── values + │ │ │ └── () + │ │ └── zip + │ │ └── json_each_text(row:5) + │ └── filters (true) + └── aggregations + └── count-rows [as=count_rows:8]