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

     1  // Copyright 2023 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/plan"
    20  	"github.com/dolthub/go-mysql-server/sql/types"
    21  )
    22  
    23  var AnsiQuotesTests = []ScriptTest{
    24  	{
    25  		Name: "ANSI_QUOTES: basic cases",
    26  		SetUpScript: []string{
    27  			"SET @@sql_mode='ANSI_QUOTES,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';",
    28  			"create table auctions (ai int auto_increment, id varchar(32), data varchar(100), primary key (ai));",
    29  			"insert into auctions (id, data) values (42, 'forty-two');",
    30  		},
    31  		Assertions: []ScriptTestAssertion{
    32  			{
    33  				// When ANSI_QUOTES mode is enabled, double quotes become identifier quotes.
    34  				Query:    `select "data" from auctions order by "ai" desc;`,
    35  				Expected: []sql.Row{{"forty-two"}},
    36  			},
    37  			{
    38  				// Backtick quotes are always valid as identifier characters, even if
    39  				// ANSI_QUOTES mode is enabled.
    40  				Query:    "select `data` from auctions order by `ai` desc;",
    41  				Expected: []sql.Row{{"forty-two"}},
    42  			},
    43  			{
    44  				Query:    `PREPARE prep1 FROM 'select "data" from auctions order by "ai" desc;'`,
    45  				Expected: []sql.Row{{types.OkResult{RowsAffected: 0x0, InsertID: 0x0, Info: plan.PrepareInfo{}}}},
    46  			},
    47  			{
    48  				Query:    `PREPARE prep2 FROM 'INSERT INTO auctions (id, "data") VALUES (?, ?);';`,
    49  				Expected: []sql.Row{{types.OkResult{RowsAffected: 0x0, InsertID: 0x0, Info: plan.PrepareInfo{}}}},
    50  			},
    51  			{
    52  				Query:    `select "data", '"' from auctions order by "ai";`,
    53  				Expected: []sql.Row{{"forty-two", "\""}},
    54  			},
    55  			{
    56  				Query:    `select "data", '\"' from auctions order by "ai";`,
    57  				Expected: []sql.Row{{"forty-two", "\""}},
    58  			},
    59  			{
    60  				Query:    `select '''foo''';`,
    61  				Expected: []sql.Row{{`'foo'`}},
    62  			},
    63  			{
    64  				Query:          `select """""foo""""";`,
    65  				ExpectedErrStr: `column "\"\"foo\"\"" could not be found in any table in scope`,
    66  			},
    67  			{
    68  				Query:          "select ```foo```;",
    69  				ExpectedErrStr: "column \"`foo`\" could not be found in any table in scope",
    70  			},
    71  			{
    72  				// Disable ANSI_QUOTES and make sure we can still run queries
    73  				Query:    `SET @@sql_mode='NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
    74  				Expected: []sql.Row{{}},
    75  			},
    76  			{
    77  				Query:    `select "data" from auctions order by "ai" desc;`,
    78  				Expected: []sql.Row{{"data"}},
    79  			},
    80  			{
    81  				Query:    `show tables;`,
    82  				Expected: []sql.Row{{"auctions"}},
    83  			},
    84  		},
    85  	},
    86  	{
    87  		// ANSI mode is a special "combination" mode that includes ANSI_QUOTES and other modes
    88  		// https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sql-mode-combo
    89  		Name: "ANSI_QUOTES: ANSI combination mode",
    90  		SetUpScript: []string{
    91  			`SET @@sql_mode='ANSI';`,
    92  		},
    93  		Assertions: []ScriptTestAssertion{
    94  			{
    95  				// Assert that we can create a table using ANSI style quotes
    96  				Query:    `create table "t" ("pk" int primary key, "data" varchar(100));`,
    97  				Expected: []sql.Row{{types.NewOkResult(0)}},
    98  			},
    99  			{
   100  				Query:    `insert into t ("pk", "data") values (1, 'one');`,
   101  				Expected: []sql.Row{{types.NewOkResult(1)}},
   102  			},
   103  			{
   104  				Query:    `select "pk", "data" from "t" order by "pk" asc;`,
   105  				Expected: []sql.Row{{1, "one"}},
   106  			},
   107  		},
   108  	},
   109  	{
   110  		Name: "ANSI_QUOTES: views",
   111  		SetUpScript: []string{
   112  			`SET @@sql_mode='ANSI_QUOTES,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   113  		},
   114  		Assertions: []ScriptTestAssertion{
   115  			{
   116  				// https://github.com/dolthub/dolt/issues/6305
   117  				Query:    `CREATE TABLE public_keys (item INTEGER, type CHAR(4), hash INTEGER, "count" INTEGER, "public" VARCHAR(8000))`,
   118  				Expected: []sql.Row{{types.NewOkResult(0)}},
   119  			},
   120  			{
   121  				Query:    `insert into public_keys("item", "type", "hash", "count", "public") values (42, 'type', 1010, 1, 'public');`,
   122  				Expected: []sql.Row{{types.NewOkResult(1)}},
   123  			},
   124  			{
   125  				Query:    `create view view1 as select public_keys."public", public_keys."count" from public_keys;`,
   126  				Expected: []sql.Row{{types.NewOkResult(0)}},
   127  			},
   128  			{
   129  				Query:    `show tables;`,
   130  				Expected: []sql.Row{{"public_keys"}, {"view1"}},
   131  			},
   132  			{
   133  				Query:    `show create table view1;`,
   134  				Expected: []sql.Row{{"view1", "CREATE VIEW `view1` AS select public_keys.\"public\", public_keys.\"count\" from public_keys", "utf8mb4", "utf8mb4_0900_bin"}},
   135  			},
   136  			{
   137  				// TODO: MySQL returns view definitions according to the session's current
   138  				//       SQL_MODE settings, but we currently don't normalize the view
   139  				//       definition based on the current setting for ANSI_QUOTES. We should
   140  				//       fix that, remove the test above, and unskip this test.
   141  				Skip:     true,
   142  				Query:    `show create table view1;`,
   143  				Expected: []sql.Row{{"view1", "CREATE VIEW `view1` AS select public_keys.`public`, public_keys.`count` from public_keys", "utf8mb4", "utf8mb4_0900_bin"}},
   144  			},
   145  			{
   146  				Query:    `select "public", "count" from view1;`,
   147  				Expected: []sql.Row{{"public", 1}},
   148  			},
   149  			{
   150  				// Assert that we can load and parse views for information_schema when ANSI_QUOTES mode is enabled
   151  				Query:    `select table_name, view_definition from information_schema.views where table_name='view1';`,
   152  				Expected: []sql.Row{{"view1", `select public_keys."public", public_keys."count" from public_keys`}},
   153  			},
   154  			{
   155  				// Disable ANSI_QUOTES mode
   156  				Query:    `SET @@sql_mode='NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   157  				Expected: []sql.Row{{}},
   158  			},
   159  			{
   160  				Query:    `show create table view1;`,
   161  				Expected: []sql.Row{{"view1", "CREATE VIEW `view1` AS select public_keys.\"public\", public_keys.\"count\" from public_keys", "utf8mb4", "utf8mb4_0900_bin"}},
   162  			},
   163  			{
   164  				Query:    `show create table public_keys;`,
   165  				Expected: []sql.Row{{"public_keys", "CREATE TABLE `public_keys` (\n  `item` int,\n  `type` char(4),\n  `hash` int,\n  `count` int,\n  `public` varchar(8000)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
   166  			},
   167  			{
   168  				Query:    "select public, `count` from view1;",
   169  				Expected: []sql.Row{{"public", 1}},
   170  			},
   171  			{
   172  				// Assert that we can still load and parse views for information_schema when ANSI_QUOTES mode is disabled
   173  				Query:    `select table_name, view_definition from information_schema.views where table_name='view1';`,
   174  				Expected: []sql.Row{{"view1", `select public_keys."public", public_keys."count" from public_keys`}},
   175  			},
   176  		},
   177  	},
   178  	{
   179  		Name: "ANSI_QUOTES: triggers",
   180  		SetUpScript: []string{
   181  			`SET @@sql_mode='ANSI_QUOTES,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   182  			`create table t (pk int primary key, name varchar(32), data varchar(100));`,
   183  			`create trigger ansi_quotes_trigger BEFORE INSERT ON "t" FOR EACH ROW SET new."data" = 'triggered!';`,
   184  			`insert into t values (1, 'John', 'FooBar');`,
   185  		},
   186  		Assertions: []ScriptTestAssertion{
   187  			{
   188  				// Assert the trigger ran correctly with ANSI_QUOTES mode enabled
   189  				Query:    `select "name", "data" from t order by "pk";`,
   190  				Expected: []sql.Row{{"John", "triggered!"}},
   191  			},
   192  			{
   193  				// Assert that we can read and parse the trigger definition from information_schema
   194  				Query:    `select action_statement from information_schema.triggers where trigger_name='ansi_quotes_trigger';`,
   195  				Expected: []sql.Row{{`SET new."data" = 'triggered!'`}},
   196  			},
   197  			{
   198  				// Disable ANSI_QUOTES mode
   199  				Query:    `SET @@sql_mode='NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   200  				Expected: []sql.Row{{}},
   201  			},
   202  			{
   203  				Query:    `insert into t values (2, 'George', 'SomethingElse');`,
   204  				Expected: []sql.Row{{types.NewOkResult(1)}},
   205  			},
   206  			{
   207  				// Assert the trigger still runs correctly after disabling ANSI_QUOTES mode
   208  				Query:    `select name, data from t where pk=2;`,
   209  				Expected: []sql.Row{{"George", "triggered!"}},
   210  			},
   211  			{
   212  				// Assert that we can still read and parse the trigger definition from information_schema
   213  				Query:    `select action_statement from information_schema.triggers where trigger_name='ansi_quotes_trigger';`,
   214  				Expected: []sql.Row{{`SET new."data" = 'triggered!'`}},
   215  			},
   216  		},
   217  	},
   218  	{
   219  		Name: "ANSI_QUOTES: stored procedures",
   220  		SetUpScript: []string{
   221  			`SET @@sql_mode='ANSI_QUOTES,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   222  			`create table t (pk int primary key, name varchar(32), data varchar(100));`,
   223  			`create procedure AnsiProcedure() BEGIN SELECT "name" from "t" where "pk" = 1; END`,
   224  			`insert into t values (1, 'John', 'FooBar');`,
   225  		},
   226  		Assertions: []ScriptTestAssertion{
   227  			{
   228  				// Assert the procedure runs correctly with ANSI_QUOTES mode enabled
   229  				Query:    `call AnsiProcedure();`,
   230  				Expected: []sql.Row{{"John"}},
   231  			},
   232  			{
   233  				// Assert that we can read and parse the procedure definition from information_schema
   234  				Query:    `select routine_definition from information_schema.routines where routine_name='AnsiProcedure';`,
   235  				Expected: []sql.Row{{`BEGIN SELECT "name" from "t" where "pk" = 1; END`}},
   236  			},
   237  			{
   238  				// Disable ANSI_QUOTES mode
   239  				Query:    `SET @@sql_mode='NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   240  				Expected: []sql.Row{{}},
   241  			},
   242  			{
   243  				// Assert the procedure runs correctly with ANSI_QUOTES mode disabled
   244  				Query:    `call AnsiProcedure();`,
   245  				Expected: []sql.Row{{"John"}},
   246  			},
   247  			{
   248  				// Assert that we can read and parse the procedure definition from information_schema
   249  				// TODO: This one doesn't work yet, until we fix information_schema ROUTINES table support for parsing ANSI_QUOTES
   250  				Skip:     true,
   251  				Query:    `select routine_definition from information_schema.routines where routine_name='AnsiProcedure';`,
   252  				Expected: []sql.Row{{`BEGIN SELECT "name" from "t" where "pk" = 1; END`}},
   253  			},
   254  		},
   255  	},
   256  	{
   257  		Name: "ANSI_QUOTES: column defaults",
   258  		SetUpScript: []string{
   259  			`SET @@sql_mode='ANSI_QUOTES,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   260  			`create table t ("pk" int primary key, "name" varchar(20), data varchar(100) DEFAULT(CONCAT("name", '!')));`,
   261  			`insert into t (pk, name) values (1, 'John');`,
   262  		},
   263  		Assertions: []ScriptTestAssertion{
   264  			{
   265  				// Assert the column default is applied correctly when ANSI_QUOTES mode is enabled
   266  				Query:    `select "name", "data" from t where "pk"=1;`,
   267  				Expected: []sql.Row{{"John", "John!"}},
   268  			},
   269  			{
   270  				// Disable ANSI_QUOTES mode
   271  				Query:    `SET @@sql_mode='NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   272  				Expected: []sql.Row{{}},
   273  			},
   274  			{
   275  				// Insert a row with ANSI_QUOTES mode disabled
   276  				Query:    `insert into t (pk, name) values (2, 'Jill');`,
   277  				Expected: []sql.Row{{types.NewOkResult(1)}},
   278  			},
   279  			{
   280  				// Assert the column default was applied correctly when ANSI_QUOTES mode is disabled
   281  				Query:    `select name, data from t where pk=2;`,
   282  				Expected: []sql.Row{{"Jill", "Jill!"}},
   283  			},
   284  		},
   285  	},
   286  	{
   287  		Name: "ANSI_QUOTES: check constraints",
   288  		SetUpScript: []string{
   289  			`SET @@sql_mode='ANSI_QUOTES,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   290  			`create table t (pk int primary key, data varchar(100), CONSTRAINT "ansi_check" CHECK ("data" != 'forbidden'));`,
   291  		},
   292  		Assertions: []ScriptTestAssertion{
   293  			{
   294  				// Assert the check constraint runs correctly in ANSI_QUOTES mode
   295  				Query:          `insert into t values (1, 'forbidden');`,
   296  				ExpectedErrStr: `Check constraint "ansi_check" violated`,
   297  			},
   298  			{
   299  				// Disable ANSI_QUOTES mode
   300  				Query:    `SET @@sql_mode='NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   301  				Expected: []sql.Row{{}},
   302  			},
   303  			{
   304  				// Assert the check constraint runs correctly when ANSI_QUOTES mode is disabled
   305  				Query:          `insert into t values (1, 'forbidden');`,
   306  				ExpectedErrStr: `Check constraint "ansi_check" violated`,
   307  			},
   308  		},
   309  	},
   310  	{
   311  		Name: "ANSI_QUOTES: events",
   312  		SetUpScript: []string{
   313  			`SET @@sql_mode='ANSI_QUOTES,NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   314  			`create table t (pk int primary key, "count" int);`,
   315  			`insert into t values (1, 0);`,
   316  		},
   317  		Assertions: []ScriptTestAssertion{
   318  			{
   319  				Query: `CREATE EVENT myevent 
   320  							ON SCHEDULE EVERY 1 SECOND STARTS '2037-10-16 23:59:00' DO
   321        						UPDATE "t" SET "count"="count"+1;`,
   322  				Expected: []sql.Row{{types.NewOkResult(0)}},
   323  			},
   324  			{
   325  				Query:    `SHOW EVENTS;`,
   326  				Expected: []sql.Row{{"mydb", "myevent", "`root`@`localhost`", "SYSTEM", "RECURRING", nil, "1", "SECOND", "2037-10-16 23:59:00", nil, "ENABLED", 0, "utf8mb4", "utf8mb4_0900_bin", "utf8mb4_0900_bin"}},
   327  			},
   328  			{
   329  				// Disable ANSI_QUOTES mode and make sure we can still list and run events
   330  				Query:    `SET @@sql_mode='NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';`,
   331  				Expected: []sql.Row{{}},
   332  			},
   333  			{
   334  				Query:    `SHOW EVENTS;`,
   335  				Expected: []sql.Row{{"mydb", "myevent", "`root`@`localhost`", "SYSTEM", "RECURRING", nil, "1", "SECOND", "2037-10-16 23:59:00", nil, "ENABLED", 0, "utf8mb4", "utf8mb4_0900_bin", "utf8mb4_0900_bin"}},
   336  			},
   337  		},
   338  	},
   339  }