From d367726709f1d8698b571f04f1831d680523edf4 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sun, 3 Dec 2017 00:48:50 +0000 Subject: [PATCH] Ensure NULL/VOID/EMPTY values are compared correctly --- db/fetch.go | 178 ++++++++++++++++++++++++++++++---------------- db/select_test.go | 90 +++++++++++++++++++++++ 2 files changed, 207 insertions(+), 61 deletions(-) diff --git a/db/fetch.go b/db/fetch.go index a2a869ad..e8e0561b 100644 --- a/db/fetch.go +++ b/db/fetch.go @@ -409,94 +409,150 @@ func binaryMath(op sql.Token, l, r interface{}) interface{} { func binaryCheck(op sql.Token, l, r, lo, ro interface{}, d *data.Doc) interface{} { + switch lo.(type) { + case *sql.Void: + switch ro.(type) { + default: + return op == sql.NEQ + case nil: + return op == sql.NEQ + case *sql.Void: + return op == sql.EQ + case *sql.Empty: + return op == sql.EQ + case *sql.Ident: + break + } + case *sql.Empty: + switch ro.(type) { + default: + return op == sql.NEQ + case nil: + return op == sql.EQ + case *sql.Void: + return op == sql.EQ + case *sql.Empty: + return op == sql.EQ + case *sql.Param: + break + case *sql.Ident: + break + } + } + + switch ro.(type) { + case *sql.Void: + switch lo.(type) { + default: + return op == sql.NEQ + case nil: + return op == sql.NEQ + case *sql.Void: + return op == sql.EQ + case *sql.Empty: + return op == sql.EQ + case *sql.Ident: + break + } + case *sql.Empty: + switch lo.(type) { + default: + return op == sql.NEQ + case nil: + return op == sql.EQ + case *sql.Void: + return op == sql.EQ + case *sql.Empty: + return op == sql.EQ + case *sql.Param: + break + case *sql.Ident: + break + } + } + if d != nil { - switch l := lo.(type) { - + switch lo.(type) { case *sql.Void: - - switch r.(type) { - case nil: - return op == sql.NEQ - } - - case *sql.Ident: - - switch r.(type) { - - case *sql.Void: - if op == sql.EQ { - return d.Exists(l.ID) == false - } else if op == sql.NEQ { - return d.Exists(l.ID) == true - } - - case nil: - if op == sql.EQ { - return d.Exists(l.ID) == true && d.Get(l.ID).Data() == nil - } else if op == sql.NEQ { - return d.Exists(l.ID) == false || d.Get(l.ID).Data() != nil - } - - case *sql.Empty: - if op == sql.EQ { - return d.Exists(l.ID) == false || d.Get(l.ID).Data() == nil - } else if op == sql.NEQ { - return d.Exists(l.ID) == true && d.Get(l.ID).Data() != nil - } - - } - - } - - switch r := ro.(type) { - - case *sql.Void: - - switch l.(type) { - case nil: - return op == sql.NEQ - } - - case *sql.Ident: - - switch l.(type) { - - case *sql.Void: + switch r := ro.(type) { + case *sql.Ident: if op == sql.EQ { return d.Exists(r.ID) == false } else if op == sql.NEQ { return d.Exists(r.ID) == true } - - case nil: - if op == sql.EQ { - return d.Exists(r.ID) == true && d.Get(r.ID).Data() == nil - } else if op == sql.NEQ { - return d.Exists(r.ID) == false || d.Get(r.ID).Data() != nil - } - - case *sql.Empty: + } + case *sql.Empty: + switch r := ro.(type) { + case *sql.Ident: if op == sql.EQ { return d.Exists(r.ID) == false || d.Get(r.ID).Data() == nil } else if op == sql.NEQ { return d.Exists(r.ID) == true && d.Get(r.ID).Data() != nil } - } + case nil: + switch r := ro.(type) { + case *sql.Ident: + if op == sql.EQ { + return d.Exists(r.ID) == true && d.Get(r.ID).Data() == nil + } else if op == sql.NEQ { + return d.Exists(r.ID) == false || d.Get(r.ID).Data() != nil + } + } + } + switch ro.(type) { + case *sql.Void: + switch l := lo.(type) { + case *sql.Ident: + if op == sql.EQ { + return d.Exists(l.ID) == false + } else if op == sql.NEQ { + return d.Exists(l.ID) == true + } + } + case *sql.Empty: + switch l := lo.(type) { + case *sql.Ident: + if op == sql.EQ { + return d.Exists(l.ID) == false || d.Get(l.ID).Data() == nil + } else if op == sql.NEQ { + return d.Exists(l.ID) == true && d.Get(l.ID).Data() != nil + } + } + case nil: + switch l := lo.(type) { + case *sql.Ident: + if op == sql.EQ { + return d.Exists(l.ID) == true && d.Get(l.ID).Data() == nil + } else if op == sql.NEQ { + return d.Exists(l.ID) == false || d.Get(l.ID).Data() != nil + } + } } } switch l := l.(type) { + case *sql.Empty: + switch r.(type) { + default: + return op == sql.NEQ || op == sql.SNI || op == sql.NIS || op == sql.CONTAINSNONE + case nil: + return op == sql.EQ + } + case nil: switch r := r.(type) { default: return op == sql.NEQ || op == sql.SNI || op == sql.NIS || op == sql.CONTAINSNONE case nil: return op == sql.EQ + case *sql.Empty: + return op == sql.EQ case []interface{}: return chkArrayR(op, l, r) case map[string]interface{}: diff --git a/db/select_test.go b/db/select_test.go index 4b42403a..c7b07f76 100644 --- a/db/select_test.go +++ b/db/select_test.go @@ -24,6 +24,96 @@ import ( func TestSelect(t *testing.T) { + Convey("Select temps", t, func() { + + setupDB() + + txt := ` + + USE NS test DB test; + + LET var = NULL; + + SELECT * FROM "test" WHERE NULL = ""; + SELECT * FROM "test" WHERE NULL = $var; + SELECT * FROM "test" WHERE NULL = VOID; + SELECT * FROM "test" WHERE NULL = NULL; + SELECT * FROM "test" WHERE NULL = EMPTY; + + SELECT * FROM "test" WHERE "" = NULL; + SELECT * FROM "test" WHERE $var = NULL; + SELECT * FROM "test" WHERE VOID = NULL; + SELECT * FROM "test" WHERE NULL = NULL; + SELECT * FROM "test" WHERE EMPTY = NULL; + + SELECT * FROM "test" WHERE VOID = ""; + SELECT * FROM "test" WHERE VOID = $var; + SELECT * FROM "test" WHERE VOID = NULL; + SELECT * FROM "test" WHERE VOID = VOID; + SELECT * FROM "test" WHERE VOID = EMPTY; + + SELECT * FROM "test" WHERE "" = VOID; + SELECT * FROM "test" WHERE $var = VOID; + SELECT * FROM "test" WHERE NULL = VOID; + SELECT * FROM "test" WHERE VOID = VOID; + SELECT * FROM "test" WHERE EMPTY = VOID; + + SELECT * FROM "test" WHERE EMPTY = ""; + SELECT * FROM "test" WHERE EMPTY = $var; + SELECT * FROM "test" WHERE EMPTY = NULL; + SELECT * FROM "test" WHERE EMPTY = VOID; + SELECT * FROM "test" WHERE EMPTY = EMPTY; + + SELECT * FROM "test" WHERE "" = EMPTY; + SELECT * FROM "test" WHERE $var = EMPTY; + SELECT * FROM "test" WHERE NULL = EMPTY; + SELECT * FROM "test" WHERE VOID = EMPTY; + SELECT * FROM "test" WHERE EMPTY = EMPTY; + + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 32) + + So(res[2].Result, ShouldHaveLength, 0) + So(res[3].Result, ShouldHaveLength, 1) + So(res[4].Result, ShouldHaveLength, 0) + So(res[5].Result, ShouldHaveLength, 1) + So(res[6].Result, ShouldHaveLength, 1) + + So(res[7].Result, ShouldHaveLength, 0) + So(res[8].Result, ShouldHaveLength, 1) + So(res[9].Result, ShouldHaveLength, 0) + So(res[10].Result, ShouldHaveLength, 1) + So(res[11].Result, ShouldHaveLength, 1) + + So(res[12].Result, ShouldHaveLength, 0) + So(res[13].Result, ShouldHaveLength, 0) + So(res[14].Result, ShouldHaveLength, 0) + So(res[15].Result, ShouldHaveLength, 1) + So(res[16].Result, ShouldHaveLength, 1) + + So(res[17].Result, ShouldHaveLength, 0) + So(res[18].Result, ShouldHaveLength, 0) + So(res[19].Result, ShouldHaveLength, 0) + So(res[20].Result, ShouldHaveLength, 1) + So(res[21].Result, ShouldHaveLength, 1) + + So(res[22].Result, ShouldHaveLength, 0) + So(res[23].Result, ShouldHaveLength, 1) + So(res[24].Result, ShouldHaveLength, 1) + So(res[25].Result, ShouldHaveLength, 1) + So(res[26].Result, ShouldHaveLength, 1) + + So(res[27].Result, ShouldHaveLength, 0) + So(res[28].Result, ShouldHaveLength, 1) + So(res[29].Result, ShouldHaveLength, 1) + So(res[30].Result, ShouldHaveLength, 1) + So(res[31].Result, ShouldHaveLength, 1) + + }) + Convey("Select records from one thing", t, func() { setupDB()