diff --git a/postgres/messages/row_description.go b/postgres/messages/row_description.go index ee8b1b981d..e862c9f711 100644 --- a/postgres/messages/row_description.go +++ b/postgres/messages/row_description.go @@ -174,8 +174,10 @@ func VitessFieldToDataTypeObjectID(field *query.Field) (int32, error) { return 1114, nil case query.Type_DATE: return 1082, nil + case query.Type_NULL_TYPE: + return 25, nil // NULL is treated as TEXT on the wire default: - return 0, fmt.Errorf("unsupported type returned from engine") + return 0, fmt.Errorf("unsupported type returned from engine: %s", field.Type) } } @@ -210,8 +212,10 @@ func VitessFieldToDataTypeSize(field *query.Field) (int16, error) { return 8, nil case query.Type_DATE: return 4, nil + case query.Type_NULL_TYPE: + return -1, nil // NULL is treated as TEXT on the wire default: - return 0, fmt.Errorf("unsupported type returned from engine") + return 0, fmt.Errorf("unsupported type returned from engine: %s", field.Type) } } @@ -255,7 +259,9 @@ func VitessFieldToDataTypeModifier(field *query.Field) (int32, error) { return -1, nil case query.Type_DATE: return -1, nil + case query.Type_NULL_TYPE: + return -1, nil // NULL is treated as TEXT on the wire default: - return 0, fmt.Errorf("unsupported type returned from engine") + return 0, fmt.Errorf("unsupported type returned from engine: %s", field.Type) } } diff --git a/server/ast/expr.go b/server/ast/expr.go index 0296a417b5..49777c797c 100644 --- a/server/ast/expr.go +++ b/server/ast/expr.go @@ -210,7 +210,15 @@ func nodeExpr(node tree.Expr) (vitess.Expr, error) { Type: convertType, }, nil case *tree.CoalesceExpr: - return nil, fmt.Errorf("COALESCE is not yet supported") + exprs, err := nodeExprsToSelectExprs(node.Exprs) + if err != nil { + return nil, err + } + + return &vitess.FuncExpr{ + Name: vitess.NewColIdent("COALESCE"), + Exprs: exprs, + }, nil case *tree.CollateExpr: return nil, fmt.Errorf("collations are not yet supported") case *tree.ColumnAccessExpr: @@ -400,8 +408,20 @@ func nodeExpr(node tree.Expr) (vitess.Expr, error) { Expr: expr, }, nil case *tree.NullIfExpr: - //TODO: probably should be the IF function: IF(Expr1 == Expr2, NULL, Expr1) - return nil, fmt.Errorf("NULLIF is not yet supported") + expr1, err := nodeExprToSelectExpr(node.Expr1) + if err != nil { + return nil, err + } + + expr2, err := nodeExprToSelectExpr(node.Expr2) + if err != nil { + return nil, err + } + + return &vitess.FuncExpr{ + Name: vitess.NewColIdent("NULLIF"), + Exprs: vitess.SelectExprs{expr1, expr2}, + }, nil case tree.NullLiteral: return &vitess.NullVal{}, nil case *tree.NumVal: diff --git a/server/ast/func_expr.go b/server/ast/func_expr.go index 5064cb1d7e..beb171c99a 100644 --- a/server/ast/func_expr.go +++ b/server/ast/func_expr.go @@ -54,12 +54,12 @@ func nodeFuncExpr(node *tree.FuncExpr) (*vitess.FuncExpr, error) { } var distinct bool switch node.Type { - case 0: + case 0, tree.AllFuncType: distinct = false case tree.DistinctFuncType: distinct = true - case tree.AllFuncType: - return nil, fmt.Errorf("function spec is not yet supported") + default: + return nil, fmt.Errorf("unknown function spec type %d", node.Type) } windowDef, err := nodeWindowDef(node.WindowDef) if err != nil { diff --git a/server/ast/table_expr.go b/server/ast/table_expr.go index b8ef6c4a75..450c3b8b5d 100644 --- a/server/ast/table_expr.go +++ b/server/ast/table_expr.go @@ -51,6 +51,8 @@ func nodeTableExpr(node tree.TableExpr) (vitess.TableExpr, error) { for i := range treeCondition.Cols { condition.Using[i] = vitess.NewColIdent(string(treeCondition.Cols[i])) } + case nil: + // cross join (no join condition) default: return nil, fmt.Errorf("unknown JOIN condition: `%T`", treeCondition) } @@ -70,10 +72,7 @@ func nodeTableExpr(node tree.TableExpr) (vitess.TableExpr, error) { } else { joinType = vitess.RightJoinStr } - case tree.AstCross: - // GMS doesn't have any support for CROSS joins, as MySQL doesn't actually implement them - return nil, fmt.Errorf("CROSS joins are not yet supported") - case tree.AstInner: + case tree.AstCross, tree.AstInner: joinType = vitess.JoinStr case "": if condition.On == nil && len(condition.Using) == 0 { diff --git a/testing/go/regression_test.go b/testing/go/regression_test.go new file mode 100755 index 0000000000..4f86f14463 --- /dev/null +++ b/testing/go/regression_test.go @@ -0,0 +1,134 @@ +// 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" +) + +func TestRegressions(t *testing.T) { + RunScripts(t, []ScriptTest{ + // { + // Name: "nullif", + // SetUpScript: []string{}, + // Assertions: []ScriptTestAssertion{ + // { + // Query: "select nullif(1, 1);", + // Expected: []sql.Row{{nil}}, + // }, + // { + // Query: "select nullif('', null);", + // Expected: []sql.Row{{""}}, + // }, + // { + // Query: "select nullif(10, 'a');", + // Expected: []sql.Row{{10}}, + // }, + // }, + // }, + // { + // Name: "coalesce", + // SetUpScript: []string{}, + // Assertions: []ScriptTestAssertion{ + // { + // Query: "select coalesce(null + 5, 100);", + // Expected: []sql.Row{{100.0}}, // TODO: this should be an integer + // }, + // { + // Query: "select coalesce(null, null, 'abc');", + // Expected: []sql.Row{{"abc"}}, + // }, + // { + // Query: "select coalesce(null, null);", + // Expected: []sql.Row{{nil}}, + // }, + // }, + // }, + { + Name: "case / when", + SetUpScript: []string{}, + Assertions: []ScriptTestAssertion{ + { + Query: "SELECT\n" + + " CASE\n" + + " WHEN 1 = 1 THEN 'One is equal to One'\n" + + " ELSE 'One is not equal to One'\n" + + " END AS result;", + Expected: []sql.Row{{"One is equal to One"}}, + }, + { + Query: "SELECT\n" + + " CASE\n" + + " WHEN NULL IS NULL THEN 'NULL is NULL'\n" + + " ELSE 'NULL is not NULL'\n" + + " END AS result;", + Expected: []sql.Row{{"NULL is NULL"}}, + }, + }, + }, + { + Name: "ALL / DISTINCT in functions", + SetUpScript: []string{ + "create table t1 (pk int);", + "insert into t1 values (1), (2), (3), (1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select all count(*) from t1;", + Expected: []sql.Row{{4}}, + }, + { + Query: "select all count(distinct pk) from t1;", + Expected: []sql.Row{{3}}, + }, + { + Query: "select all count(all pk) from t1;", + Expected: []sql.Row{{4}}, + }, + }, + }, + { + Name: "cross joins", + SetUpScript: []string{ + "create table t1 (pk1 int);", + "create table t2 (pk2 int);", + "insert into t1 values (1), (2);", + "insert into t2 values (3), (4);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select * from t1 cross join t2 order by pk1, pk2;", + Expected: []sql.Row{ + {1, 3}, + {1, 4}, + {2, 3}, + {2, 4}, + }, + }, + { + Query: "select * from t1, t2 order by pk1, pk2;", + Expected: []sql.Row{ + {1, 3}, + {1, 4}, + {2, 3}, + {2, 4}, + }, + }, + }, + }, + }) +} diff --git a/testing/logictest/harness/doltgres_harness.go b/testing/logictest/harness/doltgres_harness.go index 9f0fcc1646..f2ce6ea6d6 100755 --- a/testing/logictest/harness/doltgres_harness.go +++ b/testing/logictest/harness/doltgres_harness.go @@ -238,6 +238,10 @@ func columns(rows *sql.Rows) (string, []interface{}, error) { colVal := sql.NullInt64{} columns = append(columns, &colVal) sb.WriteString("I") + case "UNKNOWN": // used for NULL values + colVal := sql.NullString{} + columns = append(columns, &colVal) + sb.WriteString("I") // is this right? default: return "", nil, fmt.Errorf("Unhandled type %s", columnType.DatabaseTypeName()) }