Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Allow nil/NULL to be used as a CASE expression value

Previously, you could not use nil/NULL as a case expression:

  {1=>2}.case(0, nil)
  # CASE WHEN 1 THEN 2 ELSE 0 END

With this commit, if you provide a 2nd argument to case, the
expression is always used, even if it is nil, so you get:

  # CASE NULL WHEN 1 THEN 2 ELSE 0 END

This behavior makes more sense when you consider the following
scenario:

  {1=>2}.case(0, team_id)

If team_id is nil, you want to use NULL, because without the
NULL expression, the result would be 2 instead of 0 (or an
exception would be raised if the database doesn't allow integers
to be used for booleans).

Implementing this is fairly easy using a similar hack to the one
used for Symbol#*, which allows you to recognized whether or not
an optional was given, even if it has the same value as the
default.

This breaks backwards compatibility if you were previously
passing nil as the 2nd argument to case and did not want to
use an expression value.
  • Loading branch information...
commit 5098c26125aae15388178890ee40dd32b97dec62 1 parent 323811f
@jeremyevans authored
View
2  CHANGELOG
@@ -1,5 +1,7 @@
=== HEAD
+* Allow nil/NULL to be used as a CASE expression value (jeremyevans)
+
* Support bitwise operators on more databases (jeremyevans)
* Make PostgreSQL do bitwise xor instead of exponentiation for ^ operator (jeremyevans)
View
8 lib/sequel/core_sql.rb
@@ -29,8 +29,8 @@ def all_two_pairs?
#
# [[{:a=>[2,3]}, 1]].case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
# [[:a, 1], [:b, 2]].case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
- def case(default, expression = nil)
- ::Sequel::SQL::CaseExpression.new(self, default, expression)
+ def case(*args)
+ ::Sequel::SQL::CaseExpression.new(self, *args)
end
# Return a <tt>Sequel::SQL::ValueList</tt> created from this array. Used if this array contains
@@ -147,8 +147,8 @@ def ~
# {{:a=>[2,3]}=>1}.case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
# {:a=>1, :b=>2}.case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
# # or: CASE c WHEN b THEN 2 WHEN a THEN 1 ELSE d END
- def case(default, expression = nil)
- ::Sequel::SQL::CaseExpression.new(to_a, default, expression)
+ def case(*args)
+ ::Sequel::SQL::CaseExpression.new(to_a, *args)
end
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching all of the
View
6 lib/sequel/dataset/sql.rb
@@ -228,7 +228,7 @@ def boolean_constant_sql(constant)
# SQL fragment for specifying given CaseExpression.
def case_expression_sql(ce)
sql = '(CASE '
- sql << "#{literal(ce.expression)} " if ce.expression
+ sql << "#{literal(ce.expression)} " if ce.expression?
ce.conditions.collect{ |c,r|
sql << "WHEN #{literal(c)} THEN #{literal(r)} "
}
@@ -813,7 +813,9 @@ def qualified_expression(e, table)
when SQL::AliasedExpression
SQL::AliasedExpression.new(qualified_expression(e.expression, table), e.aliaz)
when SQL::CaseExpression
- SQL::CaseExpression.new(qualified_expression(e.conditions, table), qualified_expression(e.default, table), qualified_expression(e.expression, table))
+ args = [qualified_expression(e.conditions, table), qualified_expression(e.default, table)]
+ args << qualified_expression(e.expression, table) if e.expression?
+ SQL::CaseExpression.new(*args)
when SQL::Cast
SQL::Cast.new(qualified_expression(e.expr, table), e.type)
when SQL::Function
View
9 lib/sequel/sql.rb
@@ -618,9 +618,14 @@ class CaseExpression < GenericExpression
# default value. An expression can be provided to
# test each condition against, instead of having
# all conditions represent their own boolean expression.
- def initialize(conditions, default, expression = nil)
+ def initialize(conditions, default, expression=(no_expression=true; nil))
raise(Sequel::Error, 'CaseExpression conditions must be a hash or array of all two pairs') unless Sequel.condition_specifier?(conditions)
- @conditions, @default, @expression = conditions.to_a, default, expression
+ @conditions, @default, @expression, @no_expression = conditions.to_a, default, expression, no_expression
+ end
+
+ # Whether to use an expression for this CASE expression.
+ def expression?
+ !@no_expression
end
to_s_method :case_expression_sql
View
4 spec/core/core_sql_spec.rb
@@ -39,6 +39,10 @@
@d.literal([[:x, :y], [:a, :b]].case(:z, :exp__w)).should == '(CASE exp.w WHEN x THEN y WHEN a THEN b ELSE z END)'
end
+ specify "should return SQL CASE expression with expression even if nil" do
+ @d.literal({:x=>:y}.case(:z, nil)).should == '(CASE NULL WHEN x THEN y ELSE z END)'
+ end
+
specify "should raise an error if an array that isn't all two pairs is used" do
proc{[:b].case(:a)}.should raise_error(Sequel::Error)
proc{[:b, :c].case(:a)}.should raise_error(Sequel::Error)
Please sign in to comment.
Something went wrong with that request. Please try again.