sequel 3.10.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/CHANGELOG +68 -0
  2. data/COPYING +1 -1
  3. data/README.rdoc +87 -27
  4. data/bin/sequel +2 -4
  5. data/doc/association_basics.rdoc +1383 -0
  6. data/doc/dataset_basics.rdoc +106 -0
  7. data/doc/opening_databases.rdoc +45 -16
  8. data/doc/querying.rdoc +210 -0
  9. data/doc/release_notes/3.11.0.txt +254 -0
  10. data/doc/virtual_rows.rdoc +217 -31
  11. data/lib/sequel/adapters/ado.rb +28 -12
  12. data/lib/sequel/adapters/ado/mssql.rb +33 -1
  13. data/lib/sequel/adapters/amalgalite.rb +13 -8
  14. data/lib/sequel/adapters/db2.rb +1 -2
  15. data/lib/sequel/adapters/dbi.rb +7 -4
  16. data/lib/sequel/adapters/do.rb +14 -15
  17. data/lib/sequel/adapters/do/postgres.rb +4 -5
  18. data/lib/sequel/adapters/do/sqlite.rb +9 -0
  19. data/lib/sequel/adapters/firebird.rb +5 -10
  20. data/lib/sequel/adapters/informix.rb +2 -4
  21. data/lib/sequel/adapters/jdbc.rb +111 -49
  22. data/lib/sequel/adapters/jdbc/mssql.rb +1 -2
  23. data/lib/sequel/adapters/jdbc/mysql.rb +11 -0
  24. data/lib/sequel/adapters/jdbc/oracle.rb +4 -7
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +8 -1
  26. data/lib/sequel/adapters/jdbc/sqlite.rb +12 -0
  27. data/lib/sequel/adapters/mysql.rb +14 -5
  28. data/lib/sequel/adapters/odbc.rb +2 -4
  29. data/lib/sequel/adapters/odbc/mssql.rb +2 -4
  30. data/lib/sequel/adapters/openbase.rb +1 -2
  31. data/lib/sequel/adapters/oracle.rb +4 -8
  32. data/lib/sequel/adapters/postgres.rb +4 -11
  33. data/lib/sequel/adapters/shared/mssql.rb +22 -9
  34. data/lib/sequel/adapters/shared/mysql.rb +33 -30
  35. data/lib/sequel/adapters/shared/oracle.rb +0 -5
  36. data/lib/sequel/adapters/shared/postgres.rb +13 -11
  37. data/lib/sequel/adapters/shared/sqlite.rb +56 -10
  38. data/lib/sequel/adapters/sqlite.rb +16 -9
  39. data/lib/sequel/connection_pool.rb +6 -1
  40. data/lib/sequel/connection_pool/single.rb +1 -0
  41. data/lib/sequel/core.rb +6 -1
  42. data/lib/sequel/database.rb +52 -23
  43. data/lib/sequel/database/schema_generator.rb +6 -0
  44. data/lib/sequel/database/schema_methods.rb +5 -5
  45. data/lib/sequel/database/schema_sql.rb +1 -1
  46. data/lib/sequel/dataset.rb +4 -190
  47. data/lib/sequel/dataset/actions.rb +323 -1
  48. data/lib/sequel/dataset/features.rb +18 -2
  49. data/lib/sequel/dataset/graph.rb +7 -0
  50. data/lib/sequel/dataset/misc.rb +119 -0
  51. data/lib/sequel/dataset/mutation.rb +64 -0
  52. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  53. data/lib/sequel/dataset/query.rb +272 -6
  54. data/lib/sequel/dataset/sql.rb +186 -394
  55. data/lib/sequel/model.rb +4 -2
  56. data/lib/sequel/model/associations.rb +31 -14
  57. data/lib/sequel/model/base.rb +32 -13
  58. data/lib/sequel/model/exceptions.rb +8 -4
  59. data/lib/sequel/model/plugins.rb +3 -13
  60. data/lib/sequel/plugins/active_model.rb +26 -7
  61. data/lib/sequel/plugins/instance_filters.rb +98 -0
  62. data/lib/sequel/plugins/many_through_many.rb +1 -1
  63. data/lib/sequel/plugins/optimistic_locking.rb +25 -9
  64. data/lib/sequel/version.rb +1 -1
  65. data/spec/adapters/mssql_spec.rb +26 -0
  66. data/spec/adapters/mysql_spec.rb +33 -4
  67. data/spec/adapters/postgres_spec.rb +24 -1
  68. data/spec/adapters/spec_helper.rb +6 -0
  69. data/spec/adapters/sqlite_spec.rb +28 -0
  70. data/spec/core/connection_pool_spec.rb +17 -5
  71. data/spec/core/database_spec.rb +101 -1
  72. data/spec/core/dataset_spec.rb +42 -4
  73. data/spec/core/schema_spec.rb +13 -0
  74. data/spec/extensions/active_model_spec.rb +34 -11
  75. data/spec/extensions/caching_spec.rb +2 -0
  76. data/spec/extensions/instance_filters_spec.rb +55 -0
  77. data/spec/extensions/spec_helper.rb +2 -0
  78. data/spec/integration/dataset_test.rb +12 -1
  79. data/spec/integration/model_test.rb +12 -0
  80. data/spec/integration/plugin_test.rb +61 -1
  81. data/spec/integration/schema_test.rb +14 -3
  82. data/spec/model/base_spec.rb +27 -0
  83. data/spec/model/plugins_spec.rb +0 -22
  84. data/spec/model/record_spec.rb +32 -1
  85. data/spec/model/spec_helper.rb +2 -0
  86. metadata +14 -3
  87. data/lib/sequel/dataset/convenience.rb +0 -326
@@ -1,29 +1,197 @@
1
1
  module Sequel
2
2
  class Dataset
3
+ # ---------------------
4
+ # :section: User Methods relating to SQL Creation
5
+ # These are methods you can call to see what SQL will be generated by the dataset.
6
+ # ---------------------
7
+
8
+ # Formats a DELETE statement using the given options and dataset options.
9
+ #
10
+ # dataset.filter{|o| o.price >= 100}.delete_sql #=>
11
+ # "DELETE FROM items WHERE (price >= 100)"
12
+ def delete_sql
13
+ return static_sql(opts[:sql]) if opts[:sql]
14
+ check_modification_allowed!
15
+ clause_sql(:delete)
16
+ end
17
+
18
+ # Returns an EXISTS clause for the dataset as a LiteralString.
19
+ #
20
+ # DB.select(1).where(DB[:items].exists).sql
21
+ # #=> "SELECT 1 WHERE (EXISTS (SELECT * FROM items))"
22
+ def exists
23
+ LiteralString.new("EXISTS (#{select_sql})")
24
+ end
25
+
26
+ # Formats an INSERT statement using the given values. The API is a little
27
+ # complex, and best explained by example:
28
+ #
29
+ # # Default values
30
+ # DB[:items].insert_sql #=> 'INSERT INTO items DEFAULT VALUES'
31
+ # DB[:items].insert_sql({}) #=> 'INSERT INTO items DEFAULT VALUES'
32
+ # # Values without columns
33
+ # DB[:items].insert_sql(1,2,3) #=> 'INSERT INTO items VALUES (1, 2, 3)'
34
+ # DB[:items].insert_sql([1,2,3]) #=> 'INSERT INTO items VALUES (1, 2, 3)'
35
+ # # Values with columns
36
+ # DB[:items].insert_sql([:a, :b], [1,2]) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
37
+ # DB[:items].insert_sql(:a => 1, :b => 2) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
38
+ # # Using a subselect
39
+ # DB[:items].insert_sql(DB[:old_items]) #=> 'INSERT INTO items SELECT * FROM old_items
40
+ # # Using a subselect with columns
41
+ # DB[:items].insert_sql([:a, :b], DB[:old_items]) #=> 'INSERT INTO items (a, b) SELECT * FROM old_items
42
+ def insert_sql(*values)
43
+ return static_sql(@opts[:sql]) if @opts[:sql]
44
+
45
+ check_modification_allowed!
46
+
47
+ columns = []
48
+
49
+ case values.size
50
+ when 0
51
+ return insert_sql({})
52
+ when 1
53
+ case vals = values.at(0)
54
+ when Hash
55
+ vals = @opts[:defaults].merge(vals) if @opts[:defaults]
56
+ vals = vals.merge(@opts[:overrides]) if @opts[:overrides]
57
+ values = []
58
+ vals.each do |k,v|
59
+ columns << k
60
+ values << v
61
+ end
62
+ when Dataset, Array, LiteralString
63
+ values = vals
64
+ else
65
+ if vals.respond_to?(:values) && (v = vals.values).is_a?(Hash)
66
+ return insert_sql(v)
67
+ end
68
+ end
69
+ when 2
70
+ if (v0 = values.at(0)).is_a?(Array) && ((v1 = values.at(1)).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
71
+ columns, values = v0, v1
72
+ raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
73
+ end
74
+ end
75
+
76
+ columns = columns.map{|k| literal(String === k ? k.to_sym : k)}
77
+ clone(:columns=>columns, :values=>values)._insert_sql
78
+ end
79
+
80
+ # Returns a literal representation of a value to be used as part
81
+ # of an SQL expression.
82
+ #
83
+ # dataset.literal("abc'def\\") #=> "'abc''def\\\\'"
84
+ # dataset.literal(:items__id) #=> "items.id"
85
+ # dataset.literal([1, 2, 3]) => "(1, 2, 3)"
86
+ # dataset.literal(DB[:items]) => "(SELECT * FROM items)"
87
+ # dataset.literal(:x + 1 > :y) => "((x + 1) > y)"
88
+ #
89
+ # If an unsupported object is given, an exception is raised.
90
+ def literal(v)
91
+ case v
92
+ when String
93
+ return v if v.is_a?(LiteralString)
94
+ v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
95
+ when Symbol
96
+ literal_symbol(v)
97
+ when Integer
98
+ literal_integer(v)
99
+ when Hash
100
+ literal_hash(v)
101
+ when SQL::Expression
102
+ literal_expression(v)
103
+ when Float
104
+ literal_float(v)
105
+ when BigDecimal
106
+ literal_big_decimal(v)
107
+ when NilClass
108
+ literal_nil
109
+ when TrueClass
110
+ literal_true
111
+ when FalseClass
112
+ literal_false
113
+ when Array
114
+ literal_array(v)
115
+ when Time
116
+ literal_time(v)
117
+ when DateTime
118
+ literal_datetime(v)
119
+ when Date
120
+ literal_date(v)
121
+ when Dataset
122
+ literal_dataset(v)
123
+ else
124
+ literal_other(v)
125
+ end
126
+ end
127
+
128
+ # Returns an array of insert statements for inserting multiple records.
129
+ # This method is used by #multi_insert to format insert statements and
130
+ # expects a keys array and and an array of value arrays.
131
+ #
132
+ # This method should be overridden by descendants if the support
133
+ # inserting multiple records in a single SQL statement.
134
+ def multi_insert_sql(columns, values)
135
+ values.map{|r| insert_sql(columns, r)}
136
+ end
137
+
138
+ # Formats a SELECT statement
139
+ #
140
+ # dataset.select_sql # => "SELECT * FROM items"
141
+ def select_sql
142
+ return static_sql(@opts[:sql]) if @opts[:sql]
143
+ clause_sql(:select)
144
+ end
145
+
146
+ # Same as select_sql, not aliased directly to make subclassing simpler.
147
+ def sql
148
+ select_sql
149
+ end
150
+
151
+ # SQL query to truncate the table
152
+ def truncate_sql
153
+ if opts[:sql]
154
+ static_sql(opts[:sql])
155
+ else
156
+ check_modification_allowed!
157
+ raise(InvalidOperation, "Can't truncate filtered datasets") if opts[:where]
158
+ _truncate_sql(source_list(opts[:from]))
159
+ end
160
+ end
161
+
162
+ # Formats an UPDATE statement using the given values.
163
+ #
164
+ # dataset.update_sql(:price => 100, :category => 'software') #=>
165
+ # "UPDATE items SET price = 100, category = 'software'"
166
+ #
167
+ # Raises an error if the dataset is grouped or includes more
168
+ # than one table.
169
+ def update_sql(values = {})
170
+ return static_sql(opts[:sql]) if opts[:sql]
171
+ check_modification_allowed!
172
+ clone(:values=>values)._update_sql
173
+ end
174
+
175
+ # ---------------------
176
+ # :section: Internal Methods relating to SQL Creation
177
+ # These methods, while public, are not designed to be used directly by the end user.
178
+ # ---------------------
179
+
3
180
  # Given a type (e.g. select) and an array of clauses,
4
181
  # return an array of methods to call to build the SQL string.
5
182
  def self.clause_methods(type, clauses)
6
183
  clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
7
184
  end
8
185
 
9
- # These symbols have _join methods created (e.g. inner_join) that
10
- # call join_table with the symbol, passing along the arguments and
11
- # block from the method call.
12
- CONDITIONED_JOIN_TYPES = [:inner, :full_outer, :right_outer, :left_outer, :full, :right, :left]
13
-
14
- # These symbols have _join methods created (e.g. natural_join) that
15
- # call join_table with the symbol. They only accept a single table
16
- # argument which is passed to join_table, and they raise an error
17
- # if called with a block.
18
- UNCONDITIONED_JOIN_TYPES = [:natural, :natural_left, :natural_right, :natural_full, :cross]
19
-
20
186
  AND_SEPARATOR = " AND ".freeze
21
187
  BOOL_FALSE = "'f'".freeze
22
188
  BOOL_TRUE = "'t'".freeze
189
+ COMMA_SEPARATOR = ', '.freeze
23
190
  COLUMN_REF_RE1 = /\A([\w ]+)__([\w ]+)___([\w ]+)\z/.freeze
24
191
  COLUMN_REF_RE2 = /\A([\w ]+)___([\w ]+)\z/.freeze
25
192
  COLUMN_REF_RE3 = /\A([\w ]+)__([\w ]+)\z/.freeze
26
193
  COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
194
+ COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
27
195
  DATASET_ALIAS_BASE_NAME = 't'.freeze
28
196
  FOR_UPDATE = ' FOR UPDATE'.freeze
29
197
  IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
@@ -39,7 +207,7 @@ module Sequel
39
207
  TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S%N%z'".freeze
40
208
  STANDARD_TIMESTAMP_FORMAT = "TIMESTAMP #{TIMESTAMP_FORMAT}".freeze
41
209
  TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
42
- WILDCARD = '*'.freeze
210
+ WILDCARD = LiteralString.new('*').freeze
43
211
  SQL_WITH = "WITH ".freeze
44
212
 
45
213
  # SQL fragment for the aliased expression
@@ -149,139 +317,12 @@ module Sequel
149
317
  constant.to_s
150
318
  end
151
319
 
152
- # Returns the number of records in the dataset.
153
- def count
154
- aggregate_dataset.get{COUNT(:*){}.as(count)}.to_i
155
- end
156
-
157
- # Formats a DELETE statement using the given options and dataset options.
158
- #
159
- # dataset.filter{|o| o.price >= 100}.delete_sql #=>
160
- # "DELETE FROM items WHERE (price >= 100)"
161
- def delete_sql
162
- return static_sql(opts[:sql]) if opts[:sql]
163
- check_modification_allowed!
164
- clause_sql(:delete)
165
- end
166
-
167
- # Returns an EXISTS clause for the dataset as a LiteralString.
168
- #
169
- # DB.select(1).where(DB[:items].exists).sql
170
- # #=> "SELECT 1 WHERE (EXISTS (SELECT * FROM items))"
171
- def exists
172
- LiteralString.new("EXISTS (#{select_sql})")
173
- end
174
-
175
- # The first source (primary table) for this dataset. If the dataset doesn't
176
- # have a table, raises an error. If the table is aliased, returns the aliased name.
177
- def first_source_alias
178
- source = @opts[:from]
179
- if source.nil? || source.empty?
180
- raise Error, 'No source specified for query'
181
- end
182
- case s = source.first
183
- when SQL::AliasedExpression
184
- s.aliaz
185
- when Symbol
186
- sch, table, aliaz = split_symbol(s)
187
- aliaz ? aliaz.to_sym : s
188
- else
189
- s
190
- end
191
- end
192
- alias first_source first_source_alias
193
-
194
- # The first source (primary table) for this dataset. If the dataset doesn't
195
- # have a table, raises an error. If the table is aliased, returns the original
196
- # table, not the alias
197
- def first_source_table
198
- source = @opts[:from]
199
- if source.nil? || source.empty?
200
- raise Error, 'No source specified for query'
201
- end
202
- case s = source.first
203
- when SQL::AliasedExpression
204
- s.expression
205
- when Symbol
206
- sch, table, aliaz = split_symbol(s)
207
- aliaz ? (sch ? SQL::QualifiedIdentifier.new(sch, table) : table.to_sym) : s
208
- else
209
- s
210
- end
211
- end
212
-
213
320
  # SQL fragment specifying an SQL function call
214
321
  def function_sql(f)
215
322
  args = f.args
216
323
  "#{f.f}#{args.empty? ? '()' : literal(args)}"
217
324
  end
218
325
 
219
- # Inserts multiple values. If a block is given it is invoked for each
220
- # item in the given array before inserting it. See #multi_insert as
221
- # a possible faster version that inserts multiple records in one
222
- # SQL statement.
223
- def insert_multiple(array, &block)
224
- if block
225
- array.each {|i| insert(block[i])}
226
- else
227
- array.each {|i| insert(i)}
228
- end
229
- end
230
-
231
- # Formats an INSERT statement using the given values. The API is a little
232
- # complex, and best explained by example:
233
- #
234
- # # Default values
235
- # DB[:items].insert_sql #=> 'INSERT INTO items DEFAULT VALUES'
236
- # DB[:items].insert_sql({}) #=> 'INSERT INTO items DEFAULT VALUES'
237
- # # Values without columns
238
- # DB[:items].insert_sql(1,2,3) #=> 'INSERT INTO items VALUES (1, 2, 3)'
239
- # DB[:items].insert_sql([1,2,3]) #=> 'INSERT INTO items VALUES (1, 2, 3)'
240
- # # Values with columns
241
- # DB[:items].insert_sql([:a, :b], [1,2]) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
242
- # DB[:items].insert_sql(:a => 1, :b => 2) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
243
- # # Using a subselect
244
- # DB[:items].insert_sql(DB[:old_items]) #=> 'INSERT INTO items SELECT * FROM old_items
245
- # # Using a subselect with columns
246
- # DB[:items].insert_sql([:a, :b], DB[:old_items]) #=> 'INSERT INTO items (a, b) SELECT * FROM old_items
247
- def insert_sql(*values)
248
- return static_sql(@opts[:sql]) if @opts[:sql]
249
-
250
- check_modification_allowed!
251
-
252
- columns = []
253
-
254
- case values.size
255
- when 0
256
- return insert_sql({})
257
- when 1
258
- case vals = values.at(0)
259
- when Hash
260
- vals = @opts[:defaults].merge(vals) if @opts[:defaults]
261
- vals = vals.merge(@opts[:overrides]) if @opts[:overrides]
262
- values = []
263
- vals.each do |k,v|
264
- columns << k
265
- values << v
266
- end
267
- when Dataset, Array, LiteralString
268
- values = vals
269
- else
270
- if vals.respond_to?(:values) && (v = vals.values).is_a?(Hash)
271
- return insert_sql(v)
272
- end
273
- end
274
- when 2
275
- if (v0 = values.at(0)).is_a?(Array) && ((v1 = values.at(1)).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
276
- columns, values = v0, v1
277
- raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
278
- end
279
- end
280
-
281
- columns = columns.map{|k| literal(String === k ? k.to_sym : k)}
282
- clone(:columns=>columns, :values=>values)._insert_sql
283
- end
284
-
285
326
  # SQL fragment specifying a JOIN clause without ON or USING.
286
327
  def join_clause_sql(jc)
287
328
  table = jc.table
@@ -300,150 +341,6 @@ module Sequel
300
341
  def join_using_clause_sql(jc)
301
342
  "#{join_clause_sql(jc)} USING (#{column_list(jc.using)})"
302
343
  end
303
-
304
- # Returns a joined dataset. Uses the following arguments:
305
- #
306
- # * type - The type of join to do (e.g. :inner)
307
- # * table - Depends on type:
308
- # * Dataset - a subselect is performed with an alias of tN for some value of N
309
- # * Model (or anything responding to :table_name) - table.table_name
310
- # * String, Symbol: table
311
- # * expr - specifies conditions, depends on type:
312
- # * Hash, Array with all two pairs - Assumes key (1st arg) is column of joined table (unless already
313
- # qualified), and value (2nd arg) is column of the last joined or primary table (or the
314
- # :implicit_qualifier option).
315
- # To specify multiple conditions on a single joined table column, you must use an array.
316
- # Uses a JOIN with an ON clause.
317
- # * Array - If all members of the array are symbols, considers them as columns and
318
- # uses a JOIN with a USING clause. Most databases will remove duplicate columns from
319
- # the result set if this is used.
320
- # * nil - If a block is not given, doesn't use ON or USING, so the JOIN should be a NATURAL
321
- # or CROSS join. If a block is given, uses a ON clause based on the block, see below.
322
- # * Everything else - pretty much the same as a using the argument in a call to filter,
323
- # so strings are considered literal, symbols specify boolean columns, and blockless
324
- # filter expressions can be used. Uses a JOIN with an ON clause.
325
- # * options - a hash of options, with any of the following keys:
326
- # * :table_alias - the name of the table's alias when joining, necessary for joining
327
- # to the same table more than once. No alias is used by default.
328
- # * :implicit_qualifier - The name to use for qualifying implicit conditions. By default,
329
- # the last joined or primary table is used.
330
- # * block - The block argument should only be given if a JOIN with an ON clause is used,
331
- # in which case it yields the table alias/name for the table currently being joined,
332
- # the table alias/name for the last joined (or first table), and an array of previous
333
- # SQL::JoinClause.
334
- def join_table(type, table, expr=nil, options={}, &block)
335
- using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
336
- if using_join && !supports_join_using?
337
- h = {}
338
- expr.each{|s| h[s] = s}
339
- return join_table(type, table, h, options)
340
- end
341
-
342
- case options
343
- when Hash
344
- table_alias = options[:table_alias]
345
- last_alias = options[:implicit_qualifier]
346
- when Symbol, String, SQL::Identifier
347
- table_alias = options
348
- last_alias = nil
349
- else
350
- raise Error, "invalid options format for join_table: #{options.inspect}"
351
- end
352
-
353
- if Dataset === table
354
- if table_alias.nil?
355
- table_alias_num = (@opts[:num_dataset_sources] || 0) + 1
356
- table_alias = dataset_alias(table_alias_num)
357
- end
358
- table_name = table_alias
359
- else
360
- table = table.table_name if table.respond_to?(:table_name)
361
- table_name = table_alias || table
362
- end
363
-
364
- join = if expr.nil? and !block_given?
365
- SQL::JoinClause.new(type, table, table_alias)
366
- elsif using_join
367
- raise(Sequel::Error, "can't use a block if providing an array of symbols as expr") if block_given?
368
- SQL::JoinUsingClause.new(expr, type, table, table_alias)
369
- else
370
- last_alias ||= @opts[:last_joined_table] || first_source_alias
371
- if Sequel.condition_specifier?(expr)
372
- expr = expr.collect do |k, v|
373
- k = qualified_column_name(k, table_name) if k.is_a?(Symbol)
374
- v = qualified_column_name(v, last_alias) if v.is_a?(Symbol)
375
- [k,v]
376
- end
377
- end
378
- if block_given?
379
- expr2 = yield(table_name, last_alias, @opts[:join] || [])
380
- expr = expr ? SQL::BooleanExpression.new(:AND, expr, expr2) : expr2
381
- end
382
- SQL::JoinOnClause.new(expr, type, table, table_alias)
383
- end
384
-
385
- opts = {:join => (@opts[:join] || []) + [join], :last_joined_table => table_name}
386
- opts[:num_dataset_sources] = table_alias_num if table_alias_num
387
- clone(opts)
388
- end
389
-
390
- # Returns a literal representation of a value to be used as part
391
- # of an SQL expression.
392
- #
393
- # dataset.literal("abc'def\\") #=> "'abc''def\\\\'"
394
- # dataset.literal(:items__id) #=> "items.id"
395
- # dataset.literal([1, 2, 3]) => "(1, 2, 3)"
396
- # dataset.literal(DB[:items]) => "(SELECT * FROM items)"
397
- # dataset.literal(:x + 1 > :y) => "((x + 1) > y)"
398
- #
399
- # If an unsupported object is given, an exception is raised.
400
- def literal(v)
401
- case v
402
- when String
403
- return v if v.is_a?(LiteralString)
404
- v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
405
- when Symbol
406
- literal_symbol(v)
407
- when Integer
408
- literal_integer(v)
409
- when Hash
410
- literal_hash(v)
411
- when SQL::Expression
412
- literal_expression(v)
413
- when Float
414
- literal_float(v)
415
- when BigDecimal
416
- literal_big_decimal(v)
417
- when NilClass
418
- literal_nil
419
- when TrueClass
420
- literal_true
421
- when FalseClass
422
- literal_false
423
- when Array
424
- literal_array(v)
425
- when Time
426
- literal_time(v)
427
- when DateTime
428
- literal_datetime(v)
429
- when Date
430
- literal_date(v)
431
- when Dataset
432
- literal_dataset(v)
433
- else
434
- literal_other(v)
435
- end
436
- end
437
-
438
- # Returns an array of insert statements for inserting multiple records.
439
- # This method is used by #multi_insert to format insert statements and
440
- # expects a keys array and and an array of value arrays.
441
- #
442
- # This method should be overridden by descendants if the support
443
- # inserting multiple records in a single SQL statement.
444
- def multi_insert_sql(columns, values)
445
- values.map{|r| insert_sql(columns, r)}
446
- end
447
344
 
448
345
  # SQL fragment for NegativeBooleanConstants
449
346
  def negative_boolean_constant_sql(constant)
@@ -476,35 +373,6 @@ module Sequel
476
373
  [qcr.table, qcr.column].map{|x| [SQL::QualifiedIdentifier, SQL::Identifier, Symbol].any?{|c| x.is_a?(c)} ? literal(x) : quote_identifier(x)}.join('.')
477
374
  end
478
375
 
479
- # Qualify to the given table, or first source if not table is given.
480
- def qualify(table=first_source)
481
- qualify_to(table)
482
- end
483
-
484
- # Return a copy of the dataset with unqualified identifiers in the
485
- # SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the
486
- # given table. If no columns are currently selected, select all
487
- # columns of the given table.
488
- def qualify_to(table)
489
- o = @opts
490
- return clone if o[:sql]
491
- h = {}
492
- (o.keys & QUALIFY_KEYS).each do |k|
493
- h[k] = qualified_expression(o[k], table)
494
- end
495
- h[:select] = [SQL::ColumnAll.new(table)] if !o[:select] || o[:select].empty?
496
- clone(h)
497
- end
498
-
499
- # Qualify the dataset to its current first source. This is useful
500
- # if you have unqualified identifiers in the query that all refer to
501
- # the first source, and you want to join to another table which
502
- # has columns with the same name as columns in the current dataset.
503
- # See qualify_to.
504
- def qualify_to_first_source
505
- qualify_to(first_source)
506
- end
507
-
508
376
  # Adds quoting to identifiers (columns and tables). If identifiers are not
509
377
  # being quoted, returns name as a string. If identifiers are being quoted
510
378
  # quote the name with quoted_identifier.
@@ -548,55 +416,10 @@ module Sequel
548
416
  end
549
417
  end
550
418
 
551
- # Formats a SELECT statement
552
- #
553
- # dataset.select_sql # => "SELECT * FROM items"
554
- def select_sql
555
- return static_sql(@opts[:sql]) if @opts[:sql]
556
- clause_sql(:select)
557
- end
558
-
559
- # Same as select_sql, not aliased directly to make subclassing simpler.
560
- def sql
561
- select_sql
562
- end
563
-
564
419
  # SQL fragment for specifying subscripts (SQL arrays)
565
420
  def subscript_sql(s)
566
421
  "#{literal(s.f)}[#{expression_list(s.sub)}]"
567
422
  end
568
-
569
- # SQL query to truncate the table
570
- def truncate_sql
571
- if opts[:sql]
572
- static_sql(opts[:sql])
573
- else
574
- check_modification_allowed!
575
- raise(InvalidOperation, "Can't truncate filtered datasets") if opts[:where]
576
- _truncate_sql(source_list(opts[:from]))
577
- end
578
- end
579
-
580
- # Formats an UPDATE statement using the given values.
581
- #
582
- # dataset.update_sql(:price => 100, :category => 'software') #=>
583
- # "UPDATE items SET price = 100, category = 'software'"
584
- #
585
- # Raises an error if the dataset is grouped or includes more
586
- # than one table.
587
- def update_sql(values = {})
588
- return static_sql(opts[:sql]) if opts[:sql]
589
- check_modification_allowed!
590
- clone(:values=>values)._update_sql
591
- end
592
-
593
- # Add a condition to the WHERE clause. See #filter for argument types.
594
- #
595
- # dataset.group(:a).having(:a).filter(:b) # SELECT * FROM items GROUP BY a HAVING a AND b
596
- # dataset.group(:a).having(:a).where(:b) # SELECT * FROM items WHERE b GROUP BY a HAVING a
597
- def where(*cond, &block)
598
- _filter(:where, *cond, &block)
599
- end
600
423
 
601
424
  # The SQL fragment for the given window's options.
602
425
  def window_sql(opts)
@@ -621,43 +444,6 @@ module Sequel
621
444
  def window_function_sql(function, window)
622
445
  "#{literal(function)} OVER #{literal(window)}"
623
446
  end
624
-
625
- # Add a simple common table expression (CTE) with the given name and a dataset that defines the CTE.
626
- # A common table expression acts as an inline view for the query.
627
- # Options:
628
- # * :args - Specify the arguments/columns for the CTE, should be an array of symbols.
629
- # * :recursive - Specify that this is a recursive CTE
630
- def with(name, dataset, opts={})
631
- raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
632
- clone(:with=>(@opts[:with]||[]) + [opts.merge(:name=>name, :dataset=>dataset)])
633
- end
634
-
635
- # Add a recursive common table expression (CTE) with the given name, a dataset that
636
- # defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
637
- # of the CTE. Options:
638
- # * :args - Specify the arguments/columns for the CTE, should be an array of symbols.
639
- # * :union_all - Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
640
- def with_recursive(name, nonrecursive, recursive, opts={})
641
- raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
642
- clone(:with=>(@opts[:with]||[]) + [opts.merge(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
643
- end
644
-
645
- # Returns a copy of the dataset with the static SQL used. This is useful if you want
646
- # to keep the same row_proc/graph, but change the SQL used to custom SQL.
647
- #
648
- # dataset.with_sql('SELECT * FROM foo') # SELECT * FROM foo
649
- def with_sql(sql, *args)
650
- sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
651
- clone(:sql=>sql)
652
- end
653
-
654
- CONDITIONED_JOIN_TYPES.each do |jtype|
655
- class_eval("def #{jtype}_join(*args, &block); join_table(:#{jtype}, *args, &block) end", __FILE__, __LINE__)
656
- end
657
- UNCONDITIONED_JOIN_TYPES.each do |jtype|
658
- class_eval("def #{jtype}_join(table); raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if block_given?; join_table(:#{jtype}, table) end", __FILE__, __LINE__)
659
- end
660
- alias join inner_join
661
447
 
662
448
  protected
663
449
 
@@ -816,6 +602,12 @@ module Sequel
816
602
  def identifier_list(columns)
817
603
  columns.map{|i| quote_identifier(i)}.join(COMMA_SEPARATOR)
818
604
  end
605
+
606
+ # Modify the identifier returned from the database based on the
607
+ # identifier_output_method.
608
+ def input_identifier(v)
609
+ (i = identifier_input_method) ? v.to_s.send(i) : v.to_s
610
+ end
819
611
 
820
612
  # SQL fragment specifying the table to insert INTO
821
613
  def insert_into_sql(sql)