diff --git a/exp/bool.go b/exp/bool.go index a38f356f..f1a6c945 100644 --- a/exp/bool.go +++ b/exp/bool.go @@ -3,6 +3,8 @@ package exp import ( "reflect" "regexp" + + "github.com/doug-martin/goqu/v9/internal/util" ) type boolean struct { @@ -155,7 +157,7 @@ func checkLikeExp(op BooleanOperation, lhs Expression, val interface{}, invert b // checks a boolean operation normalizing the operation based on the RHS (e.g. "a" = true vs "a" IS TRUE func checkBoolExpType(op BooleanOperation, lhs Expression, rhs interface{}, invert bool) BooleanExpression { - if rhs == nil { + if rhs == nil || util.IsNil(rhs) { op = IsOp } else { switch reflect.Indirect(reflect.ValueOf(rhs)).Kind() { diff --git a/expressions_example_test.go b/expressions_example_test.go index 69ba5df2..7d0823d0 100644 --- a/expressions_example_test.go +++ b/expressions_example_test.go @@ -1008,6 +1008,7 @@ func ExampleOr_withAnd() { } func ExampleOr_withExMap() { + var val *int ds := goqu.From("test").Where( goqu.Or( // Ex will be anded together @@ -1018,6 +1019,7 @@ func ExampleOr_withExMap() { goqu.Ex{ "col3": nil, "col4": "foo", + "col5": val, }, ), ) @@ -1028,8 +1030,8 @@ func ExampleOr_withExMap() { fmt.Println(sql, args) // Output: - // SELECT * FROM "test" WHERE ((("col1" = 1) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = 'foo'))) [] - // SELECT * FROM "test" WHERE ((("col1" = ?) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = ?))) [1 foo] + // SELECT * FROM "test" WHERE ((("col1" = 1) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = 'foo') AND ("col5" IS NULL))) [] + // SELECT * FROM "test" WHERE ((("col1" = ?) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = ?) AND ("col5" IS NULL))) [1 foo] } func ExampleRange_numbers() { diff --git a/internal/util/reflect.go b/internal/util/reflect.go index bc18b8bd..60f50a01 100644 --- a/internal/util/reflect.go +++ b/internal/util/reflect.go @@ -83,6 +83,18 @@ func IsEmptyValue(v reflect.Value) bool { } } +func IsNil(i interface{}) bool { + if i == nil { + return true + } + switch reflect.TypeOf(i).Kind() { + case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + return reflect.ValueOf(i).IsNil() + default: + return false + } +} + var ( structMapCache = make(map[interface{}]ColumnMap) structMapCacheLock = sync.Mutex{} diff --git a/sqlgen/expression_sql_generator.go b/sqlgen/expression_sql_generator.go index 82ce15c5..0ca38ed3 100644 --- a/sqlgen/expression_sql_generator.go +++ b/sqlgen/expression_sql_generator.go @@ -405,13 +405,15 @@ func (esg *expressionSQLGenerator) booleanExpressionSQL(b sb.SQLBuilder, operato if (operatorOp == exp.IsOp || operatorOp == exp.IsNotOp) && esg.dialectOptions.UseLiteralIsBools { // these values must be interpolated because preparing them generates invalid SQL - switch rhs { - case true: - rhs = TrueLiteral - case false: - rhs = FalseLiteral - case nil: + if util.IsNil(rhs) { rhs = exp.NewLiteralExpression(string(esg.dialectOptions.Null)) + } else { + switch rhs { + case true: + rhs = TrueLiteral + case false: + rhs = FalseLiteral + } } } b.WriteRunes(esg.dialectOptions.SpaceRune)