Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit 452abbf

Browse files
committed
sql/(parse,expression): implement unary minus
Signed-off-by: Miguel Molina <miguel@erizocosmi.co>
1 parent 873d454 commit 452abbf

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed

engine_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ var queries = []struct {
418418
{"c", int32(0)},
419419
},
420420
},
421+
{
422+
`SELECT -1`,
423+
[]sql.Row{{int64(-1)}},
424+
},
421425
}
422426

423427
func TestQueries(t *testing.T) {

sql/expression/arithmetic.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package expression
22

33
import (
44
"fmt"
5+
"reflect"
56

67
errors "gopkg.in/src-d/go-errors.v1"
78
"gopkg.in/src-d/go-vitess.v1/vt/sqlparser"
@@ -407,3 +408,81 @@ func mod(lval, rval interface{}) (interface{}, error) {
407408

408409
return nil, errUnableToCast.New(lval, rval)
409410
}
411+
412+
// UnaryMinus is an unary minus operator.
413+
type UnaryMinus struct {
414+
UnaryExpression
415+
}
416+
417+
// NewUnaryMinus creates a new UnaryMinus expression node.
418+
func NewUnaryMinus(child sql.Expression) *UnaryMinus {
419+
return &UnaryMinus{UnaryExpression{Child: child}}
420+
}
421+
422+
// Eval implements the sql.Expression interface.
423+
func (e *UnaryMinus) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
424+
child, err := e.Child.Eval(ctx, row)
425+
if err != nil {
426+
return nil, err
427+
}
428+
429+
if child == nil {
430+
return nil, nil
431+
}
432+
433+
if !sql.IsNumber(e.Child.Type()) {
434+
child, err = sql.Float64.Convert(child)
435+
if err != nil {
436+
child = 0.0
437+
}
438+
}
439+
440+
switch n := child.(type) {
441+
case float64:
442+
return -n, nil
443+
case float32:
444+
return -n, nil
445+
case int64:
446+
return -n, nil
447+
case uint64:
448+
return -int64(n), nil
449+
case int32:
450+
return -n, nil
451+
case uint32:
452+
return -int32(n), nil
453+
default:
454+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(n))
455+
}
456+
}
457+
458+
// Type implements the sql.Expression interface.
459+
func (e *UnaryMinus) Type() sql.Type {
460+
typ := e.Child.Type()
461+
if !sql.IsNumber(typ) {
462+
return sql.Float64
463+
}
464+
465+
if typ == sql.Uint32 {
466+
return sql.Int32
467+
}
468+
469+
if typ == sql.Uint64 {
470+
return sql.Int64
471+
}
472+
473+
return e.Child.Type()
474+
}
475+
476+
func (e *UnaryMinus) String() string {
477+
return fmt.Sprintf("-%s", e.Child)
478+
}
479+
480+
// TransformUp implements the sql.Expression interface.
481+
func (e *UnaryMinus) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
482+
c, err := e.Child.TransformUp(f)
483+
if err != nil {
484+
return nil, err
485+
}
486+
487+
return f(NewUnaryMinus(c))
488+
}

sql/expression/arithmetic_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,31 @@ func TestAllInt64(t *testing.T) {
360360
})
361361
}
362362
}
363+
364+
func TestUnaryMinus(t *testing.T) {
365+
testCases := []struct {
366+
name string
367+
input interface{}
368+
typ sql.Type
369+
expected interface{}
370+
}{
371+
{"int32", int32(1), sql.Int32, int32(-1)},
372+
{"uint32", uint32(1), sql.Uint32, int32(-1)},
373+
{"int64", int64(1), sql.Int64, int64(-1)},
374+
{"uint64", uint64(1), sql.Uint64, int64(-1)},
375+
{"float32", float32(1), sql.Float32, float32(-1)},
376+
{"float64", float64(1), sql.Float64, float64(-1)},
377+
{"int text", "1", sql.Text, float64(-1)},
378+
{"float text", "1.2", sql.Text, float64(-1.2)},
379+
{"nil", nil, sql.Text, nil},
380+
}
381+
382+
for _, tt := range testCases {
383+
t.Run(tt.name, func(t *testing.T) {
384+
f := NewUnaryMinus(NewLiteral(tt.input, tt.typ))
385+
result, err := f.Eval(sql.NewEmptyContext(), nil)
386+
require.NoError(t, err)
387+
require.Equal(t, tt.expected, result)
388+
})
389+
}
390+
}

sql/parse/parse.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,8 @@ func exprToExpression(e sqlparser.Expr) (sql.Expression, error) {
737737

738738
case *sqlparser.BinaryExpr:
739739
return binaryExprToExpression(v)
740+
case *sqlparser.UnaryExpr:
741+
return unaryExprToExpression(v)
740742
}
741743
}
742744

@@ -882,6 +884,21 @@ func selectExprToExpression(se sqlparser.SelectExpr) (sql.Expression, error) {
882884
}
883885
}
884886

887+
func unaryExprToExpression(e *sqlparser.UnaryExpr) (sql.Expression, error) {
888+
switch e.Operator {
889+
case sqlparser.MinusStr:
890+
expr, err := exprToExpression(e.Expr)
891+
if err != nil {
892+
return nil, err
893+
}
894+
895+
return expression.NewUnaryMinus(expr), nil
896+
897+
default:
898+
return nil, ErrUnsupportedFeature.New("unary operator: " + e.Operator)
899+
}
900+
}
901+
885902
func binaryExprToExpression(be *sqlparser.BinaryExpr) (sql.Expression, error) {
886903
switch be.Operator {
887904
case

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy