sequel 3.10.0 → 3.11.0

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.
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)