diff --git a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go index cd40c7d78b9..dda4f703959 100644 --- a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go +++ b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go @@ -44,7 +44,6 @@ var ( PRIMARY KEY (id) ) ENGINE=Innodb; - CREATE TABLE lookup1 ( field BIGINT NOT NULL, keyspace_id binary(8), @@ -56,6 +55,12 @@ CREATE TABLE lookup2 ( keyspace_id binary(8), UNIQUE KEY (field2) ) ENGINE=Innodb; + +CREATE TABLE thex ( + id VARBINARY(64) NOT NULL, + field BIGINT NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB; ` VSchema = ` @@ -65,6 +70,18 @@ CREATE TABLE lookup2 ( "hash": { "type": "hash" }, + "binary_vdx": { + "type": "binary" + }, + "binary_md5_vdx": { + "type": "binary_md5" + }, + "xxhash_vdx": { + "type": "xxhash" + }, + "numeric_vdx": { + "type": "numeric" + }, "lookup1": { "type": "consistent_lookup", "params": { @@ -118,6 +135,14 @@ CREATE TABLE lookup2 ( "name": "hash" } ] + }, + "thex": { + "column_vindexes": [ + { + "column": "id", + "name": "binary_vdx" + } + ] } } }` @@ -162,6 +187,28 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } +func TestVindexHexTypes(t *testing.T) { + defer cluster.PanicHandler(t) + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.Nil(t, err) + defer conn.Close() + + exec(t, conn, "INSERT INTO thex (id, field) VALUES "+ + "(0x01,1), "+ + "(x'a5',2), "+ + "(0x48656c6c6f20476f7068657221,3), "+ + "(x'c26caa1a5eb94096d29a1bec',4)") + result := exec(t, conn, "select id, field from thex order by id") + + expected := + "[[VARBINARY(\"\\x01\") INT64(1)] " + + "[VARBINARY(\"Hello Gopher!\") INT64(3)] " + + "[VARBINARY(\"\\xa5\") INT64(2)] " + + "[VARBINARY(\"\\xc2l\\xaa\\x1a^\\xb9@\\x96Қ\\x1b\\xec\") INT64(4)]]" + assert.Equal(t, expected, fmt.Sprintf("%v", result.Rows)) +} + func TestVindexBindVarOverlap(t *testing.T) { defer cluster.PanicHandler(t) ctx := context.Background() diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go index aee795f2155..0672ce32e3a 100644 --- a/go/vt/sqlparser/ast_funcs.go +++ b/go/vt/sqlparser/ast_funcs.go @@ -1450,23 +1450,6 @@ const ( DoubleAt ) -// handleUnaryMinus handles the case when a unary minus operator is seen in the parser. It takes 1 argument which is the expr to which the unary minus has been added to. -func handleUnaryMinus(expr Expr) Expr { - if num, ok := expr.(*Literal); ok && (num.Type == IntVal || num.Type == FloatVal || num.Type == DecimalVal) { - // Handle double negative - if num.Val[0] == '-' { - num.Val = num.Val[1:] - return num - } - num.Val = "-" + num.Val - return num - } - if unaryExpr, ok := expr.(*UnaryExpr); ok && unaryExpr.Operator == UMinusOp { - return unaryExpr.Expr - } - return &UnaryExpr{Operator: UMinusOp, Expr: expr} -} - // encodeSQLString encodes the string as a SQL string. func encodeSQLString(val string) string { return sqltypes.EncodeStringSQL(val) @@ -1586,6 +1569,19 @@ func (es *ExtractedSubquery) updateAlternative() { } } +func isExprLiteral(expr Expr) bool { + switch expr := expr.(type) { + case *Literal: + return true + case BoolVal: + return true + case *UnaryExpr: + return isExprLiteral(expr.Expr) + default: + return false + } +} + func defaultRequiresParens(ct *ColumnType) bool { // in 5.7 null value should be without parenthesis, in 8.0 it is allowed either way. // so it is safe to not keep parenthesis around null. @@ -1601,10 +1597,7 @@ func defaultRequiresParens(ct *ColumnType) bool { return true } - _, isLiteral := ct.Options.Default.(*Literal) - _, isBool := ct.Options.Default.(BoolVal) - - if isLiteral || isBool || isExprAliasForCurrentTimeStamp(ct.Options.Default) { + if isExprLiteral(ct.Options.Default) || isExprAliasForCurrentTimeStamp(ct.Options.Default) { return false } diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 21f9871361a..ac909968071 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -208,7 +208,7 @@ var ( input: "select -1 from t where b = -2", }, { input: "select - -1 from t", - output: "select 1 from t", + output: "select - -1 from t", }, { input: "select a from t", }, { @@ -785,7 +785,7 @@ var ( output: "select /* binary unary */ a - -b from t", }, { input: "select /* - - */ - -b from t", - output: "select /* - - */ b from t", + output: "select /* - - */ - -b from t", }, { input: "select /* binary binary */ binary binary b from t", }, { @@ -1093,10 +1093,10 @@ var ( input: "set @period.variable = 42", }, { input: "set S= +++-++-+(4+1)", - output: "set S = 4 + 1", + output: "set S = - -(4 + 1)", }, { input: "set S= +- - - - -(4+1)", - output: "set S = -(4 + 1)", + output: "set S = - - - - -(4 + 1)", }, { input: "alter table a add foo int references simple (a) on delete restrict first", output: "alter table a add column foo int references simple (a) on delete restrict first", @@ -1354,9 +1354,11 @@ var ( }, { input: "create table a (\n\ta int not null default 0\n)", }, { - input: "create table a (\n\ta float not null default -1\n)", + input: "create table a (\n\ta float not null default -1\n)", + output: "create table a (\n\ta float not null default -1\n)", }, { - input: "create table a (\n\ta float not null default -2.1\n)", + input: "create table a (\n\ta float not null default -2.1\n)", + output: "create table a (\n\ta float not null default -2.1\n)", }, { input: "create table a (a int not null default 0, primary key(a))", output: "create table a (\n\ta int not null default 0,\n\tprimary key (a)\n)", diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index f3870d848dc..084dd2c6354 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -7896,7 +7896,7 @@ yydefault: var yyLOCAL Expr //line sql.y:1348 { - yyLOCAL = handleUnaryMinus(yyDollar[2].exprUnion()) + yyLOCAL = &UnaryExpr{Operator: UMinusOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL case 195: @@ -12621,7 +12621,7 @@ yydefault: var yyLOCAL Expr //line sql.y:4293 { - yyLOCAL = handleUnaryMinus(yyDollar[2].exprUnion()) + yyLOCAL = &UnaryExpr{Operator: UMinusOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL case 843: diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 52117cecc46..2c588e7ecbe 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -1346,7 +1346,7 @@ NULL } | '-' NUM_literal { - $$ = handleUnaryMinus($2) + $$ = &UnaryExpr{Operator: UMinusOp, Expr: $2} } literal: @@ -4291,7 +4291,7 @@ function_call_keyword } | '-' simple_expr %prec UNARY { - $$ = handleUnaryMinus($2) + $$ = &UnaryExpr{Operator: UMinusOp, Expr: $2} } | '~' simple_expr %prec UNARY { diff --git a/go/vt/sqlparser/testdata/select_cases.txt b/go/vt/sqlparser/testdata/select_cases.txt index 423a878521f..7d8b74d8193 100644 --- a/go/vt/sqlparser/testdata/select_cases.txt +++ b/go/vt/sqlparser/testdata/select_cases.txt @@ -3248,7 +3248,7 @@ INPUT select date_add("1997-12-31 23:59:59",INTERVAL -100000 MINUTE); END OUTPUT -select date_add('1997-12-31 23:59:59', interval -100000 MINUTE) from dual +select date_add('1997-12-31 23:59:59', interval (-100000) MINUTE) from dual END INPUT select insert('hello', 18446744073709551616, 1, 'hi'); @@ -8108,7 +8108,7 @@ INPUT select -(-9223372036854775808), -(-(-9223372036854775808)); END OUTPUT -select 9223372036854775808, -9223372036854775808 from dual +select - -9223372036854775808, - - -9223372036854775808 from dual END INPUT select from t1_mrg; @@ -12176,7 +12176,7 @@ INPUT select date_add("2001-01-01 23:59:59",INTERVAL -2000 YEAR); END OUTPUT -select date_add('2001-01-01 23:59:59', interval -2000 YEAR) from dual +select date_add('2001-01-01 23:59:59', interval (-2000) YEAR) from dual END INPUT select avg(2) from t1; @@ -12812,7 +12812,7 @@ INPUT select date_add("1997-12-31 23:59:59",INTERVAL -100000 DAY); END OUTPUT -select date_add('1997-12-31 23:59:59', interval -100000 DAY) from dual +select date_add('1997-12-31 23:59:59', interval (-100000) DAY) from dual END INPUT select dummy1,count(distinct id) from t1 group by dummy1; @@ -13832,7 +13832,7 @@ INPUT select date_add("1997-12-31 23:59:59",INTERVAL -100000 YEAR); END OUTPUT -select date_add('1997-12-31 23:59:59', interval -100000 YEAR) from dual +select date_add('1997-12-31 23:59:59', interval (-100000) YEAR) from dual END INPUT select substring_index('the king of the the hill',' ',-2); @@ -20180,7 +20180,7 @@ INPUT select -(-(9223372036854775808)); END OUTPUT -select 9223372036854775808 from dual +select - -9223372036854775808 from dual END INPUT select format(col2,7) from t1; diff --git a/go/vt/vtgate/evalengine/arithmetic.go b/go/vt/vtgate/evalengine/arithmetic.go index c57c3922eca..d1cf4f9d060 100644 --- a/go/vt/vtgate/evalengine/arithmetic.go +++ b/go/vt/vtgate/evalengine/arithmetic.go @@ -19,6 +19,8 @@ package evalengine import ( "bytes" "fmt" + "math" + "os" "strings" "vitess.io/vitess/go/hack" @@ -558,7 +560,9 @@ func floatPlusAny(v1 float64, v2 *EvalResult, out *EvalResult) error { if err != nil { return err } - out.setFloat(v1 + v2f) + add := v1 + v2f + fmt.Fprintf(os.Stderr, "%f (%v) + %f (%v) = %f (%v)\n", v1, math.Signbit(v1), v2f, math.Signbit(v2f), add, math.Signbit(add)) + out.setFloat(add) return nil } diff --git a/go/vt/vtgate/evalengine/cached_size.go b/go/vt/vtgate/evalengine/cached_size.go index 52af6589c8e..d7e7cc7f0a7 100644 --- a/go/vt/vtgate/evalengine/cached_size.go +++ b/go/vt/vtgate/evalengine/cached_size.go @@ -75,7 +75,7 @@ func (cached *BindVariable) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.Key))) return size } -func (cached *CallExpression) CachedSize(alloc bool) int64 { +func (cached *CallExpr) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) } diff --git a/go/vt/vtgate/evalengine/casting_test.go b/go/vt/vtgate/evalengine/casting_test.go index d5c29c44a80..c85f4c3d516 100644 --- a/go/vt/vtgate/evalengine/casting_test.go +++ b/go/vt/vtgate/evalengine/casting_test.go @@ -28,35 +28,35 @@ import ( func TestEvalResultToBooleanStrict(t *testing.T) { trueValues := []*EvalResult{{ - type_: sqltypes.Int64, + type_: int16(sqltypes.Int64), numeric_: 1, }, { - type_: sqltypes.Uint64, + type_: int16(sqltypes.Uint64), numeric_: 1, }, { - type_: sqltypes.Int8, + type_: int16(sqltypes.Int8), numeric_: 1, }} falseValues := []*EvalResult{{ - type_: sqltypes.Int64, + type_: int16(sqltypes.Int64), numeric_: 0, }, { - type_: sqltypes.Uint64, + type_: int16(sqltypes.Uint64), numeric_: 0, }, { - type_: sqltypes.Int8, + type_: int16(sqltypes.Int8), numeric_: 0, }} invalid := []*EvalResult{{ - type_: sqltypes.VarChar, + type_: int16(sqltypes.VarChar), bytes_: []byte("foobar"), }, { - type_: sqltypes.Float32, + type_: int16(sqltypes.Float32), numeric_: math.Float64bits(1.0), }, { - type_: sqltypes.Int64, + type_: int16(sqltypes.Int64), numeric_: 12, }} diff --git a/go/vt/vtgate/evalengine/convert.go b/go/vt/vtgate/evalengine/convert.go index 0776a0a0919..0bb4bea38aa 100644 --- a/go/vt/vtgate/evalengine/convert.go +++ b/go/vt/vtgate/evalengine/convert.go @@ -217,6 +217,8 @@ func convertExpr(e sqlparser.Expr, lookup ConverterLookup) (Expr, error) { case sqlparser.StrVal: collation := getCollation(e, lookup) return NewLiteralString(node.Bytes(), collation), nil + case sqlparser.HexNum: + return NewLiteralBinaryFromHexNum(node.Bytes()) case sqlparser.HexVal: return NewLiteralBinaryFromHex(node.Bytes()) } @@ -317,10 +319,10 @@ func convertExpr(e sqlparser.Expr, lookup ConverterLookup) (Expr, error) { case *Literal: switch collation { case collations.CollationBinaryID: - lit.Val.type_ = querypb.Type_VARBINARY + lit.Val.type_ = int16(querypb.Type_VARBINARY) lit.Val.collation_ = collationBinary default: - lit.Val.type_ = querypb.Type_VARCHAR + lit.Val.type_ = int16(querypb.Type_VARCHAR) lit.Val.replaceCollationID(collation) } case *BindVariable: @@ -359,12 +361,22 @@ func convertExpr(e sqlparser.Expr, lookup ConverterLookup) (Expr, error) { args = append(args, convertedExpr) aliases = append(aliases, aliased.As) } - return &CallExpression{ + return &CallExpr{ Arguments: args, Aliases: aliases, Method: method, Call: call, }, nil + case *sqlparser.UnaryExpr: + expr, err := convertExpr(node.Expr, lookup) + if err != nil { + return nil, err + } + + switch node.Operator { + case sqlparser.UMinusOp: + return &NegateExpr{UnaryExpr: UnaryExpr{expr}}, nil + } } return nil, convertNotSupported(e) } diff --git a/go/vt/vtgate/evalengine/eval_result.go b/go/vt/vtgate/evalengine/eval_result.go index bbac44880b2..ececfa9eeb4 100644 --- a/go/vt/vtgate/evalengine/eval_result.go +++ b/go/vt/vtgate/evalengine/eval_result.go @@ -18,6 +18,7 @@ package evalengine import ( "bytes" + "encoding/binary" "fmt" "math" "strconv" @@ -32,6 +33,10 @@ import ( "vitess.io/vitess/go/vt/vtgate/evalengine/decimal" ) +const ( + flagHex = 1 << iota +) + type ( // EvalResult is a lazily computed result of an evaluation EvalResult struct { @@ -47,7 +52,8 @@ type ( // Must not be accessed directly: call EvalResult.typeof() instead. // For most expression types, this is known ahead of time and calling typeof() does not require // an evaluation, so the type of an expression can be known without evaluating it. - type_ querypb.Type //nolint + type_ int16 //nolint + flags_ uint16 //nolint // collation_ is the collation of this result. It may be uninitialized. // Must not be accessed directly: call EvalResult.collation() instead. collation_ collations.TypedCollation //nolint @@ -77,7 +83,7 @@ type ( func (er *EvalResult) init(env *ExpressionEnv, expr Expr) { er.expr = expr er.env = env - er.type_ = expr.typeof(env) + er.type_ = int16(expr.typeof(env)) } const typecheckEval = false @@ -91,7 +97,9 @@ func (er *EvalResult) resolve() { before := er.type_ er.expr.eval(er.env, er) if er.type_ != before { - panic(fmt.Sprintf("did not pre-compute the right type: %v before evaluation, %v after", before.String(), er.type_.String())) + panic(fmt.Sprintf("did not pre-compute the right type: %v before evaluation, %v after", + querypb.Type(before).String(), + querypb.Type(er.type_).String())) } } else { er.expr.eval(er.env, er) @@ -104,7 +112,12 @@ func (er *EvalResult) typeof() querypb.Type { if er.type_ < 0 { er.resolve() } - return er.type_ + return querypb.Type(er.type_) +} + +func (er *EvalResult) hasFlag(f uint16) bool { + er.resolve() + return (er.flags_ & f) != 0 } func (er *EvalResult) collation() collations.TypedCollation { @@ -156,13 +169,13 @@ func (er *EvalResult) null() bool { } func (er *EvalResult) setNull() { - er.type_ = sqltypes.Null + er.type_ = int16(sqltypes.Null) er.collation_ = collationNull } func (er *EvalResult) setBool(b bool) { er.collation_ = collationNumeric - er.type_ = sqltypes.Int64 + er.type_ = int16(sqltypes.Int64) if b { er.numeric_ = 1 } else { @@ -179,49 +192,56 @@ func (er *EvalResult) setBoolean(b boolean) { } func (er *EvalResult) setRaw(typ querypb.Type, raw []byte, coll collations.TypedCollation) { - er.type_ = typ + er.type_ = int16(typ) er.bytes_ = raw er.collation_ = coll } +func (er *EvalResult) setBinaryHex(raw []byte) { + er.type_ = int16(sqltypes.VarBinary) + er.bytes_ = raw + er.collation_ = collationBinary + er.flags_ = flagHex +} + func (er *EvalResult) setString(str string, coll collations.TypedCollation) { - er.type_ = sqltypes.VarChar + er.type_ = int16(sqltypes.VarChar) er.bytes_ = []byte(str) er.collation_ = coll } func (er *EvalResult) setRawNumeric(typ querypb.Type, u uint64) { - er.type_ = typ + er.type_ = int16(typ) er.numeric_ = u er.collation_ = collationNumeric } func (er *EvalResult) setInt64(i int64) { - er.type_ = sqltypes.Int64 + er.type_ = int16(sqltypes.Int64) er.numeric_ = uint64(i) er.collation_ = collationNumeric } func (er *EvalResult) setUint64(u uint64) { - er.type_ = sqltypes.Uint64 + er.type_ = int16(sqltypes.Uint64) er.numeric_ = u er.collation_ = collationNumeric } func (er *EvalResult) setFloat(f float64) { - er.type_ = sqltypes.Float64 + er.type_ = int16(sqltypes.Float64) er.numeric_ = math.Float64bits(f) er.collation_ = collationNumeric } func (er *EvalResult) setDecimal(dec *decimalResult) { - er.type_ = sqltypes.Decimal + er.type_ = int16(sqltypes.Decimal) er.decimal_ = dec er.collation_ = collationNumeric } func (er *EvalResult) setTuple(t []EvalResult) { - er.type_ = querypb.Type_TUPLE + er.type_ = int16(querypb.Type_TUPLE) er.tuple_ = &t er.collation_ = collations.TypedCollation{} } @@ -352,7 +372,7 @@ func (er *EvalResult) ToBooleanStrict() (bool, error) { } } - switch er.type_ { + switch er.typeof() { case sqltypes.Int8, sqltypes.Int16, sqltypes.Int32, sqltypes.Int64: return intToBool(er.uint64()) case sqltypes.Uint8, sqltypes.Uint16, sqltypes.Uint32, sqltypes.Uint64: @@ -377,7 +397,7 @@ func (er *EvalResult) textual() bool { } func (er *EvalResult) truthy() boolean { - switch er.type_ { + switch er.typeof() { case sqltypes.Null: return boolNULL case sqltypes.Int8, sqltypes.Int16, sqltypes.Int32, sqltypes.Int64, sqltypes.Uint8, sqltypes.Uint16, sqltypes.Uint32, sqltypes.Uint64: @@ -395,7 +415,8 @@ func (er *EvalResult) truthy() boolean { } } -func formatMySQLFloat(typ querypb.Type, f float64) []byte { +// FormatFloat formats a float64 as a byte string in a similar way to what MySQL does +func FormatFloat(typ querypb.Type, f float64) []byte { format := byte('g') if typ == sqltypes.Decimal { format = 'f' @@ -423,7 +444,7 @@ func (er *EvalResult) toRawBytes() []byte { case sqltypes.Uint64, sqltypes.Uint32: return strconv.AppendUint(nil, er.uint64(), 10) case sqltypes.Float64, sqltypes.Float32: - return formatMySQLFloat(sqltypes.Float64, er.float64()) + return FormatFloat(sqltypes.Float64, er.float64()) case sqltypes.Decimal: dec := er.decimal() return dec.num.FormatCustom(dec.frac, roundingModeFormat) @@ -459,7 +480,7 @@ func (er *EvalResult) toSQLValue(resultType querypb.Type) sqltypes.Value { case sqltypes.Uint64, sqltypes.Uint32: return sqltypes.MakeTrusted(resultType, strconv.AppendUint(nil, er.uint64(), 10)) case sqltypes.Float64, sqltypes.Float32: - return sqltypes.MakeTrusted(resultType, formatMySQLFloat(resultType, er.float64())) + return sqltypes.MakeTrusted(resultType, FormatFloat(resultType, er.float64())) case sqltypes.Decimal: dec := er.decimal() return sqltypes.MakeTrusted(resultType, dec.num.FormatCustom(dec.frac, roundingModeFormat)) @@ -623,6 +644,18 @@ func (er *EvalResult) setBindVar1(typ querypb.Type, value []byte, collation coll throwEvalError(err) } er.setDecimal(dec) + case sqltypes.HexNum: + raw, err := parseHexNumber(value) + if err != nil { + throwEvalError(err) + } + er.setBinaryHex(raw) + case sqltypes.HexVal: + raw, err := parseHexLiteral(value[2 : len(value)-1]) + if err != nil { + throwEvalError(err) + } + er.setBinaryHex(raw) case sqltypes.VarChar, sqltypes.Text: er.setRaw(sqltypes.VarChar, value, collation) case sqltypes.VarBinary: @@ -632,7 +665,7 @@ func (er *EvalResult) setBindVar1(typ querypb.Type, value []byte, collation coll case sqltypes.Null: er.setNull() default: - throwEvalError(vterrors.Errorf(vtrpcpb.Code_INTERNAL, "Type is not supported: %s", typ.String())) + throwEvalError(vterrors.Errorf(vtrpcpb.Code_INTERNAL, "Type is not supported: %q %s", value, typ.String())) } } @@ -709,8 +742,24 @@ func (er *EvalResult) makeFloat() { } func (er *EvalResult) makeNumeric() { + er.resolve() if er.numeric() { - er.resolve() + return + } + if er.typeof() == querypb.Type_VARBINARY && er.hasFlag(flagHex) { + raw := er.bytes() + if len(raw) > 8 { + // overflow + er.setFloat(0) + return + } + + var number [8]byte + for i, b := range raw { + number[8-len(raw)+i] = b + } + u := binary.BigEndian.Uint64(number[:]) + er.setUint64(u) return } if ival, err := strconv.ParseInt(er.string(), 10, 64); err == nil { @@ -724,6 +773,39 @@ func (er *EvalResult) makeNumeric() { er.setFloat(0) } +func (er *EvalResult) negateNumeric() { + er.makeNumeric() + switch er.typeof() { + case querypb.Type_INT8, querypb.Type_INT16, querypb.Type_INT32, querypb.Type_INT64: + i := er.int64() + if i == math.MinInt64 { + dec := newDecimalInt64(i) + dec.num.SetSignbit(false) + er.setDecimal(dec) + } else { + er.setInt64(-i) + } + case querypb.Type_UINT8, querypb.Type_UINT16, querypb.Type_UINT32, querypb.Type_UINT64: + u := er.uint64() + if er.hasFlag(flagHex) { + er.setFloat(-float64(u)) + } else if u > math.MaxInt64+1 { + dec := newDecimalUint64(u) + dec.num.SetSignbit(true) + er.setDecimal(dec) + } else { + er.setInt64(-int64(u)) + } + case querypb.Type_FLOAT32, querypb.Type_FLOAT64: + er.setFloat(-er.float64()) + case querypb.Type_DECIMAL: + dec := er.decimal() + if !dec.num.IsZero() { + dec.num.SetSignbit(!dec.num.Signbit()) + } + } +} + func (er *EvalResult) coerceDecimalToFloat() (float64, bool) { dec := &er.decimal().num if f, ok := dec.Float64(); ok { diff --git a/go/vt/vtgate/evalengine/evalengine.go b/go/vt/vtgate/evalengine/evalengine.go index fcae7c3e78a..5e7bc70de14 100644 --- a/go/vt/vtgate/evalengine/evalengine.go +++ b/go/vt/vtgate/evalengine/evalengine.go @@ -22,9 +22,7 @@ import ( "time" "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/sqltypes" - querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" diff --git a/go/vt/vtgate/evalengine/expressions.go b/go/vt/vtgate/evalengine/expressions.go index 3b63cb506a8..8a649e56a4f 100644 --- a/go/vt/vtgate/evalengine/expressions.go +++ b/go/vt/vtgate/evalengine/expressions.go @@ -18,6 +18,7 @@ package evalengine import ( "encoding/hex" + "math" "strconv" "unicode/utf8" @@ -68,10 +69,6 @@ type ( TypedCollation collations.TypedCollation } - UnaryExpr struct { - Inner Expr - } - BinaryExpr struct { Left, Right Expr } @@ -88,7 +85,7 @@ var _ Expr = (TupleExpr)(nil) var _ Expr = (*CollateExpr)(nil) var _ Expr = (*LogicalExpr)(nil) var _ Expr = (*NotExpr)(nil) -var _ Expr = (*CallExpression)(nil) +var _ Expr = (*CallExpr)(nil) type evalError struct { error @@ -270,22 +267,20 @@ func init() { // NewLiteralIntegralFromBytes returns a literal expression. // It tries to return an int64, but if the value is too large, it tries with an uint64 func NewLiteralIntegralFromBytes(val []byte) (Expr, error) { - str := string(val) - ival, err := strconv.ParseInt(str, 10, 64) - if err == nil { - return NewLiteralInt(ival), nil - } - - // let's try with uint if we overflowed - numError, ok := err.(*strconv.NumError) - if !ok || numError.Err != strconv.ErrRange { - return nil, err + if val[0] == '-' { + panic("NewLiteralIntegralFromBytes: negative value") } - uval, err := strconv.ParseUint(str, 0, 64) + uval, err := strconv.ParseUint(string(val), 10, 64) if err != nil { + if numError, ok := err.(*strconv.NumError); ok && numError.Err == strconv.ErrRange { + return NewLiteralDecimalFromBytes(val) + } return nil, err } + if uval <= math.MaxInt64 { + return NewLiteralInt(int64(uval)), nil + } return NewLiteralUint(uval), nil } @@ -345,13 +340,49 @@ func NewLiteralString(val []byte, collation collations.TypedCollation) Expr { return lit } -func NewLiteralBinaryFromHex(val []byte) (Expr, error) { +func parseHexLiteral(val []byte) ([]byte, error) { raw := make([]byte, hex.DecodedLen(len(val))) if _, err := hex.Decode(raw, val); err != nil { return nil, err } + return raw, nil +} + +func parseHexNumber(val []byte) ([]byte, error) { + if val[0] != '0' || val[1] != 'x' { + panic("malformed hex literal from parser") + } + if len(val)%2 == 0 { + return parseHexLiteral(val[2:]) + } + // If the hex literal doesn't have an even amount of hex digits, we need + // to pad it with a '0' in the left. Instead of allocating a new slice + // for padding pad in-place by replacing the 'x' in the original slice with + // a '0', and clean it up after parsing. + val[1] = '0' + defer func() { + val[1] = 'x' + }() + return parseHexLiteral(val[1:]) +} + +func NewLiteralBinaryFromHex(val []byte) (Expr, error) { + raw, err := parseHexLiteral(val) + if err != nil { + return nil, err + } + lit := &Literal{} + lit.Val.setBinaryHex(raw) + return lit, nil +} + +func NewLiteralBinaryFromHexNum(val []byte) (Expr, error) { + raw, err := parseHexNumber(val) + if err != nil { + return nil, err + } lit := &Literal{} - lit.Val.setRaw(sqltypes.VarBinary, raw, collationBinary) + lit.Val.setBinaryHex(raw) return lit, nil } @@ -381,10 +412,6 @@ func NewTupleExpr(exprs ...Expr) TupleExpr { return tupleExpr } -func (c *UnaryExpr) typeof(env *ExpressionEnv) querypb.Type { - return c.Inner.typeof(env) -} - // eval implements the Expr interface func (l *Literal) eval(_ *ExpressionEnv, result *EvalResult) { *result = l.Val diff --git a/go/vt/vtgate/evalengine/format.go b/go/vt/vtgate/evalengine/format.go index 1e8209c29ae..f03ce422602 100644 --- a/go/vt/vtgate/evalengine/format.go +++ b/go/vt/vtgate/evalengine/format.go @@ -171,7 +171,7 @@ func (i *IsExpr) format(w *formatter, depth int) { } } -func (c *CallExpression) format(w *formatter, depth int) { +func (c *CallExpr) format(w *formatter, depth int) { w.Indent(depth) w.WriteString(strings.ToUpper(c.Method)) w.WriteByte('(') @@ -187,3 +187,9 @@ func (c *CallExpression) format(w *formatter, depth int) { } w.WriteByte(')') } + +func (n *NegateExpr) format(w *formatter, depth int) { + w.Indent(depth) + w.WriteByte('-') + n.Inner.format(w, depth) +} diff --git a/go/vt/vtgate/evalengine/func.go b/go/vt/vtgate/evalengine/func.go index b98ec9506c7..9b2df8d7e77 100644 --- a/go/vt/vtgate/evalengine/func.go +++ b/go/vt/vtgate/evalengine/func.go @@ -26,18 +26,18 @@ import ( "vitess.io/vitess/go/vt/sqlparser" ) -type CallExpression struct { +type CallExpr struct { Arguments TupleExpr Aliases []sqlparser.ColIdent Method string Call func(*ExpressionEnv, []EvalResult, *EvalResult) } -func (c *CallExpression) typeof(*ExpressionEnv) querypb.Type { +func (c *CallExpr) typeof(*ExpressionEnv) querypb.Type { return -1 } -func (c *CallExpression) eval(env *ExpressionEnv, result *EvalResult) { +func (c *CallExpr) eval(env *ExpressionEnv, result *EvalResult) { var args []EvalResult = make([]EvalResult, len(c.Arguments)) for i, arg := range c.Arguments { args[i].init(env, arg) @@ -76,7 +76,8 @@ func getMultiComparisonFunc(args []EvalResult) multiComparisonFunc { In all other cases, the arguments are compared as binary strings. */ - for _, arg := range args { + for i := range args { + arg := &args[i] switch arg.typeof() { case querypb.Type_NULL_TYPE: return func(args []EvalResult, result *EvalResult, cmp int) { diff --git a/go/vt/vtgate/evalengine/integration/comparison_test.go b/go/vt/vtgate/evalengine/integration/comparison_test.go index d827a971ed0..73b5a15d9d8 100644 --- a/go/vt/vtgate/evalengine/integration/comparison_test.go +++ b/go/vt/vtgate/evalengine/integration/comparison_test.go @@ -17,6 +17,7 @@ limitations under the License. package integration import ( + "flag" "fmt" "math" "strconv" @@ -24,6 +25,8 @@ import ( "testing" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/vtgate/evalengine" ) func perm(a []string, f func([]string)) { @@ -43,6 +46,31 @@ func perm1(a []string, f func([]string), i int) { } } +func normalize(v sqltypes.Value) string { + typ := v.Type() + if typ == sqltypes.Null { + return "NULL" + } + if v.IsQuoted() || typ == sqltypes.Bit { + return fmt.Sprintf("%v(%q)", typ, v.Raw()) + } + if typ == sqltypes.Float32 || typ == sqltypes.Float64 { + var bitsize = 64 + if typ == sqltypes.Float32 { + bitsize = 32 + } + f, err := strconv.ParseFloat(v.RawStr(), bitsize) + if err != nil { + panic(err) + } + return fmt.Sprintf("%v(%s)", typ, evalengine.FormatFloat(typ, f)) + } + return fmt.Sprintf("%v(%s)", typ, v.Raw()) +} + +var debugPrintAll = flag.Bool("print-all", false, "print all matching tests") +var debugNormalize = flag.Bool("normalize", true, "normalize comparisons against MySQL values") + func compareRemoteQuery(t *testing.T, conn *mysql.Conn, query string) { t.Helper() @@ -54,10 +82,16 @@ func compareRemoteQuery(t *testing.T, conn *mysql.Conn, query string) { localVal = local.Value().String() } if remoteErr == nil { - remoteVal = remote.Rows[0][0].String() + if *debugNormalize { + remoteVal = normalize(remote.Rows[0][0]) + } else { + remoteVal = remote.Rows[0][0].String() + } } if diff := compareResult(localErr, remoteErr, localVal, remoteVal, evaluated); diff != "" { t.Errorf("%s\nquery: %s", diff, query) + } else if *debugPrintAll { + t.Logf("local=%s mysql=%s\nquery: %s", localVal, remoteVal, query) } } @@ -227,6 +261,93 @@ func TestCollationOperations(t *testing.T) { } } +func TestNegateArithmetic(t *testing.T) { + *debugPrintAll = true + var cases = []string{ + `0`, `1`, `1.0`, `0.0`, `1.0e0`, `0.0e0`, + `X'00'`, `X'1234'`, `X'ff'`, + `0x00`, `0x1`, `0x1234`, + `0xff`, `0xffff`, `0xffffffff`, `0xffffffffffffffff`, + strconv.FormatUint(math.MaxUint64, 10), + strconv.FormatUint(math.MaxInt64, 10), + strconv.FormatInt(math.MinInt64, 10), + `'foobar'`, `'FOOBAR'`, + } + + var conn = mysqlconn(t) + defer conn.Close() + + for _, rhs := range cases { + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT - %s", rhs)) + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT -%s", rhs)) + } +} + +func TestNumericTypes(t *testing.T) { + var numbers = []string{ + `1234`, `-1234`, + `18446744073709551614`, + `18446744073709551615`, // MaxUint64 + `18446744073709551616`, + `-18446744073709551614`, + `-18446744073709551615`, // -MaxUint64 + `-18446744073709551616`, + `9223372036854775805`, + `9223372036854775806`, + `9223372036854775807`, // MaxInt64 + `9223372036854775808`, // -MinInt64 + `9223372036854775809`, + `-9223372036854775805`, + `-9223372036854775806`, + `-9223372036854775807`, // -MaxInt64 + `-9223372036854775808`, // MinInt64 + `-9223372036854775809`, + } + + var conn = mysqlconn(t) + defer conn.Close() + + for _, rhs := range numbers { + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT %s", rhs)) + } +} + +func TestHexArithmetic(t *testing.T) { + var cases = []string{ + `0`, `1`, `1.0`, `0.0`, `1.0e0`, `0.0e0`, + `X'00'`, `X'1234'`, `X'ff'`, + `0x00`, `0x1`, `0x1234`, + `0xff`, `0xffff`, `0xffffffff`, `0xffffffffffffffff`, + } + + var conn = mysqlconn(t) + defer conn.Close() + + for _, lhs := range cases { + for _, rhs := range cases { + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT %s + %s", lhs, rhs)) + + // compare with negative values too + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT -%s + -%s", lhs, rhs)) + } + } +} + +func TestX(t *testing.T) { + var conn = mysqlconn(t) + defer conn.Close() + + for _, expr := range []string{ + "0 + -X'00'", + "0 - X'00'", + "-X'00'", + "X'00'", + "X'00'+0e0", + } { + compareRemoteQuery(t, conn, "SELECT "+expr) + } +} + func TestTypes(t *testing.T) { var conn = mysqlconn(t) defer conn.Close() @@ -259,3 +380,33 @@ func TestTypes(t *testing.T) { compareRemoteQuery(t, conn, "SELECT "+query) } } + +func TestFloatFormatting(t *testing.T) { + var floats = []string{ + `18446744073709551615`, + `9223372036854775807`, + `0xff`, `0xffff`, `0xffffffff`, + `0xffffffffffffffff`, + `0xfffffffffffffffe`, + `0xffffffffffffffff0`, + `0x1fffffffffffffff`, + } + + var conn = mysqlconn(t) + defer conn.Close() + + for _, f := range floats { + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT %s + 0.0e0", f)) + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT -%s", f)) + } + + for i := 0; i < 64; i++ { + v := uint64(1) << i + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT %d + 0.0e0", v)) + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT %d + 0.0e0", v+1)) + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT %d + 0.0e0", ^v)) + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT -%de0", v)) + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT -%de0", v+1)) + compareRemoteQuery(t, conn, fmt.Sprintf("SELECT -%de0", ^v)) + } +} diff --git a/go/vt/vtgate/evalengine/mysql_test.go b/go/vt/vtgate/evalengine/mysql_test.go index fcdd41a39d7..ee54ad44ff2 100644 --- a/go/vt/vtgate/evalengine/mysql_test.go +++ b/go/vt/vtgate/evalengine/mysql_test.go @@ -52,7 +52,7 @@ func testSingle(t *testing.T, query string) (EvalResult, error) { } astExpr := stmt.(*sqlparser.Select).SelectExprs[0].(*sqlparser.AliasedExpr).Expr - converted, err := ConvertEx(astExpr, LookupDefaultCollation(255), false) + converted, err := ConvertEx(astExpr, LookupDefaultCollation(255), true) if err == nil { if knownBadQuery(converted) { return EvalResult{}, errKnownBadQuery @@ -118,6 +118,6 @@ func TestMySQLGolden(t *testing.T) { func TestDebug1(t *testing.T) { // Debug - eval, err := testSingle(t, `SELECT 'foo' = _latin1 'foo'`) + eval, err := testSingle(t, `SELECT -0.0 + -0.0e0`) t.Logf("eval=%s err=%v", eval.Value(), err) // want value="" } diff --git a/go/vt/vtgate/evalengine/simplify.go b/go/vt/vtgate/evalengine/simplify.go index c19131c0d99..1638b9eeb9c 100644 --- a/go/vt/vtgate/evalengine/simplify.go +++ b/go/vt/vtgate/evalengine/simplify.go @@ -162,11 +162,11 @@ func (expr *UnaryExpr) simplify(env *ExpressionEnv) error { return err } -func (c *CallExpression) constant() bool { +func (c *CallExpr) constant() bool { return c.Arguments.constant() } -func (c *CallExpression) simplify(env *ExpressionEnv) error { +func (c *CallExpr) simplify(env *ExpressionEnv) error { return c.Arguments.simplify(env) } diff --git a/go/vt/vtgate/evalengine/unary.go b/go/vt/vtgate/evalengine/unary.go new file mode 100644 index 00000000000..5f063702a09 --- /dev/null +++ b/go/vt/vtgate/evalengine/unary.go @@ -0,0 +1,44 @@ +/* +Copyright 2021 The Vitess Authors. + +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 evalengine + +import querypb "vitess.io/vitess/go/vt/proto/query" + +type ( + UnaryExpr struct { + Inner Expr + } + + NegateExpr struct { + UnaryExpr + } +) + +func (c *UnaryExpr) typeof(env *ExpressionEnv) querypb.Type { + return c.Inner.typeof(env) +} + +func (n *NegateExpr) eval(env *ExpressionEnv, result *EvalResult) { + result.init(env, n.Inner) + result.negateNumeric() +} + +func (n *NegateExpr) typeof(env *ExpressionEnv) querypb.Type { + // the type of a NegateExpr is not known beforehand because negating + // a large enough value can cause it to be upcasted into a larger type + return -1 +} diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt index ca0a6f3721d..eef2d5519fa 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt @@ -829,7 +829,22 @@ Gen4 plan same as above # insert unsharded, invalid value for auto-inc "insert into unsharded_auto(id, val) values(18446744073709551616, 'aa')" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" +{ + "QueryType": "INSERT", + "Original": "insert into unsharded_auto(id, val) values(18446744073709551616, 'aa')", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "MultiShardAutocommit": false, + "Query": "insert into unsharded_auto(id, val) values (:__seq0, 'aa')", + "TableName": "unsharded_auto" + } +} Gen4 plan same as above # insert unsharded, column present @@ -1239,12 +1254,42 @@ Gen4 plan same as above # insert for non-vindex autoinc, invalid value "insert into user_extra(nonid, extra_id) values (2, 18446744073709551616)" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" +{ + "QueryType": "INSERT", + "Original": "insert into user_extra(nonid, extra_id) values (2, 18446744073709551616)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "MultiShardAutocommit": false, + "Query": "insert into user_extra(nonid, extra_id, user_id) values (2, :__seq0, :_user_id_0)", + "TableName": "user_extra" + } +} Gen4 plan same as above # insert invalid index value "insert into music_extra(music_id, user_id) values(1, 18446744073709551616)" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" +{ + "QueryType": "INSERT", + "Original": "insert into music_extra(music_id, user_id) values(1, 18446744073709551616)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "MultiShardAutocommit": false, + "Query": "insert into music_extra(music_id, user_id) values (:_music_id_0, :_user_id_0)", + "TableName": "music_extra" + } +} Gen4 plan same as above # insert invalid index value @@ -1365,7 +1410,22 @@ Gen4 plan same as above # replace unsharded, invalid value for auto-inc "replace into unsharded_auto(id, val) values(18446744073709551616, 'aa')" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" +{ + "QueryType": "INSERT", + "Original": "replace into unsharded_auto(id, val) values(18446744073709551616, 'aa')", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "MultiShardAutocommit": false, + "Query": "replace into unsharded_auto(id, val) values (:__seq0, 'aa')", + "TableName": "unsharded_auto" + } +} Gen4 plan same as above # replace unsharded, column present diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt index 9bdc1019d2c..88abec9aae3 100644 --- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt @@ -2223,3 +2223,35 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non "Table": "unsharded" } } + +# Equal filter with hexadecimal value +"select count(*) a from user having a = 0x01" +"unsupported: filtering on results of aggregates" +{ + "QueryType": "SELECT", + "Original": "select count(*) a from user having a = 0x01", + "Instructions": { + "OperatorType": "Filter", + "Predicate": "a = 0x01", + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "count(0) AS a", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*) as a from `user` where 1 != 1", + "Query": "select count(*) as a from `user`", + "Table": "`user`" + } + ] + } + ] + } +} diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt index e3542235701..7ee7d2fe0ba 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt @@ -981,7 +981,25 @@ Gen4 plan same as above "Table": "`user`" } } -Gen4 plan same as above +{ + "QueryType": "SELECT", + "Original": "select * from user where id = 0x04", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from `user` where 1 != 1", + "Query": "select * from `user` where id = 0x04", + "Table": "`user`", + "Values": [ + "VARBINARY(\"\\x04\")" + ], + "Vindex": "user_index" + } +} # sharded limit offset "select user_id from music order by user_id limit 10, 20" diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt index 065c29cf59a..126dd878a93 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt @@ -628,11 +628,6 @@ Gen4 plan same as above "unsupported: cross-shard query with aggregates" Gen4 plan same as above -# Equal filter with hexadecimal value -"select count(*) a from user having a = 0x01" -"unsupported: filtering on results of aggregates" -Gen4 error: expr cannot be converted, not supported: 0x01 - # scatter aggregate with complex select list (can't build order by) "select distinct a+1 from user" "generating order by clause: cannot reference a complex expression" diff --git a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt index f6f52c098f6..cf4b759107c 100644 --- a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt @@ -1335,26 +1335,29 @@ # Invalid value in IN clause "select id from user where id in (18446744073709551616, 1)" -"strconv.ParseUint: parsing "18446744073709551616": value out of range" { "QueryType": "SELECT", "Original": "select id from user where id in (18446744073709551616, 1)", "Instructions": { "OperatorType": "Route", - "Variant": "Scatter", + "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, "FieldQuery": "select id from `user` where 1 != 1", - "Query": "select id from `user` where id in (18446744073709551616, 1)", - "Table": "`user`" + "Query": "select id from `user` where id in ::__vals", + "Table": "`user`", + "Values": [ + "(DECIMAL(18446744073709551616), INT64(1))" + ], + "Vindex": "user_index" } } +Gen4 plan same as above # Invalid value in IN clause from LHS of join "select u1.id from user u1 join user u2 where u1.id = 18446744073709551616" -"strconv.ParseUint: parsing "18446744073709551616": value out of range" { "QueryType": "SELECT", "Original": "select u1.id from user u1 join user u2 where u1.id = 18446744073709551616", @@ -1366,14 +1369,18 @@ "Inputs": [ { "OperatorType": "Route", - "Variant": "Scatter", + "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, "FieldQuery": "select u1.id from `user` as u1 where 1 != 1", "Query": "select u1.id from `user` as u1 where u1.id = 18446744073709551616", - "Table": "`user`" + "Table": "`user`", + "Values": [ + "DECIMAL(18446744073709551616)" + ], + "Vindex": "user_index" }, { "OperatorType": "Route", @@ -1389,10 +1396,10 @@ ] } } +Gen4 plan same as above # Invalid value in IN clause from RHS of join "select u1.id from user u1 join user u2 where u2.id = 18446744073709551616" -"strconv.ParseUint: parsing "18446744073709551616": value out of range" { "QueryType": "SELECT", "Original": "select u1.id from user u1 join user u2 where u2.id = 18446744073709551616", @@ -1415,18 +1422,23 @@ }, { "OperatorType": "Route", - "Variant": "Scatter", + "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, "FieldQuery": "select 1 from `user` as u2 where 1 != 1", "Query": "select 1 from `user` as u2 where u2.id = 18446744073709551616", - "Table": "`user`" + "Table": "`user`", + "Values": [ + "DECIMAL(18446744073709551616)" + ], + "Vindex": "user_index" } ] } } +Gen4 plan same as above # derived table with column aliases not supported by v3, but planner is overridden with hint "select /*vt+ PLANNER=gen4 */ u.a from (select id as b, name from user) u(a, n) where u.n = 1" diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index c3f314bad37..731c0f0d797 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -184,7 +184,17 @@ # squence with bad value "select next 12345667852342342342323423423 values from seq" -"strconv.ParseUint: parsing "12345667852342342342323423423": value out of range" +{ + "PlanID": "Nextval", + "TableName": "seq", + "Permissions": [ + { + "TableName": "seq", + "Role": 0 + } + ], + "NextCount": "DECIMAL(12345667852342342342323423423)" +} # nextval on non-sequence table "select next value from a"