github.com/dolthub/go-mysql-server@v0.18.0/enginetest/queries/order_by_group_by_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/go-mysql-server/sql"
    19  	"github.com/dolthub/go-mysql-server/sql/analyzer/analyzererrors"
    20  )
    21  
    22  var OrderByGroupByScriptTests = []ScriptTest{
    23  	{
    24  		Name: "Basic order by/group by cases",
    25  		SetUpScript: []string{
    26  			"use mydb;",
    27  			"create table members (id bigint primary key, team text);",
    28  			"insert into members values (3,'red'), (4,'red'),(5,'orange'),(6,'orange'),(7,'orange'),(8,'purple');",
    29  		},
    30  		Assertions: []ScriptTestAssertion{
    31  			{
    32  				Query:    "select team as f from members order by id, f",
    33  				Expected: []sql.Row{{"red"}, {"red"}, {"orange"}, {"orange"}, {"orange"}, {"purple"}},
    34  			},
    35  			{
    36  				Query: "SELECT team, COUNT(*) FROM members GROUP BY team ORDER BY 2",
    37  				Expected: []sql.Row{
    38  					{"purple", int64(1)},
    39  					{"red", int64(2)},
    40  					{"orange", int64(3)},
    41  				},
    42  			},
    43  			{
    44  				Query: "SELECT team, COUNT(*) FROM members GROUP BY 1 ORDER BY 2",
    45  				Expected: []sql.Row{
    46  					{"purple", int64(1)},
    47  					{"red", int64(2)},
    48  					{"orange", int64(3)},
    49  				},
    50  			},
    51  			{
    52  				Query:       "SELECT team, COUNT(*) FROM members GROUP BY team ORDER BY columndoesnotexist",
    53  				ExpectedErr: sql.ErrColumnNotFound,
    54  			},
    55  			{
    56  				Query:    "SELECT DISTINCT BINARY t1.id as id FROM members AS t1 JOIN members AS t2 ON t1.id = t2.id WHERE t1.id > 0 ORDER BY BINARY t1.id",
    57  				Expected: []sql.Row{{[]uint8{0x33}}, {[]uint8{0x34}}, {[]uint8{0x35}}, {[]uint8{0x36}}, {[]uint8{0x37}}, {[]uint8{0x38}}},
    58  			},
    59  			{
    60  				Query:    "SELECT DISTINCT BINARY t1.id as id FROM members AS t1 JOIN members AS t2 ON t1.id = t2.id WHERE t1.id > 0 ORDER BY t1.id",
    61  				Expected: []sql.Row{{[]uint8{0x33}}, {[]uint8{0x34}}, {[]uint8{0x35}}, {[]uint8{0x36}}, {[]uint8{0x37}}, {[]uint8{0x38}}},
    62  			},
    63  			{
    64  				Query:    "SELECT DISTINCT t1.id as id FROM members AS t1 JOIN members AS t2 ON t1.id = t2.id WHERE t2.id > 0 ORDER BY t1.id",
    65  				Expected: []sql.Row{{3}, {4}, {5}, {6}, {7}, {8}},
    66  			},
    67  			{
    68  				// aliases from outer scopes can be used in a subquery's having clause.
    69  				// https://github.com/dolthub/dolt/issues/4723
    70  				Query:    "SELECT id as alias1, (SELECT alias1+1 group by alias1 having alias1 > 0) FROM members where id < 6;",
    71  				Expected: []sql.Row{{3, 4}, {4, 5}, {5, 6}},
    72  			},
    73  			{
    74  				// columns from outer scopes can be used in a subquery's having clause.
    75  				// https://github.com/dolthub/dolt/issues/4723
    76  				Query:    "SELECT id, (SELECT UPPER(team) having id > 3) as upper_team FROM members where id < 6;",
    77  				Expected: []sql.Row{{3, nil}, {4, "RED"}, {5, "ORANGE"}},
    78  			},
    79  			{
    80  				// When there is ambiguity between a reference in an outer scope and a reference in the current
    81  				// scope, the reference in the innermost scope will be used.
    82  				// https://github.com/dolthub/dolt/issues/4723
    83  				Query:    "SELECT id, (SELECT -1 as id having id < 10) as upper_team FROM members where id < 6;",
    84  				Expected: []sql.Row{{3, -1}, {4, -1}, {5, -1}},
    85  			},
    86  		},
    87  	},
    88  	{
    89  		Name: "Group by BINARY: https://github.com/dolthub/dolt/issues/6179",
    90  		SetUpScript: []string{
    91  			"create table t (s varchar(100));",
    92  			"insert into t values ('abc'), ('def');",
    93  			"create table t1 (b binary(3));",
    94  			"insert into t1 values ('abc'), ('abc'), ('def'), ('abc'), ('def');",
    95  		},
    96  		Assertions: []ScriptTestAssertion{
    97  			{
    98  				Query: "select binary s from t group by binary s order by binary s",
    99  				Expected: []sql.Row{
   100  					{[]uint8("abc")},
   101  					{[]uint8("def")},
   102  				},
   103  			},
   104  			{
   105  				Query: "select count(b), b from t1 group by b order by b",
   106  				Expected: []sql.Row{
   107  					{3, []uint8("abc")},
   108  					{2, []uint8("def")},
   109  				},
   110  			},
   111  			{
   112  				Query: "select binary s from t group by binary s order by s",
   113  				Expected: []sql.Row{
   114  					{[]uint8("abc")},
   115  					{[]uint8("def")},
   116  				},
   117  			},
   118  		},
   119  	},
   120  	{
   121  		Name: "https://github.com/dolthub/dolt/issues/3016",
   122  		SetUpScript: []string{
   123  			"CREATE TABLE `users` (`id` int NOT NULL AUTO_INCREMENT,  `username` varchar(255) NOT NULL,  PRIMARY KEY (`id`));",
   124  			"INSERT INTO `users` (`id`,`username`) VALUES (1,'u2');",
   125  			"INSERT INTO `users` (`id`,`username`) VALUES (2,'u3');",
   126  			"INSERT INTO `users` (`id`,`username`) VALUES (3,'u4');",
   127  			"CREATE TABLE `tweet` (`id` int NOT NULL AUTO_INCREMENT,  `user_id` int NOT NULL,  `content` text NOT NULL,  `timestamp` bigint NOT NULL,  PRIMARY KEY (`id`),  KEY `tweet_user_id` (`user_id`));",
   128  			"INSERT INTO `tweet` (`id`,`user_id`,`content`,`timestamp`) VALUES (1,1,'meow',1647463727);",
   129  			"INSERT INTO `tweet` (`id`,`user_id`,`content`,`timestamp`) VALUES (2,1,'purr',1647463727);",
   130  			"INSERT INTO `tweet` (`id`,`user_id`,`content`,`timestamp`) VALUES (3,2,'hiss',1647463727);",
   131  			"INSERT INTO `tweet` (`id`,`user_id`,`content`,`timestamp`) VALUES (4,3,'woof',1647463727);",
   132  		},
   133  		Assertions: []ScriptTestAssertion{
   134  			{
   135  				Query:    "SELECT t1.username, COUNT(t1.id) FROM ((SELECT t2.id, t2.content, t3.username FROM tweet AS t2 INNER JOIN users AS t3 ON (-t2.user_id = -t3.id) WHERE (t3.username = 'u3')) UNION (SELECT t4.id, t4.content, `t5`.`username` FROM `tweet` AS t4 INNER JOIN users AS t5 ON (-t4.user_id = -t5.id) WHERE (t5.username IN ('u2', 'u4')))) AS t1 GROUP BY `t1`.`username` ORDER BY 1,2 DESC;",
   136  				Expected: []sql.Row{{"u2", 2}, {"u3", 1}, {"u4", 1}},
   137  			},
   138  			{
   139  				Query:    "SELECT t1.username, COUNT(t1.id) AS ct FROM ((SELECT t2.id, t2.content, t3.username FROM tweet AS t2 INNER JOIN users AS t3 ON (-t2.user_id = -t3.id) WHERE (t3.username = 'u3')) UNION (SELECT t4.id, t4.content, `t5`.`username` FROM `tweet` AS t4 INNER JOIN users AS t5 ON (-t4.user_id = -t5.id) WHERE (t5.username IN ('u2', 'u4')))) AS t1 GROUP BY `t1`.`username` ORDER BY 1,2 DESC;",
   140  				Expected: []sql.Row{{"u2", 2}, {"u3", 1}, {"u4", 1}},
   141  			},
   142  			{
   143  				Query:    "SELECT COUNT(id) as ct, user_id as uid FROM tweet GROUP BY tweet.user_id ORDER BY COUNT(id), user_id;",
   144  				Expected: []sql.Row{{1, 2}, {1, 3}, {2, 1}},
   145  			},
   146  			{
   147  				Query:    "SELECT COUNT(tweet.id) as ct, user_id as uid FROM tweet GROUP BY tweet.user_id ORDER BY COUNT(id), user_id;",
   148  				Expected: []sql.Row{{1, 2}, {1, 3}, {2, 1}},
   149  			},
   150  			{
   151  				Query:    "SELECT COUNT(id) as ct, user_id as uid FROM tweet GROUP BY tweet.user_id ORDER BY COUNT(tweet.id), user_id;",
   152  				Expected: []sql.Row{{1, 2}, {1, 3}, {2, 1}},
   153  			},
   154  			{
   155  				Query:    "SELECT COUNT(id) as ct, user_id as uid FROM tweet GROUP BY tweet.user_id HAVING COUNT(tweet.id) > 0 ORDER BY COUNT(tweet.id), user_id;",
   156  				Expected: []sql.Row{{1, 2}, {1, 3}, {2, 1}},
   157  			},
   158  			{
   159  				Query:    "SELECT COUNT(id) as ct, user_id as uid FROM tweet WHERE tweet.id is NOT NULL GROUP BY tweet.user_id ORDER BY COUNT(tweet.id), user_id;",
   160  				Expected: []sql.Row{{1, 2}, {1, 3}, {2, 1}},
   161  			},
   162  			{
   163  				Query:    "SELECT COUNT(id) as ct, user_id as uid FROM tweet WHERE tweet.id is NOT NULL GROUP BY tweet.user_id HAVING COUNT(tweet.id) > 0 ORDER BY COUNT(tweet.id), user_id;",
   164  				Expected: []sql.Row{{1, 2}, {1, 3}, {2, 1}},
   165  			},
   166  			{
   167  				Query:    "SELECT COUNT(id) as ct, user_id as uid FROM tweet WHERE tweet.id is NOT NULL GROUP BY tweet.user_id HAVING COUNT(tweet.id) > 0 ORDER BY COUNT(tweet.id), user_id LIMIT 1;",
   168  				Expected: []sql.Row{{1, 2}},
   169  			},
   170  		},
   171  	},
   172  	{
   173  		Name: "Group by with decimal columns",
   174  		Assertions: []ScriptTestAssertion{
   175  			{
   176  				Query:    "SELECT column_0, sum(column_1) FROM (values row(1.00,1), row(1.00,3), row(2,2), row(2,5), row(3,9)) a group by 1 order by 1;",
   177  				Expected: []sql.Row{{"1.00", float64(4)}, {"2.00", float64(7)}, {"3.00", float64(9)}},
   178  			},
   179  		},
   180  	},
   181  	{
   182  		// https://github.com/dolthub/dolt/issues/4739
   183  		Name: "Validation for use of non-aggregated columns with implicit grouping of all rows",
   184  		SetUpScript: []string{
   185  			"CREATE TABLE t (num INTEGER, val DOUBLE);",
   186  			"INSERT INTO t VALUES (1, 0.01), (2,0.5);",
   187  		},
   188  		Assertions: []ScriptTestAssertion{
   189  			{
   190  				Query:       "SELECT AVG(val), LAST_VALUE(val) OVER w FROM t WINDOW w AS (ORDER BY num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);",
   191  				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
   192  			},
   193  			{
   194  				Query:       "SELECT 1 + AVG(val) + 1, LAST_VALUE(val) OVER w FROM t WINDOW w AS (ORDER BY num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);",
   195  				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
   196  			},
   197  			{
   198  				Query:       "SELECT AVG(1), 1 + LAST_VALUE(val) OVER w FROM t WINDOW w AS (ORDER BY num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);",
   199  				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
   200  			},
   201  			{
   202  				// GMS currently allows this query to execute and chooses the first result for val.
   203  				// To match MySQL's behavior, GMS should be throwing an ErrNonAggregatedColumnWithoutGroupBy error.
   204  				Skip:        true,
   205  				Query:       "select AVG(val), val from t;",
   206  				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
   207  			},
   208  			{
   209  				// Test validation for a derived table opaque node
   210  				Query:       "select * from (SELECT AVG(val), LAST_VALUE(val) OVER w FROM t WINDOW w AS (ORDER BY num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) as dt;",
   211  				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
   212  			},
   213  			{
   214  				// Test validation for a union opaque node
   215  				Query:       "select 1, 1 union SELECT AVG(val), LAST_VALUE(val) OVER w FROM t WINDOW w AS (ORDER BY num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);",
   216  				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
   217  			},
   218  			{
   219  				// Test validation for a recursive CTE opaque node
   220  				Query:       "select * from (with recursive a as (select 1 as c1, 1 as c2 union SELECT AVG(t.val), LAST_VALUE(t.val) OVER w FROM t WINDOW w AS (ORDER BY num RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) select * from a union select * from a limit 1) as dt;",
   221  				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
   222  			},
   223  		},
   224  	},
   225  	{
   226  		Name: "group by with any_value()",
   227  		SetUpScript: []string{
   228  			"use mydb;",
   229  			"create table members (id bigint primary key, team text);",
   230  			"insert into members values (3,'red'), (4,'red'),(5,'orange'),(6,'orange'),(7,'orange'),(8,'purple');",
   231  		},
   232  		Assertions: []ScriptTestAssertion{
   233  			{
   234  				Query: "select @@global.sql_mode",
   235  				Expected: []sql.Row{
   236  					{"NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES"},
   237  				},
   238  			},
   239  			{
   240  				Query: "select @@session.sql_mode",
   241  				Expected: []sql.Row{
   242  					{"NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES"},
   243  				},
   244  			},
   245  			{
   246  				Query: "select any_value(id), any_value(team) from members order by id",
   247  				Expected: []sql.Row{
   248  					{3, "red"},
   249  					{4, "red"},
   250  					{5, "orange"},
   251  					{6, "orange"},
   252  					{7, "orange"},
   253  					{8, "purple"},
   254  				},
   255  			},
   256  		},
   257  	},
   258  	{
   259  		Name: "group by with strict errors",
   260  		SetUpScript: []string{
   261  			"use mydb;",
   262  			"create table members (id bigint primary key, team text);",
   263  			"insert into members values (3,'red'), (4,'red'),(5,'orange'),(6,'orange'),(7,'orange'),(8,'purple');",
   264  		},
   265  		Assertions: []ScriptTestAssertion{
   266  			{
   267  				Query: "select @@global.sql_mode",
   268  				Expected: []sql.Row{
   269  					{"NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES"},
   270  				},
   271  			},
   272  			{
   273  				Query: "select @@session.sql_mode",
   274  				Expected: []sql.Row{
   275  					{"NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES"},
   276  				},
   277  			},
   278  			{
   279  				Query:       "select id, team from members group by team",
   280  				ExpectedErr: analyzererrors.ErrValidationGroupBy,
   281  			},
   282  		},
   283  	},
   284  	{
   285  		Name: "Group by null handling",
   286  		// https://github.com/dolthub/go-mysql-server/issues/1503
   287  		SetUpScript: []string{
   288  			"create table t (pk int primary key, c1 varchar(10));",
   289  			"insert into t values (1, 'foo'), (2, 'foo'), (3, NULL);",
   290  		},
   291  		Assertions: []ScriptTestAssertion{
   292  			{
   293  				Query: "select c1, count(pk) from t group by c1;",
   294  				Expected: []sql.Row{
   295  					{"foo", 2},
   296  					{nil, 1},
   297  				},
   298  			},
   299  			{
   300  				Query: "select c1, count(c1) from t group by c1;",
   301  				Expected: []sql.Row{
   302  					{"foo", 2},
   303  					{nil, 0},
   304  				},
   305  			},
   306  		},
   307  	},
   308  }