Files
dolthub--doltgresql/testing/go/issues_test.go
T
2026-07-13 12:32:25 +08:00

652 lines
18 KiB
Go

// Copyright 2023 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"
"github.com/jackc/pgx/v5/pgproto3"
"github.com/dolthub/doltgresql/core/id"
pgtypes "github.com/dolthub/doltgresql/server/types"
)
func TestIssues(t *testing.T) {
RunScripts(t, []ScriptTest{
{
Name: "Issue #25",
SetUpScript: []string{
"create table tbl (pk int);",
"insert into tbl values (1);",
},
Assertions: []ScriptTestAssertion{
{
Query: `select dolt_add(".");`,
ExpectedErr: "could not be found in any table in scope",
},
{
Query: `select dolt_add('.');`,
Expected: []sql.Row{{"{0}"}},
},
{
Query: `select dolt_commit("-m", "look ma");`,
ExpectedErr: "could not be found in any table in scope",
},
{
Query: `select length(dolt_commit('-m', 'look ma')::text);`,
Expected: []sql.Row{{34}},
},
{
Query: `select dolt_branch("br1");`,
ExpectedErr: "could not be found in any table in scope",
},
{
Query: `select dolt_branch('br1');`,
Expected: []sql.Row{{"{0}"}},
},
},
},
{
Name: "Issue #2030",
SetUpScript: []string{
`CREATE TABLE sub_entities (
project_id VARCHAR(256) NOT NULL,
entity_id VARCHAR(256) NOT NULL,
id VARCHAR(256) NOT NULL,
name VARCHAR(256) NOT NULL,
PRIMARY KEY (project_id, entity_id, id)
);
`,
`
CREATE TABLE entities (
project_id VARCHAR(256) NOT NULL,
id VARCHAR(256) NOT NULL,
name VARCHAR(256) NOT NULL,
default_sub_entity_id VARCHAR(256),
PRIMARY KEY (project_id, id)
);
`,
`
CREATE TABLE conversations (
id VARCHAR(256) NOT NULL,
tenant_id VARCHAR(256) NOT NULL,
project_id VARCHAR(256) NOT NULL,
active_sub_agent_id VARCHAR(256) NOT NULL,
PRIMARY KEY (tenant_id, project_id, id)
);
`,
`INSERT INTO sub_entities (project_id, entity_id, id, name) VALUES
('projectA', 'entityA', 'subA1', 'Sub-Entity A1'),
('projectA', 'entityB', 'subB1', 'Sub-Entity B1');
`,
`INSERT INTO entities (project_id, id, name, default_sub_entity_id) VALUES
('projectA', 'entityA', 'Entity A', 'subA1'),
('projectA', 'entityB', 'Entity B', 'subB1');
`,
`INSERT INTO conversations (tenant_id, project_id, id, active_sub_agent_id) VALUES
('tenant1', 'projectA', 'conv1', 'subA1'),
('tenant1', 'projectA', 'conv2', 'subB1');
`,
},
Assertions: []ScriptTestAssertion{
{
Query: `select
"entities"."project_id",
"entities"."id",
"entities"."name",
"entities"."default_sub_entity_id",
"entities_defaultSubEntity"."data" as "defaultSubEntity"
from "entities" "entities"
left join lateral (
select json_build_array(
"entities_defaultSubEntity"."project_id",
"entities_defaultSubEntity"."entity_id",
"entities_defaultSubEntity"."id",
"entities_defaultSubEntity"."name"
) as "data"
from (
select * from "sub_entities" "entities_defaultSubEntity"
where "entities_defaultSubEntity"."id" = "entities"."default_sub_entity_id"
limit $1
) "entities_defaultSubEntity"
) "entities_defaultSubEntity" on true
where ("entities"."project_id" = $2 and "entities"."id" = $3)
limit $4`,
BindVars: []any{
int64(1),
"projectA",
"entityA",
int64(1),
},
Expected: []sql.Row{
{
"projectA",
"entityA",
"Entity A",
"subA1",
`["projectA", "entityA", "subA1", "Sub-Entity A1"]`,
},
},
},
{
Query: `select
"entities"."project_id",
"entities"."id",
"entities"."name",
"entities"."default_sub_entity_id",
"entities_defaultSubEntity"."data" as "defaultSubEntity"
from "entities" "entities"
left join lateral (
select json_build_array(
"entities_defaultSubEntity"."project_id",
"entities_defaultSubEntity"."entity_id",
"entities_defaultSubEntity"."id",
"entities_defaultSubEntity"."name"
) as "data"
from (
select * from "sub_entities" "entities_defaultSubEntity"
where "entities_defaultSubEntity"."id" = "entities"."default_sub_entity_id"
limit 1
) "entities_defaultSubEntity"
) "entities_defaultSubEntity" on true
where ("entities"."project_id" = 'projectA' and "entities"."id" = 'entityA')
limit 1`,
Expected: []sql.Row{
{
"projectA",
"entityA",
"Entity A",
"subA1",
`["projectA", "entityA", "subA1", "Sub-Entity A1"]`,
},
},
},
},
},
{
Name: "Issue #2049",
SetUpScript: []string{
`CREATE TABLE jsonb_test (id VARCHAR(256) NOT NULL PRIMARY KEY, "jsonbColumn" JSONB);`,
`INSERT INTO jsonb_test VALUES ('test', '{"test": "value\n"}');`,
`INSERT INTO jsonb_test VALUES ('test2', '{"test": "value\t"}');`,
`INSERT INTO jsonb_test VALUES ('test3', '{"test": "value\r"}');`,
},
Assertions: []ScriptTestAssertion{
{
Query: "SELECT * FROM jsonb_test;",
// The pgx library incorrectly reinterprets our JSON value by replacing the individual newline
// characters (ASCII 92,110) with the actual newline character (ASCII 10), which is incorrect for us.
// Therefore, we have to use the raw returned values. To make it more clear, we aren't using a raw
// string literal and instead escaping the characters in the byte slice. We also test other escape
// characters that are replaced.
ExpectedRaw: [][][]byte{
{[]byte("test"), []byte("{\"test\": \"value\\n\"}")},
{[]byte("test2"), []byte("{\"test\": \"value\\t\"}")},
{[]byte("test3"), []byte("{\"test\": \"value\\r\"}")},
},
},
},
},
{
Name: "Issue #2197 Part 1",
SetUpScript: []string{
`CREATE TABLE t1 (a INT, b VARCHAR(3));`,
`CREATE TABLE t2(id SERIAL, t1 t1);`,
},
Assertions: []ScriptTestAssertion{
{
Query: `INSERT INTO t2(t1) VALUES (ROW(1, 'abc'));`,
Expected: []sql.Row{},
},
{
Query: `SELECT * FROM t2;`,
Expected: []sql.Row{{1, "(1,abc)"}},
},
{
Query: `INSERT INTO t2(t1) VALUES (ROW('a', 'def'));`,
ExpectedErr: "invalid input syntax for type",
},
{
Query: `INSERT INTO t2(t1) VALUES (ROW(true, 'def'));`,
ExpectedErr: "Cannot cast type",
},
{
Query: `INSERT INTO t2(t1) VALUES (ROW(2, 'def', 'ghi'));`,
ExpectedErr: "cannot cast type",
},
{
Query: `INSERT INTO t2(t1) VALUES (ROW(2));`,
ExpectedErr: "cannot cast type",
},
},
},
{
Name: "Issue #2197 Part 2",
SetUpScript: []string{
`CREATE TABLE t1a (a INT4, b VARCHAR(3));`,
`CREATE TABLE t1b (a INT4 NOT NULL, b VARCHAR(3) NOT NULL);`,
`CREATE TABLE t2 (id SERIAL, t1a t1a, t1b t1b);`,
`INSERT INTO t2 (t1a) VALUES (ROW(1, 'abc'));`,
`INSERT INTO t2 (t1b) VALUES (ROW(1, 'abc'));`,
},
Assertions: []ScriptTestAssertion{
{
Query: `SELECT * FROM t2;`,
Expected: []sql.Row{
{1, "(1,abc)", nil},
{2, nil, "(1,abc)"},
},
},
{
Query: `ALTER TABLE t1a ADD COLUMN c VARCHAR(10);`,
Expected: []sql.Row{},
},
{
Query: `ALTER TABLE t1b ADD COLUMN c VARCHAR(10) NOT NULL;`,
Expected: []sql.Row{},
},
{
Query: `SELECT * FROM t2 ORDER BY id;`,
Expected: []sql.Row{
{1, "(1,abc,)", nil},
{2, nil, "(1,abc,)"},
},
},
{
Query: `ALTER TABLE t1a DROP COLUMN b;`,
Expected: []sql.Row{},
},
{
Query: `ALTER TABLE t1b DROP COLUMN b;`,
Expected: []sql.Row{},
},
{
Query: `SELECT * FROM t2 ORDER BY id;`,
Expected: []sql.Row{
{1, "(1,)", nil},
{2, nil, "(1,)"},
},
},
{
Query: `INSERT INTO t1a VALUES (2, 'def');`,
Expected: []sql.Row{},
},
{
Query: `INSERT INTO t1b VALUES (3, 'xyzzy');`,
Expected: []sql.Row{},
},
{
Query: `INSERT INTO t2 (t1a) SELECT ROW(a,c)::t1a FROM t1a;`,
Expected: []sql.Row{},
},
{
Query: `INSERT INTO t2 (t1b) SELECT ROW(a,c)::t1b FROM t1b;`,
Expected: []sql.Row{},
},
{
Query: `SELECT * FROM t2 ORDER BY id;`,
Expected: []sql.Row{
{1, "(1,)", nil},
{2, nil, "(1,)"},
{3, "(2,def)", nil},
{4, nil, "(3,xyzzy)"},
},
},
{
Query: `SELECT ((t1a).@1), ((t1b).@2) FROM t2 ORDER BY id;`,
Expected: []sql.Row{
{1, nil},
{nil, nil},
{2, nil},
{nil, "xyzzy"},
},
},
{
Query: `UPDATE t2 SET t1a=ROW((t1a).a+100, (t1a).c)::t1a WHERE length(t1a::text) > 0;`,
Expected: []sql.Row{},
},
{
Query: `UPDATE t2 SET t1b=ROW((t1b).@1+100, (t1b).@2)::t1b WHERE length(t1b::text) > 0;`,
Expected: []sql.Row{},
},
{
Query: `SELECT * FROM t2 ORDER BY id;`,
Expected: []sql.Row{
{1, "(101,)", nil},
{2, nil, "(101,)"},
{3, "(102,def)", nil},
{4, nil, "(103,xyzzy)"},
},
},
{
Query: `SELECT (id).a FROM t2;`,
ExpectedErr: "column notation .a applied to type",
},
{
Query: `SELECT (t1a).g FROM t2;`,
ExpectedErr: `column "g" not found in data type`,
},
{
Query: `SELECT (t1a).@0 FROM t2;`,
ExpectedErr: "out of bounds",
},
{
Query: `SELECT (t1a).@3 FROM t2;`,
ExpectedErr: "out of bounds",
},
{
Query: `ALTER TABLE t1a ADD COLUMN d VARCHAR(10) DEFAULT 'abc';`,
ExpectedErr: `cannot alter table "t1a" because column "t2.t1a" uses its row type`,
},
{
Query: `ALTER TABLE t1a ADD COLUMN d VARCHAR(10);`,
Expected: []sql.Row{},
},
{
Query: `ALTER TABLE t1a DROP COLUMN c;`,
Expected: []sql.Row{},
},
{
Query: `SELECT * FROM t2 ORDER BY id;`,
Expected: []sql.Row{
{1, "(101,)", nil},
{2, nil, "(101,)"},
{3, "(102,)", nil},
{4, nil, "(103,xyzzy)"},
},
},
},
},
{
Name: "Issue #2299",
SetUpScript: []string{
"CREATE TYPE team_role AS ENUM ('admin', 'editor', 'member');",
},
Assertions: []ScriptTestAssertion{
{
Query: `CREATE TABLE users (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), role team_role NOT NULL DEFAULT 'member');`,
Expected: []sql.Row{},
},
{
Query: `INSERT INTO users (role) VALUES (DEFAULT);`,
Expected: []sql.Row{},
},
{
Query: `SELECT role FROM users;`,
Expected: []sql.Row{{"member"}},
},
},
},
{
Name: "Issue #2307",
SetUpScript: []string{
"CREATE TABLE test (pk INT4);",
},
Assertions: []ScriptTestAssertion{
{
Query: `SELECT EXISTS(SELECT 1 FROM pg_catalog.pg_tables WHERE tablename = 'test');`,
ExpectedColTypes: []id.Type{pgtypes.Bool.ID},
Expected: []sql.Row{{"t"}},
},
{
Query: `SELECT NOT EXISTS(SELECT 1 FROM pg_catalog.pg_tables WHERE tablename = 'test');`,
ExpectedColTypes: []id.Type{pgtypes.Bool.ID},
Expected: []sql.Row{{"f"}},
},
},
},
{
Name: "Issue #2548",
SetUpScript: []string{
"CREATE TABLE test (pk INT4 PRIMARY KEY, v1 TIMESTAMP WITH TIME ZONE);",
},
Assertions: []ScriptTestAssertion{
{
Query: `SET TimeZone = 'UTC-01:00';`,
Expected: []sql.Row{},
},
{
Query: `INSERT INTO test VALUES (1, '2026-04-15 10:11:12');`,
Expected: []sql.Row{},
},
{
Query: `SET TimeZone = 'UTC-03:00';`,
Expected: []sql.Row{},
},
{
Query: `INSERT INTO test VALUES (2, '2026-04-15 10:11:12');`,
Expected: []sql.Row{},
},
{
Query: `SELECT (SELECT v1 FROM test WHERE pk = 2) - (SELECT v1 FROM test WHERE pk = 1);`,
Expected: []sql.Row{{"-02:00:00"}},
},
},
},
{
Name: "Issue #2604",
SetUpScript: []string{
"CREATE TABLE t (id INT PRIMARY KEY, a TEXT, b TEXT DEFAULT 'x');",
"CREATE UNIQUE INDEX idx_t_a ON t(a);",
"SELECT dolt_add('-A');",
"SELECT dolt_commit('-m', 'schema');",
"SELECT dolt_branch('f', 'main');",
"SELECT dolt_checkout('f');",
"INSERT INTO t (id, a) VALUES (1, 'feat');",
"SELECT dolt_add('-A');",
"SELECT dolt_commit('-m', 'feat');",
"SELECT dolt_checkout('main');",
"INSERT INTO t (id, a) VALUES (2, 'main');",
"SELECT dolt_add('-A');",
"SELECT dolt_commit('-m', 'main');",
"SELECT dolt_checkout('f');",
},
Assertions: []ScriptTestAssertion{
{
Query: "SELECT length(dolt_merge('main')::text) = 57;",
Expected: []sql.Row{{"t"}},
},
},
},
})
}
func TestIssuesWire(t *testing.T) {
RunWireScripts(t, []WireScriptTest{
{
Name: "Issue #2546",
Assertions: []WireScriptTestAssertion{
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Query{String: "SELECT 'foo';"},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.RowDescription{
Fields: []pgproto3.FieldDescription{
{
Name: []byte("?column?"),
TableOID: 0,
TableAttributeNumber: 0,
DataTypeOID: 25,
DataTypeSize: -1,
TypeModifier: -1,
Format: 0,
},
},
},
&pgproto3.DataRow{Values: [][]byte{[]byte("foo")}},
&pgproto3.CommandComplete{CommandTag: []byte("SELECT 1")},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
},
},
{
Name: "Issue #2557",
Assertions: []WireScriptTestAssertion{
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Parse{
Name: "stmt_name1",
Query: `SELECT '{"v":"a\\nb"}'::jsonb;`,
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.ParseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Bind{
PreparedStatement: "stmt_name1",
ResultFormatCodes: []int16{0},
},
&pgproto3.Execute{},
&pgproto3.Close{
ObjectType: 'P',
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.BindComplete{},
&pgproto3.DataRow{
Values: [][]byte{
[]byte(`{"v": "a\\nb"}`),
},
},
&pgproto3.CommandComplete{CommandTag: []byte("SELECT 1")},
&pgproto3.CloseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Parse{
Name: "stmt_name2",
Query: `SELECT $${"v":"a\\nb"}$$::jsonb;`,
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.ParseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Bind{
PreparedStatement: "stmt_name2",
ResultFormatCodes: []int16{0},
},
&pgproto3.Execute{},
&pgproto3.Close{
ObjectType: 'P',
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.BindComplete{},
&pgproto3.DataRow{
Values: [][]byte{
[]byte(`{"v": "a\\nb"}`),
},
},
&pgproto3.CommandComplete{CommandTag: []byte("SELECT 1")},
&pgproto3.CloseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Parse{
Name: "stmt_name3",
Query: `SELECT $${"v":"a\\\nb"}$$::jsonb;`,
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.ParseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Bind{
PreparedStatement: "stmt_name3",
ResultFormatCodes: []int16{0},
},
&pgproto3.Execute{},
&pgproto3.Close{
ObjectType: 'P',
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.BindComplete{},
&pgproto3.DataRow{
Values: [][]byte{
[]byte(`{"v": "a\\\nb"}`),
},
},
&pgproto3.CommandComplete{CommandTag: []byte("SELECT 1")},
&pgproto3.CloseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Parse{
Name: "stmt_name4",
Query: `select json '{ "a": "dollar \\u0024 character" }' ->> 'a' as not_an_escape;`,
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.ParseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
{
Send: []pgproto3.FrontendMessage{
&pgproto3.Bind{
PreparedStatement: "stmt_name4",
ResultFormatCodes: []int16{0},
},
&pgproto3.Execute{},
&pgproto3.Close{
ObjectType: 'P',
},
&pgproto3.Sync{},
},
Receive: []pgproto3.BackendMessage{
&pgproto3.BindComplete{},
&pgproto3.DataRow{
Values: [][]byte{
[]byte(`dollar \u0024 character`),
},
},
&pgproto3.CommandComplete{CommandTag: []byte("SELECT 1")},
&pgproto3.CloseComplete{},
&pgproto3.ReadyForQuery{TxStatus: 'I'},
},
},
},
},
})
}