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

Commit 4095f3d

Browse files
authored
Merge pull request #456 from erizocosmico/feature/negative-numbers
sql/(parse,expression): implement unary minus
2 parents b1203b4 + 03bb18b commit 4095f3d

File tree

5 files changed

+136
-0
lines changed

5 files changed

+136
-0
lines changed

engine_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ var queries = []struct {
513513
"CREATE DATABASE `mydb` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8_bin */",
514514
}},
515515
},
516+
{
517+
`SELECT -1`,
518+
[]sql.Row{{int64(-1)}},
519+
},
516520
}
517521

518522
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
@@ -748,6 +748,8 @@ func exprToExpression(e sqlparser.Expr) (sql.Expression, error) {
748748

749749
case *sqlparser.BinaryExpr:
750750
return binaryExprToExpression(v)
751+
case *sqlparser.UnaryExpr:
752+
return unaryExprToExpression(v)
751753
}
752754
}
753755

@@ -893,6 +895,21 @@ func selectExprToExpression(se sqlparser.SelectExpr) (sql.Expression, error) {
893895
}
894896
}
895897

898+
func unaryExprToExpression(e *sqlparser.UnaryExpr) (sql.Expression, error) {
899+
switch e.Operator {
900+
case sqlparser.MinusStr:
901+
expr, err := exprToExpression(e.Expr)
902+
if err != nil {
903+
return nil, err
904+
}
905+
906+
return expression.NewUnaryMinus(expr), nil
907+
908+
default:
909+
return nil, ErrUnsupportedFeature.New("unary operator: " + e.Operator)
910+
}
911+
}
912+
896913
func binaryExprToExpression(be *sqlparser.BinaryExpr) (sql.Expression, error) {
897914
switch be.Operator {
898915
case

sql/parse/parse_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,14 @@ var fixtures = map[string]sql.Node{
829829
`SHOW CREATE SCHEMA foo`: plan.NewShowCreateDatabase(sql.UnresolvedDatabase("foo"), false),
830830
`SHOW CREATE DATABASE IF NOT EXISTS foo`: plan.NewShowCreateDatabase(sql.UnresolvedDatabase("foo"), true),
831831
`SHOW CREATE SCHEMA IF NOT EXISTS foo`: plan.NewShowCreateDatabase(sql.UnresolvedDatabase("foo"), true),
832+
`SELECT -i FROM mytable`: plan.NewProject(
833+
[]sql.Expression{
834+
expression.NewUnaryMinus(
835+
expression.NewUnresolvedColumn("i"),
836+
),
837+
},
838+
plan.NewUnresolvedTable("mytable", ""),
839+
),
832840
}
833841

834842
func TestParse(t *testing.T) {

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