// Copyright 2025 Dolthub, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package _go import ( "testing" "github.com/dolthub/go-mysql-server/sql" ) func TestRecords(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "Record cannot be used as column type", SetUpScript: []string{ "CREATE TABLE t2 (pk INT PRIMARY KEY, c1 VARCHAR(100));", }, Assertions: []ScriptTestAssertion{ { Query: "CREATE TABLE t (pk INT PRIMARY KEY, r RECORD);", ExpectedErr: `column "r" has pseudo-type record`, }, { Query: "ALTER TABLE t2 ADD COLUMN c2 RECORD;", ExpectedErr: `column "c2" has pseudo-type record`, }, { Query: "ALTER TABLE t2 ALTER COLUMN c1 TYPE RECORD;", ExpectedErr: `column "c1" has pseudo-type record`, }, { Query: "CREATE DOMAIN my_domain AS record;", ExpectedErr: `"record" is not a valid base type for a domain`, }, { Query: "CREATE SEQUENCE my_seq AS record;", ExpectedErr: "sequence type must be smallint, integer, or bigint", }, { Query: "CREATE TYPE outer_type AS (id int, payload record);", ExpectedErr: `column "payload" has pseudo-type record`, }, }, }, { Name: "Casting to record", Assertions: []ScriptTestAssertion{ { Query: "select row(1, 1)::record;", Expected: []sql.Row{{[]any{1, 1}}}, }, }, }, { // TODO: Wrapping table rows with ROW() is not supported yet. Planbuilder assumes the // table alias is a column name and not a table. Name: "ROW() wrapping table rows", SetUpScript: []string{ "create table users (name text, location text, age int);", "insert into users values ('jason', 'SEA', 42), ('max', 'SFO', 31);", }, Assertions: []ScriptTestAssertion{ { // TODO: ERROR: column "p" could not be found in any table in scope Skip: true, Query: "select row(p) from users p;", Expected: []sql.Row{{`("(jason,SEA,44)")`}, {`("(max,SFO,31)")`}}, }, { // TODO: ERROR: name resolution on this statement is not yet supported Skip: true, Query: "select row(p.*, 42) from users p;", Expected: []sql.Row{{`(jason,SEA,42,42)`}, {`(max,SFO,31,42)`}}, }, { // TODO: ERROR: (E).x is not yet supported Skip: true, Query: "SELECT (u).location FROM users u;", Expected: []sql.Row{{"SEA"}, {"SFO"}}, }, }, }, { Name: "ROW() wrapping values", Assertions: []ScriptTestAssertion{ { Query: "SELECT ROW(1, 2, 3) as myRow;", Expected: []sql.Row{{[]any{1, 2, 3}}}, }, { Query: "SELECT (4, 5, 6) as myRow;", Expected: []sql.Row{{[]any{4, 5, 6}}}, }, { Query: "SELECT (NULL, 'foo', NULL) as myRow;", Expected: []sql.Row{{[]any{nil, "foo", nil}}}, }, { Query: "SELECT (NULL, (1 > 0), 'baz') as myRow;", Expected: []sql.Row{{[]any{nil, true, "baz"}}}, }, }, }, { Name: "ROW() equality and comparison", Assertions: []ScriptTestAssertion{ { Query: "SELECT ROW(1, 'x') = ROW(1, 'x');", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 'x') = ROW(1, 'y');", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(1, NULL) = ROW(1, 1);", Expected: []sql.Row{{nil}}, }, { Query: "SELECT ROW(1, 2) < ROW(1, 3);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 2) < ROW(2, NULL);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(2, 2) < ROW(2, NULL);", Expected: []sql.Row{{nil}}, }, { Query: "SELECT ROW(2, 2, 1) < ROW(2, NULL, 2);", Expected: []sql.Row{{nil}}, }, { Query: "SELECT ROW(1, 2) < ROW(NULL, 3);", Expected: []sql.Row{{nil}}, }, { Query: "SELECT ROW(NULL, NULL, NULL) < ROW(NULL, NULL, NULL);", Expected: []sql.Row{{nil}}, }, { Query: "SELECT ROW(1, 2) <= ROW(1, 3);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 2) <= ROW(1, 2);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, NULL) <= ROW(1, 2);", Expected: []sql.Row{{nil}}, }, { Query: "SELECT ROW(2, 1) > ROW(1, 999);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(2, 1) > ROW(1, NULL);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(2, 1) >= ROW(1, 999);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(2, 1) >= ROW(2, 1);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(NULL, 1) >= ROW(2, 1);", Expected: []sql.Row{{nil}}, }, { Query: "SELECT ROW(1, 2) != ROW(3, 4);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 2) != ROW(NULL, 4);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(NULL, 4) != ROW(NULL, 4);", Expected: []sql.Row{{nil}}, }, { // TODO: IS NOT DISTINCT FROM is not yet supported Skip: true, Query: "SELECT ROW(1, NULL) IS NOT DISTINCT FROM ROW(1, NULL);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, '2') = ROW(1, 2::TEXT);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 1) = ROW(1, 1);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 1) != ROW(1, 1);", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(1, 1) < ROW(1, 1);", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(1, 1) <= ROW(1, 1);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 1) > ROW(1, 1);", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(1, 1) = ROW(1, 1);", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(1, 2, null, 4) = ROW(null, 2, 3, 5);", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(1, 2, null, 4) != ROW(null, 2, 3, 5);", Expected: []sql.Row{{"t"}}, }, }, }, { Name: "ROW() use inserting and selecting composite rows", SetUpScript: []string{ "CREATE TYPE user_info AS (id INT, name TEXT, email TEXT);", "CREATE TABLE accounts (info user_info);", }, Assertions: []ScriptTestAssertion{ { Query: "INSERT INTO accounts VALUES (ROW(1, 'alice', 'a@example.com'));", Expected: []sql.Row{}, }, { Query: "SELECT info FROM accounts;", Expected: []sql.Row{{"(1,alice,a@example.com)"}}, }, { Query: "SELECT (a.info).name FROM accounts a;", Expected: []sql.Row{{"alice"}}, }, }, }, { Name: "ROW() use in WHERE clause", SetUpScript: []string{ "create table users (id int primary key, name text, email text);", "insert into users values (1, 'John', 'j@a.com'), (2, 'Joe', 'joe@joe.com');", }, Assertions: []ScriptTestAssertion{ { Query: "SELECT * FROM users WHERE ROW(id, name, email) = ROW(1, 'John', 'j@a.com');", Expected: []sql.Row{{1, "John", "j@a.com"}}, }, { // TODO: IS NOT DISTINCT FROM is not yet supported Skip: true, Query: "SELECT * FROM users WHERE ROW(id, name) IS NOT DISTINCT FROM ROW(2, 'Jane');", Expected: []sql.Row{{2, "Joe", "joe@joe.com"}}, }, }, }, { Name: "ROW() casting and type inference", Assertions: []ScriptTestAssertion{ { Query: "SELECT ROW(1, 'a')::record;", Expected: []sql.Row{{[]any{1, "a"}}}, }, { Query: "SELECT ROW(1, 2) = ROW(1, 'two');", ExpectedErr: "invalid input syntax", }, { Query: "SELECT ROW(1, 2) = ROW(1, '2');", Expected: []sql.Row{{"t"}}, }, }, }, { Name: "ROW() error cases and edge conditions", SetUpScript: []string{ "create table users (id int primary key, name text, email text);", "insert into users values (1, 'John', 'j@a.com'), (2, 'Joe', 'joe@joe.com');", }, Assertions: []ScriptTestAssertion{ { Query: "SELECT ROW(1, 2) = ROW(1);", ExpectedErr: "unequal number of entries", }, { Query: "SELECT ROW(1, 2) = ROW(1, 2, 3);", ExpectedErr: "unequal number of entries", }, { Query: "SELECT ROW(1, 2) < ROW(1);", ExpectedErr: "unequal number of entries", }, { Query: "SELECT ROW(1, 2) <= ROW(1);", ExpectedErr: "unequal number of entries", }, { Query: "SELECT ROW(1, 2) > ROW(1);", ExpectedErr: "unequal number of entries", }, { Query: "SELECT ROW(1, 2) >= ROW(1);", ExpectedErr: "unequal number of entries", }, { Query: "SELECT ROW(1, 2) != ROW(1);", ExpectedErr: "unequal number of entries", }, { Query: "SELECT NULL::record IS NULL", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(NULL) IS NULL", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(NULL, NULL, NULL) IS NULL;", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(NULL, 42, NULL) IS NULL;", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(42) IS NULL", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(NULL) IS NOT NULL;", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(NULL, NULL) IS NOT NULL;", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(NULL, 1) IS NOT NULL;", Expected: []sql.Row{{"f"}}, }, { Query: "SELECT ROW(1, 1) IS NOT NULL;", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(42) IS NOT NULL;", Expected: []sql.Row{{"t"}}, }, { Query: "SELECT ROW(id, name), COUNT(*) FROM users GROUP BY ROW(id, name);", Expected: []sql.Row{{[]any{1, "John"}, 1}, {[]any{2, "Joe"}, 1}}, }, }, }, { Name: "ROW() nesting", Assertions: []ScriptTestAssertion{ { Query: "SELECT ROW(ROW(1, 'x'), true);", Expected: []sql.Row{{[]any{[]any{1, "x"}, true}}}, }, }, }, }) }