sql-maker 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +23 -0
- data/Rakefile +14 -0
- data/lib/sql-maker.rb +5 -0
- data/lib/sql/maker.rb +676 -0
- data/lib/sql/maker/condition.rb +378 -0
- data/lib/sql/maker/error.rb +3 -0
- data/lib/sql/maker/helper.rb +22 -0
- data/lib/sql/maker/quoting.rb +138 -0
- data/lib/sql/maker/select.rb +544 -0
- data/lib/sql/maker/select/oracle.rb +30 -0
- data/lib/sql/maker/select_set.rb +194 -0
- data/lib/sql/maker/util.rb +54 -0
- data/lib/sql/query_maker.rb +429 -0
- data/scripts/perl2ruby.rb +34 -0
- data/spec/maker/bind_param_spec.rb +62 -0
- data/spec/maker/condition/add_raw_spec.rb +29 -0
- data/spec/maker/condition/compose_empty_spec.rb +72 -0
- data/spec/maker/condition/empty_values_spec.rb +25 -0
- data/spec/maker/condition/make_term_spec.rb +38 -0
- data/spec/maker/condition/where_spec.rb +35 -0
- data/spec/maker/delete_spec.rb +116 -0
- data/spec/maker/insert_empty_spec.rb +23 -0
- data/spec/maker/insert_spec.rb +61 -0
- data/spec/maker/new_line_spec.rb +9 -0
- data/spec/maker/select/oracle/oracle_spec.rb +16 -0
- data/spec/maker/select/pod_select_spec.rb +34 -0
- data/spec/maker/select/statement_spec.rb +805 -0
- data/spec/maker/select_set_spec.rb +294 -0
- data/spec/maker/select_spec.rb +470 -0
- data/spec/maker/simple_spec.rb +54 -0
- data/spec/maker/strict_spec.rb +45 -0
- data/spec/maker/subquery_spec.rb +77 -0
- data/spec/maker/update_spec.rb +138 -0
- data/spec/maker/util_spec.rb +6 -0
- data/spec/maker/where_spec.rb +28 -0
- data/spec/query_maker/and_using_hash_spec.rb +13 -0
- data/spec/query_maker/cheatsheet_spec.rb +32 -0
- data/spec/query_maker/column_bind_spec.rb +55 -0
- data/spec/query_maker/refs_in_bind_spec.rb +19 -0
- data/spec/spec_helper.rb +15 -0
- data/sql-maker.gemspec +25 -0
- metadata +185 -0
@@ -0,0 +1,544 @@
|
|
1
|
+
require 'sql/maker/condition'
|
2
|
+
require 'sql/maker/util'
|
3
|
+
|
4
|
+
class SQL::Maker::Select
|
5
|
+
include SQL::Maker::Util
|
6
|
+
|
7
|
+
attr_reader :quote_char, :name_sep, :new_line, :strict, :auto_bind
|
8
|
+
attr_accessor :select, :select_map, :select_map_reverse, :from, :joins,
|
9
|
+
:index_hint, :group_by, :order_by, :where, :having, :for_update, :subqueries
|
10
|
+
|
11
|
+
def initialize(args = {})
|
12
|
+
@select = args[:select] || []
|
13
|
+
@distinct = args[:distinct] || false
|
14
|
+
@select_map = args[:select_map] || {}
|
15
|
+
@select_map_reverse = args[:select_map_reverse] || {}
|
16
|
+
@from = args[:from] || []
|
17
|
+
@joins = args[:joins] || []
|
18
|
+
@index_hint = args[:index_hint] || {}
|
19
|
+
@group_by = args[:group_by] || []
|
20
|
+
@order_by = args[:order_by] || []
|
21
|
+
@prefix = args[:prefix] || 'SELECT '
|
22
|
+
@new_line = args[:new_line] || "\n"
|
23
|
+
@strict = args[:strict] || false
|
24
|
+
@auto_bind = args[:auto_bind] || false
|
25
|
+
@where = args[:where]
|
26
|
+
@having = args[:having]
|
27
|
+
@limit = args[:limit]
|
28
|
+
@offset = args[:offset]
|
29
|
+
@for_update = args[:for_update]
|
30
|
+
@quote_char = args[:quote_char]
|
31
|
+
@name_sep = args[:name_sep]
|
32
|
+
@subqueries = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def distinct(distinct = nil)
|
36
|
+
if distinct
|
37
|
+
@distinct = distinct
|
38
|
+
self # method chain
|
39
|
+
else
|
40
|
+
@distinct
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def prefix(prefix = nil)
|
45
|
+
if prefix
|
46
|
+
@prefix = prefix
|
47
|
+
self # method chain
|
48
|
+
else
|
49
|
+
@prefix
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def offset(offset = nil)
|
54
|
+
if offset
|
55
|
+
@offset = offset
|
56
|
+
self # method chain
|
57
|
+
else
|
58
|
+
@offset
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def limit(limit = nil)
|
63
|
+
if limit
|
64
|
+
@limit = limit
|
65
|
+
self # method chain
|
66
|
+
else
|
67
|
+
@limit
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def new_condition
|
72
|
+
SQL::Maker::Condition.new(
|
73
|
+
:quote_char => self.quote_char,
|
74
|
+
:name_sep => self.name_sep,
|
75
|
+
:strict => self.strict,
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def bind
|
80
|
+
bind = []
|
81
|
+
bind += self.subqueries if self.subqueries
|
82
|
+
bind += self.where.bind if self.where
|
83
|
+
bind += self.having.bind if self.having
|
84
|
+
bind
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_select(*args)
|
88
|
+
term, col = parse_args(*args)
|
89
|
+
term = term.to_s if term.is_a?(Symbol)
|
90
|
+
col ||= term
|
91
|
+
self.select += array_wrap(term)
|
92
|
+
self.select_map[term] = col
|
93
|
+
self.select_map_reverse[col] = term
|
94
|
+
self # method chain
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_from(*args)
|
98
|
+
table, as = parse_args(*args)
|
99
|
+
if table.respond_to?(:as_sql)
|
100
|
+
self.subqueries += table.bind
|
101
|
+
self.from += [[table, as]]
|
102
|
+
else
|
103
|
+
table = table.to_s
|
104
|
+
self.from += [[table, as]]
|
105
|
+
end
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_join(*args)
|
110
|
+
# :user => { :type => 'inner', :table => 'config', :condition => {'user.user_id' => 'config.user_id'} }
|
111
|
+
# [ subquery, 'bar' ] => { :type => 'inner', :table => 'config', :condition => {'user.user_id' => 'config.user_id'} }
|
112
|
+
table, joins = parse_args(*args)
|
113
|
+
table, as = parse_args(*table)
|
114
|
+
|
115
|
+
if table.respond_to?(:as_sql)
|
116
|
+
self.subqueries += table.bind
|
117
|
+
table = '(' + table.as_sql + ')'
|
118
|
+
else
|
119
|
+
table = table.to_s
|
120
|
+
end
|
121
|
+
|
122
|
+
self.joins += [{
|
123
|
+
:table => [ table, as ],
|
124
|
+
:joins => joins
|
125
|
+
}]
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_index_hint(*args)
|
130
|
+
table, hint = parse_args(*args)
|
131
|
+
table = table.to_s
|
132
|
+
if hint.is_a?(Hash)
|
133
|
+
# { :type => '...', :list => ['foo'] }
|
134
|
+
type = hint[:type] || 'USE'
|
135
|
+
list = array_wrap(hint[:list])
|
136
|
+
else
|
137
|
+
# ['foo, 'bar'] or just 'foo'
|
138
|
+
type = 'USE'
|
139
|
+
list = array_wrap(hint)
|
140
|
+
end
|
141
|
+
|
142
|
+
self.index_hint[table] = {
|
143
|
+
:type => type,
|
144
|
+
:list => list,
|
145
|
+
}
|
146
|
+
|
147
|
+
return self
|
148
|
+
end
|
149
|
+
|
150
|
+
def _quote(label)
|
151
|
+
SQL::Maker::Util.quote_identifier(label, self.quote_char, self.name_sep)
|
152
|
+
end
|
153
|
+
|
154
|
+
def as_sql
|
155
|
+
sql = ''
|
156
|
+
new_line = self.new_line
|
157
|
+
|
158
|
+
unless self.select.empty?
|
159
|
+
sql += self.prefix
|
160
|
+
sql += 'DISTINCT ' if self.distinct
|
161
|
+
sql += self.select.map {|col|
|
162
|
+
as = self.select_map[col]
|
163
|
+
col = col.respond_to?(:as_sql) ? col.as_sql : self._quote(col)
|
164
|
+
next col if as.nil?
|
165
|
+
as = as.respond_to?(:as_sql) ? as.as_sql : self._quote(as)
|
166
|
+
if as && col =~ /(?:^|\.)#{Regexp.escape(as)}$/
|
167
|
+
col
|
168
|
+
else
|
169
|
+
col + ' AS ' + as
|
170
|
+
end
|
171
|
+
}.join(', ') + new_line
|
172
|
+
end
|
173
|
+
|
174
|
+
sql += 'FROM '
|
175
|
+
|
176
|
+
## Add any explicit JOIN statements before the non-joined tables.
|
177
|
+
unless self.joins.empty?
|
178
|
+
initial_table_written = 0
|
179
|
+
self.joins.each do |j|
|
180
|
+
table = j[:table]
|
181
|
+
join = j[:joins]
|
182
|
+
table = self._add_index_hint(table); ## index hint handling
|
183
|
+
sql += table if initial_table_written == 0
|
184
|
+
initial_table_written += 1
|
185
|
+
sql += ' ' + join[:type].upcase if join[:type]
|
186
|
+
sql += ' JOIN ' + self._quote(join[:table])
|
187
|
+
sql += ' ' + self._quote(join[:alias]) if join[:alias]
|
188
|
+
|
189
|
+
if condition = join[:condition]
|
190
|
+
if condition.is_a?(Array)
|
191
|
+
sql += ' USING (' + condition.map {|e| self._quote(e) }.join(', ') + ')'
|
192
|
+
elsif condition.is_a?(Hash)
|
193
|
+
conds = []
|
194
|
+
condition.keys.each do |key|
|
195
|
+
conds += [self._quote(key) + ' = ' + self._quote(condition[key])]
|
196
|
+
end
|
197
|
+
sql += ' ON ' + conds.join(' AND ')
|
198
|
+
else
|
199
|
+
sql += ' ON ' + condition
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
sql += ', ' unless self.from.empty?
|
204
|
+
end
|
205
|
+
|
206
|
+
unless self.from.empty?
|
207
|
+
sql += self.from.map {|e| self._add_index_hint(e[0], e[1]) }.join(', ')
|
208
|
+
end
|
209
|
+
|
210
|
+
sql += new_line
|
211
|
+
sql += self.as_sql_where if self.where
|
212
|
+
|
213
|
+
sql += self.as_sql_group_by if self.group_by
|
214
|
+
sql += self.as_sql_having if self.having
|
215
|
+
sql += self.as_sql_order_by if self.order_by
|
216
|
+
|
217
|
+
sql += self.as_sql_limit if self.limit
|
218
|
+
|
219
|
+
sql += self.as_sql_for_update
|
220
|
+
sql.gsub!(/#{new_line}+$/, '')
|
221
|
+
|
222
|
+
@auto_bind ? bind_param(sql, self.bind) : sql
|
223
|
+
end
|
224
|
+
|
225
|
+
def as_sql_limit
|
226
|
+
return '' unless n = self.limit
|
227
|
+
croak("Non-numerics in limit clause (n)") if n =~ /\D/
|
228
|
+
return sprintf "LIMIT %d%s" + self.new_line, n,
|
229
|
+
(self.offset ? " OFFSET " + self.offset.to_i.to_s : "")
|
230
|
+
end
|
231
|
+
|
232
|
+
def add_order_by(*args)
|
233
|
+
col, type = parse_args(*args)
|
234
|
+
self.order_by += [[col, type]]
|
235
|
+
return self
|
236
|
+
end
|
237
|
+
|
238
|
+
def as_sql_order_by
|
239
|
+
attrs = self.order_by
|
240
|
+
return '' if attrs.empty?
|
241
|
+
|
242
|
+
return 'ORDER BY ' + attrs.map {|e|
|
243
|
+
col, type = e
|
244
|
+
if col.respond_to?(:as_sql)
|
245
|
+
col.as_sql
|
246
|
+
else
|
247
|
+
type ? self._quote(col) + " #{type}" : self._quote(col)
|
248
|
+
end
|
249
|
+
}.join(', ') + self.new_line
|
250
|
+
end
|
251
|
+
|
252
|
+
def add_group_by(*args)
|
253
|
+
group, order = parse_args(*args)
|
254
|
+
self.group_by +=
|
255
|
+
if group.respond_to?(:as_sql)
|
256
|
+
[group.as_sql]
|
257
|
+
else
|
258
|
+
order ? [self._quote(group) + " #{order}"] : [self._quote(group)]
|
259
|
+
end
|
260
|
+
return self
|
261
|
+
end
|
262
|
+
|
263
|
+
def as_sql_group_by
|
264
|
+
elems = self.group_by
|
265
|
+
return '' if elems.empty?
|
266
|
+
|
267
|
+
return 'GROUP BY ' + elems.join(', ') + self.new_line
|
268
|
+
end
|
269
|
+
|
270
|
+
def set_where(where)
|
271
|
+
self.where = where
|
272
|
+
return self
|
273
|
+
end
|
274
|
+
|
275
|
+
def add_where(*args)
|
276
|
+
self.where ||= self.new_condition()
|
277
|
+
self.where.add(*args)
|
278
|
+
return self
|
279
|
+
end
|
280
|
+
|
281
|
+
def add_where_raw(*args)
|
282
|
+
self.where ||= self.new_condition()
|
283
|
+
self.where.add_raw(*args)
|
284
|
+
return self
|
285
|
+
end
|
286
|
+
|
287
|
+
def as_sql_where
|
288
|
+
where = self.where.as_sql()
|
289
|
+
where and !where.empty? ? "WHERE #{where}" + self.new_line : ''
|
290
|
+
end
|
291
|
+
|
292
|
+
def as_sql_having
|
293
|
+
if self.having
|
294
|
+
'HAVING ' + self.having.as_sql + self.new_line
|
295
|
+
else
|
296
|
+
''
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def add_having(*args)
|
301
|
+
col, val = parse_args(*args)
|
302
|
+
col = col.to_s
|
303
|
+
if orig = self.select_map_reverse[col]
|
304
|
+
col = orig
|
305
|
+
end
|
306
|
+
|
307
|
+
self.having ||= self.new_condition()
|
308
|
+
self.having.add(col, val)
|
309
|
+
return self
|
310
|
+
end
|
311
|
+
|
312
|
+
def as_sql_for_update
|
313
|
+
self.for_update ? ' FOR UPDATE' : ''
|
314
|
+
end
|
315
|
+
|
316
|
+
def _add_index_hint(*args)
|
317
|
+
table, as = parse_args(*args)
|
318
|
+
tbl_name =
|
319
|
+
if table.respond_to?(:as_sql)
|
320
|
+
'(' + table.as_sql + ')'
|
321
|
+
else
|
322
|
+
self._quote(table)
|
323
|
+
end
|
324
|
+
quoted = as ? tbl_name + ' ' + self._quote(as) : tbl_name
|
325
|
+
hint = self.index_hint[table]
|
326
|
+
return quoted unless hint && hint.is_a?(Hash)
|
327
|
+
if hint[:list]&& !hint[:list].empty?
|
328
|
+
return quoted + ' ' + (hint[:type].upcase || 'USE') + ' INDEX (' +
|
329
|
+
hint[:list].map {|e| self._quote(e) }.join(',') + ')'
|
330
|
+
end
|
331
|
+
return quoted
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
__END__
|
336
|
+
|
337
|
+
=head1 NAME
|
338
|
+
|
339
|
+
SQL::Maker::Select - dynamic SQL generator
|
340
|
+
|
341
|
+
=head1 SYNOPSIS
|
342
|
+
|
343
|
+
sql = SQL::Maker::Select.new
|
344
|
+
.add_select('foo')
|
345
|
+
.add_select('bar')
|
346
|
+
.add_select('baz')
|
347
|
+
.add_from('table_name' => 't')
|
348
|
+
.as_sql
|
349
|
+
# => "SELECT foo, bar, baz FROM table_name t"
|
350
|
+
|
351
|
+
=head1 DESCRIPTION
|
352
|
+
|
353
|
+
=head1 METHODS
|
354
|
+
|
355
|
+
=over 4
|
356
|
+
|
357
|
+
=item C<< sql = stmt.as_sql(); >>
|
358
|
+
|
359
|
+
Render the SQL string.
|
360
|
+
|
361
|
+
=item C<< @bind = stmt.bind(); >>
|
362
|
+
|
363
|
+
Get the bind variables.
|
364
|
+
|
365
|
+
=item C<< stmt.add_select('*') >>
|
366
|
+
|
367
|
+
=item C<< stmt.add_select(:col => alias) >>
|
368
|
+
|
369
|
+
=item C<< stmt.add_select(\'COUNT(*)' => 'cnt') >>
|
370
|
+
|
371
|
+
Add a new select term. It's automatically quoted.
|
372
|
+
|
373
|
+
=item C<< stmt.add_from(table :Str | select :SQL::Maker::Select) : SQL::Maker::Select >>
|
374
|
+
|
375
|
+
Add a new FROM clause. You can specify the table name or an instance of L<SQL::Maker::Select> for a sub-query.
|
376
|
+
|
377
|
+
I<Return:> stmt itself.
|
378
|
+
|
379
|
+
=item C<< stmt.add_join(:user => {:type => 'inner', :table => 'config', :condition => 'user.user_id = config.user_id'}); >>
|
380
|
+
|
381
|
+
=item C<< stmt.add_join(:user => {:type => 'inner', :table => 'config', :condition => {'user.user_id' => 'config.user_id'}); >>
|
382
|
+
|
383
|
+
=item C<< stmt.add_join(:user => {:type => 'inner', :table => 'config', :condition => ['user_id']}); >>
|
384
|
+
|
385
|
+
Add a new JOIN clause. If you pass an arrayref for 'condition' then it uses 'USING'. If 'type' is omitted
|
386
|
+
it falls back to plain JOIN.
|
387
|
+
|
388
|
+
stmt = SQL::Maker::Select.new
|
389
|
+
stmt.add_join(
|
390
|
+
:user => {
|
391
|
+
:type => 'inner',
|
392
|
+
:table => 'config',
|
393
|
+
:condition => 'user.user_id = config.user_id',
|
394
|
+
}
|
395
|
+
)
|
396
|
+
stmt.as_sql
|
397
|
+
# => 'FROM user INNER JOIN config ON user.user_id = config.user_id'
|
398
|
+
|
399
|
+
stmt = SQL::Maker::Select.new(:quote_char => '`', :name_sep => '.')
|
400
|
+
stmt.add_join(
|
401
|
+
:user => {
|
402
|
+
:type => 'inner',
|
403
|
+
:table => 'config',
|
404
|
+
:condition => {'user.user_id' => 'config.user_id'},
|
405
|
+
}
|
406
|
+
)
|
407
|
+
stmt.as_sql
|
408
|
+
# => 'FROM `user` INNER JOIN `config` ON `user`.`user_id` = `config`.`user_id`'
|
409
|
+
|
410
|
+
stmt = SQL::Maker::Select.new
|
411
|
+
stmt.add_select('name')
|
412
|
+
stmt.add_join(
|
413
|
+
:user => {
|
414
|
+
:type => 'inner',
|
415
|
+
:table => 'config',
|
416
|
+
:condition => ['user_id'],
|
417
|
+
}
|
418
|
+
)
|
419
|
+
stmt.as_sql
|
420
|
+
# => 'SELECT name FROM user INNER JOIN config USING (user_id)'
|
421
|
+
|
422
|
+
subquery = SQL::Maker::Select.new
|
423
|
+
subquery.add_select('*')
|
424
|
+
subquery.add_from( 'foo' )
|
425
|
+
subquery.add_where( 'hoge' => 'fuga' )
|
426
|
+
stmt = SQL::Maker::Select.new
|
427
|
+
stmt.add_join(
|
428
|
+
[ subquery, 'bar' ] => {
|
429
|
+
:type => 'inner',
|
430
|
+
:table => 'baz',
|
431
|
+
:alias => 'b1',
|
432
|
+
:condition => 'bar.baz_id = b1.baz_id'
|
433
|
+
},
|
434
|
+
)
|
435
|
+
stmt.as_sql
|
436
|
+
# => "FROM (SELECT * FROM foo WHERE (hoge = ?)) bar INNER JOIN baz b1 ON bar.baz_id = b1.baz_id"
|
437
|
+
|
438
|
+
=item C<< stmt.add_index_hint(:foo => {:type => 'USE', :list => ['index_hint']}); >>
|
439
|
+
|
440
|
+
=item C<< stmt.add_index_hint(:foo => 'index_hint'); >>
|
441
|
+
|
442
|
+
=item C<< stmt.add_index_hint(:foo => ['index_hint']); >>
|
443
|
+
|
444
|
+
stmt = SQL::Maker::Select.new
|
445
|
+
stmt.add_select('name')
|
446
|
+
stmt.add_from('user')
|
447
|
+
stmt.add_index_hint(:user => {:type => 'USE', :list => ['index_hint']})
|
448
|
+
stmt.as_sql
|
449
|
+
# => "SELECT name FROM user USE INDEX (index_hint)"
|
450
|
+
|
451
|
+
=item C<< stmt.add_where('foo_id' => 'bar'); >>
|
452
|
+
|
453
|
+
Add a new WHERE clause.
|
454
|
+
|
455
|
+
stmt = SQL::Maker::Select.new.add_select('c')
|
456
|
+
.add_from('foo')
|
457
|
+
.add_where('name' => 'john')
|
458
|
+
.add_where('type' => {:IN => %w/1 2 3/})
|
459
|
+
.as_sql
|
460
|
+
# => "SELECT c FROM foo WHERE (name = ?) AND (type IN (?, ?, ?))"
|
461
|
+
|
462
|
+
=item C<< stmt.add_where_raw('id = ?', [1]) >>
|
463
|
+
|
464
|
+
Add a new WHERE clause from raw placeholder string and bind variables.
|
465
|
+
|
466
|
+
stmt = SQL::Maker::Select.new.add_select('c')
|
467
|
+
.add_from('foo')
|
468
|
+
.add_where_raw('EXISTS(SELECT * FROM bar WHERE name = ?)' => ['john'])
|
469
|
+
.add_where_raw('type IS NOT NULL')
|
470
|
+
.as_sql
|
471
|
+
# => "SELECT c FROM foo WHERE (EXISTS(SELECT * FROM bar WHERE name = ?)) AND (type IS NOT NULL)"
|
472
|
+
|
473
|
+
|
474
|
+
=item C<< stmt.set_where(condition) >>
|
475
|
+
|
476
|
+
Set the WHERE clause.
|
477
|
+
|
478
|
+
condition should be instance of L<SQL::Maker::Condition>.
|
479
|
+
|
480
|
+
cond1 = SQL::Maker::Condition.new.add("name" => "john")
|
481
|
+
cond2 = SQL::Maker::Condition.new.add("type" => {:IN => %w/1 2 3/})
|
482
|
+
stmt = SQL::Maker::Select.new.add_select('c')
|
483
|
+
.add_from('foo')
|
484
|
+
.set_where(cond1 & cond2)
|
485
|
+
.as_sql
|
486
|
+
# => "SELECT c FROM foo WHERE ((name = ?)) AND ((type IN (?, ?, ?)))"
|
487
|
+
|
488
|
+
=item C<< stmt.add_order_by('foo'); >>
|
489
|
+
|
490
|
+
=item C<< stmt.add_order_by({'foo' => 'DESC'}); >>
|
491
|
+
|
492
|
+
Add a new ORDER BY clause.
|
493
|
+
|
494
|
+
stmt = SQL::Maker::Select.new.add_select('c')
|
495
|
+
.add_from('foo')
|
496
|
+
.add_order_by('name' => 'DESC')
|
497
|
+
.add_order_by('id')
|
498
|
+
.as_sql
|
499
|
+
# => "SELECT c FROM foo ORDER BY name DESC, id"
|
500
|
+
|
501
|
+
=item C<< stmt.add_group_by('foo'); >>
|
502
|
+
|
503
|
+
Add a new GROUP BY clause.
|
504
|
+
|
505
|
+
stmt = SQL::Maker::Select.new.add_select('c')
|
506
|
+
.add_from('foo')
|
507
|
+
.add_group_by('id')
|
508
|
+
.as_sql
|
509
|
+
# => "SELECT c FROM foo GROUP BY id"
|
510
|
+
|
511
|
+
stmt = SQL::Maker::Select.new.add_select('c')
|
512
|
+
.add_from('foo')
|
513
|
+
.add_group_by('id' => 'DESC')
|
514
|
+
.as_sql
|
515
|
+
# => "SELECT c FROM foo GROUP BY id DESC"
|
516
|
+
|
517
|
+
=item C<< stmt.limit(30) >>
|
518
|
+
|
519
|
+
=item C<< stmt.offset(5) >>
|
520
|
+
|
521
|
+
Add LIMIT and OFFSET.
|
522
|
+
|
523
|
+
stmt = SQL::Maker::Select.new.add_select('c')
|
524
|
+
.add_from('foo')
|
525
|
+
.limit(30)
|
526
|
+
.offset(5)
|
527
|
+
.as_sql
|
528
|
+
# => "SELECT c FROM foo LIMIT 30 OFFSET 5"
|
529
|
+
|
530
|
+
=item C<< stmt.add_having(:cnt => 2) >>
|
531
|
+
|
532
|
+
Add a HAVING clause.
|
533
|
+
|
534
|
+
# stmt = SQL::Maker::Select.new.add_from('foo')
|
535
|
+
# .add_select(\'COUNT(*)' => 'cnt')
|
536
|
+
# .add_having(:cnt => 2)
|
537
|
+
# .as_sql
|
538
|
+
# # => "SELECT COUNT(*) AS cnt FROM foo HAVING (COUNT(*) = ?)"
|
539
|
+
|
540
|
+
=back
|
541
|
+
|
542
|
+
=head1 SEE ALSO
|
543
|
+
|
544
|
+
L<Data::ObjectDriver::SQL>
|