From 00af0401d945267d83e280541657057b45da4eaf Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Mon, 11 Oct 2021 06:38:02 -0700 Subject: [PATCH] opt: fix implicit lateral for SRFs when using JOIN syntax When SRFs are used as a table, they imply that the join is lateral. This works for the multiple table syntax, but not for the JOIN syntax. This commit addresses this omission. Fixes #70342. Release note (bug fix): Fixes an incorrect "no data source matches prefix" error for queries that use a set-returning function on the right-hand side of a `JOIN` (unless `LATERAL` is explicitly specified). --- pkg/sql/opt/optbuilder/join.go | 12 ++--- pkg/sql/opt/optbuilder/select.go | 1 + pkg/sql/opt/optbuilder/testdata/srfs | 75 +++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 9 deletions(-) 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]