From d7d4abdd15b98b98970849487a83d41beecd0711 Mon Sep 17 00:00:00 2001 From: AshSgDe29071999 Date: Sat, 29 Aug 2026 22:49:21 +0530 Subject: [PATCH] Ignore a nil Or in Where, matching nil predicates Where(nil) is a no-op, but a typed-nil Or (return nil from a helper typed as sq.Or) is a non-nil interface and became WHERE (1=0). Treat a nil Or like a nil predicate so optional filters do not invert the query. An empty Or{} is unchanged. See #382 --- delete.go | 3 +++ expr.go | 41 ++++++++++++++++++++++++++++------------- expr_test.go | 8 ++++++++ select.go | 5 +++-- select_test.go | 7 +++++++ statement.go | 3 +++ update.go | 3 +++ where.go | 10 ++++++++++ 8 files changed, 65 insertions(+), 15 deletions(-) diff --git a/delete.go b/delete.go index f3f31e63..32fbdde7 100644 --- a/delete.go +++ b/delete.go @@ -150,6 +150,9 @@ func (b DeleteBuilder) From(from string) DeleteBuilder { // // See SelectBuilder.Where for more information. func (b DeleteBuilder) Where(pred interface{}, args ...interface{}) DeleteBuilder { + if ignoredWherePred(pred) { + return b + } return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(DeleteBuilder) } diff --git a/expr.go b/expr.go index eba1b457..842ab3d6 100644 --- a/expr.go +++ b/expr.go @@ -23,7 +23,8 @@ type expr struct { // Expr builds an expression from a SQL fragment and arguments. // // Ex: -// Expr("FROM_UNIXTIME(?)", t) +// +// Expr("FROM_UNIXTIME(?)", t) func Expr(sql string, args ...interface{}) Sqlizer { return expr{sql: sql, args: args} } @@ -105,8 +106,9 @@ func (ce concatExpr) ToSql() (sql string, args []interface{}, err error) { // ConcatExpr builds an expression by concatenating strings and other expressions. // // Ex: -// name_expr := Expr("CONCAT(?, ' ', ?)", firstName, lastName) -// ConcatExpr("COALESCE(full_name,", name_expr, ")") +// +// name_expr := Expr("CONCAT(?, ' ', ?)", firstName, lastName) +// ConcatExpr("COALESCE(full_name,", name_expr, ")") func ConcatExpr(parts ...interface{}) concatExpr { return concatExpr(parts) } @@ -120,7 +122,8 @@ type aliasExpr struct { // Alias allows to define alias for column in SelectBuilder. Useful when column is // defined as complex expression like IF or CASE // Ex: -// .Column(Alias(caseStmt, "case_column")) +// +// .Column(Alias(caseStmt, "case_column")) func Alias(expr Sqlizer, alias string) aliasExpr { return aliasExpr{expr, alias} } @@ -212,7 +215,8 @@ func (eq Eq) ToSql() (sql string, args []interface{}, err error) { // NotEq is syntactic sugar for use with Where/Having/Set methods. // Ex: -// .Where(NotEq{"id": 1}) == "id <> 1" +// +// .Where(NotEq{"id": 1}) == "id <> 1" type NotEq Eq func (neq NotEq) ToSql() (sql string, args []interface{}, err error) { @@ -221,7 +225,8 @@ func (neq NotEq) ToSql() (sql string, args []interface{}, err error) { // Like is syntactic sugar for use with LIKE conditions. // Ex: -// .Where(Like{"name": "%irrel"}) +// +// .Where(Like{"name": "%irrel"}) type Like map[string]interface{} func (lk Like) toSql(opr string) (sql string, args []interface{}, err error) { @@ -260,7 +265,8 @@ func (lk Like) ToSql() (sql string, args []interface{}, err error) { // NotLike is syntactic sugar for use with LIKE conditions. // Ex: -// .Where(NotLike{"name": "%irrel"}) +// +// .Where(NotLike{"name": "%irrel"}) type NotLike Like func (nlk NotLike) ToSql() (sql string, args []interface{}, err error) { @@ -269,7 +275,8 @@ func (nlk NotLike) ToSql() (sql string, args []interface{}, err error) { // ILike is syntactic sugar for use with ILIKE conditions. // Ex: -// .Where(ILike{"name": "sq%"}) +// +// .Where(ILike{"name": "sq%"}) type ILike Like func (ilk ILike) ToSql() (sql string, args []interface{}, err error) { @@ -278,7 +285,8 @@ func (ilk ILike) ToSql() (sql string, args []interface{}, err error) { // NotILike is syntactic sugar for use with ILIKE conditions. // Ex: -// .Where(NotILike{"name": "sq%"}) +// +// .Where(NotILike{"name": "sq%"}) type NotILike Like func (nilk NotILike) ToSql() (sql string, args []interface{}, err error) { @@ -287,7 +295,8 @@ func (nilk NotILike) ToSql() (sql string, args []interface{}, err error) { // Lt is syntactic sugar for use with Where/Having/Set methods. // Ex: -// .Where(Lt{"id": 1}) +// +// .Where(Lt{"id": 1}) type Lt map[string]interface{} func (lt Lt) toSql(opposite, orEq bool) (sql string, args []interface{}, err error) { @@ -339,7 +348,8 @@ func (lt Lt) ToSql() (sql string, args []interface{}, err error) { // LtOrEq is syntactic sugar for use with Where/Having/Set methods. // Ex: -// .Where(LtOrEq{"id": 1}) == "id <= 1" +// +// .Where(LtOrEq{"id": 1}) == "id <= 1" type LtOrEq Lt func (ltOrEq LtOrEq) ToSql() (sql string, args []interface{}, err error) { @@ -348,7 +358,8 @@ func (ltOrEq LtOrEq) ToSql() (sql string, args []interface{}, err error) { // Gt is syntactic sugar for use with Where/Having/Set methods. // Ex: -// .Where(Gt{"id": 1}) == "id > 1" +// +// .Where(Gt{"id": 1}) == "id > 1" type Gt Lt func (gt Gt) ToSql() (sql string, args []interface{}, err error) { @@ -357,7 +368,8 @@ func (gt Gt) ToSql() (sql string, args []interface{}, err error) { // GtOrEq is syntactic sugar for use with Where/Having/Set methods. // Ex: -// .Where(GtOrEq{"id": 1}) == "id >= 1" +// +// .Where(GtOrEq{"id": 1}) == "id >= 1" type GtOrEq Lt func (gtOrEq GtOrEq) ToSql() (sql string, args []interface{}, err error) { @@ -398,6 +410,9 @@ func (a And) ToSql() (string, []interface{}, error) { type Or conj func (o Or) ToSql() (string, []interface{}, error) { + if o == nil { + return "", nil, nil + } return conj(o).join(" OR ", sqlFalse) } diff --git a/expr_test.go b/expr_test.go index 60d2ae5c..5da4010e 100644 --- a/expr_test.go +++ b/expr_test.go @@ -337,6 +337,14 @@ func TestEmptyOrToSql(t *testing.T) { assert.Equal(t, expectedArgs, args) } +func TestNilOrToSql(t *testing.T) { + var filter Or + sql, args, err := filter.ToSql() + assert.NoError(t, err) + assert.Equal(t, "", sql) + assert.Nil(t, args) +} + func TestLikeToSql(t *testing.T) { b := Like{"name": "%irrel"} sql, args, err := b.ToSql() diff --git a/select.go b/select.go index d55ce4c7..5c089395 100644 --- a/select.go +++ b/select.go @@ -272,7 +272,8 @@ func (b SelectBuilder) RemoveColumns() SelectBuilder { // Column adds a result column to the query. // Unlike Columns, Column accepts args which will be bound to placeholders in // the columns string, for example: -// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3) +// +// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3) func (b SelectBuilder) Column(column interface{}, args ...interface{}) SelectBuilder { return builder.Append(b, "Columns", newPart(column, args...)).(SelectBuilder) } @@ -340,7 +341,7 @@ func (b SelectBuilder) CrossJoin(join string, rest ...interface{}) SelectBuilder // // Where will panic if pred isn't any of the above types. func (b SelectBuilder) Where(pred interface{}, args ...interface{}) SelectBuilder { - if pred == nil || pred == "" { + if ignoredWherePred(pred) { return b } return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(SelectBuilder) diff --git a/select_test.go b/select_test.go index 80161bf5..21431220 100644 --- a/select_test.go +++ b/select_test.go @@ -241,6 +241,13 @@ func TestSelectWithNilWhereClause(t *testing.T) { assert.Equal(t, "SELECT * FROM users", sql) } +func TestSelectWithNilOrClause(t *testing.T) { + var filter Or + sql, _, err := Select("*").From("users").Where(filter).ToSql() + assert.NoError(t, err) + assert.Equal(t, "SELECT * FROM users", sql) +} + func TestSelectWithEmptyStringWhereClause(t *testing.T) { sql, _, err := Select("*").From("users").Where("").ToSql() assert.NoError(t, err) diff --git a/statement.go b/statement.go index 9420c67f..956474db 100644 --- a/statement.go +++ b/statement.go @@ -45,6 +45,9 @@ func (b StatementBuilderType) RunWith(runner BaseRunner) StatementBuilderType { // // See SelectBuilder.Where for more information. func (b StatementBuilderType) Where(pred interface{}, args ...interface{}) StatementBuilderType { + if ignoredWherePred(pred) { + return b + } return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(StatementBuilderType) } diff --git a/update.go b/update.go index eb2a9c4d..20a47b4e 100644 --- a/update.go +++ b/update.go @@ -259,6 +259,9 @@ func (b UpdateBuilder) FromSelect(from SelectBuilder, alias string) UpdateBuilde // // See SelectBuilder.Where for more information. func (b UpdateBuilder) Where(pred interface{}, args ...interface{}) UpdateBuilder { + if ignoredWherePred(pred) { + return b + } return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(UpdateBuilder) } diff --git a/where.go b/where.go index 976b63ac..05b88170 100644 --- a/where.go +++ b/where.go @@ -10,6 +10,16 @@ func newWherePart(pred interface{}, args ...interface{}) Sqlizer { return &wherePart{pred: pred, args: args} } +func ignoredWherePred(pred interface{}) bool { + if pred == nil || pred == "" { + return true + } + if or, ok := pred.(Or); ok && or == nil { + return true + } + return false +} + func (p wherePart) ToSql() (sql string, args []interface{}, err error) { switch pred := p.pred.(type) { case nil: