Normalize best-effort vs strict operator usage

This commit is contained in:
Loic Nageleisen 2017-11-22 17:22:27 +01:00
parent 354f4a6860
commit 6ffa4e88f0
3 changed files with 76 additions and 22 deletions

View file

@ -213,6 +213,60 @@ Rebel::SQL() { select count(:id).as(:count), from: :customers, where: { :age =>
# While we're counting things, let's group results # While we're counting things, let's group results
Rebel::SQL() { select count(:id).as(:count), :country, from: :customers, group: by(:country).having(count(:customer_id) => 5) } Rebel::SQL() { select count(:id).as(:count), :country, from: :customers, group: by(:country).having(count(:customer_id) => 5) }
#=> SELECT COUNT("id") AS "count", "country" FROM "customers" GROUP BY "country" HAVING COUNT("customer_id") = 5 #=> SELECT COUNT("id") AS "count", "country" FROM "customers" GROUP BY "country" HAVING COUNT("customer_id") = 5
# Passing a hash does a best effort to map Ruby to SQL
Rebel::SQL() { select :id, from: :customers, where: { :age => 42 } }
#=> SELECT "id" FROM "customers" WHERE "age" = 42
Rebel::SQL() { select :id, from: :customers, where: { :age => [20, 21, 22] } }
#=> SELECT "id" FROM "customers" WHERE "age" IN (20, 21, 22)
Rebel::SQL() { select :id, from: :customers, where: { :age => nil } }
#=> SELECT "id" FROM "customers" WHERE "age" IS NULL
# Using operators ensures the expected SQL operator is used
Rebel::SQL() { select :id, from: :customers, where: name(:age).eq(42) }
#=> SELECT "id" FROM "customers" WHERE "age" = 42
Rebel::SQL() { select :id, from: :customers, where: name(:age).eq(nil) }
#=> SELECT "id" FROM "customers" WHERE "age" = NULL
Rebel::SQL() { select :id, from: :customers, where: name(:age).ne(42) }
#=> SELECT "id" FROM "customers" WHERE "age" != NULL
Rebel::SQL() { select :id, from: :customers, where: name(:age).is(42) }
#=> SELECT "id" FROM "customers" WHERE "age" IS 42
Rebel::SQL() { select :id, from: :customers, where: name(:age).is(nil) }
#=> SELECT "id" FROM "customers" WHERE "age" IS NULL
Rebel::SQL() { select :id, from: :customers, where: name(:age).is_not(nil) }
#=> SELECT "id" FROM "customers" WHERE "age" IS NOT NULL
# Other operators are available
Rebel::SQL() { select :id, from: :customers, where: name(:age).ge(42) }
#=> SELECT "id" FROM "customers" WHERE "age" >= 42
Rebel::SQL() { select :id, from: :customers, where: name(:age).in(21, 22, 23) }
#=> SELECT "id" FROM "customers" WHERE "age" IN (21, 22, 23)
Rebel::SQL() { select :id, from: :customers, where: name(:first_name).like("J%") }
#=> SELECT "id" FROM "customers" WHERE "first_name" LIKE "J%"
# Aliases to overload operators are available
Rebel::SQL() { select :id, from: :customers, where: name(:age) == 42 }
#=> SELECT "id" FROM "customers" WHERE "age" = 42
Rebel::SQL() { select :id, from: :customers, where: name(:age) < 42 }
#=> SELECT "id" FROM "customers" WHERE "age" < 42
Rebel::SQL() { select :id, from: :customers, where: name(:age) >= 42 }
#=> SELECT "id" FROM "customers" WHERE "age" >= 42
Rebel::SQL() { select :id, from: :customers, where: name(:age) != 42 }
#=> SELECT "id" FROM "customers" WHERE "age" != 42
# Conditions can be combined
Rebel::SQL() { select :id, from: :customers, where: name(:age).gt(42).or(name(:age).lt(21)) }
#=> SELECT "id" FROM "customers" WHERE ("age" > 42 OR "age" < 21)
Rebel::SQL() { select :id, from: :customers, where: (name(:age) < 42).and(name(:age) > 21) }
#=> SELECT "id" FROM "customers" WHERE ("age" < 42 AND "age" > 21)
# Binary-wise operators can be used to tie conditions
# WARNING: Usefulness if this hack is still debated. It might be removed in the future.
Rebel::SQL() { select :id, from: :customers, where: ((name(:age) > 42) | (name(:age) < 21)) }
#=> SELECT "id" FROM "customers" WHERE ("age" > 42 OR "age" < 21)
Rebel::SQL() { select :id, from: :customers, where: name(:age).lt(42) & (name(:age).gt(21)) }
#=> SELECT "id" FROM "customers" WHERE ("age" < 42 AND "age" > 21)
``` ```
### Query execution ### Query execution

View file

@ -117,26 +117,22 @@ module Rebel
alias | or alias | or
def eq(n) def eq(n)
case n sql.raw("#{self} = #{sql.name_or_value(n)}")
when nil
sql.raw("#{self} IS NULL")
else
sql.raw("#{self} = #{sql.name_or_value(n)}")
end
end end
alias == eq alias == eq
alias is eq
def is(n)
sql.raw("#{self} IS #{sql.name_or_value(n)}")
end
def ne(n) def ne(n)
case n sql.raw("#{self} != #{sql.name_or_value(n)}")
when nil
sql.raw("#{self} IS NOT NULL")
else
sql.raw("#{self} != #{sql.name_or_value(n)}")
end
end end
alias != ne alias != ne
alias is_not ne
def is_not(n)
sql.raw("#{self} IS NOT #{sql.name_or_value(n)}")
end
def lt(n) def lt(n)
sql.raw("#{self} < #{sql.name_or_value(n)}") sql.raw("#{self} < #{sql.name_or_value(n)}")
@ -343,6 +339,8 @@ module Rebel
case right case right
when Array when Array
name(left).in(*right) name(left).in(*right)
when nil
name(left).is(name_or_value(right))
else else
name(left).eq(name_or_value(right)) name(left).eq(name_or_value(right))
end end

View file

@ -48,19 +48,19 @@ class TestRaw < Minitest::Test
def test_is def test_is
assert_sql('"foo" IS NULL') { name(:foo).is(nil) } assert_sql('"foo" IS NULL') { name(:foo).is(nil) }
assert_sql('"foo" = 42') { name(:foo).is(42) } assert_sql('"foo" IS 42') { name(:foo).is(42) }
assert_sql('"foo" = "bar"') { name(:foo).is(name(:bar)) } assert_sql('"foo" IS "bar"') { name(:foo).is(name(:bar)) }
end end
def test_is_not def test_is_not
assert_sql('"foo" IS NOT NULL') { name(:foo).is_not(nil) } assert_sql('"foo" IS NOT NULL') { name(:foo).is_not(nil) }
assert_sql('"foo" != 42') { name(:foo).is_not(42) } assert_sql('"foo" IS NOT 42') { name(:foo).is_not(42) }
assert_sql('"foo" != "bar"') { name(:foo).is_not(name(:bar)) } assert_sql('"foo" IS NOT "bar"') { name(:foo).is_not(name(:bar)) }
end end
def test_eq def test_eq
assert_sql('"foo" IS NULL') { name(:foo).eq(nil) } assert_sql('"foo" = NULL') { name(:foo).eq(nil) }
assert_sql('"foo" IS NULL') { name(:foo) == nil } assert_sql('"foo" = NULL') { name(:foo) == nil }
assert_sql('"foo" = "bar"') { name(:foo).eq(name(:bar)) } assert_sql('"foo" = "bar"') { name(:foo).eq(name(:bar)) }
assert_sql('"foo" = "bar"') { name(:foo) == name(:bar) } assert_sql('"foo" = "bar"') { name(:foo) == name(:bar) }
end end
@ -68,8 +68,8 @@ class TestRaw < Minitest::Test
def test_ne def test_ne
assert_sql('"foo" != "bar"') { name(:foo).ne(name(:bar)) } assert_sql('"foo" != "bar"') { name(:foo).ne(name(:bar)) }
assert_sql('"foo" != "bar"') { name(:foo) != name(:bar) } assert_sql('"foo" != "bar"') { name(:foo) != name(:bar) }
assert_sql('"foo" IS NOT NULL') { name(:foo).ne(nil) } assert_sql('"foo" != NULL') { name(:foo).ne(nil) }
assert_sql('"foo" IS NOT NULL') { name(:foo) != nil } assert_sql('"foo" != NULL') { name(:foo) != nil }
end end
def test_lt def test_lt
@ -112,6 +112,8 @@ class TestRaw < Minitest::Test
assert_sql('WHERE "foo" = 1 AND "bar" = 2 AND "baz" = 3') { where?(foo: 1, bar: 2, baz: 3) } assert_sql('WHERE "foo" = 1 AND "bar" = 2 AND "baz" = 3') { where?(foo: 1, bar: 2, baz: 3) }
assert_sql('WHERE ("foo" = 1 OR "bar" = 2) AND "baz" = 3') { where?(name(:foo).eq(1).or(name(:bar).eq(2)), name(:baz).eq(3)) } assert_sql('WHERE ("foo" = 1 OR "bar" = 2) AND "baz" = 3') { where?(name(:foo).eq(1).or(name(:bar).eq(2)), name(:baz).eq(3)) }
assert_sql('WHERE ("foo" = 1 OR "bar" = 2)') { where?(name(:foo).eq(1).or(name(:bar).eq(2))) } assert_sql('WHERE ("foo" = 1 OR "bar" = 2)') { where?(name(:foo).eq(1).or(name(:bar).eq(2))) }
assert_sql('WHERE "foo" IS NULL') { where?(foo: nil) }
assert_sql('WHERE "foo" IN (1, 2, 3)') { where?(foo: [1, 2, 3]) }
end end
def test_join def test_join