github.com/dolthub/go-mysql-server@v0.18.0/enginetest/queries/column_alias_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 (
    18  	"github.com/dolthub/vitess/go/sqltypes"
    19  
    20  	"github.com/dolthub/go-mysql-server/sql/types"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  )
    24  
    25  var ColumnAliasQueries = []ScriptTest{
    26  	{
    27  		Name: "column aliases in a single scope",
    28  		SetUpScript: []string{
    29  			"create table xy (x int primary key, y int);",
    30  			"create table uv (u int primary key, v int);",
    31  			"create table wz (w int, z int);",
    32  			"insert into xy values (0,0),(1,1),(2,2),(3,3);",
    33  			"insert into uv values (0,3),(3,0),(2,1),(1,2);",
    34  			"insert into wz values (0, 0), (1, 0), (1, 2)",
    35  		},
    36  		Assertions: []ScriptTestAssertion{
    37  			{
    38  				// Projections can create expression aliases
    39  				Query: `SELECT i AS cOl FROM mytable`,
    40  				ExpectedColumns: sql.Schema{
    41  					{
    42  						Name: "cOl",
    43  						Type: types.Int64,
    44  					},
    45  				},
    46  				Expected: []sql.Row{{int64(1)}, {int64(2)}, {int64(3)}},
    47  			},
    48  			{
    49  				Query: `SELECT i AS cOl, s as COL FROM mytable`,
    50  				ExpectedColumns: sql.Schema{
    51  					{
    52  						Name: "cOl",
    53  						Type: types.Int64,
    54  					},
    55  					{
    56  						Name: "COL",
    57  						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
    58  					},
    59  				},
    60  				Expected: []sql.Row{{int64(1), "first row"}, {int64(2), "second row"}, {int64(3), "third row"}},
    61  			},
    62  			{
    63  				// Projection expressions may NOT reference aliases defined in projection expressions
    64  				// in the same scope
    65  				Query:       `SELECT i AS new1, new1 as new2 FROM mytable`,
    66  				ExpectedErr: sql.ErrMisusedAlias,
    67  			},
    68  			{
    69  				// The SQL standard disallows aliases in the same scope from being used in filter conditions
    70  				Query:       `SELECT i AS cOl, s as COL FROM mytable where cOl = 1`,
    71  				ExpectedErr: sql.ErrColumnNotFound,
    72  			},
    73  			{
    74  				// Alias expressions may NOT be used in from clauses
    75  				Query:       "select t1.i as a, t1.s as b from mytable as t1 left join mytable as t2 on a = t2.i;",
    76  				ExpectedErr: sql.ErrColumnNotFound,
    77  			},
    78  			{
    79  				// OrderBy clause may reference expression aliases at current scope
    80  				Query:    "select 1 as a order by a desc;",
    81  				Expected: []sql.Row{{1}},
    82  			},
    83  			{
    84  				// If there is ambiguity between one table column and one alias, the alias gets precedence in the order
    85  				// by clause. (This is different from subqueries in projection expressions.)
    86  				Query:    "select v as u from uv order by u;",
    87  				Expected: []sql.Row{{0}, {1}, {2}, {3}},
    88  			},
    89  			{
    90  				// If there is ambiguity between multiple aliases in an order by clause, it is an error
    91  				Query:       "select u as u, v as u from uv order by u;",
    92  				ExpectedErr: sql.ErrAmbiguousColumnOrAliasName,
    93  			},
    94  			{
    95  				// If there is ambiguity between one selected table column and one alias, the table column gets
    96  				// precedence in the group by clause.
    97  				Query:    "select w, min(z) as w, max(z) as w from wz group by w;",
    98  				Expected: []sql.Row{{0, 0, 0}, {1, 0, 2}},
    99  			},
   100  			{
   101  				// GroupBy may use a column that is selected multiple times.
   102  				Query:    "select w, w from wz group by w;",
   103  				Expected: []sql.Row{{0, 0}, {1, 1}},
   104  			},
   105  			{
   106  				// GroupBy may use expression aliases in grouping expressions
   107  				Query: `SELECT s as COL1, SUM(i) COL2 FROM mytable group by col1 order by col2`,
   108  				ExpectedColumns: sql.Schema{
   109  					{
   110  						Name: "COL1",
   111  						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
   112  					},
   113  					{
   114  						Name: "COL2",
   115  						Type: types.Float64,
   116  					},
   117  				},
   118  				Expected: []sql.Row{
   119  					{"first row", float64(1)},
   120  					{"second row", float64(2)},
   121  					{"third row", float64(3)},
   122  				},
   123  			},
   124  			{
   125  				// Having clause may reference expression aliases current scope
   126  				Query:    "select t1.u as a from uv as t1 having a > 0 order by a;",
   127  				Expected: []sql.Row{{1}, {2}, {3}},
   128  			},
   129  			{
   130  				// Having clause may reference expression aliases from current scope
   131  				Query:    "select t1.u as a from uv as t1 having a = t1.u order by a;",
   132  				Expected: []sql.Row{{0}, {1}, {2}, {3}},
   133  			},
   134  			{
   135  				// Expression aliases work when implicitly referenced by ordinal position
   136  				Query: `SELECT s as coL1, SUM(i) coL2 FROM mytable group by 1 order by 2`,
   137  				ExpectedColumns: sql.Schema{
   138  					{
   139  						Name: "coL1",
   140  						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
   141  					},
   142  					{
   143  						Name: "coL2",
   144  						Type: types.Float64,
   145  					},
   146  				},
   147  				Expected: []sql.Row{
   148  					{"first row", float64(1)},
   149  					{"second row", float64(2)},
   150  					{"third row", float64(3)},
   151  				},
   152  			},
   153  			{
   154  				// Expression aliases work when implicitly referenced by ordinal position
   155  				Query: `SELECT s as Date, SUM(i) TimeStamp FROM mytable group by 1 order by 2`,
   156  				ExpectedColumns: sql.Schema{
   157  					{
   158  						Name: "Date",
   159  						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
   160  					},
   161  					{
   162  						Name: "TimeStamp",
   163  						Type: types.Float64,
   164  					},
   165  				},
   166  				Expected: []sql.Row{
   167  					{"first row", float64(1)},
   168  					{"second row", float64(2)},
   169  					{"third row", float64(3)},
   170  				},
   171  			},
   172  			{
   173  				Query:    "select t1.i as a from mytable as t1 having a = t1.i;",
   174  				Expected: []sql.Row{{1}, {2}, {3}},
   175  			},
   176  		},
   177  	},
   178  	{
   179  		Name: "column aliases in two scopes",
   180  		SetUpScript: []string{
   181  			"create table xy (x int primary key, y int);",
   182  			"create table uv (u int primary key, v int);",
   183  			"insert into xy values (0,0),(1,1),(2,2),(3,3);",
   184  			"insert into uv values (0,3),(3,0),(2,1),(1,2);",
   185  		},
   186  		Assertions: []ScriptTestAssertion{
   187  			{
   188  				Query:    `select "foo" as dummy, (select dummy)`,
   189  				Expected: []sql.Row{{"foo", "foo"}},
   190  			},
   191  			{
   192  				// https://github.com/dolthub/dolt/issues/4344
   193  				Query:    "select x as v, (select u from uv where v = y) as u from xy;",
   194  				Expected: []sql.Row{{0, 3}, {1, 2}, {2, 1}, {3, 0}},
   195  			},
   196  			{
   197  				// GMS currently returns {0, 0, 0} The second alias seems to get overwritten.
   198  				// https://github.com/dolthub/go-mysql-server/issues/1286
   199  				Skip: true,
   200  
   201  				// When multiple aliases are defined with the same name, a subquery prefers the first definition
   202  				Query:    "select 0 as a, 1 as a, (SELECT x from xy where x = a);",
   203  				Expected: []sql.Row{{0, 1, 0}},
   204  			},
   205  			{
   206  				Query:    "SELECT 1 as a, (select a) as a;",
   207  				Expected: []sql.Row{{1, 1}},
   208  			},
   209  			{
   210  				Query:    "SELECT 1 as a, (select a) as b;",
   211  				Expected: []sql.Row{{1, 1}},
   212  			},
   213  			{
   214  				Query:    "SELECT 1 as a, (select a) as b from dual;",
   215  				Expected: []sql.Row{{1, 1}},
   216  			},
   217  			{
   218  				Query:    "SELECT 1 as a, (select a) as b from xy;",
   219  				Expected: []sql.Row{{1, 1}, {1, 1}, {1, 1}, {1, 1}},
   220  			},
   221  			{
   222  				Query:    "select x, (select 1) as y from xy;",
   223  				Expected: []sql.Row{{0, 1}, {1, 1}, {2, 1}, {3, 1}},
   224  			},
   225  			{
   226  				Query:    "SELECT 1 as a, (select a) from xy;",
   227  				Expected: []sql.Row{{1, 1}, {1, 1}, {1, 1}, {1, 1}},
   228  			},
   229  		},
   230  	},
   231  	{
   232  		Name: "column aliases in three scopes",
   233  		SetUpScript: []string{
   234  			"create table xy (x int primary key, y int);",
   235  			"create table uv (u int primary key, v int);",
   236  			"insert into xy values (0,0),(1,1),(2,2),(3,3);",
   237  			"insert into uv values (0,3),(3,0),(2,1),(1,2);",
   238  		},
   239  		Assertions: []ScriptTestAssertion{
   240  			{
   241  				Query:    "select x, (select 1) as y, (select (select y as q)) as z from (select * from xy) as xy;",
   242  				Expected: []sql.Row{{0, 1, 0}, {1, 1, 1}, {2, 1, 2}, {3, 1, 3}},
   243  			},
   244  		},
   245  	},
   246  	{
   247  		Name: "various broken alias queries",
   248  		Assertions: []ScriptTestAssertion{
   249  			{
   250  				// The second query in the union subquery returns "x" instead of mytable.i
   251  				// https://github.com/dolthub/dolt/issues/4256
   252  				Skip:     true,
   253  				Query:    `SELECT *, (select i union select i) as a from mytable;`,
   254  				Expected: []sql.Row{{1, "first row", 1}, {2, "second row", 2}, {3, "third row", 3}},
   255  			},
   256  			{
   257  				// Fails with an unresolved *plan.Project node error
   258  				// The second Project in the union subquery doens't seem to get its alias reference resolved
   259  				Skip:     true,
   260  				Query:    `SELECT 1 as a, (select a union select a) as b;`,
   261  				Expected: []sql.Row{{1, 1}},
   262  			},
   263  			{
   264  				// GMS executes this query, but it is not valid because of the forward ref of alias b.
   265  				// GMS should return an error about an invalid forward-ref.
   266  				Skip:        true,
   267  				Query:       `select 1 as a, (select b), 0 as b;`,
   268  				ExpectedErr: sql.ErrColumnNotFound,
   269  			},
   270  			{
   271  				// GMS returns "expression 'dt.two' doesn't appear in the group by expressions", but MySQL will execute
   272  				// this query.
   273  				Skip:  true,
   274  				Query: "select 1 as a, one + 1 as mod1, dt.* from mytable as t1, (select 1, 2 from mytable) as dt (one, two) where dt.one > 0 group by one;",
   275  				// column names:  a, mod1, one, two
   276  				Expected: []sql.Row{{1, 2, 1, 2}},
   277  			},
   278  			{
   279  				// GMS returns `ambiguous column or alias name "b"` on both cases of `group by b` and `group by 1` inside subquery, but MySQL executes.
   280  				Skip:     true,
   281  				Query:    "select 1 as b, (select b group by b order by b) order by 1;",
   282  				Expected: []sql.Row{{1, 1}},
   283  			},
   284  		},
   285  	},
   286  }