github.com/dolthub/go-mysql-server@v0.18.0/enginetest/queries/derived_table_outer_scope_visibility_queries.go (about)

     1  // Copyright 2022 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package queries
    16  
    17  import "github.com/dolthub/go-mysql-server/sql"
    18  
    19  var DerivedTableOuterScopeVisibilityQueries = []ScriptTest{
    20  	{
    21  		Name: "outer scope visibility for derived tables",
    22  		SetUpScript: []string{
    23  			"create table t1 (a int primary key, b int, c int, d int, e int);",
    24  			"create table t2 (a int primary key, b int, c int, d int, e int);",
    25  			"insert into t1 values (1, 1, 1, 100, 100), (2, 2, 2, 200, 200);",
    26  			"insert into t2 values (2, 2, 2, 2, 2);",
    27  			"create table numbers (val int);",
    28  			"insert into numbers values (1), (1), (2), (3), (3), (3), (4), (5), (6), (6), (6);",
    29  		},
    30  		Assertions: []ScriptTestAssertion{
    31  			{
    32  				// A subquery containing a derived table, used in the WHERE clause of a top-level query, has visibility
    33  				// to tables and columns in the top-level query.
    34  				Query:    "SELECT * FROM t1 WHERE t1.d > (SELECT dt.a FROM (SELECT t2.a AS a FROM t2 WHERE t2.b = t1.b) dt);",
    35  				Expected: []sql.Row{{2, 2, 2, 200, 200}},
    36  			},
    37  			{
    38  				// A subquery containing a derived table, used in the HAVING clause of a top-level query, has visibility
    39  				// to tables and columns in the top-level query.
    40  				Query:    "SELECT * FROM t1 HAVING t1.d > (SELECT dt.a FROM (SELECT t2.a AS a FROM t2 WHERE t2.b = t1.b) dt);",
    41  				Expected: []sql.Row{{2, 2, 2, 200, 200}},
    42  			},
    43  			{
    44  				Query:    "SELECT (SELECT dt.z FROM (SELECT t2.a AS z FROM t2 WHERE t2.b = t1.b) dt) FROM t1;",
    45  				Expected: []sql.Row{{nil}, {2}},
    46  			},
    47  			{
    48  				Query:    "SELECT (SELECT max(dt.z) FROM (SELECT t2.a AS z FROM t2 WHERE t2.b = t1.b) dt) FROM t1;",
    49  				Expected: []sql.Row{{nil}, {2}},
    50  			},
    51  			{
    52  				// A subquery containing a derived table, projected in a SELECT query, has visibility to tables and columns
    53  				// in the top-level query.
    54  				Query:    "SELECT t1.*, (SELECT max(dt.a) FROM (SELECT t2.a AS a FROM t2 WHERE t2.b = t1.b) dt) FROM t1;",
    55  				Expected: []sql.Row{{1, 1, 1, 100, 100, nil}, {2, 2, 2, 200, 200, 2}},
    56  			},
    57  			{
    58  				// A subquery containing a derived table, projected in a GROUPBY query, has visibility to tables and columns
    59  				// in the top-level query.
    60  				Query:    "SELECT t1.a, t1.b, (SELECT max(dt.a) FROM (SELECT t2.a AS a FROM t2 WHERE t2.b = t1.b) dt) FROM t1 GROUP BY 1, 2, 3;",
    61  				Expected: []sql.Row{{1, 1, nil}, {2, 2, 2}},
    62  			},
    63  			{
    64  				// A subquery containing a derived table, projected in a WINDOW query, has visibility to tables and columns
    65  				// in the top-level query.
    66  				Query:    "SELECT val, row_number() over (partition by val) as 'row_number', (SELECT two from (SELECT val*2, val*3) as dt(one, two)) as a1 from numbers having a1 > 10;",
    67  				Expected: []sql.Row{{4, 1, 12}, {5, 1, 15}, {6, 1, 18}, {6, 2, 18}, {6, 3, 18}},
    68  			},
    69  			{
    70  				// A subquery containing a derived table, used in the GROUP BY clause of a top-level query as a grouping
    71  				// expression, has visibility to tables and columns in the top-level query.
    72  				Query:    "SELECT max(val), (select max(dt.a) from (SELECT val as a) as dt(a)) as a1 from numbers group by a1;",
    73  				Expected: []sql.Row{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}},
    74  			},
    75  			{
    76  				// CTEs are eligible for outer scope visibility, as long as they are contained in a subquery expression.
    77  				Query:    "SELECT DISTINCT numbers.val, (WITH cte1 AS (SELECT val * 2 as val2 from numbers) SELECT count(*) from cte1 where numbers.val = cte1.val2) as count from numbers having count > 0;",
    78  				Expected: []sql.Row{{2, 2}, {4, 1}, {6, 3}},
    79  			},
    80  			{
    81  				// Recursive CTEs are eligible for outer scope visibility as well, as long as they are contained in a
    82  				// subquery expression.
    83  				Query:    "select distinct n1.val, (with recursive cte1(n) as (select (n1.val) from dual union all select n + 1 from cte1 where n < 10) select sum(n) from cte1) from numbers n1 where n1.val > 4;",
    84  				Expected: []sql.Row{{5, 45.0}, {6, 40.0}},
    85  			},
    86  		},
    87  	},
    88  	{
    89  		Name: "outer scope visibility for derived tables – error cases",
    90  		SetUpScript: []string{
    91  			"create table numbers (val int);",
    92  			"insert into numbers values (1), (1), (2), (3), (3), (3), (4), (5), (6), (6), (6);",
    93  		},
    94  		Assertions: []ScriptTestAssertion{
    95  			{
    96  				// expression aliases are NOT visible from outer scopes to derived tables
    97  				Query:       "select 'foo' as foo, (select dt.b from (select 1 as a, foo as b) dt);",
    98  				ExpectedErr: sql.ErrColumnNotFound,
    99  			},
   100  			{
   101  				// a derived table NOT inside a subquery expression does NOT have access to any lateral scope tables.
   102  				Query:       "SELECT n1.val as a1 from numbers n1, (select n1.val, n2.val * -1 from numbers n2 where n1.val = n2.val) as dt;",
   103  				ExpectedErr: sql.ErrTableNotFound,
   104  			},
   105  			{
   106  				// A derived table inside a derived table does NOT have visibility to any outer scopes.
   107  				Query:       "SELECT 1 as a1, dt.* from (select * from (select a1 from numbers group by val having val = a1) as dt2(val)) as dt(val);",
   108  				ExpectedErr: sql.ErrColumnNotFound,
   109  			},
   110  			{
   111  				// The analyzer rewrites this query so that the CTE is embedded in the projected subquery expression,
   112  				// which provides outer scope visibility for the opk table. It seems like MySQL is attaching the CTE
   113  				// to the top level of the query, and not directly inside the subquery expression, which would explain
   114  				// why MySQL does NOT give this query visibility to the 'opk' table alias. We should match MySQL's
   115  				// behavior, but this is a small edge case we can follow up on.
   116  				Skip: true,
   117  
   118  				// CTEs and Recursive CTEs may receive outer scope visibility, but only when they are contained in a
   119  				// subquery expression. The CTE in this query should NOT have visibility to the 'opk' table alias.
   120  				Query:       "with cte1 as (SELECT c3 FROM one_pk WHERE c4 < opk.c2 ORDER BY 1 DESC LIMIT 1)  SELECT pk, (select c3 from cte1) FROM one_pk opk ORDER BY 1",
   121  				ExpectedErr: sql.ErrTableNotFound,
   122  			},
   123  		},
   124  	},
   125  	{
   126  		Name: "https://github.com/dolthub/go-mysql-server/issues/1282",
   127  		SetUpScript: []string{
   128  			"CREATE TABLE `dcim_rackgroup` (`id` char(32) NOT NULL, `lft` int unsigned NOT NULL, `rght` int unsigned NOT NULL, `tree_id` int unsigned NOT NULL, `level` int unsigned NOT NULL, `parent_id` char(32), PRIMARY KEY (`id`), KEY `dcim_rackgroup_tree_id_9c2ad6f4` (`tree_id`), CONSTRAINT `dcim_rackgroup_parent_id_cc315105_fk_dcim_rackgroup_id` FOREIGN KEY (`parent_id`) REFERENCES `dcim_rackgroup` (`id`));",
   129  			"CREATE TABLE `dcim_rack` (`id` char(32) NOT NULL, `group_id` char(32), PRIMARY KEY (`id`), KEY `dcim_rack_group_id_44e90ea9` (`group_id`), CONSTRAINT `dcim_rack_group_id_44e90ea9_fk_dcim_rackgroup_id` FOREIGN KEY (`group_id`) REFERENCES `dcim_rackgroup` (`id`));",
   130  			"INSERT INTO dcim_rackgroup VALUES ('rackgroup-parent', 100, 200, 1000, 1, NULL), ('rackgroup1', 101, 201, 1000, 1, 'rackgroup-parent'), ('rackgroup2', 102, 202, 1000, 1, 'rackgroup-parent');",
   131  			"INSERT INTO dcim_rack VALUES ('rack1', 'rackgroup1'), ('rack2', 'rackgroup1'), ('rack3', 'rackgroup1'), ('rack4', 'rackgroup2');",
   132  		},
   133  		Assertions: []ScriptTestAssertion{
   134  			{
   135  				Query: `
   136  SELECT (
   137    SELECT count(*) FROM (
   138      SELECT U0.id
   139      FROM dcim_rack U0
   140      INNER JOIN dcim_rackgroup U1 ON (U0.group_id = U1.id)
   141      WHERE 
   142        U1.lft >= dcim_rackgroup.lft AND
   143        U1.lft <= dcim_rackgroup.rght AND
   144        U1.tree_id = dcim_rackgroup.tree_id
   145    ) _count
   146  ) AS rack_count
   147  FROM dcim_rackgroup
   148  WHERE dcim_rackgroup.id IN ('rackgroup1', 'rackgroup2')`,
   149  				Expected: []sql.Row{{4}, {1}},
   150  			},
   151  			{
   152  				Query:    "SELECT COUNT(*) FROM (SELECT (SELECT count(*) FROM (SELECT U0.`id` FROM `dcim_rack` U0 INNER JOIN `dcim_rackgroup` U1 ON (U0.`group_id` = U1.`id`) WHERE (U1.`lft` >= `dcim_rackgroup`.`lft` AND U1.`lft` <= `dcim_rackgroup`.`rght` AND U1.`tree_id` = `dcim_rackgroup`.`tree_id`)) _count) AS `rack_count` FROM `dcim_rackgroup` WHERE `dcim_rackgroup`.`id` IN ('rackgroup1', 'rackgroup2')) subquery;",
   153  				Expected: []sql.Row{{2}},
   154  			},
   155  		},
   156  	},
   157  }