sequel 3.21.0 → 3.31.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.
- data/CHANGELOG +485 -3
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +249 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +45 -0
- data/lib/sequel/adapters/jdbc/derby.rb +247 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +56 -29
- data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +335 -0
- data/lib/sequel/adapters/mysql.rb +82 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +351 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +321 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +231 -106
- data/lib/sequel/adapters/shared/mysql.rb +181 -63
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +231 -41
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +4 -7
- data/lib/sequel/adapters/shared/sqlite.rb +201 -57
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +231 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +48 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +665 -253
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1020 -337
- data/lib/sequel/model/base.rb +414 -132
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +88 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +111 -29
- data/spec/adapters/mysql_spec.rb +135 -123
- data/spec/adapters/oracle_spec.rb +49 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +788 -512
- data/spec/core/dataset_spec.rb +1073 -1053
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +72 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +537 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +461 -65
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +212 -122
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +865 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +115 -73
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that execute code on the database
|
|
4
|
+
# :section: 2 - Methods that execute code on the database
|
|
5
5
|
# These methods all execute the dataset's SQL on the database.
|
|
6
6
|
# They don't return modified datasets, so if used in a method chain
|
|
7
7
|
# they should be the last method called.
|
|
8
8
|
# ---------------------
|
|
9
9
|
|
|
10
10
|
# Action methods defined by Sequel that execute code on the database.
|
|
11
|
-
ACTION_METHODS =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
ACTION_METHODS = (<<-METHS).split.map{|x| x.to_sym}
|
|
12
|
+
<< [] []= all avg count columns columns! delete each
|
|
13
|
+
empty? fetch_rows first get import insert insert_multiple interval last
|
|
14
|
+
map max min multi_insert range select_hash select_map select_order_map
|
|
15
|
+
set single_record single_value sum to_csv to_hash truncate update
|
|
16
|
+
METHS
|
|
15
17
|
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
# Inserts the given argument into the database. Returns self so it
|
|
19
|
+
# can be used safely when chaining:
|
|
20
|
+
#
|
|
21
|
+
# DB[:items] << {:id=>0, :name=>'Zero'} << DB[:old_items].select(:id, name)
|
|
22
|
+
def <<(arg)
|
|
23
|
+
insert(arg)
|
|
24
|
+
self
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
# Returns the first record matching the conditions. Examples:
|
|
@@ -72,7 +77,7 @@ module Sequel
|
|
|
72
77
|
# # => [:id, :name]
|
|
73
78
|
def columns
|
|
74
79
|
return @columns if @columns
|
|
75
|
-
ds = unfiltered.unordered.clone(:distinct => nil, :limit => 1)
|
|
80
|
+
ds = unfiltered.unordered.clone(:distinct => nil, :limit => 1, :offset=>nil)
|
|
76
81
|
ds.each{break}
|
|
77
82
|
@columns = ds.instance_variable_get(:@columns)
|
|
78
83
|
@columns || []
|
|
@@ -101,10 +106,15 @@ module Sequel
|
|
|
101
106
|
#
|
|
102
107
|
# DB[:table].delete # DELETE * FROM table
|
|
103
108
|
# # => 3
|
|
104
|
-
def delete
|
|
105
|
-
|
|
109
|
+
def delete(&block)
|
|
110
|
+
sql = delete_sql
|
|
111
|
+
if uses_returning?(:delete)
|
|
112
|
+
returning_fetch_rows(sql, &block)
|
|
113
|
+
else
|
|
114
|
+
execute_dui(sql)
|
|
115
|
+
end
|
|
106
116
|
end
|
|
107
|
-
|
|
117
|
+
|
|
108
118
|
# Iterates over the records in the dataset as they are yielded from the
|
|
109
119
|
# database adapter, and returns self.
|
|
110
120
|
#
|
|
@@ -127,10 +137,10 @@ module Sequel
|
|
|
127
137
|
|
|
128
138
|
# Returns true if no records exist in the dataset, false otherwise
|
|
129
139
|
#
|
|
130
|
-
# DB[:table].empty? # SELECT 1 FROM table LIMIT 1
|
|
140
|
+
# DB[:table].empty? # SELECT 1 AS one FROM table LIMIT 1
|
|
131
141
|
# # => false
|
|
132
142
|
def empty?
|
|
133
|
-
get(1).nil?
|
|
143
|
+
get(Sequel::SQL::AliasedExpression.new(1, :one)).nil?
|
|
134
144
|
end
|
|
135
145
|
|
|
136
146
|
# Executes a select query and fetches records, yielding each record to the
|
|
@@ -219,43 +229,47 @@ module Sequel
|
|
|
219
229
|
# DB[:table].import([:x, :y], DB[:table2].select(:a, :b))
|
|
220
230
|
# # INSERT INTO table (x, y) SELECT a, b FROM table2
|
|
221
231
|
#
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
#
|
|
232
|
+
# Options:
|
|
233
|
+
# :commit_every :: Open a new transaction for every given number of records.
|
|
234
|
+
# For example, if you provide a value of 50, will commit
|
|
235
|
+
# after every 50 records.
|
|
236
|
+
# :server :: Set the server/shard to use for the transaction and insert
|
|
237
|
+
# queries.
|
|
238
|
+
# :slice :: Same as :commit_every, :commit_every takes precedence.
|
|
228
239
|
def import(columns, values, opts={})
|
|
229
240
|
return @db.transaction{insert(columns, values)} if values.is_a?(Dataset)
|
|
230
241
|
|
|
231
242
|
return if values.empty?
|
|
232
243
|
raise(Error, IMPORT_ERROR_MSG) if columns.empty?
|
|
244
|
+
ds = opts[:server] ? server(opts[:server]) : self
|
|
233
245
|
|
|
234
246
|
if slice_size = opts[:commit_every] || opts[:slice]
|
|
235
247
|
offset = 0
|
|
236
|
-
|
|
237
|
-
|
|
248
|
+
rows = []
|
|
249
|
+
while offset < values.length
|
|
250
|
+
rows << ds._import(columns, values[offset, slice_size], opts)
|
|
238
251
|
offset += slice_size
|
|
239
|
-
break if offset >= values.length
|
|
240
252
|
end
|
|
253
|
+
rows.flatten
|
|
241
254
|
else
|
|
242
|
-
|
|
243
|
-
@db.transaction{statements.each{|st| execute_dui(st)}}
|
|
255
|
+
ds._import(columns, values, opts)
|
|
244
256
|
end
|
|
245
257
|
end
|
|
246
|
-
|
|
258
|
+
|
|
247
259
|
# Inserts values into the associated table. The returned value is generally
|
|
248
260
|
# the value of the primary key for the inserted row, but that is adapter dependent.
|
|
249
261
|
#
|
|
250
262
|
# +insert+ handles a number of different argument formats:
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
#
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
#
|
|
263
|
+
# no arguments or single empty hash :: Uses DEFAULT VALUES
|
|
264
|
+
# single hash :: Most common format, treats keys as columns an values as values
|
|
265
|
+
# single array :: Treats entries as values, with no columns
|
|
266
|
+
# two arrays :: Treats first array as columns, second array as values
|
|
267
|
+
# single Dataset :: Treats as an insert based on a selection from the dataset given,
|
|
268
|
+
# with no columns
|
|
269
|
+
# array and dataset :: Treats as an insert based on a selection from the dataset
|
|
270
|
+
# given, with the columns given by the array.
|
|
271
|
+
#
|
|
272
|
+
# Examples:
|
|
259
273
|
#
|
|
260
274
|
# DB[:items].insert
|
|
261
275
|
# # INSERT INTO items DEFAULT VALUES
|
|
@@ -277,27 +291,35 @@ module Sequel
|
|
|
277
291
|
#
|
|
278
292
|
# DB[:items].insert([:a, :b], DB[:old_items])
|
|
279
293
|
# # INSERT INTO items (a, b) SELECT * FROM old_items
|
|
280
|
-
def insert(*values)
|
|
281
|
-
|
|
294
|
+
def insert(*values, &block)
|
|
295
|
+
sql = insert_sql(*values)
|
|
296
|
+
if uses_returning?(:insert)
|
|
297
|
+
returning_fetch_rows(sql, &block)
|
|
298
|
+
else
|
|
299
|
+
execute_insert(sql)
|
|
300
|
+
end
|
|
282
301
|
end
|
|
283
302
|
|
|
284
303
|
# Inserts multiple values. If a block is given it is invoked for each
|
|
285
304
|
# item in the given array before inserting it. See +multi_insert+ as
|
|
286
|
-
# a
|
|
287
|
-
# SQL statement.
|
|
305
|
+
# a possibly faster version that may be able to insert multiple
|
|
306
|
+
# records in one SQL statement (if supported by the database).
|
|
307
|
+
# Returns an array of primary keys of inserted rows.
|
|
288
308
|
#
|
|
289
309
|
# DB[:table].insert_multiple([{:x=>1}, {:x=>2}])
|
|
310
|
+
# # => [4, 5]
|
|
290
311
|
# # INSERT INTO table (x) VALUES (1)
|
|
291
312
|
# # INSERT INTO table (x) VALUES (2)
|
|
292
313
|
#
|
|
293
314
|
# DB[:table].insert_multiple([{:x=>1}, {:x=>2}]){|row| row[:y] = row[:x] * 2}
|
|
315
|
+
# # => [6, 7]
|
|
294
316
|
# # INSERT INTO table (x, y) VALUES (1, 2)
|
|
295
317
|
# # INSERT INTO table (x, y) VALUES (2, 4)
|
|
296
318
|
def insert_multiple(array, &block)
|
|
297
319
|
if block
|
|
298
|
-
array.
|
|
320
|
+
array.map{|i| insert(block.call(i))}
|
|
299
321
|
else
|
|
300
|
-
array.
|
|
322
|
+
array.map{|i| insert(i)}
|
|
301
323
|
end
|
|
302
324
|
end
|
|
303
325
|
|
|
@@ -310,7 +332,7 @@ module Sequel
|
|
|
310
332
|
aggregate_dataset.get{max(column) - min(column)}
|
|
311
333
|
end
|
|
312
334
|
|
|
313
|
-
# Reverses the order and then runs first. Note that this
|
|
335
|
+
# Reverses the order and then runs #first with the given arguments and block. Note that this
|
|
314
336
|
# will not necessarily give you the last record in the dataset,
|
|
315
337
|
# unless you have an unambiguous order. If there is not
|
|
316
338
|
# currently an order for this dataset, raises an +Error+.
|
|
@@ -334,10 +356,20 @@ module Sequel
|
|
|
334
356
|
#
|
|
335
357
|
# DB[:table].map{|r| r[:id] * 2} # SELECT * FROM table
|
|
336
358
|
# # => [2, 4, 6, ...]
|
|
359
|
+
#
|
|
360
|
+
# You can also provide an array of column names:
|
|
361
|
+
#
|
|
362
|
+
# DB[:table].map([:id, :name]) # SELECT * FROM table
|
|
363
|
+
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
|
337
364
|
def map(column=nil, &block)
|
|
338
365
|
if column
|
|
339
366
|
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
340
|
-
|
|
367
|
+
return naked.map(column) if row_proc
|
|
368
|
+
if column.is_a?(Array)
|
|
369
|
+
super(){|r| r.values_at(*column)}
|
|
370
|
+
else
|
|
371
|
+
super(){|r| r[column]}
|
|
372
|
+
end
|
|
341
373
|
else
|
|
342
374
|
super(&block)
|
|
343
375
|
end
|
|
@@ -370,7 +402,7 @@ module Sequel
|
|
|
370
402
|
# otherwise some columns could be missed or set to null instead of to default
|
|
371
403
|
# values.
|
|
372
404
|
#
|
|
373
|
-
#
|
|
405
|
+
# This respects the same options as #import.
|
|
374
406
|
def multi_insert(hashes, opts={})
|
|
375
407
|
return if hashes.empty?
|
|
376
408
|
columns = hashes.first.keys
|
|
@@ -393,49 +425,63 @@ module Sequel
|
|
|
393
425
|
#
|
|
394
426
|
# DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
|
|
395
427
|
# # => {1=>'a', 2=>'b', ...}
|
|
428
|
+
#
|
|
429
|
+
# You can also provide an array of column names for either the key_column,
|
|
430
|
+
# the value column, or both:
|
|
431
|
+
#
|
|
432
|
+
# DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
|
|
433
|
+
# # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
|
|
396
434
|
def select_hash(key_column, value_column)
|
|
397
|
-
|
|
435
|
+
if key_column.is_a?(Array)
|
|
436
|
+
if value_column.is_a?(Array)
|
|
437
|
+
select(*(key_column + value_column)).to_hash(key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
|
|
438
|
+
else
|
|
439
|
+
select(*(key_column + [value_column])).to_hash(key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
|
|
440
|
+
end
|
|
441
|
+
elsif value_column.is_a?(Array)
|
|
442
|
+
select(key_column, *value_column).to_hash(hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
|
|
443
|
+
else
|
|
444
|
+
select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
|
|
445
|
+
end
|
|
398
446
|
end
|
|
399
447
|
|
|
400
448
|
# Selects the column given (either as an argument or as a block), and
|
|
401
449
|
# returns an array of all values of that column in the dataset. If you
|
|
402
450
|
# give a block argument that returns an array with multiple entries,
|
|
403
|
-
# the contents of the resulting array are undefined.
|
|
451
|
+
# the contents of the resulting array are undefined. Raises an Error
|
|
452
|
+
# if called with both an argument and a block.
|
|
404
453
|
#
|
|
405
454
|
# DB[:table].select_map(:id) # SELECT id FROM table
|
|
406
455
|
# # => [3, 5, 8, 1, ...]
|
|
407
456
|
#
|
|
408
|
-
# DB[:table].select_map{
|
|
409
|
-
# # => [
|
|
457
|
+
# DB[:table].select_map{id * 2} # SELECT (id * 2) FROM table
|
|
458
|
+
# # => [6, 10, 16, 2, ...]
|
|
459
|
+
#
|
|
460
|
+
# You can also provide an array of column names:
|
|
461
|
+
#
|
|
462
|
+
# DB[:table].select_map([:id, :name]) # SELECT id, name FROM table
|
|
463
|
+
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
|
410
464
|
def select_map(column=nil, &block)
|
|
411
|
-
|
|
412
|
-
ds = if column
|
|
413
|
-
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
414
|
-
ds.select(column)
|
|
415
|
-
else
|
|
416
|
-
ds.select(&block)
|
|
417
|
-
end
|
|
418
|
-
ds.map{|r| r.values.first}
|
|
465
|
+
_select_map(column, false, &block)
|
|
419
466
|
end
|
|
467
|
+
|
|
420
468
|
|
|
421
469
|
# The same as select_map, but in addition orders the array by the column.
|
|
422
470
|
#
|
|
423
471
|
# DB[:table].select_order_map(:id) # SELECT id FROM table ORDER BY id
|
|
424
472
|
# # => [1, 2, 3, 4, ...]
|
|
425
473
|
#
|
|
426
|
-
# DB[:table].select_order_map{
|
|
427
|
-
# # => [
|
|
474
|
+
# DB[:table].select_order_map{id * 2} # SELECT (id * 2) FROM table ORDER BY (id * 2)
|
|
475
|
+
# # => [2, 4, 6, 8, ...]
|
|
476
|
+
#
|
|
477
|
+
# You can also provide an array of column names:
|
|
478
|
+
#
|
|
479
|
+
# DB[:table].select_order_map([:id, :name]) # SELECT id, name FROM table ORDER BY id, name
|
|
480
|
+
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
|
428
481
|
def select_order_map(column=nil, &block)
|
|
429
|
-
|
|
430
|
-
ds = if column
|
|
431
|
-
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
432
|
-
ds.select(column).order(unaliased_identifier(column))
|
|
433
|
-
else
|
|
434
|
-
ds.select(&block).order(&block)
|
|
435
|
-
end
|
|
436
|
-
ds.map{|r| r.values.first}
|
|
482
|
+
_select_map(column, true, &block)
|
|
437
483
|
end
|
|
438
|
-
|
|
484
|
+
|
|
439
485
|
# Alias for update, but not aliased directly so subclasses
|
|
440
486
|
# don't have to override both methods.
|
|
441
487
|
def set(*args)
|
|
@@ -499,11 +545,38 @@ module Sequel
|
|
|
499
545
|
#
|
|
500
546
|
# DB[:table].to_hash(:id) # SELECT * FROM table
|
|
501
547
|
# # {1=>{:id=>1, :name=>'Jim'}, 2=>{:id=>2, :name=>'Bob'}, ...}
|
|
548
|
+
#
|
|
549
|
+
# You can also provide an array of column names for either the key_column,
|
|
550
|
+
# the value column, or both:
|
|
551
|
+
#
|
|
552
|
+
# DB[:table].to_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
|
|
553
|
+
# # {[1, 3]=>['Jim', 'bo'], [2, 4]=>['Bob', 'be'], ...}
|
|
554
|
+
#
|
|
555
|
+
# DB[:table].to_hash([:id, :name]) # SELECT * FROM table
|
|
556
|
+
# # {[1, 'Jim']=>{:id=>1, :name=>'Jim'}, [2, 'Bob'=>{:id=>2, :name=>'Bob'}, ...}
|
|
502
557
|
def to_hash(key_column, value_column = nil)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
558
|
+
h = {}
|
|
559
|
+
if value_column
|
|
560
|
+
return naked.to_hash(key_column, value_column) if row_proc
|
|
561
|
+
if value_column.is_a?(Array)
|
|
562
|
+
if key_column.is_a?(Array)
|
|
563
|
+
each{|r| h[r.values_at(*key_column)] = r.values_at(*value_column)}
|
|
564
|
+
else
|
|
565
|
+
each{|r| h[r[key_column]] = r.values_at(*value_column)}
|
|
566
|
+
end
|
|
567
|
+
else
|
|
568
|
+
if key_column.is_a?(Array)
|
|
569
|
+
each{|r| h[r.values_at(*key_column)] = r[value_column]}
|
|
570
|
+
else
|
|
571
|
+
each{|r| h[r[key_column]] = r[value_column]}
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
elsif key_column.is_a?(Array)
|
|
575
|
+
each{|r| h[r.values_at(*key_column)] = r}
|
|
576
|
+
else
|
|
577
|
+
each{|r| h[r[key_column]] = r}
|
|
506
578
|
end
|
|
579
|
+
h
|
|
507
580
|
end
|
|
508
581
|
|
|
509
582
|
# Truncates the dataset. Returns nil.
|
|
@@ -522,14 +595,64 @@ module Sequel
|
|
|
522
595
|
# DB[:table].update(:x=>nil) # UPDATE table SET x = NULL
|
|
523
596
|
# # => 10
|
|
524
597
|
#
|
|
525
|
-
# DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1),
|
|
598
|
+
# DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1), y = 0
|
|
526
599
|
# # => 10
|
|
527
|
-
def update(values={})
|
|
528
|
-
|
|
600
|
+
def update(values={}, &block)
|
|
601
|
+
sql = update_sql(values)
|
|
602
|
+
if uses_returning?(:update)
|
|
603
|
+
returning_fetch_rows(sql, &block)
|
|
604
|
+
else
|
|
605
|
+
execute_dui(sql)
|
|
606
|
+
end
|
|
529
607
|
end
|
|
530
608
|
|
|
609
|
+
protected
|
|
610
|
+
|
|
611
|
+
# Internals of #import. If primary key values are requested, use
|
|
612
|
+
# separate insert commands for each row. Otherwise, call #multi_insert_sql
|
|
613
|
+
# and execute each statement it gives separately.
|
|
614
|
+
def _import(columns, values, opts)
|
|
615
|
+
trans_opts = opts.merge(:server=>@opts[:server])
|
|
616
|
+
if opts[:return] == :primary_key
|
|
617
|
+
@db.transaction(trans_opts){values.map{|v| insert(columns, v)}}
|
|
618
|
+
else
|
|
619
|
+
stmts = multi_insert_sql(columns, values)
|
|
620
|
+
@db.transaction(trans_opts){stmts.each{|st| execute_dui(st)}}
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# Return an array of arrays of values given by the symbols in ret_cols.
|
|
625
|
+
def _select_map_multiple(ret_cols)
|
|
626
|
+
map{|r| r.values_at(*ret_cols)}
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
# Returns an array of the first value in each row.
|
|
630
|
+
def _select_map_single
|
|
631
|
+
map{|r| r.values.first}
|
|
632
|
+
end
|
|
633
|
+
|
|
531
634
|
private
|
|
532
635
|
|
|
636
|
+
# Internals of +select_map+ and +select_order_map+
|
|
637
|
+
def _select_map(column, order, &block)
|
|
638
|
+
ds = naked.ungraphed
|
|
639
|
+
if column
|
|
640
|
+
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
641
|
+
columns = Array(column)
|
|
642
|
+
select_cols = order ? columns.map{|c| c.is_a?(SQL::OrderedExpression) ? c.expression : c} : columns
|
|
643
|
+
ds = ds.select(*select_cols)
|
|
644
|
+
ds = ds.order(*columns.map{|c| unaliased_identifier(c)}) if order
|
|
645
|
+
else
|
|
646
|
+
ds = ds.select(&block)
|
|
647
|
+
ds = ds.order(&block) if order
|
|
648
|
+
end
|
|
649
|
+
if column.is_a?(Array)
|
|
650
|
+
ds._select_map_multiple(select_cols.map{|c| hash_key_symbol(c)})
|
|
651
|
+
else
|
|
652
|
+
ds._select_map_single
|
|
653
|
+
end
|
|
654
|
+
end
|
|
655
|
+
|
|
533
656
|
# Set the server to use to :default unless it is already set in the passed opts
|
|
534
657
|
def default_server_opts(opts)
|
|
535
658
|
{:server=>@opts[:server] || :default}.merge(opts)
|
|
@@ -561,9 +684,19 @@ module Sequel
|
|
|
561
684
|
# specifying the symbol that is likely to be used as the hash key
|
|
562
685
|
# for the column when records are returned.
|
|
563
686
|
def hash_key_symbol(s)
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
687
|
+
case s
|
|
688
|
+
when Symbol
|
|
689
|
+
_, c, a = split_symbol(s)
|
|
690
|
+
(a || c).to_sym
|
|
691
|
+
when SQL::Identifier
|
|
692
|
+
hash_key_symbol(s.value)
|
|
693
|
+
when SQL::QualifiedIdentifier
|
|
694
|
+
hash_key_symbol(s.column)
|
|
695
|
+
when SQL::AliasedExpression
|
|
696
|
+
hash_key_symbol(s.aliaz)
|
|
697
|
+
else
|
|
698
|
+
raise(Error, "#{s.inspect} is not supported, should be a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or SQL::AliasedExpression")
|
|
699
|
+
end
|
|
567
700
|
end
|
|
568
701
|
|
|
569
702
|
# Modify the identifier returned from the database based on the
|
|
@@ -580,6 +713,20 @@ module Sequel
|
|
|
580
713
|
def post_load(all_records)
|
|
581
714
|
end
|
|
582
715
|
|
|
716
|
+
# Called by insert/update/delete when returning is used.
|
|
717
|
+
# Yields each row as a plain hash to the block if one is given, or returns
|
|
718
|
+
# an array of plain hashes for all rows if a block is not given
|
|
719
|
+
def returning_fetch_rows(sql, &block)
|
|
720
|
+
if block
|
|
721
|
+
default_server.fetch_rows(sql, &block)
|
|
722
|
+
nil
|
|
723
|
+
else
|
|
724
|
+
rows = []
|
|
725
|
+
default_server.fetch_rows(sql){|r| rows << r}
|
|
726
|
+
rows
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
|
|
583
730
|
# Return the unaliased part of the identifier. Handles both
|
|
584
731
|
# implicit aliases in symbols, as well as SQL::AliasedExpression
|
|
585
732
|
# objects. Other objects are returned as is.
|
|
@@ -590,6 +737,14 @@ module Sequel
|
|
|
590
737
|
c_table ? SQL::QualifiedIdentifier.new(c_table, column.to_sym) : column.to_sym
|
|
591
738
|
when SQL::AliasedExpression
|
|
592
739
|
c.expression
|
|
740
|
+
when SQL::OrderedExpression
|
|
741
|
+
expr = c.expression
|
|
742
|
+
if expr.is_a?(Symbol)
|
|
743
|
+
expr = unaliased_identifier(expr)
|
|
744
|
+
SQL::OrderedExpression.new(unaliased_identifier(c.expression), c.descending, :nulls=>c.nulls)
|
|
745
|
+
else
|
|
746
|
+
c
|
|
747
|
+
end
|
|
593
748
|
else
|
|
594
749
|
c
|
|
595
750
|
end
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that describe what the dataset supports
|
|
4
|
+
# :section: 4 - Methods that describe what the dataset supports
|
|
5
5
|
# These methods all return booleans, with most describing whether or not the
|
|
6
6
|
# dataset supports a feature.
|
|
7
7
|
# ---------------------
|
|
8
8
|
|
|
9
|
-
# Method used to check if WITH is supported
|
|
10
|
-
WITH_SUPPORTED=:select_with_sql
|
|
11
|
-
|
|
12
9
|
# Whether this dataset quotes identifiers.
|
|
13
10
|
def quote_identifiers?
|
|
14
|
-
@quote_identifiers
|
|
11
|
+
if defined?(@quote_identifiers)
|
|
12
|
+
@quote_identifiers
|
|
13
|
+
elsif db.respond_to?(:quote_identifiers?)
|
|
14
|
+
@quote_identifiers = db.quote_identifiers?
|
|
15
|
+
else
|
|
16
|
+
@quote_identifiers = false
|
|
17
|
+
end
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
# Whether this dataset will provide accurate number of rows matched for
|
|
@@ -21,22 +24,59 @@ module Sequel
|
|
|
21
24
|
true
|
|
22
25
|
end
|
|
23
26
|
|
|
27
|
+
# Whether you must use a column alias list for recursive CTEs (false by
|
|
28
|
+
# default).
|
|
29
|
+
def recursive_cte_requires_column_aliases?
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
24
33
|
# Whether the dataset requires SQL standard datetimes (false by default,
|
|
25
34
|
# as most allow strings with ISO 8601 format).
|
|
26
35
|
def requires_sql_standard_datetimes?
|
|
27
36
|
false
|
|
28
37
|
end
|
|
29
38
|
|
|
39
|
+
# Whether type specifiers are required for prepared statement/bound
|
|
40
|
+
# variable argument placeholders (i.e. :bv__integer)
|
|
41
|
+
def requires_placeholder_type_specifiers?
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
30
45
|
# Whether the dataset supports common table expressions (the WITH clause).
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
# If given, +type+ can be :select, :insert, :update, or :delete, in which case it
|
|
47
|
+
# determines whether WITH is supported for the respective statement type.
|
|
48
|
+
def supports_cte?(type=:select)
|
|
49
|
+
send(:"#{type}_clause_methods").include?(:"#{type}_with_sql")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Whether the dataset supports common table expressions (the WITH clause)
|
|
53
|
+
# in subqueries. If false, applies the WITH clause to the main query, which can cause issues
|
|
54
|
+
# if multiple WITH clauses use the same name.
|
|
55
|
+
def supports_cte_in_subqueries?
|
|
56
|
+
false
|
|
33
57
|
end
|
|
34
58
|
|
|
35
|
-
# Whether the dataset supports the DISTINCT ON clause, false by default.
|
|
59
|
+
# Whether the dataset supports or can emulate the DISTINCT ON clause, false by default.
|
|
36
60
|
def supports_distinct_on?
|
|
37
61
|
false
|
|
38
62
|
end
|
|
39
63
|
|
|
64
|
+
# Whether the dataset supports CUBE with GROUP BY.
|
|
65
|
+
def supports_group_cube?
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Whether the dataset supports ROLLUP with GROUP BY.
|
|
70
|
+
def supports_group_rollup?
|
|
71
|
+
false
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Whether this dataset supports the +insert_select+ method for returning all columns values
|
|
75
|
+
# directly from an insert query.
|
|
76
|
+
def supports_insert_select?
|
|
77
|
+
supports_returning?(:insert)
|
|
78
|
+
end
|
|
79
|
+
|
|
40
80
|
# Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
|
|
41
81
|
def supports_intersect_except?
|
|
42
82
|
true
|
|
@@ -67,7 +107,24 @@ module Sequel
|
|
|
67
107
|
def supports_multiple_column_in?
|
|
68
108
|
true
|
|
69
109
|
end
|
|
110
|
+
|
|
111
|
+
# Whether the dataset supports or can fully emulate the DISTINCT ON clause,
|
|
112
|
+
# including respecting the ORDER BY clause, false by default
|
|
113
|
+
def supports_ordered_distinct_on?
|
|
114
|
+
supports_distinct_on?
|
|
115
|
+
end
|
|
70
116
|
|
|
117
|
+
# Whether the RETURNING clause is supported for the given type of query.
|
|
118
|
+
# +type+ can be :insert, :update, or :delete.
|
|
119
|
+
def supports_returning?(type)
|
|
120
|
+
send(:"#{type}_clause_methods").include?(:"#{type}_returning_sql")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Whether the database supports SELECT *, column FROM table
|
|
124
|
+
def supports_select_all_and_column?
|
|
125
|
+
true
|
|
126
|
+
end
|
|
127
|
+
|
|
71
128
|
# Whether the dataset supports timezones in literal timestamps
|
|
72
129
|
def supports_timestamp_timezones?
|
|
73
130
|
false
|
|
@@ -82,5 +139,31 @@ module Sequel
|
|
|
82
139
|
def supports_window_functions?
|
|
83
140
|
false
|
|
84
141
|
end
|
|
142
|
+
|
|
143
|
+
# Whether the dataset supports WHERE TRUE (or WHERE 1 for databases that
|
|
144
|
+
# that use 1 for true).
|
|
145
|
+
def supports_where_true?
|
|
146
|
+
true
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
# Whether insert(nil) or insert({}) must be emulated by
|
|
152
|
+
# using at least one value, false by default.
|
|
153
|
+
def insert_supports_empty_values?
|
|
154
|
+
true
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Whether using an offset returns an extra row number column that should be
|
|
158
|
+
# eliminated, false by default.
|
|
159
|
+
def offset_returns_row_number_column?
|
|
160
|
+
false
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Whether the RETURNING clause is used for the given dataset.
|
|
164
|
+
# +type+ can be :insert, :update, or :delete.
|
|
165
|
+
def uses_returning?(type)
|
|
166
|
+
opts[:returning] && !@opts[:sql] && supports_returning?(type)
|
|
167
|
+
end
|
|
85
168
|
end
|
|
86
169
|
end
|