Class: SQL::Maker::Condition

Inherits:
Object
  • Object
show all
Includes:
Util
Defined in:
lib/sql/maker/condition.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util

#array_wrap, #bind_param, bind_param, #croak, included, #parse_args, #quote_identifier, quote_identifier

Constructor Details

#initialize(args = {}) ⇒ Condition

Returns a new instance of Condition.



9
10
11
12
13
14
15
16
17
# File 'lib/sql/maker/condition.rb', line 9

def initialize(args = {})
  @quote_char = args[:quote_char] || ''
  @name_sep = args[:name_sep] || '.'
  @strict = args[:strict] || false
  @auto_bind = args[:auto_bind] || false

  @sql = args[:sql] || []
  @bind = args[:bind] || []
end

Instance Attribute Details

#auto_bindObject

Returns the value of attribute auto_bind.



6
7
8
# File 'lib/sql/maker/condition.rb', line 6

def auto_bind
  @auto_bind
end

#bindObject

Returns the value of attribute bind.



7
8
9
# File 'lib/sql/maker/condition.rb', line 7

def bind
  @bind
end

#name_sepObject

Returns the value of attribute name_sep.



6
7
8
# File 'lib/sql/maker/condition.rb', line 6

def name_sep
  @name_sep
end

#quote_charObject

Returns the value of attribute quote_char.



6
7
8
# File 'lib/sql/maker/condition.rb', line 6

def quote_char
  @quote_char
end

#sqlObject

Returns the value of attribute sql.



7
8
9
# File 'lib/sql/maker/condition.rb', line 7

def sql
  @sql
end

#strictObject

Returns the value of attribute strict.



6
7
8
# File 'lib/sql/maker/condition.rb', line 6

def strict
  @strict
end

Instance Method Details

#&(other) ⇒ Object



27
28
29
# File 'lib/sql/maker/condition.rb', line 27

def &(other)
  self.compose_and(other)
end

#_make_in_term(col, op, v) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/sql/maker/condition.rb', line 96

def _make_in_term(col, op, v)
  if v.respond_to?(:as_sql)
    # make_term(foo => { 'IN' => sql_raw('SELECT foo FROM bar') }) => foo IN (SELECT foo FROM bar)
    term = "#{self._quote(col)} #{op} (#{v.as_sql})"
    [term, v.bind]
  elsif v.is_a?(Array)
    if v.size == 0
      if op == 'IN'
        # make_term(foo => {'IN' => []}) => 0=1
        return ['0=1', []]
      else
        # make_term(foo => {'NOT IN' => []}) => 1=1
        return ['1=1', []]
      end
    else
      # make_term(foo => { 'IN' => [1,2,3] }) => [foo IN (?,?,?), [1,2,3]]
      term = "#{self._quote(col)} #{op} (#{(['?'] * v.size).join(', ')})"
      return [term, v]
    end
  else
    croad("_make_in_term: arguments must be either of query instance or array")
  end
end

#_make_or_term(col, op, values) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/sql/maker/condition.rb', line 83

def _make_or_term(col, op, values)
  binds = []
  terms = []
  values.each do |v|
    term, bind = self._make_term(col => v)
    terms.push "(#{term})"
    binds.push bind
  end
  term = terms.join(" #{op} ")
  bind = binds.flatten
  return [term, bind]
end

#_make_term(*args) ⇒ Object

_make_term(:x => 1)



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/sql/maker/condition.rb', line 40

def _make_term(*args)
  col, val = parse_args(*args)
  col = col.to_s

  if val.is_a?(SQL::QueryMaker)
    return [val.as_sql(col, self.method(:_quote)), val.bind]
  elsif self.strict
    croak("Condition#add: can pass only SQL::QueryMaker object as an argument in strict mode")
  end

  if val.is_a?(Array)
    if val.first.is_a?(Hash)
      # {'foo'=>[{'>' => 'bar'},{'<' => 'baz'}]} => (`foo` > ?) OR (`foo` < ?)
      return self._make_or_term(col, 'OR', val)
    else
      # {'foo'=>['bar','baz']} => `foo` IN (?, ?)
      return self._make_in_term(col, 'IN', val)
    end
  elsif val.is_a?(Hash)
    op, v = val.each.first
    op = op.to_s.upcase
    if ( op == 'AND' || op == 'OR' ) && v.is_a?(Array)
      # {'foo'=>{'or' => [{'>' => 'bar'},{'<' => 'baz'}]}} => (`foo` > ?) OR (`foo` < ?)
      return self._make_or_term(col, op, v)
    elsif ( op == 'IN' || op == 'NOT IN' )
      # {'foo'=>{'in' => ['bar','baz']}} => `foo` IN (?, ?)
      return self._make_in_term(col, op, v)
    elsif ( op == 'BETWEEN' ) && v.is_a?(Array)
      croak("USAGE: add(foo => {BETWEEN => [a, b]})") if v.size != 2
      return [self._quote(col) + " BETWEEN ? AND ?", v]
    else
      # make_term(foo => { '<' => 3 }) => foo < 3
      return [self._quote(col) + " #{op} ?", [v]]
    end
  elsif val
    # make_term(foo => "3") => foo = 3
    return [self._quote(col) + " = ?", [val]]
  else
    # make_term(foo => nil) => foo IS NULL
    return [self._quote(col) + " IS NULL", []]
  end
end

#_quote(label) ⇒ Object



35
36
37
# File 'lib/sql/maker/condition.rb', line 35

def _quote(label)
  quote_identifier(label, self.quote_char, self.name_sep)
end

#add(*args) ⇒ Object



120
121
122
123
124
125
126
# File 'lib/sql/maker/condition.rb', line 120

def add(*args)
  term, bind = self._make_term(*args)
  self.sql.push "(#{term})" if term
  self.bind += array_wrap(bind) if bind

  return self # for influent interface
end

#add_raw(*args) ⇒ Object



128
129
130
131
132
133
# File 'lib/sql/maker/condition.rb', line 128

def add_raw(*args)
  term, bind = parse_args(*args)
  self.sql.push "(#{term})"
  self.bind += array_wrap(bind) if bind
  return self
end

#as_sqlObject



183
184
185
186
# File 'lib/sql/maker/condition.rb', line 183

def as_sql
  sql = self.sql.join(' AND ')
  @auto_bind ? bind_param(sql, self.bind) : sql
end

#compose_and(other) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/sql/maker/condition.rb', line 135

def compose_and(other)
  if self.sql.empty?
    if other.sql.empty?
      return new_condition
    end
    return new_condition(
      :sql => ['(' + other.as_sql() + ')'],
      :bind => other.bind,
    )
  end
  if other.sql.empty?
    return new_condition(
      :sql => ['(' + self.as_sql() + ')'],
      :bind => self.bind,
    )
  end

  return new_condition(
    :sql => ['(' + self.as_sql() + ') AND (' + other.as_sql() + ')'],
    :bind => self.bind + other.bind,
  )
end

#compose_or(other) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/sql/maker/condition.rb', line 158

def compose_or(other)
  if self.sql.empty?
    if other.sql.empty?
      return new_condition
    end
    return new_condition(
      :sql => ['(' + other.as_sql() + ')'],
      :bind => other.bind,
    )
  end
  if other.sql.empty?
    return new_condition(
      :sql => ['(' + self.as_sql() + ')'],
      :bind => self.bind,
    )
  end

  # return value is enclosed with '()'.
  # because 'OR' operator priority less than 'AND'.
  return new_condition(
    :sql => ['((' + self.as_sql() + ') OR (' + other.as_sql() + '))'],
    :bind => self.bind + other.bind,
  )
end

#new_condition(args = {}) ⇒ Object



19
20
21
22
23
24
25
# File 'lib/sql/maker/condition.rb', line 19

def new_condition(args = {})
  SQL::Maker::Condition.new({
    :quote_char => self.quote_char,
    :name_sep   => self.name_sep,
    :strict     => self.strict,
  }.merge(args))
end

#|(other) ⇒ Object



31
32
33
# File 'lib/sql/maker/condition.rb', line 31

def |(other)
  self.compose_or(other)
end