diff --git a/go.mod b/go.mod index 6571b4f943..1595f0c4db 100644 --- a/go.mod +++ b/go.mod @@ -8,11 +8,11 @@ require ( github.com/PuerkitoBio/goquery v1.8.1 github.com/cockroachdb/apd/v2 v2.0.3-0.20200518165714-d020e156310a github.com/cockroachdb/errors v1.7.5 - github.com/dolthub/dolt/go v0.40.5-0.20240919183209-28badac50053 + github.com/dolthub/dolt/go v0.40.5-0.20240924203939-2f79023217f7 github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20240827111219-e4bb9ca3442d github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 github.com/dolthub/go-icu-regex v0.0.0-20240916130659-0118adc6b662 - github.com/dolthub/go-mysql-server v0.18.2-0.20240920224603-6f1a8518b048 + github.com/dolthub/go-mysql-server v0.18.2-0.20240923181307-5aacdb13e45a github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 github.com/dolthub/vitess v0.0.0-20240919225659-2ad81685e772 github.com/fatih/color v1.13.0 diff --git a/go.sum b/go.sum index 8374508b2f..3337577554 100644 --- a/go.sum +++ b/go.sum @@ -214,8 +214,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dolthub/dolt/go v0.40.5-0.20240919183209-28badac50053 h1:R0nHYkk/1a6BuDwcrGVhtJANqFmKmAfMGK0cUwo1/P0= -github.com/dolthub/dolt/go v0.40.5-0.20240919183209-28badac50053/go.mod h1:OkulohTdSGOFzq3YR3NxVxYLU4Z2GLUaYL8fb/f8cho= +github.com/dolthub/dolt/go v0.40.5-0.20240924203939-2f79023217f7 h1:Tgw1FOKBmOd0C+jZ/8nJuTwCdc5Sac8rlZLu6K0Pz70= +github.com/dolthub/dolt/go v0.40.5-0.20240924203939-2f79023217f7/go.mod h1:1HBB+xUaDISZ6GrUmiiD//Ij5wExAAV1nPDpU4xPrmg= github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20240827111219-e4bb9ca3442d h1:RZkQeYOrDrOWzCxaP2ttkvg4E2TM9n8lnEsIBLKjqkM= github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20240827111219-e4bb9ca3442d/go.mod h1:L5RDYZbC9BBWmoU2+TjTekeqqhFXX5EqH9ln00O0stY= github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww= @@ -224,8 +224,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20240916130659-0118adc6b662 h1:aC17hZD6iwzBwwfO5M+3oBT5E5gGRiQPdn+vzpDXqIA= github.com/dolthub/go-icu-regex v0.0.0-20240916130659-0118adc6b662/go.mod h1:KPUcpx070QOfJK1gNe0zx4pA5sicIK1GMikIGLKC168= -github.com/dolthub/go-mysql-server v0.18.2-0.20240920224603-6f1a8518b048 h1:cYdcm/bKRAupP51QfcWhelgDBNg1r36OKlAf7+meU4E= -github.com/dolthub/go-mysql-server v0.18.2-0.20240920224603-6f1a8518b048/go.mod h1:lGbU2bK+QNnlETdUjOOaE+UnlEUu31VaQOFKAFGyZN4= +github.com/dolthub/go-mysql-server v0.18.2-0.20240923181307-5aacdb13e45a h1:rpCmZj332eiBbzsHsq3Sj5AWzl3Q7szDObwI49UqA8Y= +github.com/dolthub/go-mysql-server v0.18.2-0.20240923181307-5aacdb13e45a/go.mod h1:lGbU2bK+QNnlETdUjOOaE+UnlEUu31VaQOFKAFGyZN4= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q= github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE= diff --git a/server/ast/alter_table.go b/server/ast/alter_table.go index 451401620f..64da6f42a0 100644 --- a/server/ast/alter_table.go +++ b/server/ast/alter_table.go @@ -27,7 +27,22 @@ func nodeAlterTable(node *tree.AlterTable) (*vitess.AlterTable, error) { if node == nil { return nil, nil } - return nil, fmt.Errorf("ALTER TABLE is not yet supported") + + treeTableName := node.Table.ToTableName() + tableName, err := nodeTableName(&treeTableName) + if err != nil { + return nil, err + } + + statements, err := nodeAlterTableCmds(node.Cmds, tableName, node.IfExists) + if err != nil { + return nil, err + } + + return &vitess.AlterTable{ + Table: tableName, + Statements: statements, + }, nil } // nodeAlterTableSetSchema handles *tree.AlterTableSetSchema nodes. @@ -37,3 +52,58 @@ func nodeAlterTableSetSchema(node *tree.AlterTableSetSchema) (vitess.Statement, } return nil, fmt.Errorf("ALTER TABLE SET SCHEMA is not yet supported") } + +// nodeAlterTableCmds converts tree.AlterTableCmds into a slice of vitess.DDL +// instances that can be executed by GMS. |tableName| identifies the table +// being altered, and |ifExists| indicates whether the IF EXISTS clause was +// specified. +func nodeAlterTableCmds( + node tree.AlterTableCmds, + tableName vitess.TableName, + ifExists bool) ([]*vitess.DDL, error) { + + if len(node) == 0 { + return nil, fmt.Errorf("no commands specified for ALTER TABLE statement") + } else if len(node) > 1 { + return nil, fmt.Errorf("ALTER TABLE with multiple commands is not yet supported") + } + + vitessDdlCmds := make([]*vitess.DDL, 0, len(node)) + for _, cmd := range node { + switch cmd := cmd.(type) { + case *tree.AlterTableAddConstraint: + statement, err := nodeAlterTableAddConstraint(cmd, tableName, ifExists) + if err != nil { + return nil, err + } + vitessDdlCmds = append(vitessDdlCmds, statement) + + default: + return nil, fmt.Errorf("ALTER TABLE with unsupported command type %T", cmd) + } + } + + return vitessDdlCmds, nil +} + +// nodeAlterTableAddConstraint converts a tree.AlterTableAddConstraint instance +// into a vitess.DDL instance that can be executed by GMS. |tableName| identifies +// the table being altered, and |ifExists| indicates whether the IF EXISTS clause +// was specified. +func nodeAlterTableAddConstraint( + node *tree.AlterTableAddConstraint, + tableName vitess.TableName, + ifExists bool) (*vitess.DDL, error) { + + if node.ValidationBehavior == tree.ValidationSkip { + return nil, fmt.Errorf("NOT VALID is not supported yet") + } + + switch constraintDef := node.ConstraintDef.(type) { + case *tree.UniqueConstraintTableDef: + return nodeUniqueConstraintTableDef(constraintDef, tableName, ifExists) + default: + return nil, fmt.Errorf("ALTER TABLE with unsupported constraint "+ + "definition type %T", node) + } +} diff --git a/server/ast/constraint_table_def.go b/server/ast/constraint_table_def.go new file mode 100644 index 0000000000..6c1693a5c7 --- /dev/null +++ b/server/ast/constraint_table_def.go @@ -0,0 +1,65 @@ +// Copyright 2024 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 ast + +import ( + "fmt" + + vitess "github.com/dolthub/vitess/go/vt/sqlparser" + + "github.com/dolthub/doltgresql/postgres/parser/sem/tree" +) + +// nodeUniqueConstraintTableDef converts a tree.UniqueConstraintTableDef instance +// into a vitess.DDL instance that can be executed by GMS. |tableName| identifies +// the table being altered, and |ifExists| indicates whether the IF EXISTS clause +// was specified. +func nodeUniqueConstraintTableDef( + node *tree.UniqueConstraintTableDef, + tableName vitess.TableName, + ifExists bool) (*vitess.DDL, error) { + + if len(node.IndexParams.StorageParams) > 0 { + return nil, fmt.Errorf("STORAGE parameters not yet supported for indexes") + } + + if node.IndexParams.Tablespace != "" { + return nil, fmt.Errorf("TABLESPACE is not yet supported") + } + + if node.NullsNotDistinct { + return nil, fmt.Errorf("NULLS NOT DISTINCT is not yet supported") + } + + columns, err := nodeIndexElemList(node.Columns) + if err != nil { + return nil, err + } + + if node.PrimaryKey { + return &vitess.DDL{ + Action: "alter", + Table: tableName, + IfExists: ifExists, + IndexSpec: &vitess.IndexSpec{ + Action: "create", + Type: "primary", + Columns: columns, + }, + }, nil + } else { + return nil, fmt.Errorf("Only PRIMARY KEY constraints are supported currently") + } +} diff --git a/server/ast/index_elem.go b/server/ast/index_elem.go new file mode 100644 index 0000000000..95a739261a --- /dev/null +++ b/server/ast/index_elem.go @@ -0,0 +1,73 @@ +// 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 ast + +import ( + "fmt" + + vitess "github.com/dolthub/vitess/go/vt/sqlparser" + "github.com/sirupsen/logrus" + + "github.com/dolthub/doltgresql/postgres/parser/sem/tree" +) + +// nodeIndexElemList converts a tree.IndexElemList to a slice of vitess.IndexColumn. +func nodeIndexElemList(node tree.IndexElemList) ([]*vitess.IndexColumn, error) { + vitessIndexColumns := make([]*vitess.IndexColumn, 0, len(node)) + for _, inputColumn := range node { + if inputColumn.Expr != nil { + return nil, fmt.Errorf("expression index attribute is not yet supported") + } + if inputColumn.Collation != "" { + return nil, fmt.Errorf("index attribute collation is not yet supported") + } + if inputColumn.OpClass != nil { + return nil, fmt.Errorf("index attribute operator class is not yet supported") + } + if inputColumn.ExcludeOp != nil { + return nil, fmt.Errorf("index attribute exclude operator is not yet supported") + } + + switch inputColumn.Direction { + case tree.DefaultDirection: + // Defaults to ASC + case tree.Ascending: + // The only default supported in GMS for now + case tree.Descending: + logrus.Warn("descending indexes are not yet supported, ignoring sort order") + default: + return nil, fmt.Errorf("unknown index sorting direction encountered") + } + + switch inputColumn.NullsOrder { + case tree.DefaultNullsOrder: + // TODO: the default NULL order is reversed compared to MySQL, so the default is technically always wrong. + // To prevent choking on every index, we allow this to proceed (even with incorrect results) for now. + case tree.NullsFirst: + // The only form supported in GMS for now + case tree.NullsLast: + return nil, fmt.Errorf("NULLS LAST for indexes is not yet supported") + default: + return nil, fmt.Errorf("unknown NULL ordering for index") + } + + vitessIndexColumns = append(vitessIndexColumns, &vitess.IndexColumn{ + Column: vitess.NewColIdent(string(inputColumn.Column)), + Order: vitess.AscScr, + }) + } + + return vitessIndexColumns, nil +} diff --git a/server/ast/index_table_def.go b/server/ast/index_table_def.go index 47d82902a7..90a1d3bf74 100644 --- a/server/ast/index_table_def.go +++ b/server/ast/index_table_def.go @@ -18,7 +18,6 @@ import ( "fmt" vitess "github.com/dolthub/vitess/go/vt/sqlparser" - "github.com/sirupsen/logrus" "github.com/dolthub/doltgresql/postgres/parser/sem/tree" ) @@ -38,49 +37,12 @@ func nodeIndexTableDef(node *tree.IndexTableDef) (*vitess.IndexDefinition, error if node.IndexParams.Tablespace != "" { return nil, fmt.Errorf("tablespace is not yet supported") } - columns := make([]*vitess.IndexColumn, len(node.Columns)) - for i, indexElem := range node.Columns { - if indexElem.Expr != nil { - return nil, fmt.Errorf("expression index attribute is not yet supported") - } - if indexElem.Collation != "" { - return nil, fmt.Errorf("index attribute collation is not yet supported") - } - if indexElem.OpClass != nil { - return nil, fmt.Errorf("index attribute operator class is not yet supported") - } - switch indexElem.Direction { - case tree.DefaultDirection: - // Defaults to ASC - case tree.Ascending: - // The only default supported in GMS for now - case tree.Descending: - logrus.Warn("descending indexes are not yet supported, ignoring sort order") - default: - return nil, fmt.Errorf("unknown index sorting direction encountered") - } - if indexElem.Direction == tree.Descending { - logrus.Warn("descending indexes are not yet supported, ignoring sort order") - } - switch indexElem.NullsOrder { - case tree.DefaultNullsOrder: - // TODO: the default NULL order is reversed compared to MySQL, so the default is technically always wrong. - // To prevent choking on every index, we allow this to proceed (even with incorrect results) for now. - case tree.NullsFirst: - // The only form supported in GMS for now - case tree.NullsLast: - return nil, fmt.Errorf("NULLS LAST for indexes is not yet supported") - default: - return nil, fmt.Errorf("unknown NULL ordering for index") - } - if indexElem.ExcludeOp != nil { - return nil, fmt.Errorf("index attribute exclude operator is not yet supported") - } - columns[i] = &vitess.IndexColumn{ - Column: vitess.NewColIdent(string(indexElem.Column)), - Order: vitess.AscScr, - } + + columns, err := nodeIndexElemList(node.Columns) + if err != nil { + return nil, err } + return &vitess.IndexDefinition{ Info: &vitess.IndexInfo{ Type: "", diff --git a/testing/go/alter_table_test.go b/testing/go/alter_table_test.go new file mode 100644 index 0000000000..4cc2923064 --- /dev/null +++ b/testing/go/alter_table_test.go @@ -0,0 +1,72 @@ +// Copyright 2024 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 TestAlterTableAddPrimaryKey(t *testing.T) { + RunScripts(t, []ScriptTest{ + { + Name: "Add Primary Key", + SetUpScript: []string{ + "CREATE TABLE test1 (a INT, b INT);", + "CREATE TABLE test2 (a INT, b INT, c INT);", + "CREATE TABLE pkTable1 (a INT PRIMARY KEY);", + "CREATE TABLE duplicateRows (a INT, b INT);", + "INSERT INTO duplicateRows VALUES (1, 2), (1, 2);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "ALTER TABLE test1 ADD PRIMARY KEY (a);", + Expected: []sql.Row{}, + }, + { + // Test the pk by inserting a duplicate value + Query: "INSERT into test1 values (1, 2), (1, 3);", + ExpectedErr: "duplicate primary key", + }, + { + Query: "ALTER TABLE test2 ADD PRIMARY KEY (a, b);", + Expected: []sql.Row{}, + }, + { + // Test the pk by inserting a duplicate value + Query: "INSERT into test2 values (1, 2, 3), (1, 2, 4);", + ExpectedErr: "duplicate primary key", + }, + { + Query: "ALTER TABLE pkTable1 ADD PRIMARY KEY (a);", + ExpectedErr: "Multiple primary keys defined", + }, + { + Query: "ALTER TABLE duplicateRows ADD PRIMARY KEY (a);", + ExpectedErr: "duplicate primary key", + }, + { + // TODO: This statement fails in analysis, because it can't find a table named + // doesNotExist – since IF EXISTS is specified, the analyzer should skip + // errors on resolving the table in this case. + Skip: true, + Query: "ALTER TABLE IF EXISTS doesNotExist ADD PRIMARY KEY (a, b);", + Expected: []sql.Row{}, + }, + }, + }, + }) +} diff --git a/testing/go/dolt_functions_test.go b/testing/go/dolt_functions_test.go index 8575c663af..024293f896 100755 --- a/testing/go/dolt_functions_test.go +++ b/testing/go/dolt_functions_test.go @@ -52,5 +52,144 @@ func TestDoltFunctions(t *testing.T) { }, }, }, + { + Name: "smoke test select dolt_merge", + SetUpScript: []string{ + "CREATE TABLE t1 (pk int primary key);", + "SELECT DOLT_COMMIT('-Am', 'new table');", + "SELECT DOLT_CHECKOUT('-b', 'new-branch');", + "CREATE TABLE t2 (pk int primary key);", + "SELECT DOLT_COMMIT('-Am', 'new table on new branch');", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT DOLT_MERGE_BASE('main', 'new-branch');", + SkipResultsCheck: true, + }, + { + Query: "SELECT DOLT_CHECKOUT('main');", + Expected: []sql.Row{ + {"{0,\"Switched to branch 'main'\"}"}, + }, + }, + { + Query: "select count(*) from dolt_log", + Expected: []sql.Row{ + {3}, // initial commit, CREATE DATABASE commit, CREATE TABLE commit + }, + }, + { + Query: "SELECT DOLT_MERGE('new-branch', '--no-ff', '-m', 'merge new-branch into main');", + SkipResultsCheck: true, + }, + { + Query: "select count(*) from dolt_log", + Expected: []sql.Row{ + {5}, // initial commit, CREATE DATABASE commit, CREATE TABLE t1 commit, new CREATE TABLE t2 commit, merge commit + }, + }, + }, + }, + { + Name: "smoke test select dolt_reset", + SetUpScript: []string{ + "CREATE TABLE t1 (pk int primary key);", + "INSERT INTO t1 VALUES (1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{ + {"t1", 0, "new table"}, + }, + }, + { + Query: "SELECT DOLT_ADD('t1');", + Expected: []sql.Row{{"{0}"}}, + }, + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{ + {"t1", 1, "new table"}, + }, + }, + { + Query: "SELECT DOLT_RESET('t1');", + Expected: []sql.Row{{"{0}"}}, + }, + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{ + {"t1", 0, "new table"}, + }, + }, + }, + }, + { + Name: "smoke test select dolt_clean", + SetUpScript: []string{ + "CREATE TABLE t1 (pk int primary key);", + "INSERT INTO t1 VALUES (1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{ + {"t1", 0, "new table"}, + }, + }, + { + Query: "SELECT DOLT_CLEAN('t1');", + Expected: []sql.Row{{"{0}"}}, + }, + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{}, + }, + { + Query: "CREATE TABLE t1 (pk int primary key);", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{ + {"t1", 0, "new table"}, + }, + }, + { + Skip: true, // TODO: function dolt_clean() does not exist + Query: "SELECT DOLT_CLEAN();", + Expected: []sql.Row{{"{0}"}}, + }, + { + Skip: true, + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "smoke test select dolt_checkout(table)", + SetUpScript: []string{ + "CREATE TABLE t1 (pk int primary key);", + "INSERT INTO t1 VALUES (1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{ + {"t1", 0, "new table"}, + }, + }, + { + Query: "SELECT DOLT_CHECKOUT('t1');", + Expected: []sql.Row{{"{0}"}}, + }, + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{}, + }, + }, + }, }) } diff --git a/testing/go/index_test.go b/testing/go/index_test.go index 284d2f9a93..5dd4e3e344 100644 --- a/testing/go/index_test.go +++ b/testing/go/index_test.go @@ -68,6 +68,28 @@ func TestBasicIndexing(t *testing.T) { }, }, }, + { + Name: "Unique Covering Index", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT);", + "INSERT INTO test VALUES (13, 3), (11, 1), (15, 5), (12, 2), (14, 4);", + "CREATE unique INDEX v1_idx ON test(v1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM test WHERE v1 > 2 ORDER BY pk;", + Expected: []sql.Row{ + {13, 3}, + {14, 4}, + {15, 5}, + }, + }, + { + Query: "insert into test values (16, 3);", + ExpectedErr: "duplicate unique key given", + }, + }, + }, { Name: "Covering Composite Index", SetUpScript: []string{ @@ -346,6 +368,28 @@ func TestBasicIndexing(t *testing.T) { }, }, }, + { + Name: "Unique Non-Covering Index", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT, v2 BIGINT);", + "INSERT INTO test VALUES (13, 3, 23), (11, 1, 21), (15, 5, 25), (12, 2, 22), (14, 4, 24);", + "CREATE UNIQUE INDEX v1_idx ON test(v1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM test WHERE v1 > 2 ORDER BY pk;", + Expected: []sql.Row{ + {13, 3, 23}, + {14, 4, 24}, + {15, 5, 25}, + }, + }, + { + Query: "insert into test values (16, 3, 23);", + ExpectedErr: "duplicate unique key given", + }, + }, + }, { Name: "Non-Covering Composite Index", SetUpScript: []string{ @@ -422,6 +466,33 @@ func TestBasicIndexing(t *testing.T) { }, }, }, + { + Name: "Unique Non-Covering Composite Index", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT, v2 BIGINT, v3 BIGINT);", + "INSERT INTO test VALUES (13, 3, 23, 33), (11, 1, 21, 31), (15, 5, 25, 35), (12, 2, 22, 32), (14, 4, 24, 34);", + "CREATE UNIQUE INDEX v1_idx ON test(v1, v2);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM test WHERE v1 < 3 AND v2 = 21 ORDER BY pk;", + Expected: []sql.Row{ + {11, 1, 21, 31}, + }, + }, + { + Query: "SELECT * FROM test WHERE v1 <= 3 AND v2 < 23 ORDER BY pk;", + Expected: []sql.Row{ + {11, 1, 21, 31}, + {12, 2, 22, 32}, + }, + }, + { + Query: "insert into test values (16, 3, 23, 33);", + ExpectedErr: "duplicate unique key given", + }, + }, + }, { Name: "Keyless Index", SetUpScript: []string{ @@ -468,6 +539,34 @@ func TestBasicIndexing(t *testing.T) { }, }, }, + { + Name: "Unique Keyless Index", + SetUpScript: []string{ + "CREATE TABLE test (v0 BIGINT, v1 BIGINT, v2 BIGINT);", + "INSERT INTO test VALUES (13, 3, 23), (11, 1, 21), (15, 5, 25), (12, 2, 22), (14, 4, 24);", + "CREATE UNIQUE INDEX v1_idx ON test(v1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM test WHERE v1 = 2 ORDER BY v0;", + Expected: []sql.Row{ + {12, 2, 22}, + }, + }, + { + Query: "SELECT * FROM test WHERE v1 > 2 ORDER BY v0;", + Expected: []sql.Row{ + {13, 3, 23}, + {14, 4, 24}, + {15, 5, 25}, + }, + }, + { + Query: "INSERT INTO test VALUES (16, 3, 23);", + ExpectedErr: "duplicate unique key given", + }, + }, + }, { Name: "Keyless Composite Index", SetUpScript: []string{ @@ -544,6 +643,33 @@ func TestBasicIndexing(t *testing.T) { }, }, }, + { + Name: "Unique Keyless Composite Index", + SetUpScript: []string{ + "CREATE TABLE test (v0 BIGINT, v1 BIGINT, v2 BIGINT, v3 BIGINT);", + "INSERT INTO test VALUES (13, 3, 23, 33), (11, 1, 21, 31), (15, 5, 25, 35), (12, 2, 22, 32), (14, 4, 24, 34);", + "CREATE UNIQUE INDEX v1_idx ON test(v1, v2);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT * FROM test WHERE v1 = 2 AND v2 < 23 ORDER BY v0;", + Expected: []sql.Row{ + {12, 2, 22, 32}, + }, + }, + { + Query: "SELECT * FROM test WHERE v1 <= 3 AND v2 < 23 ORDER BY v0;", + Expected: []sql.Row{ + {11, 1, 21, 31}, + {12, 2, 22, 32}, + }, + }, + { + Query: "insert into test values (16, 3, 23, 33);", + ExpectedErr: "duplicate unique key given", + }, + }, + }, { Name: "Indexed Join Covering Indexes", SetUpScript: []string{ diff --git a/testing/postgres-client-tests/node/fields.js b/testing/postgres-client-tests/node/fields.js index 71dc65e8d4..cb9c76b636 100644 --- a/testing/postgres-client-tests/node/fields.js +++ b/testing/postgres-client-tests/node/fields.js @@ -22,6 +22,18 @@ export const doltAddFields = [ }, ]; +export const doltResetFields = [ + { + name: "dolt_reset", + tableID: 0, + columnID: 0, + dataTypeID: 1009, + dataTypeSize: -1, + dataTypeModifier: -1, + format: "text", + }, +]; + export const doltBranchFields = [ { name: "dolt_branch", @@ -46,6 +58,18 @@ export const doltCheckoutFields = [ }, ]; +export const doltCleanFields = [ + { + name: "dolt_clean", + tableID: 0, + columnID: 0, + dataTypeID: 1009, + dataTypeSize: -1, + dataTypeModifier: -1, + format: "text", + }, +]; + export const doltCommitFields = [ { name: "dolt_commit", @@ -87,3 +111,114 @@ export const doltStatusFields = [ format: "text", }, ]; + +export const infoSchemaKeyColumnUsageFields = [ + { + name: "CONSTRAINT_CATALOG", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "CONSTRAINT_SCHEMA", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "CONSTRAINT_NAME", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "TABLE_CATALOG", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "TABLE_SCHEMA", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "TABLE_NAME", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "COLUMN_NAME", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "ORDINAL_POSITION", + tableID: 0, + columnID: 0, + dataTypeID: 26, + dataTypeSize: 10, + dataTypeModifier: -1, + format: "text", + }, + { + name: "POSITION_IN_UNIQUE_CONSTRAINT", + tableID: 0, + columnID: 0, + dataTypeID: 26, + dataTypeSize: 10, + dataTypeModifier: -1, + format: "text", + }, + { + name: "REFERENCED_TABLE_SCHEMA", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "REFERENCED_TABLE_NAME", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "REFERENCED_COLUMN_NAME", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, +]; diff --git a/testing/postgres-client-tests/node/helpers.js b/testing/postgres-client-tests/node/helpers.js index 1183c8de08..86bbd1006c 100644 --- a/testing/postgres-client-tests/node/helpers.js +++ b/testing/postgres-client-tests/node/helpers.js @@ -23,6 +23,20 @@ export function getConfig() { }; } +export function assertEqualRows(test, data) { + const expected = test.res; + const resultStr = JSON.stringify(data); + const result = JSON.parse(resultStr); + if (!assertQueryResult(test.q, expected, data, test.matcher)) { + console.log("Query:", test.q); + console.log("Results:", result); + console.log("Expected:", expected); + throw new Error("Query failed"); + } else { + console.log("Query succeeded:", test.q); + } +} + export function assertQueryResult(q, expected, data, matcher) { if (matcher) { return matcher(data, expected); @@ -30,18 +44,11 @@ export function assertQueryResult(q, expected, data, matcher) { if (q.toLowerCase().includes("dolt_commit")) { if (data.rows.length !== 1) return false; const hash = data.rows[0].dolt_commit[0]; - // dolt_commit row returns 32 character hash - return hash.length === 32; - } - if (q.toLowerCase().includes("dolt_merge")) { - if (data.rows.length !== 1) return false; - const [hash, fastForward, conflicts, message] = data.rows[0].dolt_merge; - return ( - hash.length === 32 && - expected.fastForward === fastForward && - expected.conflicts === conflicts && - expected.message === message - ); + if (hash.length !== 32) { + console.log("Invalid hash for dolt_commit:", hash); + return false; + } + expected.rows[0].dolt_commit = data.rows[0].dolt_commit; } // Does partial matching of actual and expected results. diff --git a/testing/postgres-client-tests/node/index.js b/testing/postgres-client-tests/node/index.js index 2e8dbfd224..2b60fa1847 100644 --- a/testing/postgres-client-tests/node/index.js +++ b/testing/postgres-client-tests/node/index.js @@ -1,11 +1,12 @@ import { Database } from "./database.js"; -import { assertQueryResult, getConfig } from "./helpers.js"; +import { assertEqualRows, getConfig } from "./helpers.js"; import { doltAddFields, doltCheckoutFields, doltCommitFields, countFields, } from "./fields.js"; +import { mergeMatcher } from "./workbenchTests/matchers.js"; const tests = [ { @@ -122,7 +123,7 @@ const tests = [ command: "SELECT", rowCount: 1, oid: null, - rows: [{ dolt_checkout: ["0","Switched to branch 'mybranch'"] }], + rows: [{ dolt_checkout: ["0", "Switched to branch 'mybranch'"] }], fields: doltCheckoutFields, }, }, @@ -143,17 +144,7 @@ const tests = [ rowCount: 1, oid: null, rows: [{ dolt_commit: [""] }], - fields: [ - { - name: "dolt_commit", - tableID: 0, - columnID: 0, - dataTypeID: 1009, - dataTypeSize: -1, - dataTypeModifier: -1, - format: "text", - }, - ], + fields: doltCommitFields, }, }, { @@ -162,17 +153,24 @@ const tests = [ command: "SELECT", rowCount: 1, oid: null, - rows: [{ dolt_checkout: ["0","Switched to branch 'main'"] }], + rows: [{ dolt_checkout: ["0", "Switched to branch 'main'"] }], fields: doltCheckoutFields, }, }, { q: "select dolt_merge('mybranch')", res: { - fastForward: "1", - conflicts: "0", - message: "merge successful", + command: "SELECT", + rowCount: 1, + oid: null, + rows: [ + { + dolt_merge: ["", "1", "0", "merge successful"], + }, + ], + fields: [], }, + matcher: mergeMatcher, }, { q: "select COUNT(*) FROM dolt_log", @@ -191,20 +189,10 @@ async function main() { await Promise.all( tests.map((test) => { - const expected = test.res; return database .query(test.q) .then((data) => { - const resultStr = JSON.stringify(data); - const result = JSON.parse(resultStr); - if (!assertQueryResult(test.q, expected, data)) { - console.log("Query:", test.q); - console.log("Results:", result); - console.log("Expected:", expected); - throw new Error("Query failed"); - } else { - console.log("Query succeeded:", test.q); - } + assertEqualRows(test, data); }) .catch((err) => { console.error(err); diff --git a/testing/postgres-client-tests/node/run-tests.sh b/testing/postgres-client-tests/node/run-tests.sh index dfd05c8d50..3c723a34e6 100644 --- a/testing/postgres-client-tests/node/run-tests.sh +++ b/testing/postgres-client-tests/node/run-tests.sh @@ -3,6 +3,10 @@ source ../helpers.bash echo "Running $1 tests" start_doltgres_server +query_server -c "CREATE TABLE IF NOT EXISTS test_table(pk int)" -t +query_server -c "DELETE FROM test_table" -t +query_server -c "INSERT INTO test_table VALUES (1)" -t + cd .. node $1 $USER $PORT $REPO_NAME $PWD/testdata teardown_doltgres_repo \ No newline at end of file diff --git a/testing/postgres-client-tests/node/workbenchTests/branches.js b/testing/postgres-client-tests/node/workbenchTests/branches.js index 854e5004b7..3bb965ddc8 100644 --- a/testing/postgres-client-tests/node/workbenchTests/branches.js +++ b/testing/postgres-client-tests/node/workbenchTests/branches.js @@ -1,9 +1,9 @@ import { countFields, - doltAddFields, doltBranchFields, doltCheckoutFields, doltStatusFields, + doltCommitFields, } from "../fields.js"; import { branchesMatcher } from "./matchers.js"; import { dbName } from "../helpers.js"; @@ -59,7 +59,13 @@ export const branchTests = [ { q: `SELECT DOLT_COMMIT('-Am', $1::text, '--author', $2::text);`, p: ["Create table test", "Dolt "], - res: [{ hash: "" }], + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_commit: "" }], + fields: doltCommitFields, + }, }, { q: `SELECT * FROM dolt_branches LIMIT 200`, @@ -68,10 +74,10 @@ export const branchTests = [ { name: "main", hash: "", - latest_committer: "mysql-test-runner", - latest_committer_email: "mysql-test-runner@liquidata.co", + latest_committer: "doltgres", + latest_committer_email: "doltgres@127.0.0.1", latest_commit_date: "", - latest_commit_message: "Initialize data repository", + latest_commit_message: "CREATE DATABASE", remote: "", branch: "", }, @@ -96,7 +102,7 @@ export const branchTests = [ command: "SELECT", rowCount: 1, oid: null, - rows: [{ dolt_checkout: ["0","Switched to branch 'branch-to-delete'"] }], + rows: [{ dolt_checkout: ["0", "Switched to branch 'branch-to-delete'"] }], fields: doltCheckoutFields, }, }, @@ -111,14 +117,13 @@ export const branchTests = [ }, }, { - q: `SELECT dolt_checkout($1::text)`, - p: ["main"], + q: `USE '${dbName}/main';`, res: { - command: "SELECT", - rowCount: 1, + command: "SET", + rowCount: null, oid: null, - rows: [{ dolt_checkout: ["0","Switched to branch 'main'"] }], - fields: doltCheckoutFields, + rows: [], + fields: [], }, }, { diff --git a/testing/postgres-client-tests/node/workbenchTests/databases.js b/testing/postgres-client-tests/node/workbenchTests/databases.js index 78f30f441c..0bcedf43d3 100644 --- a/testing/postgres-client-tests/node/workbenchTests/databases.js +++ b/testing/postgres-client-tests/node/workbenchTests/databases.js @@ -1,3 +1,4 @@ +import { doltCleanFields } from "../fields.js"; import { dbName } from "../helpers.js"; export const databaseTests = [ @@ -11,6 +12,16 @@ export const databaseTests = [ fields: [], }, }, + { + q: `SELECT DOLT_CLEAN('test_table')`, + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_clean: ["0"] }], + fields: doltCleanFields, + }, + }, { q: `SELECT datname FROM pg_database;`, res: { @@ -68,8 +79,8 @@ export const databaseTests = [ { q: `SELECT dolt_version();`, res: [{ "dolt_version()": "0.0.0" }], - matcher: (res) => { - return res.rows[0].dolt_version.length > 0; + matcher: (data) => { + return data.rows[0].dolt_version.length > 0; }, }, ]; diff --git a/testing/postgres-client-tests/node/workbenchTests/index.js b/testing/postgres-client-tests/node/workbenchTests/index.js index 7b1f1f53b7..9c44f8d4a6 100644 --- a/testing/postgres-client-tests/node/workbenchTests/index.js +++ b/testing/postgres-client-tests/node/workbenchTests/index.js @@ -1,14 +1,21 @@ import { branchTests } from "./branches.js"; import { databaseTests } from "./databases.js"; -import { assertQueryResult } from "../helpers.js"; +import { logTests } from "./logs.js"; +import { assertEqualRows } from "../helpers.js"; +import { mergeTests } from "./merge.js"; +import { tableTests } from "./table.js"; export default async function runWorkbenchTests(database) { - await runTests(database, databaseTests); - await runTests(database, branchTests); + await runTests(database, databaseTests, "database"); + await runTests(database, branchTests, "branches"); + await runTests(database, logTests, "logs"); + await runTests(database, mergeTests, "merge"); + await runTests(database, tableTests, "tables"); // TODO: Move over the rest of the Dolt workbench tests } -async function runTests(database, tests) { +async function runTests(database, tests, name) { + console.log("Running tests for", name); await Promise.all( tests.map((test) => { if (test.skip) return; @@ -34,15 +41,3 @@ async function runTests(database, tests) { }) ); } - -function assertEqualRows(test, data) { - const expected = test.res; - const resultStr = JSON.stringify(data); - const result = JSON.parse(resultStr); - if (!assertQueryResult(test.q, expected, data, test.matcher)) { - console.log("Query:", test.q); - console.log("Results:", result); - console.log("Expected:", expected); - throw new Error("Query failed"); - } -} diff --git a/testing/postgres-client-tests/node/workbenchTests/logs.js b/testing/postgres-client-tests/node/workbenchTests/logs.js new file mode 100644 index 0000000000..394d5751e8 --- /dev/null +++ b/testing/postgres-client-tests/node/workbenchTests/logs.js @@ -0,0 +1,105 @@ +import { logsMatcher } from "./matchers.js"; +import { dbName } from "../helpers.js"; + +export const logTests = [ + { + q: `SELECT * FROM DOLT_LOG('main', '--parents') LIMIT 10 OFFSET 0;`, // TODO: Prepared not working + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [ + { + commit_hash: "", + committer: "doltgres", + email: "doltgres@127.0.0.1", + date: "", + message: "CREATE DATABASE", + parents: ["3orrg69ou1loj2ph21guie3r2lf8bsab"], + }, + { + commit_hash: "", + committer: "Dolt System Account", + email: "doltuser@dolthub.com", + date: "", + message: "Initialize data repository", + parents: [], + }, + ], + fields: [], + }, + matcher: logsMatcher, + }, + { + q: `USE '${dbName}/mybranch';`, + res: { + command: "SET", + rowCount: null, + oid: null, + rows: [], + fields: [], + }, + }, + { + q: `SELECT * FROM dolt_log;`, // TODO: If we decide to implement AS OF, use here instead of USE statement above and below + res: { + command: "SELECT", + rowCount: 2, + oid: null, + rows: [ + { + commit_hash: "", + committer: "Dolt", + email: "dolt@dolthub.com", + date: "", + message: "Create table test", + }, + { + commit_hash: "", + committer: "doltgres", + email: "doltgres@127.0.0.1", + date: "", + message: "CREATE DATABASE", + }, + { + commit_hash: "", + committer: "Dolt System Account", + email: "doltuser@dolthub.com", + date: "", + message: "Initialize data repository", + }, + ], + }, + matcher: logsMatcher, + }, + { + q: `USE '${dbName}/main';`, + res: { + command: "SET", + rowCount: null, + oid: null, + rows: [], + fields: [], + }, + }, + { + q: `SELECT * FROM DOLT_LOG('main..mybranch', '--parents')`, // TODO: Prepared not working + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [ + { + commit_hash: "", + committer: "Dolt", + email: "dolt@dolthub.com", + date: "", + message: "Create table test", + parents: [""], + }, + ], + fields: [], + }, + matcher: logsMatcher, + }, +]; diff --git a/testing/postgres-client-tests/node/workbenchTests/matchers.js b/testing/postgres-client-tests/node/workbenchTests/matchers.js index b9e47e2aa5..6df6fd9e52 100644 --- a/testing/postgres-client-tests/node/workbenchTests/matchers.js +++ b/testing/postgres-client-tests/node/workbenchTests/matchers.js @@ -1,6 +1,7 @@ function matcher(rows, exp, exceptionKeys, getExceptionIsValid) { // Row lengths match if (rows.length !== exp.length) { + console.log("row lengths don't match", rows.length, exp.length); return false; } for (let i = 0; i < rows.length; i++) { @@ -17,7 +18,7 @@ function matcher(rows, exp, exceptionKeys, getExceptionIsValid) { if (exceptionKeys.includes(rowKey)) { const isValid = getExceptionIsValid(rows[i], rowKey, exp[i]); if (!isValid) { - console.log("exception was not valid for key", rowKey); + console.log("exception was not valid for key:", rowKey); return false; } } else { @@ -42,7 +43,7 @@ function dateIsValid(date) { return JSON.stringify(date).length > 0; } -export function branchesMatcher(rows, exp) { +export function branchesMatcher(data, exp) { const exceptionKeys = ["hash", "latest_commit_date"]; function getExceptionIsValid(row, key) { @@ -57,5 +58,59 @@ export function branchesMatcher(rows, exp) { } } - return matcher(rows, exp, exceptionKeys, getExceptionIsValid); + return matcher(data.rows, exp.rows, exceptionKeys, getExceptionIsValid); +} + +export function logsMatcher(data, exp) { + const exceptionKeys = ["commit_hash", "date", "parents"]; + + function getExceptionIsValid(row, key, expRow) { + const val = row[key]; + switch (key) { + case "commit_hash": + return commitHashIsValid(val); + case "date": + return dateIsValid(val); + case "parents": + const numParents = val.split(", ").filter((v) => !!v.length).length; + const expParents = expRow.parents.length; + return numParents === expParents; + default: + return false; + } + } + + return matcher(data.rows, exp.rows, exceptionKeys, getExceptionIsValid); +} + +export function mergeBaseMatcher(data) { + if (data.rows.length !== 1) { + return false; + } + return commitHashIsValid(data.rows[0].dolt_merge_base); +} + +export function mergeMatcher(data, exp) { + if (data.rows.length !== 1) { + console.log("Rows length not 1", data.rows.length); + return false; + } + + const row = data.rows[0].dolt_merge; + const expRow = exp.rows[0].dolt_merge; + + // Check valid commit hash + if (!commitHashIsValid(row[0])) { + console.log("Invalid commit hash", row[0]); + return false; + } + // Check the rest of the fields + for (let i = 1; i < row.length; i++) { + if (row[i] !== expRow[i]) { + console.log("Values don't match", row[i], expRow[i]); + return false; + } + } + + return true; } diff --git a/testing/postgres-client-tests/node/workbenchTests/merge.js b/testing/postgres-client-tests/node/workbenchTests/merge.js new file mode 100644 index 0000000000..cbb14f0d5e --- /dev/null +++ b/testing/postgres-client-tests/node/workbenchTests/merge.js @@ -0,0 +1,87 @@ +import { doltStatusFields } from "../fields.js"; +import { logsMatcher, mergeBaseMatcher, mergeMatcher } from "./matchers.js"; + +export const mergeTests = [ + { + q: `SELECT DOLT_MERGE_BASE($1::text, $2::text);`, + p: ["mybranch", "main"], + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_merge_base: "" }], + fields: [], + }, + matcher: mergeBaseMatcher, + }, + { + q: `SELECT * FROM dolt_status`, + res: { + command: "SELECT", + rowCount: 0, + oid: null, + rows: [], + fields: doltStatusFields, + }, + }, + { + q: `SELECT DOLT_MERGE($1::text, '--no-ff', '-m', $2::text)`, + p: ["mybranch", "Merge mybranch into main"], + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [ + { + dolt_merge: ["", "0", "0", "merge successful"], + }, + ], + fields: [], + }, + matcher: mergeMatcher, + }, + { + q: `SELECT * FROM DOLT_LOG('main', '--parents') LIMIT 10 OFFSET 0;`, // TODO: Prepared not working + res: { + command: "SELECT", + rowCount: 4, + oid: null, + rows: [ + { + commit_hash: "", + message: "Merge mybranch into main", + committer: "doltgres", + email: "doltgres@127.0.0.1", + date: "", + parents: ["", ""], + }, + { + commit_hash: "", + committer: "Dolt", + email: "dolt@dolthub.com", + date: "", + message: "Create table test", + parents: [""], + }, + { + commit_hash: "", + committer: "doltgres", + email: "doltgres@127.0.0.1", + date: "", + message: "CREATE DATABASE", + parents: [""], + }, + { + commit_hash: "", + committer: "Dolt System Account", + email: "doltuser@dolthub.com", + date: "", + message: "Initialize data repository", + parents: [], + }, + ], + fields: [], + }, + matcher: logsMatcher, + }, +]; diff --git a/testing/postgres-client-tests/node/workbenchTests/table.js b/testing/postgres-client-tests/node/workbenchTests/table.js new file mode 100644 index 0000000000..3fa13ee8ef --- /dev/null +++ b/testing/postgres-client-tests/node/workbenchTests/table.js @@ -0,0 +1,350 @@ +import { dbName } from "../helpers.js"; +import { + doltAddFields, + doltCheckoutFields, + doltCommitFields, + doltResetFields, + doltStatusFields, + infoSchemaKeyColumnUsageFields, +} from "../fields.js"; + +export const tableTests = [ + { + q: "INSERT INTO test VALUES (0, 0), (1, 1), (2,2)", + res: { + command: "INSERT", + rowCount: 3, + oid: 0, + rows: [], + fields: [], + }, + }, + { + q: `CREATE UNIQUE INDEX test_idx ON test (pk, value)`, + res: { + command: "CREATE", + rowCount: null, + oid: null, + rows: [], + fields: [], + }, + }, + { + q: `SELECT ordinal_position, column_name, udt_name as data_type, is_nullable, column_default FROM information_schema.columns WHERE table_catalog=$1 AND table_schema = $2 AND table_name = $3;`, + p: [`${dbName}/main`, "public", "test"], + res: { + command: "SELECT", + rowCount: 2, + oid: null, + rows: [ + { + ordinal_position: 1, + column_name: "pk", + data_type: "int4", + is_nullable: "NO", + column_default: null, + }, + { + ordinal_position: 2, + column_name: "value", + data_type: "int4", + is_nullable: "YES", + column_default: null, + }, + ], + fields: [ + { + name: "ordinal_position", + tableID: 0, + columnID: 0, + dataTypeID: 23, + dataTypeSize: 4, + dataTypeModifier: -1, + format: "text", + }, + { + name: "column_name", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 256, + dataTypeModifier: -1, + format: "text", + }, + { + name: "data_type", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 256, + dataTypeModifier: -1, + format: "text", + }, + { + name: "is_nullable", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 12, + dataTypeModifier: -1, + format: "text", + }, + { + name: "column_default", + tableID: 0, + columnID: 0, + dataTypeID: 25, + dataTypeSize: -1, + dataTypeModifier: -1, + format: "text", + }, + ], + }, + }, + { + q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`, + p: ["Add some rows and a column index"], + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_commit: [""] }], + fields: doltCommitFields, + }, + }, + { + skip: true, // TODO: ORDER BY is not yet supported + q: `SELECT + table_name, index_name, comment, non_unique, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS COLUMNS + FROM information_schema.statistics + WHERE table_catalog=$1 AND table_schema=$2 AND table_name=$3 AND index_name!='PRIMARY' + GROUP BY index_name;`, + p: [`${dbName}/main`, "public", "test"], + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [ + { + TABLE_NAME: "test", + INDEX_NAME: "test_idx", + COMMENT: "", + NON_UNIQUE: 0, + COLUMNS: "pk,value", + }, + ], + }, + }, + { + q: "CREATE TABLE test_info (id int, info varchar(255), test_pk int, primary key(id), foreign key (test_pk) references test(pk))", + res: { + command: "CREATE", + rowCount: null, + oid: null, + rows: [], + fields: [], + }, + }, + { + q: "INSERT INTO test_info VALUES (1, 'info about test pk 0', 0)", + res: { + command: "INSERT", + rowCount: 1, + oid: 0, + rows: [], + fields: [], + }, + }, + { + q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`, + p: ["Add test_info with foreign key"], + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_commit: [""] }], + fields: doltCommitFields, + }, + }, + { + q: `SELECT "table_schema", "table_name" FROM "information_schema"."tables" WHERE "table_schema" = $1 AND "table_catalog" = $2;`, + p: ["public", `${dbName}/main`], + res: { + command: "SELECT", + rowCount: 2, + oid: null, + rows: [ + { table_schema: "public", table_name: "test" }, + { table_schema: "public", table_name: "test_info" }, + ], + fields: [ + { + name: "table_schema", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + { + name: "table_name", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 192, + dataTypeModifier: -1, + format: "text", + }, + ], + }, + }, + { + q: `SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_name=$1 AND table_schema=$2 AND table_catalog=$3 AND referenced_table_schema IS NOT NULL`, + p: ["test_info", "public", `${dbName}/main`], + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [ + { + CONSTRAINT_CATALOG: `${dbName}/main`, + CONSTRAINT_SCHEMA: "public", + CONSTRAINT_NAME: "test_info_ibfk_1", + TABLE_CATALOG: `${dbName}/main`, + TABLE_SCHEMA: "public", + TABLE_NAME: "test_info", + COLUMN_NAME: "test_pk", + ORDINAL_POSITION: 1, + POSITION_IN_UNIQUE_CONSTRAINT: 1, + REFERENCED_TABLE_SCHEMA: dbName, + REFERENCED_TABLE_NAME: "test", + REFERENCED_COLUMN_NAME: "pk", + }, + ], + fields: infoSchemaKeyColumnUsageFields, + }, + }, + { + q: `SELECT * FROM "public"."test_info" "public.test_info" ORDER BY id ASC LIMIT 10;`, + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ id: 1, info: "info about test pk 0", test_pk: 0 }], + fields: [ + { + name: "id", + tableID: 0, + columnID: 0, + dataTypeID: 23, + dataTypeSize: 4, + dataTypeModifier: -1, + format: "text", + }, + { + name: "info", + tableID: 0, + columnID: 0, + dataTypeID: 1043, + dataTypeSize: 1020, + dataTypeModifier: -1, + format: "text", + }, + { + name: "test_pk", + tableID: 0, + columnID: 0, + dataTypeID: 23, + dataTypeSize: 4, + dataTypeModifier: -1, + format: "text", + }, + ], + }, + }, + { + q: `USE '${dbName}/main'`, + res: { + command: "SET", + rowCount: null, + oid: null, + rows: [], + fields: [], + }, + }, + + // TODO: File upload tests + + // Add and revert load data changes + { + q: `SELECT * FROM dolt_status`, + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ table_name: "test_info", staged: 0, status: "modified" }], + fields: doltStatusFields, + }, + }, + { + q: "SELECT DOLT_ADD('.')", + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_add: ["0"] }], + fields: doltAddFields, + }, + }, + { + q: `SELECT * FROM dolt_status`, + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ table_name: "test_info", staged: 1, status: "modified" }], + fields: doltStatusFields, + }, + }, + { + q: "SELECT DOLT_RESET('test_info');", + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_reset: ["0"] }], + fields: doltResetFields, + }, + }, + { + q: `SELECT * FROM dolt_status`, + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ table_name: "test_info", staged: 0, status: "modified" }], + fields: doltStatusFields, + }, + }, + { + q: "SELECT DOLT_CHECKOUT('test_info')", + res: { + command: "SELECT", + rowCount: 1, + oid: null, + rows: [{ dolt_checkout: ["0"] }], + fields: doltCheckoutFields, + }, + }, + { + q: `SELECT * FROM dolt_status`, + res: { + command: "SELECT", + rowCount: 0, + oid: null, + rows: [], + fields: doltStatusFields, + }, + }, +];