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

1582 lines
40 KiB
Go
Executable File

// 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"
"time"
"github.com/dolthub/go-mysql-server/sql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPreparedStatements(t *testing.T) {
RunScripts(t, preparedStatementTests)
}
func TestPreparedPgCatalog(t *testing.T) {
RunScripts(t, pgCatalogTests)
}
var preparedStatementTests = []ScriptTest{
{
Name: "Expressions without tables",
Assertions: []ScriptTestAssertion{
{
Query: "SELECT CONCAT($1::text, $2::text)",
BindVars: []any{"hello", "world"},
Expected: []sql.Row{
{"helloworld"},
},
},
{
Query: "SELECT $1::integer + $2::integer",
BindVars: []any{1, 2},
Expected: []sql.Row{
{3},
},
},
{
Query: "select $1 as test",
BindVars: []any{"hello"},
Expected: []sql.Row{
{"hello"},
},
},
},
},
{
Name: "Expressions with tables",
Assertions: []ScriptTestAssertion{
{
Query: "SELECT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = $1);",
BindVars: []any{"public"},
Expected: []sql.Row{{"t"}},
},
{
Query: "SELECT nspname FROM pg_namespace LIMIT $1;",
BindVars: []any{1},
Expected: []sql.Row{{"dolt"}},
},
{
Query: "SELECT nspname FROM pg_namespace OFFSET $1;",
BindVars: []any{1},
Expected: []sql.Row{
{"information_schema"},
{"pg_catalog"},
{"public"},
},
},
},
},
{
Name: "Integer insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{"1", "2", "3", "4"},
},
{
Query: "SELECT * FROM test order by pk;",
Expected: []sql.Row{
{1, 2},
{3, 4},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{"2"},
Expected: []sql.Row{
{1, 2},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{"3"},
Expected: []sql.Row{},
},
{
Query: "SELECT * FROM test WHERE v1 + $1 = $2;",
BindVars: []any{"1", "3"},
Expected: []sql.Row{
{1, 2},
},
},
{
Query: "SELECT * FROM test WHERE pk + v1 = $1;",
BindVars: []any{"3"},
Expected: []sql.Row{
{1, 2},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1::integer + $2::integer;",
BindVars: []any{"1", "3"},
Expected: []sql.Row{
{3, 4},
},
},
},
},
{
Name: "Integer insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, 2, 3, 4},
},
{
Query: "SELECT * FROM test order by pk;",
Expected: []sql.Row{
{1, 2},
{3, 4},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{2},
Expected: []sql.Row{
{1, 2},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{3},
Expected: []sql.Row{},
},
{
Query: "SELECT * FROM test WHERE v1 + $1 = $2;",
BindVars: []any{1, 3},
Expected: []sql.Row{
{1, 2},
},
},
{
Query: "SELECT * FROM test WHERE pk + v1 = $1;",
BindVars: []any{3},
Expected: []sql.Row{
{1, 2},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1::integer + $2::integer;",
BindVars: []any{1, 3},
Expected: []sql.Row{
{3, 4},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2) returning *",
BindVars: []any{2, nil},
Expected: []sql.Row{
{2, nil},
},
},
},
},
{
Name: "Integer types",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v2 SMALLINT, v4 INTEGER, v5 BIGINT);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2, $3, $4) returning *",
BindVars: []any{1, 10, 100, 1000},
Expected: []sql.Row{
{1, 10, 100, 1000},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2, $3, $4) returning *",
BindVars: []any{2, nil, nil, nil},
Expected: []sql.Row{
{2, nil, nil, nil},
},
},
},
},
{
Name: "Integer update",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, 2, 3, 4},
},
{
Query: "UPDATE test set v1 = $1 WHERE pk = $2;",
BindVars: []any{5, 1},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{5},
Expected: []sql.Row{
{1, 5},
},
},
},
},
{
Name: "Integer delete",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, 2, 3, 4},
},
{
Query: "DELETE FROM test WHERE pk = $1;",
BindVars: []any{1},
},
{
Query: "SELECT * FROM test order by 1;",
Expected: []sql.Row{
{3, 4},
},
},
},
},
{
Name: "String insert",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, s character varying(20));",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, "hello", 3, "goodbye"},
},
{
Query: "SELECT * FROM test order by pk;",
Expected: []sql.Row{
{1, "hello"},
{3, "goodbye"},
},
},
{
Query: "SELECT * FROM test WHERE s = $1;",
BindVars: []any{"hello"},
Expected: []sql.Row{
{1, "hello"},
},
},
{
Query: "SELECT * FROM test WHERE s = concat($1::text, $2::text);",
BindVars: []any{"he", "llo"},
Expected: []sql.Row{
{1, "hello"},
},
},
{
Query: "SELECT * FROM test WHERE concat(s, '!') = $1",
BindVars: []any{"hello!"},
Expected: []sql.Row{
{1, "hello"},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2) returning *",
BindVars: []any{2, nil},
Expected: []sql.Row{
{2, nil},
},
},
},
},
{
Name: "String update",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, s character varying(20));",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, "hello", 3, "goodbye"},
},
{
Query: "UPDATE test set s = $1 WHERE pk = $2;",
BindVars: []any{"new value", 1},
},
{
Query: "SELECT * FROM test WHERE s = $1;",
BindVars: []any{"new value"},
Expected: []sql.Row{
{1, "new value"},
},
},
},
},
{
Name: "String delete",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, s character varying(20));",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, "hello", 3, "goodbye"},
},
{
Query: "DELETE FROM test WHERE s = $1;",
BindVars: []any{"hello"},
},
{
Query: "SELECT * FROM test ORDER BY 1;",
Expected: []sql.Row{
{3, "goodbye"},
},
},
},
},
{
Name: "Float insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, f1 DOUBLE PRECISION);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{"1", "1.1", "3", "3.3"},
},
{
Query: "SELECT * FROM test ORDER BY 1;",
Expected: []sql.Row{
{1, 1.1},
{3, 3.3},
},
},
{
Query: "SELECT * FROM test WHERE f1 = $1;",
BindVars: []any{"1.1"},
Expected: []sql.Row{
{1, 1.1},
},
},
{
Query: "SELECT * FROM test WHERE f1 + $1 = $2;",
BindVars: []any{"1.0", "2.1"},
Expected: []sql.Row{
{1, 1.1},
},
},
{
Query: "SELECT * FROM test WHERE f1 = $1::decimal + $2::decimal;",
BindVars: []any{"1.0", "0.1"},
Expected: []sql.Row{
{1, 1.1},
},
},
},
},
{
Name: "Float insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, f1 DOUBLE PRECISION);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, 1.1, 3, 3.3},
},
{
Query: "SELECT * FROM test ORDER BY 1;",
Expected: []sql.Row{
{1, 1.1},
{3, 3.3},
},
},
{
Query: "SELECT * FROM test WHERE f1 = $1;",
BindVars: []any{1.1},
Expected: []sql.Row{
{1, 1.1},
},
},
{
Query: "SELECT * FROM test WHERE f1 + $1 = $2;",
BindVars: []any{1.0, 2.1},
Expected: []sql.Row{
{1, 1.1},
},
},
{
Query: "SELECT * FROM test WHERE f1 = $1::decimal + $2::decimal;",
BindVars: []any{1.0, 0.1},
Expected: []sql.Row{
{1, 1.1},
},
},
},
},
{
Name: "Float update",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, f1 DOUBLE PRECISION);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, 1.1, 3, 3.3},
},
{
Query: "UPDATE test set f1 = $1 WHERE f1 = $2;",
BindVars: []any{2.2, 1.1},
},
{
Query: "SELECT * FROM test WHERE f1 = $1;",
BindVars: []any{2.2},
Expected: []sql.Row{
{1, 2.2},
},
},
},
},
{
Name: "Float delete",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, f1 DOUBLE PRECISION);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, 1.1, 3, 3.3},
},
{
Query: "DELETE FROM test WHERE f1 = $1;",
BindVars: []any{1.1},
},
{
Query: "SELECT * FROM test order by 1;",
Expected: []sql.Row{
{3, 3.3},
},
},
},
},
{
Name: "Date insert, update, delete with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 DATE);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{"1", "2022-02-02", "3", "2024-04-01 -07"},
},
{
Query: "SELECT * FROM test order by pk;",
Expected: []sql.Row{
{1, "2022-02-02"},
{3, "2024-04-01"},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{"2022-02-02"},
Expected: []sql.Row{
{1, "2022-02-02"},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{"2022-02-03"},
Expected: []sql.Row{},
},
{
Query: "UPDATE test set v1 = $1 WHERE pk = $2;",
BindVars: []any{"2022-02-03", "1"},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{"2022-02-03"},
Expected: []sql.Row{
{1, "2022-02-03"},
},
},
{
Query: "DELETE FROM test WHERE pk = $1;",
BindVars: []any{"1"},
},
{
Query: "SELECT * FROM test order by 1;",
Expected: []sql.Row{
{3, "2024-04-01"},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2) returning *;",
BindVars: []any{"5", nil},
Expected: []sql.Row{
{5, nil},
},
},
},
},
{
Name: "Date insert, update, delete with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 DATE);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, Date("2022-02-02"), 3, Date("2024-04-01")},
},
{
Query: "SELECT * FROM test order by pk;",
Expected: []sql.Row{
{1, "2022-02-02"},
{3, "2024-04-01"},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{Date("2022-02-02")},
Expected: []sql.Row{
{1, "2022-02-02"},
},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{Date("2022-02-03")},
Expected: []sql.Row{},
},
{
Query: "UPDATE test set v1 = $1 WHERE pk = $2;",
BindVars: []any{Date("2022-02-03"), 1},
},
{
Query: "SELECT * FROM test WHERE v1 = $1;",
BindVars: []any{Date("2022-02-03")},
Expected: []sql.Row{
{1, "2022-02-03"},
},
},
{
Query: "DELETE FROM test WHERE pk = $1;",
BindVars: []any{1},
},
{
Query: "SELECT * FROM test order by 1;",
Expected: []sql.Row{
{3, "2024-04-01"},
},
},
},
},
{
Name: "Timestamp insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, t1 TIMESTAMP);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{
"1", "2023-01-15 14:30",
"2", "2024-12-25 09:15:30",
},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "2023-01-15 14:30:00"},
{2, "2024-12-25 09:15:30"},
},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{"2023-01-15 14:30"},
Expected: []sql.Row{
{1, "2023-01-15 14:30:00"},
},
},
{
Query: "UPDATE test SET t1 = $1 WHERE pk = $2;",
BindVars: []any{"2023-01-15 16:45", "1"},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{"2023-01-15 16:45"},
Expected: []sql.Row{
{1, "2023-01-15 16:45:00"},
},
},
{
Query: "DELETE FROM test WHERE t1 = $1;",
BindVars: []any{"2023-01-15 16:45"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, "2024-12-25 09:15:30"},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2) returning *;",
BindVars: []any{"3", nil},
Expected: []sql.Row{
{3, nil},
},
},
},
},
{
Name: "Timestamp insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, t1 TIMESTAMP);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{
1, Timestamp("2023-01-15 14:30:00"),
2, Timestamp("2024-12-25 09:15:30"),
},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "2023-01-15 14:30:00"},
{2, "2024-12-25 09:15:30"},
},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{Timestamp("2023-01-15 14:30:00")},
Expected: []sql.Row{
{1, "2023-01-15 14:30:00"},
},
},
{
Query: "UPDATE test SET t1 = $1 WHERE pk = $2;",
BindVars: []any{Timestamp("2023-01-15 16:45:00"), 1},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{Timestamp("2023-01-15 16:45:00")},
Expected: []sql.Row{
{1, "2023-01-15 16:45:00"},
},
},
{
Query: "DELETE FROM test WHERE t1 = $1;",
BindVars: []any{
Timestamp("2023-01-15 16:45:00")},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, "2024-12-25 09:15:30"},
},
},
},
},
{
Name: "Timestamp with timezone insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, t1 TIMESTAMPTZ);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{"1", "2023-01-15 14:30:00+00", "2", "2024-12-25 09:15:30-05"},
},
{
Query: "SELECT pk FROM test ORDER BY pk;",
Expected: []sql.Row{
{1},
{2},
},
},
{
Query: "SELECT pk FROM test WHERE t1 = $1;",
BindVars: []any{"2023-01-15 14:30:00+00"},
Expected: []sql.Row{
{1},
},
},
},
},
{
Name: "Timestamp with timezone insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, t1 TIMESTAMPTZ);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{
1, time.Date(2023, 1, 15, 14, 30, 0, 0, time.UTC), // +00 timezone
2, time.Date(2024, 12, 25, 9, 15, 30, 0, time.FixedZone("EST", -5*60*60)), // -05 timezone
},
},
{
Query: "SELECT pk FROM test ORDER BY pk;",
Expected: []sql.Row{
{1},
{2},
},
},
{
Query: "SELECT pk FROM test WHERE t1 = $1;",
BindVars: []any{time.Date(2023, 1, 15, 14, 30, 0, 0, time.UTC)},
Expected: []sql.Row{
{1},
},
},
},
},
{
Name: "Time insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, t1 TIME);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{"1", "14:30:00", "2", "09:15:30"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "14:30:00"},
{2, "09:15:30"},
},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{"14:30:00"},
Expected: []sql.Row{
{1, "14:30:00"},
},
},
{
Query: "UPDATE test SET t1 = $1 WHERE pk = $2;",
BindVars: []any{"16:45:00", "1"},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{"16:45:00"},
Expected: []sql.Row{
{1, "16:45:00"},
},
},
{
Query: "DELETE FROM test WHERE t1 = $1;",
BindVars: []any{"16:45:00"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, "09:15:30"},
},
},
},
},
{
Name: "Time insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, t1 TIME);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{1, time.Date(0, 1, 1, 14, 30, 0, 0, time.UTC), 2, time.Date(0, 1, 1, 9, 15, 30, 0, time.UTC)},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "14:30:00"},
{2, "09:15:30"},
},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{time.Date(0, 1, 1, 14, 30, 0, 0, time.UTC)},
Expected: []sql.Row{
{1, "14:30:00"},
},
},
{
Query: "UPDATE test SET t1 = $1 WHERE pk = $2;",
BindVars: []any{time.Date(0, 1, 1, 16, 45, 0, 0, time.UTC), 1},
},
{
Query: "SELECT * FROM test WHERE t1 = $1;",
BindVars: []any{time.Date(0, 1, 1, 16, 45, 0, 0, time.UTC)},
Expected: []sql.Row{
{1, "16:45:00"},
},
},
{
Query: "DELETE FROM test WHERE t1 = $1;",
BindVars: []any{time.Date(0, 1, 1, 16, 45, 0, 0, time.UTC)},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, "09:15:30"},
},
},
},
},
{
Name: "UUID insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, u1 UUID);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{"1", "550e8400-e29b-41d4-a716-446655440000", "2", "6ba7b810-9dad-11d1-80b4-00c04fd430c8"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "550e8400-e29b-41d4-a716-446655440000"},
{2, "6ba7b810-9dad-11d1-80b4-00c04fd430c8"},
},
},
{
Query: "SELECT * FROM test WHERE u1 = $1;",
BindVars: []any{"550e8400-e29b-41d4-a716-446655440000"},
Expected: []sql.Row{
{1, "550e8400-e29b-41d4-a716-446655440000"},
},
},
{
Query: "UPDATE test SET u1 = $1 WHERE pk = $2;",
BindVars: []any{"123e4567-e89b-12d3-a456-426614174000", "1"},
},
{
Query: "SELECT * FROM test WHERE u1 = $1;",
BindVars: []any{"123e4567-e89b-12d3-a456-426614174000"},
Expected: []sql.Row{
{1, "123e4567-e89b-12d3-a456-426614174000"},
},
},
{
Query: "DELETE FROM test WHERE u1 = $1;",
BindVars: []any{"123e4567-e89b-12d3-a456-426614174000"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, "6ba7b810-9dad-11d1-80b4-00c04fd430c8"},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2) returning *;",
BindVars: []any{"3", nil},
Expected: []sql.Row{
{3, nil},
},
},
},
},
{
Name: "UUID insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, u1 UUID);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4);",
BindVars: []any{
1, UUID("550e8400-e29b-41d4-a716-446655440000"),
2, UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "550e8400-e29b-41d4-a716-446655440000"},
{2, "6ba7b810-9dad-11d1-80b4-00c04fd430c8"},
},
},
{
Query: "SELECT * FROM test WHERE u1 = $1;",
BindVars: []any{UUID("550e8400-e29b-41d4-a716-446655440000")},
Expected: []sql.Row{
{1, "550e8400-e29b-41d4-a716-446655440000"},
},
},
{
Query: "UPDATE test SET u1 = $1 WHERE pk = $2;",
BindVars: []any{UUID("123e4567-e89b-12d3-a456-426614174000"), 1},
},
{
Query: "SELECT * FROM test WHERE u1 = $1;",
BindVars: []any{UUID("123e4567-e89b-12d3-a456-426614174000")},
Expected: []sql.Row{
{1, "123e4567-e89b-12d3-a456-426614174000"},
},
},
{
Query: "DELETE FROM test WHERE u1 = $1;",
BindVars: []any{UUID("123e4567-e89b-12d3-a456-426614174000")},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, "6ba7b810-9dad-11d1-80b4-00c04fd430c8"},
},
},
},
},
{
Name: "Numeric/Decimal insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, n1 NUMERIC(10,2), n2 DECIMAL(8,3));",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2, $3), ($4, $5, $6);",
BindVars: []any{"1", "123.45", "67.890", "2", "999.99", "12.345"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, Numeric("123.45"), Numeric("67.890")},
{2, Numeric("999.99"), Numeric("12.345")},
},
},
{
Query: "SELECT * FROM test WHERE n1 = $1;",
BindVars: []any{"123.45"},
Expected: []sql.Row{
{1, Numeric("123.45"), Numeric("67.890")},
},
},
{
Query: "UPDATE test SET n1 = $1, n2 = $2 WHERE pk = $3;",
BindVars: []any{"456.78", "98.765", "1"},
},
{
Query: "SELECT * FROM test WHERE n1 = $1;",
BindVars: []any{"456.78"},
Expected: []sql.Row{
{1, Numeric("456.78"), Numeric("98.765")},
},
},
{
Query: "DELETE FROM test WHERE n2 = $1;",
BindVars: []any{"98.765"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, Numeric("999.99"), Numeric("12.345")},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2, $3) returning *;",
BindVars: []any{"3", nil, nil},
Expected: []sql.Row{
{3, nil, nil},
},
},
},
},
{
Name: "Numeric/Decimal insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, n1 NUMERIC(10,2), n2 DECIMAL(8,3));",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2, $3), ($4, $5, $6);",
BindVars: []any{
1, Numeric("123.45"), Numeric("67.890"),
2, Numeric("999.99"), Numeric("12.345")},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, Numeric("123.45"), Numeric("67.890")},
{2, Numeric("999.99"), Numeric("12.345")},
},
},
{
Query: "SELECT * FROM test WHERE n1 = $1;",
BindVars: []any{Numeric("123.45")},
Expected: []sql.Row{
{1, Numeric("123.45"), Numeric("67.890")},
},
},
{
Query: "UPDATE test SET n1 = $1, n2 = $2 WHERE pk = $3;",
BindVars: []any{Numeric("456.78"), Numeric("98.765"), 1},
},
{
Query: "SELECT * FROM test WHERE n1 = $1;",
BindVars: []any{Numeric("456.78")},
Expected: []sql.Row{
{1, Numeric("456.78"), Numeric("98.765")},
},
},
{
Query: "DELETE FROM test WHERE n2 = $1;",
BindVars: []any{Numeric("98.765")},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{2, Numeric("999.99"), Numeric("12.345")},
},
},
},
},
{
Name: "Boolean insert with string bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, b1 BOOLEAN);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4), ($5, $6);",
BindVars: []any{"1", "true", "2", "false", "3", "true"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "t"},
{2, "f"},
{3, "t"},
},
},
{
Query: "SELECT * FROM test WHERE b1 = $1;",
BindVars: []any{"true"},
Expected: []sql.Row{
{1, "t"},
{3, "t"},
},
},
{
Query: "SELECT * FROM test WHERE b1 = $1;",
BindVars: []any{"false"},
Expected: []sql.Row{
{2, "f"},
},
},
{
Query: "UPDATE test SET b1 = $1 WHERE pk = $2;",
BindVars: []any{"false", "1"},
},
{
Query: "SELECT * FROM test WHERE b1 = $1 ORDER BY pk;",
BindVars: []any{"false"},
Expected: []sql.Row{
{1, "f"},
{2, "f"},
},
},
{
Query: "DELETE FROM test WHERE b1 = $1;",
BindVars: []any{"false"},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{3, "t"},
},
},
{
Query: "INSERT INTO test VALUES ($1, $2) returning *;",
BindVars: []any{"4", nil},
Expected: []sql.Row{
{4, nil},
},
},
},
},
{
Name: "Boolean insert with binary bindvars",
SetUpScript: []string{
"drop table if exists test",
"CREATE TABLE test (pk BIGINT PRIMARY KEY, b1 BOOLEAN);",
},
Assertions: []ScriptTestAssertion{
{
Query: "INSERT INTO test VALUES ($1, $2), ($3, $4), ($5, $6);",
BindVars: []any{1, true, 2, false, 3, true},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{1, "t"},
{2, "f"},
{3, "t"},
},
},
{
Query: "SELECT * FROM test WHERE b1 = $1;",
BindVars: []any{true},
Expected: []sql.Row{
{1, "t"},
{3, "t"},
},
},
{
Query: "SELECT * FROM test WHERE b1 = $1;",
BindVars: []any{false},
Expected: []sql.Row{
{2, "f"},
},
},
{
Query: "UPDATE test SET b1 = $1 WHERE pk = $2;",
BindVars: []any{false, 1},
},
{
Query: "SELECT * FROM test WHERE b1 = $1 ORDER BY pk;",
BindVars: []any{false},
Expected: []sql.Row{
{1, "f"},
{2, "f"},
},
},
{
Query: "DELETE FROM test WHERE b1 = $1;",
BindVars: []any{false},
},
{
Query: "SELECT * FROM test ORDER BY pk;",
Expected: []sql.Row{
{3, "t"},
},
},
},
},
{
Name: "pg_get_viewdef function",
SetUpScript: []string{
"CREATE TABLE test (id int, name text)",
"INSERT INTO test VALUES (1,'desk'), (2,'chair')",
"CREATE VIEW test_view AS SELECT name FROM test",
},
Assertions: []ScriptTestAssertion{
{
Query: `select pg_get_viewdef($1::regclass);`,
BindVars: []any{"test_view"},
Expected: []sql.Row{{"SELECT name FROM test"}},
},
},
},
{
Name: "insert returning",
SetUpScript: []string{
"CREATE TABLE test (id serial, name text)",
},
Assertions: []ScriptTestAssertion{
{
Query: `INSERT INTO test (name) VALUES ($1) RETURNING id;`,
BindVars: []any{"test_name"},
Expected: []sql.Row{{1}},
},
},
},
{
Name: "define placeholder unordered",
Assertions: []ScriptTestAssertion{
{
Query: "SELECT $3::text, $1::integer + $2::integer",
BindVars: []any{1, 3, "hi"},
Expected: []sql.Row{
{"hi", 4},
},
},
},
},
{
Name: "Bytea with binary bindvars",
SetUpScript: []string{
"CREATE TABLE t_bytea (id INTEGER primary key, v1 BYTEA);",
"INSERT INTO t_bytea VALUES (1, E'\\\\xDEADBEEF'), (2, '\\xC0FFEE'), (3, ''), (4, NULL);",
},
Assertions: []ScriptTestAssertion{
{
Query: "SELECT * FROM t_bytea ORDER BY id;",
Expected: []sql.Row{
{1, []byte{0xDE, 0xAD, 0xBE, 0xEF}},
{2, []byte{0xC0, 0xFF, 0xEE}},
{3, []byte{}},
{4, nil},
},
},
{
Query: "SELECT * FROM t_bytea WHERE v1 = $1 ORDER BY id;",
BindVars: []any{[]byte{0xC0, 0xFF, 0xEE}},
Expected: []sql.Row{
{2, []byte{0xC0, 0xFF, 0xEE}},
},
},
{
Query: "UPDATE t_bytea SET v1 = $1 WHERE id = $2;",
BindVars: []any{`\xC0FFEE`, 4},
},
{
Query: "SELECT * FROM t_bytea WHERE v1 = $1 ORDER BY id;",
BindVars: []any{[]byte{0xC0, 0xFF, 0xEE}},
Expected: []sql.Row{
{2, []byte{0xC0, 0xFF, 0xEE}},
{4, []byte{0xC0, 0xFF, 0xEE}},
},
},
{
Query: "DELETE FROM t_bytea WHERE v1 = $1;",
BindVars: []any{[]byte{0xDE, 0xAD, 0xBE, 0xEF}},
},
{
Query: "SELECT * FROM t_bytea ORDER BY id;",
Expected: []sql.Row{
{2, []byte{0xC0, 0xFF, 0xEE}},
{3, []byte{}},
{4, []byte{0xC0, 0xFF, 0xEE}},
},
},
{
Query: "INSERT INTO t_bytea VALUES ($1, $2) returning *;",
BindVars: []any{5, nil},
Expected: []sql.Row{
{5, nil},
},
},
},
},
{
Name: "Bind parameter to compatible different types",
SetUpScript: []string{
"CREATE TABLE text_test (id text, code varchar(10))",
"CREATE TABLE num_test(small int2, large int8, other float4)",
"INSERT INTO text_test values ('foo', 'bar'), ('bar', 'foo')",
"INSERT INTO num_test values (0,0,0), (1, 2, 1.5)",
},
Assertions: []ScriptTestAssertion{
{
Query: "SELECT * FROM text_test where id = any($1) and code = any($1)",
BindVars: []any{[]string{"foo", "bar"}},
Expected: []sql.Row{
{"foo", "bar"},
{"bar", "foo"},
},
},
{
Query: "SELECT * from num_test where small = $1 and large = $1 and other = $1",
BindVars: []any{0},
Expected: []sql.Row{
{0, 0, float64(0)},
},
},
{
Query: "SELECT * from num_test where small = $1::INTEGER and large = $1::INTEGER and other = $1::INTEGER",
BindVars: []any{0},
Expected: []sql.Row{
{0, 0, float64(0)},
},
},
{
Query: "SELECT * FROM num_test where small = $1 or other = $1",
BindVars: []any{1.5},
Expected: []sql.Row{
{1, 2, 1.5},
},
},
},
},
{
Name: "Cannot bind parameter to column with incompatible type",
SetUpScript: []string{
"CREATE TABLE text_test (fullname text, id int)",
},
Assertions: []ScriptTestAssertion{
{
Query: "SELECT * FROM text_test where fullname = $1 and id = $1",
BindVars: []any{1},
ExpectedErr: "parameter v1 is used for incompatible types: text and integer",
},
},
},
{
Name: "merge join planning with bindvar equality filter on indexed join column",
SetUpScript: []string{
"CREATE TABLE t1 (pk INT PRIMARY KEY, fk INT, val INT);",
"CREATE INDEX t1_fk_idx ON t1(fk);",
"CREATE TABLE t2 (pk INT PRIMARY KEY, val INT);",
"INSERT INTO t1 VALUES (1, 1, 100), (2, 2, 200), (3, 3, 300);",
"INSERT INTO t2 VALUES (1, 10), (2, 20), (3, 30);",
},
Assertions: []ScriptTestAssertion{
{
Query: "SELECT t1.val, t2.val FROM t1 JOIN t2 ON t1.fk = t2.pk WHERE t1.fk = $1;",
BindVars: []any{1},
Expected: []sql.Row{
{100, 10},
},
},
},
},
}
var pgCatalogTests = []ScriptTest{
{
Name: "pg_namespace",
SetUpScript: []string{
`CREATE SCHEMA testschema;`,
},
Assertions: []ScriptTestAssertion{
{
Query: `SELECT * FROM "pg_catalog"."pg_namespace" WHERE nspname=$1;`,
BindVars: []any{"testschema"},
Expected: []sql.Row{{2638679668, "testschema", 0, nil}},
},
{
Query: `SELECT * FROM "pg_catalog"."pg_namespace" WHERE oid=$1;`,
BindVars: []any{2638679668},
Expected: []sql.Row{{2638679668, "testschema", 0, nil}},
},
},
},
{
Name: "pg_tables",
SetUpScript: []string{
`CREATE TABLE testing (pk INT primary key, v1 INT);`,
},
Assertions: []ScriptTestAssertion{
{
Query: `SELECT * FROM "pg_catalog"."pg_tables" WHERE tablename=$1;`,
BindVars: []any{"testing"},
Expected: []sql.Row{{"public", "testing", "postgres", nil, "t", "f", "f", "f"}},
},
{
Query: `SELECT count(*) FROM "pg_catalog"."pg_tables" WHERE schemaname=$1;`,
BindVars: []any{"pg_catalog"},
Expected: []sql.Row{{139}},
},
},
},
{
Name: "pg_class",
SetUpScript: []string{
`CREATE SCHEMA testschema;`,
`CREATE TABLE testschema.testtable (id int primary key, v1 text)`,
},
Assertions: []ScriptTestAssertion{
{
Query: "select distinct relnamespace from pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname=$1;",
BindVars: []any{"testschema"},
Expected: []sql.Row{{2638679668}},
},
// TODO: when this test is run in isolation without the query above, the below query returns no rows. This is
// because the process of converting an OID to its internal ID can only proceed in one direction: from internal
// to OID. When the above query runs, it causes the internal ID for the testschema namespace to be cached,
// allowing the reverse lookup to succeed in subsequent queries. For OIDs that have not yet been cached in this
// manner, lookups by their OID will fail. This doesn't impact all queries since many of them get an index
// lookup on OID, which has the side effect of converting everything to numeric IDs anyway. But for queries
// that use a normal comparison function for an OID literal value, the conversion to an internal ID of the
// appropriate type (e.g. id.Namespace) cannot happen in the |oidin| function in some cases because the internal
// to OID mapping hasn't yet been established for that schema element, so the comparison fails, yielding
// incorrect results.
// To fix this, we need to correctly seed the internal ID cache with all schema elements in the database.
{
Query: `SELECT c.oid,pg_catalog.pg_get_expr(c.relpartbound, c.oid) as partition_expr, pg_catalog.pg_get_partkeydef(c.oid) as partition_key
FROM pg_catalog.pg_class c
WHERE c.relnamespace=$1 AND c.relkind not in ('i','I','c') and c.oid not in (select oid from pg_catalog.pg_class where left(relname, 5) = 'dolt_');`,
BindVars: []any{2638679668},
Expected: []sql.Row{{1712283605, nil, ""}},
},
{
Query: `SELECT c.oid,d.description,pg_catalog.pg_get_expr(c.relpartbound, c.oid) as partition_expr, pg_catalog.pg_get_partkeydef(c.oid) as partition_key
FROM pg_catalog.pg_class c
LEFT OUTER JOIN pg_catalog.pg_description d ON d.objoid=c.oid AND d.objsubid=0 AND d.classoid='pg_class'::regclass
WHERE c.relnamespace=$1 AND c.relkind not in ('i','I','c') and c.oid not in (select oid from pg_catalog.pg_class where left(relname, 5) = 'dolt_');`,
BindVars: []any{2638679668},
Expected: []sql.Row{{1712283605, nil, nil, ""}},
},
{
Query: `SELECT d.description from pg_catalog.pg_description d WHERE d.classoid='pg_class'::regclass`,
// TODO: add expected values (pg_description not yet implemented)
},
{
Query: `select c.oid,pg_catalog.pg_total_relation_size(c.oid) as total_rel_size,pg_catalog.pg_relation_size(c.oid) as rel_size FROM pg_class c WHERE c.relnamespace=$1 and c.oid not in (select oid from pg_catalog.pg_class where left(relname, 5) = 'dolt_');`,
BindVars: []any{2638679668},
Expected: []sql.Row{{444447634, 0, 0}, {1712283605, 0, 0}},
},
{
Query: `SELECT c.relname, a.attrelid, a.attname, a.atttypid, pg_catalog.pg_get_expr(ad.adbin, ad.adrelid, true) as def_value,dsc.description,dep.objid
FROM pg_catalog.pg_attribute a
INNER JOIN pg_catalog.pg_class c ON (a.attrelid=c.oid)
LEFT OUTER JOIN pg_catalog.pg_attrdef ad ON (a.attrelid=ad.adrelid AND a.attnum = ad.adnum)
LEFT OUTER JOIN pg_catalog.pg_description dsc ON (c.oid=dsc.objoid AND a.attnum = dsc.objsubid)
LEFT OUTER JOIN pg_depend dep on dep.refobjid = a.attrelid AND dep.deptype = 'i' and dep.refobjsubid = a.attnum and dep.classid = dep.refclassid
WHERE NOT a.attisdropped AND c.relkind not in ('i','I','c') AND c.oid=$1 ORDER BY a.attnum`,
BindVars: []any{1712283605},
Expected: []sql.Row{{"testtable", 1712283605, "id", 23, nil, nil, nil}, {"testtable", 1712283605, "v1", 25, nil, nil, nil}},
},
},
},
}
func TestPreparedErrorHandling(t *testing.T) {
tt := ScriptTest{
Name: "error handling doesn't foul session",
SetUpScript: []string{
"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT);",
"insert into test values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7);",
},
Assertions: []ScriptTestAssertion{
{
Query: "select v1 from doesNotExist where pk = 1;",
ExpectedErr: "table not found",
},
{
Query: "select v1 from test where pk = 1;",
Expected: []sql.Row{{1}},
},
{
Query: "select v1 from test where pk = 2;",
Expected: []sql.Row{{2}},
},
{
Query: "select v1 from test where pk = 3;",
Expected: []sql.Row{{3}},
},
{
Query: "select v1 from test where pk = 4;",
Expected: []sql.Row{{4}},
},
{
Query: "select v1 from test where pk = 5;",
Expected: []sql.Row{{5}},
},
{
Query: "select v1 from test where pk = 6;",
Expected: []sql.Row{{6}},
},
{
Query: "select v1 from test where pk = 7;",
Expected: []sql.Row{{7}},
},
},
}
RunScriptN(t, tt, 20)
}
// RunScriptN runs the assertions of the given script n times using the same connection
func RunScriptN(t *testing.T, script ScriptTest, n int) {
scriptDatabase := script.Database
if len(scriptDatabase) == 0 {
scriptDatabase = "postgres"
}
ctx, conn, controller := CreateServer(t, scriptDatabase)
defer func() {
conn.Close(ctx)
controller.Stop()
err := controller.WaitForStop()
require.NoError(t, err)
}()
// Run the setup
for _, query := range script.SetUpScript {
rows, err := conn.Query(ctx, query)
require.NoError(t, err)
_, _, err = ReadRows(rows, true)
assert.NoError(t, err)
}
for i := 0; i < n; i++ {
t.Run(script.Name, func(t *testing.T) {
// Run the assertions
for _, assertion := range script.Assertions {
t.Run(assertion.Query, func(t *testing.T) {
if assertion.Skip {
t.Skip("Skip has been set in the assertion")
}
rows, err := conn.Query(ctx, assertion.Query)
if err == nil {
defer rows.Close()
}
var errorSeen string
if assertion.ExpectedErr == "" {
require.NoError(t, err)
} else if err != nil {
errorSeen = err.Error()
}
if errorSeen == "" {
foundRows, foundRawRows, err := ReadRows(rows, true)
if assertion.ExpectedErr == "" {
require.NoError(t, err)
if assertion.ExpectedRaw != nil {
assert.Equal(t, assertion.ExpectedRaw, foundRawRows)
} else {
assert.Equal(t, NormalizeExpectedRow(rows.FieldDescriptions(), assertion.Expected), foundRows)
}
} else if err != nil {
errorSeen = err.Error()
}
}
if assertion.ExpectedErr != "" {
require.False(t, errorSeen == "", "Expected error but got none")
assert.Contains(t, errorSeen, assertion.ExpectedErr)
}
})
}
})
}
}