mirror of
https://github.com/lloeki/rebel.git
synced 2025-12-06 01:54:40 +01:00
Make Rebel::SQL properly configurable to database peculiarities
This commit is contained in:
parent
ddef06756f
commit
351e7e9645
2 changed files with 106 additions and 43 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
module Rebel::SQL
|
module Rebel::SQLQ
|
||||||
attr_reader :conn
|
attr_reader :conn
|
||||||
|
|
||||||
def exec(query)
|
def exec(query)
|
||||||
|
|
@ -54,7 +54,9 @@ module Rebel::SQL
|
||||||
def outer_join(table, on: nil)
|
def outer_join(table, on: nil)
|
||||||
Rebel::SQL.outer_join(table, on: on)
|
Rebel::SQL.outer_join(table, on: on)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Rebel
|
||||||
class Raw < String
|
class Raw < String
|
||||||
def wants_parens!
|
def wants_parens!
|
||||||
@wants_parens = true
|
@wants_parens = true
|
||||||
|
|
@ -67,7 +69,7 @@ module Rebel::SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
def parens
|
def parens
|
||||||
Raw.new("(#{self})")
|
sql.raw("(#{self})")
|
||||||
end
|
end
|
||||||
|
|
||||||
def parens?
|
def parens?
|
||||||
|
|
@ -75,7 +77,7 @@ module Rebel::SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
def as(n)
|
def as(n)
|
||||||
Raw.new(self + " AS #{Rebel::SQL.name(n)}")
|
sql.raw(self + " AS #{sql.name(n)}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def as?(n)
|
def as?(n)
|
||||||
|
|
@ -83,7 +85,7 @@ module Rebel::SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
def on(*clause)
|
def on(*clause)
|
||||||
Raw.new(self + " ON #{Rebel::SQL.and_clause(*clause)}")
|
sql.raw(self + " ON #{sql.and_clause(*clause)}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def on?(*clause)
|
def on?(*clause)
|
||||||
|
|
@ -91,33 +93,33 @@ module Rebel::SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
def having(*clause)
|
def having(*clause)
|
||||||
Raw.new(self + " HAVING #{Rebel::SQL.and_clause(*clause)}")
|
sql.raw(self + " HAVING #{sql.and_clause(*clause)}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def asc
|
def asc
|
||||||
Raw.new(self + " ASC")
|
sql.raw(self + " ASC")
|
||||||
end
|
end
|
||||||
|
|
||||||
def desc
|
def desc
|
||||||
Raw.new(self + " DESC")
|
sql.raw(self + " DESC")
|
||||||
end
|
end
|
||||||
|
|
||||||
def and(*clause)
|
def and(*clause)
|
||||||
Raw.new("#{self.parens?} AND #{Rebel::SQL.and_clause(*clause)}")
|
sql.raw("#{self.parens?} AND #{sql.and_clause(*clause)}")
|
||||||
end
|
end
|
||||||
alias & and
|
alias & and
|
||||||
|
|
||||||
def or(*clause)
|
def or(*clause)
|
||||||
Raw.new("#{self} OR #{Rebel::SQL.and_clause(*clause)}").wants_parens!
|
sql.raw("#{self} OR #{sql.and_clause(*clause)}").wants_parens!
|
||||||
end
|
end
|
||||||
alias | or
|
alias | or
|
||||||
|
|
||||||
def eq(n)
|
def eq(n)
|
||||||
case n
|
case n
|
||||||
when nil
|
when nil
|
||||||
Raw.new("#{self} IS NULL")
|
sql.raw("#{self} IS NULL")
|
||||||
else
|
else
|
||||||
Raw.new("#{self} = #{Rebel::SQL.name_or_value(n)}")
|
sql.raw("#{self} = #{sql.name_or_value(n)}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias == eq
|
alias == eq
|
||||||
|
|
@ -126,70 +128,60 @@ module Rebel::SQL
|
||||||
def ne(n)
|
def ne(n)
|
||||||
case n
|
case n
|
||||||
when nil
|
when nil
|
||||||
Raw.new("#{self} IS NOT NULL")
|
sql.raw("#{self} IS NOT NULL")
|
||||||
else
|
else
|
||||||
Raw.new("#{self} != #{Rebel::SQL.name_or_value(n)}")
|
sql.raw("#{self} != #{sql.name_or_value(n)}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias != ne
|
alias != ne
|
||||||
alias is_not ne
|
alias is_not ne
|
||||||
|
|
||||||
def lt(n)
|
def lt(n)
|
||||||
Raw.new("#{self} < #{Rebel::SQL.name_or_value(n)}")
|
sql.raw("#{self} < #{sql.name_or_value(n)}")
|
||||||
end
|
end
|
||||||
alias < lt
|
alias < lt
|
||||||
|
|
||||||
def gt(n)
|
def gt(n)
|
||||||
Raw.new("#{self} > #{Rebel::SQL.name_or_value(n)}")
|
sql.raw("#{self} > #{sql.name_or_value(n)}")
|
||||||
end
|
end
|
||||||
alias > gt
|
alias > gt
|
||||||
|
|
||||||
def le(n)
|
def le(n)
|
||||||
Raw.new("#{self} <= #{Rebel::SQL.name_or_value(n)}")
|
sql.raw("#{self} <= #{sql.name_or_value(n)}")
|
||||||
end
|
end
|
||||||
alias <= le
|
alias <= le
|
||||||
|
|
||||||
def ge(n)
|
def ge(n)
|
||||||
Raw.new("#{self} >= #{Rebel::SQL.name_or_value(n)}")
|
sql.raw("#{self} >= #{sql.name_or_value(n)}")
|
||||||
end
|
end
|
||||||
alias >= ge
|
alias >= ge
|
||||||
|
|
||||||
def in(*v)
|
def in(*v)
|
||||||
Raw.new("#{self} IN (#{Rebel::SQL.values(*v)})")
|
sql.raw("#{self} IN (#{sql.values(*v)})")
|
||||||
end
|
end
|
||||||
|
|
||||||
def not_in(*v)
|
def not_in(*v)
|
||||||
Raw.new("#{self} NOT IN (#{Rebel::SQL.values(*v)})")
|
sql.raw("#{self} NOT IN (#{sql.values(*v)})")
|
||||||
end
|
end
|
||||||
|
|
||||||
def like(n)
|
def like(n)
|
||||||
Raw.new("#{self} LIKE #{Rebel::SQL.value(n)}")
|
sql.raw("#{self} LIKE #{sql.value(n)}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def not_like(n)
|
def not_like(n)
|
||||||
Raw.new("#{self} NOT LIKE #{Rebel::SQL.value(n)}")
|
sql.raw("#{self} NOT LIKE #{sql.value(n)}")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def sql
|
||||||
|
@sql ||= Rebel::SQLQ
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@identifier_quote = '"'
|
module SQLB
|
||||||
@string_quote = "'"
|
|
||||||
@escaped_string_quote = "''"
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def identifier_quote=(str)
|
|
||||||
@identifier_quote = str
|
|
||||||
end
|
|
||||||
|
|
||||||
def string_quote=(str)
|
|
||||||
@string_quote = str
|
|
||||||
end
|
|
||||||
|
|
||||||
def escaped_string_quote=(str)
|
|
||||||
@escaped_string_quote = str
|
|
||||||
end
|
|
||||||
|
|
||||||
def raw(str)
|
def raw(str)
|
||||||
Raw.new(str)
|
Raw.new(str).tap { |r| r.instance_variable_set(:@sql, self) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_table(table_name, desc)
|
def create_table(table_name, desc)
|
||||||
|
|
@ -309,15 +301,15 @@ module Rebel::SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
def escape_str(str)
|
def escape_str(str)
|
||||||
str.tr(@string_quote, @escaped_string_quote)
|
str.gsub(@string_quote, @escaped_string_quote)
|
||||||
end
|
end
|
||||||
|
|
||||||
def value(v)
|
def value(v)
|
||||||
case v
|
case v
|
||||||
when Raw then v
|
when Raw then v
|
||||||
when String then raw "'#{escape_str(v)}'"
|
when String then raw "#{@string_quote}#{escape_str(v)}#{@string_quote}"
|
||||||
when Integer then raw v.to_s
|
when Integer then raw v.to_s
|
||||||
when TrueClass, FalseClass then raw(v ? 'TRUE' : 'FALSE')
|
when TrueClass, FalseClass then raw(v ? @true_literal : @false_literal)
|
||||||
when Date, Time, DateTime then value(v.iso8601)
|
when Date, Time, DateTime then value(v.iso8601)
|
||||||
when nil then raw 'NULL'
|
when nil then raw 'NULL'
|
||||||
else raise NotImplementedError, "#{v.class}: #{v.inspect}"
|
else raise NotImplementedError, "#{v.class}: #{v.inspect}"
|
||||||
|
|
@ -398,3 +390,24 @@ module Rebel::SQL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Rebel
|
||||||
|
def self.SQL(options = {}, &block)
|
||||||
|
sql = const_defined?(:SQL) && options.empty? ? SQL : Module.new do
|
||||||
|
@identifier_quote = options[:identifier_quote] || '"'
|
||||||
|
@string_quote = options[:string_quote] || "'"
|
||||||
|
@escaped_string_quote = options[:escaped_string_quote] || "''"
|
||||||
|
@true_literal = options[:true_literal] || 'TRUE'
|
||||||
|
@false_literal = options[:false_literal] || 'FALSE'
|
||||||
|
|
||||||
|
extend Rebel::SQLB
|
||||||
|
include Rebel::SQLQ
|
||||||
|
end
|
||||||
|
|
||||||
|
return sql.instance_eval(&block) unless block.nil?
|
||||||
|
|
||||||
|
sql
|
||||||
|
end
|
||||||
|
|
||||||
|
SQL = SQL()
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,19 @@ require 'rebel'
|
||||||
|
|
||||||
class TestRaw < Minitest::Test
|
class TestRaw < Minitest::Test
|
||||||
def assert_sql(expected, &actual)
|
def assert_sql(expected, &actual)
|
||||||
assert_equal(expected.to_s, Rebel::SQL.instance_eval(&actual).to_s)
|
assert_equal(expected.to_s, Rebel::SQL(&actual).to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_mysql(expected, &actual)
|
||||||
|
assert_equal(expected.to_s, Rebel::SQL(identifier_quote: '`', string_quote: '"', escaped_string_quote: '""', &actual).to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_sqlite(expected, &actual)
|
||||||
|
assert_equal(expected.to_s, Rebel::SQL(true_literal: '1', false_literal: '0', &actual).to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_postgresql(expected, &actual)
|
||||||
|
assert_equal(expected.to_s, Rebel::SQL(&actual).to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_and
|
def test_and
|
||||||
|
|
@ -114,6 +126,44 @@ class TestRaw < Minitest::Test
|
||||||
assert_sql('WHERE COALESCE("foo", 0) = 42') { where?(function('COALESCE', :foo, 0).eq 42) }
|
assert_sql('WHERE COALESCE("foo", 0) = 42') { where?(function('COALESCE', :foo, 0).eq 42) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_name
|
||||||
|
assert_sql('"foo"') { name(:foo) }
|
||||||
|
assert_mysql('`foo`') { name(:foo) }
|
||||||
|
assert_postgresql('"foo"') { name(:foo) }
|
||||||
|
assert_sqlite('"foo"') { name(:foo) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string
|
||||||
|
assert_sql("'FOO'") { value('FOO') }
|
||||||
|
assert_mysql('"FOO"') { value('FOO') }
|
||||||
|
assert_postgresql("'FOO'") { value('FOO') }
|
||||||
|
assert_sqlite("'FOO'") { value('FOO') }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_escaped_string
|
||||||
|
assert_sql("'FOO''BAR'") { value("FOO'BAR") }
|
||||||
|
assert_mysql('"FOO\'BAR"') { value("FOO'BAR") }
|
||||||
|
assert_postgresql("'FOO''BAR'") { value("FOO'BAR") }
|
||||||
|
assert_sqlite("'FOO''BAR'") { value("FOO'BAR") }
|
||||||
|
|
||||||
|
assert_sql("'FOO\"BAR'") { value('FOO"BAR') }
|
||||||
|
assert_mysql('"FOO""BAR"') { value('FOO"BAR') }
|
||||||
|
assert_postgresql("'FOO\"BAR'") { value('FOO"BAR') }
|
||||||
|
assert_sqlite("'FOO\"BAR'") { value('FOO"BAR') }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_boolean_literal
|
||||||
|
assert_sql("TRUE") { value(true) }
|
||||||
|
assert_mysql("TRUE") { value(true) }
|
||||||
|
assert_postgresql("TRUE") { value(true) }
|
||||||
|
assert_sqlite("1") { value(true) }
|
||||||
|
|
||||||
|
assert_sql("FALSE") { value(false) }
|
||||||
|
assert_mysql("FALSE") { value(false) }
|
||||||
|
assert_postgresql("FALSE") { value(false) }
|
||||||
|
assert_sqlite("0") { value(false) }
|
||||||
|
end
|
||||||
|
|
||||||
def test_value
|
def test_value
|
||||||
assert_sql("'FOO'") { value(raw("'FOO'")) }
|
assert_sql("'FOO'") { value(raw("'FOO'")) }
|
||||||
assert_sql("'FOO'") { value('FOO') }
|
assert_sql("'FOO'") { value('FOO') }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue