sequel 3.39.0 → 3.40.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +30 -0
- data/README.rdoc +4 -3
- data/doc/active_record.rdoc +1 -1
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/3.40.0.txt +73 -0
- data/lib/sequel/adapters/ado.rb +29 -3
- data/lib/sequel/adapters/ado/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +0 -6
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/jdbc.rb +26 -18
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/derby.rb +7 -7
- data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -4
- data/lib/sequel/adapters/mysql.rb +0 -3
- data/lib/sequel/adapters/mysql2.rb +0 -3
- data/lib/sequel/adapters/oracle.rb +4 -1
- data/lib/sequel/adapters/postgres.rb +4 -4
- data/lib/sequel/adapters/shared/access.rb +205 -3
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +7 -2
- data/lib/sequel/adapters/shared/mssql.rb +3 -34
- data/lib/sequel/adapters/shared/mysql.rb +4 -33
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +11 -0
- data/lib/sequel/adapters/shared/oracle.rb +5 -0
- data/lib/sequel/adapters/shared/postgres.rb +2 -1
- data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/query.rb +30 -7
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/query.rb +9 -10
- data/lib/sequel/dataset/sql.rb +14 -26
- data/lib/sequel/extensions/pg_hstore.rb +19 -0
- data/lib/sequel/extensions/pg_row.rb +5 -5
- data/lib/sequel/plugins/association_pks.rb +121 -18
- data/lib/sequel/plugins/json_serializer.rb +19 -0
- data/lib/sequel/sql.rb +11 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +42 -0
- data/spec/core/database_spec.rb +17 -0
- data/spec/core/dataset_spec.rb +11 -0
- data/spec/core/expression_filters_spec.rb +13 -0
- data/spec/extensions/association_pks_spec.rb +163 -3
- data/spec/extensions/pg_hstore_spec.rb +6 -0
- data/spec/extensions/pg_row_spec.rb +17 -0
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/dataset_test.rb +13 -13
- data/spec/integration/plugin_test.rb +232 -7
- data/spec/integration/schema_test.rb +8 -12
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/type_test.rb +6 -0
- metadata +9 -2
@@ -22,7 +22,7 @@ module Sequel
|
|
22
22
|
:serializable=>'SERIALIZABLE'.freeze}
|
23
23
|
|
24
24
|
STRING_DEFAULT_RE = /\A'(.*)'\z/
|
25
|
-
CURRENT_TIMESTAMP_RE = /now|CURRENT|getdate/io
|
25
|
+
CURRENT_TIMESTAMP_RE = /now|CURRENT|getdate|\ADate\(\)\z/io
|
26
26
|
COLUMN_SCHEMA_DATETIME_TYPES = [:date, :datetime]
|
27
27
|
COLUMN_SCHEMA_STRING_TYPES = [:string, :blob, :date, :datetime, :time, :enum, :set, :interval]
|
28
28
|
|
@@ -195,7 +195,9 @@ module Sequel
|
|
195
195
|
opts[:schema] = sch if sch && !opts.include?(:schema)
|
196
196
|
|
197
197
|
Sequel.synchronize{@schemas.delete(quoted_name)} if opts[:reload]
|
198
|
-
|
198
|
+
if v = Sequel.synchronize{@schemas[quoted_name]}
|
199
|
+
return v
|
200
|
+
end
|
199
201
|
|
200
202
|
cols = schema_parse_table(table_name, opts)
|
201
203
|
raise(Error, 'schema parsing returned no columns, table probably doesn\'t exist') if cols.nil? || cols.empty?
|
@@ -234,6 +236,14 @@ module Sequel
|
|
234
236
|
#
|
235
237
|
# The following general options are respected:
|
236
238
|
#
|
239
|
+
# :disconnect :: Can be set to :retry to automatically retry the transaction with
|
240
|
+
# a new connection object if it detects a disconnect on the connection.
|
241
|
+
# Note that this should not be used unless the entire transaction
|
242
|
+
# block is idempotent, as otherwise it can cause non-idempotent
|
243
|
+
# behavior to execute multiple times. This does no checking for
|
244
|
+
# infinite loops, so if your transaction will repeatedly raise a
|
245
|
+
# disconnection error, this will cause the transaction block to loop
|
246
|
+
# indefinitely.
|
237
247
|
# :isolation :: The transaction isolation level to use for this transaction,
|
238
248
|
# should be :uncommitted, :committed, :repeatable, or :serializable,
|
239
249
|
# used if given and the database/adapter supports customizable
|
@@ -258,9 +268,22 @@ module Sequel
|
|
258
268
|
# appropriately. Valid values true, :on, false, :off, :local (9.1+),
|
259
269
|
# and :remote_write (9.2+).
|
260
270
|
def transaction(opts={}, &block)
|
261
|
-
|
262
|
-
|
263
|
-
|
271
|
+
if opts[:disconnect] == :retry
|
272
|
+
begin
|
273
|
+
transaction(opts.merge(:disconnect=>:disallow), &block)
|
274
|
+
rescue Sequel::DatabaseDisconnectError
|
275
|
+
retry
|
276
|
+
end
|
277
|
+
else
|
278
|
+
synchronize(opts[:server]) do |conn|
|
279
|
+
if already_in_transaction?(conn, opts)
|
280
|
+
if opts[:disconnect] == :disallow
|
281
|
+
raise Sequel::Error, "cannot set :disconnect=>:retry if you are already inside a transaction"
|
282
|
+
end
|
283
|
+
return yield(conn)
|
284
|
+
end
|
285
|
+
_transaction(conn, opts, &block)
|
286
|
+
end
|
264
287
|
end
|
265
288
|
end
|
266
289
|
|
@@ -440,7 +463,7 @@ module Sequel
|
|
440
463
|
# Convert the given default, which should be a database specific string, into
|
441
464
|
# a ruby object.
|
442
465
|
def column_schema_to_ruby_default(default, type)
|
443
|
-
return
|
466
|
+
return default unless default.is_a?(String)
|
444
467
|
if COLUMN_SCHEMA_DATETIME_TYPES.include?(type)
|
445
468
|
if CURRENT_TIMESTAMP_RE.match(default)
|
446
469
|
if type == :date
|
@@ -582,7 +605,7 @@ module Sequel
|
|
582
605
|
# such as :integer or :string.
|
583
606
|
def schema_column_type(db_type)
|
584
607
|
case db_type
|
585
|
-
when /\A(character( varying)?|n?(var)?char|n?text)/io
|
608
|
+
when /\A(character( varying)?|n?(var)?char|n?text|string)/io
|
586
609
|
:string
|
587
610
|
when /\A(int(eger)?|(big|small|tiny)int)/io
|
588
611
|
:integer
|
@@ -354,7 +354,7 @@ module Sequel
|
|
354
354
|
# SQL fragment for given alter table operation.
|
355
355
|
def alter_table_op_sql(table, op)
|
356
356
|
quoted_name = quote_identifier(op[:name]) if op[:name]
|
357
|
-
|
357
|
+
case op[:op]
|
358
358
|
when :add_column
|
359
359
|
"ADD COLUMN #{column_definition_sql(op)}"
|
360
360
|
when :drop_column
|
@@ -783,7 +783,7 @@ module Sequel
|
|
783
783
|
# :text option is used, Sequel uses the :text type.
|
784
784
|
def type_literal_generic_string(column)
|
785
785
|
if column[:text]
|
786
|
-
:text
|
786
|
+
uses_clob_for_text? ? :clob : :text
|
787
787
|
elsif column[:fixed]
|
788
788
|
"char(#{column[:size]||255})"
|
789
789
|
else
|
@@ -811,5 +811,10 @@ module Sequel
|
|
811
811
|
elements = column[:size] || column[:elements]
|
812
812
|
"#{type}#{literal(Array(elements)) if elements}#{UNSIGNED if column[:unsigned]}"
|
813
813
|
end
|
814
|
+
|
815
|
+
# Whether clob should be used for String :text=>true columns.
|
816
|
+
def uses_clob_for_text?
|
817
|
+
false
|
818
|
+
end
|
814
819
|
end
|
815
820
|
end
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -974,17 +974,16 @@ module Sequel
|
|
974
974
|
# :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
|
975
975
|
# :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
|
976
976
|
#
|
977
|
-
# DB[:t].
|
978
|
-
#
|
979
|
-
#
|
980
|
-
#
|
981
|
-
#
|
982
|
-
# # WITH RECURSIVE t(
|
983
|
-
# # SELECT
|
977
|
+
# DB[:t].with_recursive(:t,
|
978
|
+
# DB[:i1].select(:id, :parent_id).filter(:parent_id=>nil),
|
979
|
+
# DB[:i1].join(:t, :id=>:parent_id).select(:i1__id, :i1__parent_id),
|
980
|
+
# :args=>[:id, :parent_id])
|
981
|
+
#
|
982
|
+
# # WITH RECURSIVE "t"("id", "parent_id") AS (
|
983
|
+
# # SELECT "id", "parent_id" FROM "i1" WHERE ("parent_id" IS NULL)
|
984
984
|
# # UNION ALL
|
985
|
-
# # SELECT i1.id, i1.parent_id FROM
|
986
|
-
# # )
|
987
|
-
# # SELECT i AS id, pi AS parent_id FROM t
|
985
|
+
# # SELECT "i1"."id", "i1"."parent_id" FROM "i1" INNER JOIN "t" ON ("t"."id" = "i1"."parent_id")
|
986
|
+
# # ) SELECT * FROM "t"
|
988
987
|
def with_recursive(name, nonrecursive, recursive, opts={})
|
989
988
|
raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
|
990
989
|
if hoist_cte?(nonrecursive)
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -306,7 +306,6 @@ module Sequel
|
|
306
306
|
window_function_sql
|
307
307
|
END
|
308
308
|
PRIVATE_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
|
309
|
-
argument_list
|
310
309
|
as_sql
|
311
310
|
column_list
|
312
311
|
compound_dataset_sql
|
@@ -783,16 +782,6 @@ module Sequel
|
|
783
782
|
options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
|
784
783
|
end
|
785
784
|
|
786
|
-
def argument_list_append(sql, args)
|
787
|
-
c = false
|
788
|
-
comma = COMMA
|
789
|
-
args.each do |a|
|
790
|
-
sql << comma if c
|
791
|
-
sql << a.to_s
|
792
|
-
c ||= true
|
793
|
-
end
|
794
|
-
end
|
795
|
-
|
796
785
|
# SQL fragment for specifying an alias. expression should already be literalized.
|
797
786
|
def as_sql_append(sql, aliaz)
|
798
787
|
sql << AS
|
@@ -940,6 +929,17 @@ module Sequel
|
|
940
929
|
end
|
941
930
|
alias table_ref_append identifier_append
|
942
931
|
|
932
|
+
# Append all identifiers in args interspersed by commas.
|
933
|
+
def identifier_list_append(sql, args)
|
934
|
+
c = false
|
935
|
+
comma = COMMA
|
936
|
+
args.each do |a|
|
937
|
+
sql << comma if c
|
938
|
+
identifier_append(sql, a)
|
939
|
+
c ||= true
|
940
|
+
end
|
941
|
+
end
|
942
|
+
|
943
943
|
# Modify the identifier returned from the database based on the
|
944
944
|
# identifier_output_method.
|
945
945
|
def input_identifier(v)
|
@@ -962,13 +962,7 @@ module Sequel
|
|
962
962
|
columns = opts[:columns]
|
963
963
|
if columns && !columns.empty?
|
964
964
|
sql << PAREN_SPACE_OPEN
|
965
|
-
|
966
|
-
co = COMMA
|
967
|
-
columns.each do |col|
|
968
|
-
sql << co if c
|
969
|
-
identifier_append(sql, col)
|
970
|
-
c ||= true
|
971
|
-
end
|
965
|
+
identifier_list_append(sql, columns)
|
972
966
|
sql << PAREN_CLOSE
|
973
967
|
end
|
974
968
|
end
|
@@ -1311,7 +1305,7 @@ module Sequel
|
|
1311
1305
|
quote_identifier_append(sql, w[:name])
|
1312
1306
|
if args = w[:args]
|
1313
1307
|
sql << PAREN_OPEN
|
1314
|
-
|
1308
|
+
identifier_list_append(sql, args)
|
1315
1309
|
sql << PAREN_CLOSE
|
1316
1310
|
end
|
1317
1311
|
sql << AS
|
@@ -1332,13 +1326,7 @@ module Sequel
|
|
1332
1326
|
# Converts an array of source names into into a comma separated list.
|
1333
1327
|
def source_list_append(sql, sources)
|
1334
1328
|
raise(Error, 'No source specified for query') if sources.nil? || sources == []
|
1335
|
-
|
1336
|
-
co = COMMA
|
1337
|
-
sources.each do |s|
|
1338
|
-
sql << co if c
|
1339
|
-
identifier_append(sql, s)
|
1340
|
-
c ||= true
|
1341
|
-
end
|
1329
|
+
identifier_list_append(sql, sources)
|
1342
1330
|
end
|
1343
1331
|
|
1344
1332
|
# Delegate to Sequel.split_symbol.
|
@@ -182,6 +182,20 @@ module Sequel
|
|
182
182
|
ESCAPE_REPLACE = '\\\\\1'.freeze
|
183
183
|
HSTORE_CAST = '::hstore'.freeze
|
184
184
|
|
185
|
+
if RUBY_VERSION >= '1.9'
|
186
|
+
# Undef 1.9 marshal_{dump,load} methods in the delegate class,
|
187
|
+
# so that ruby 1.9 uses the old style _dump/_load methods defined
|
188
|
+
# in the delegate class, instead of the marshal_{dump,load} methods
|
189
|
+
# in the Hash class.
|
190
|
+
undef_method :marshal_load
|
191
|
+
undef_method :marshal_dump
|
192
|
+
end
|
193
|
+
|
194
|
+
# Use custom marshal loading, since underlying hash uses a default proc.
|
195
|
+
def self._load(args)
|
196
|
+
new(Hash[Marshal.load(args)])
|
197
|
+
end
|
198
|
+
|
185
199
|
# Parse the given string into an HStore, assuming the str is in PostgreSQL
|
186
200
|
# hstore output format.
|
187
201
|
def self.parse(str)
|
@@ -209,6 +223,11 @@ module Sequel
|
|
209
223
|
class_eval("def #{m}(h, &block) super(convert_hash(h), &block) end", __FILE__, __LINE__)
|
210
224
|
end
|
211
225
|
|
226
|
+
# Use custom marshal dumping, since underlying hash uses a default proc.
|
227
|
+
def _dump(*)
|
228
|
+
Marshal.dump(to_a)
|
229
|
+
end
|
230
|
+
|
212
231
|
# Override to force the key argument to a string.
|
213
232
|
def fetch(key, *args, &block)
|
214
233
|
super(key.to_s, *args, &block)
|
@@ -321,7 +321,7 @@ module Sequel
|
|
321
321
|
# ruby objects, one for each converter.
|
322
322
|
def convert_columns(arr)
|
323
323
|
if ccs = @column_converters
|
324
|
-
arr.zip(ccs).map{|v, pr| pr ? pr.call(v) : v}
|
324
|
+
arr.zip(ccs).map{|v, pr| (v && pr) ? pr.call(v) : v}
|
325
325
|
else
|
326
326
|
arr
|
327
327
|
end
|
@@ -379,10 +379,10 @@ module Sequel
|
|
379
379
|
def bound_variable_arg(arg, conn)
|
380
380
|
case arg
|
381
381
|
when ArrayRow
|
382
|
-
"(#{arg.map{|v| bound_variable_array(v)}.join(COMMA)})"
|
382
|
+
"(#{arg.map{|v| bound_variable_array(v) if v}.join(COMMA)})"
|
383
383
|
when HashRow
|
384
384
|
arg.check_columns!
|
385
|
-
"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v)}.join(COMMA)})"
|
385
|
+
"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(COMMA)})"
|
386
386
|
else
|
387
387
|
super
|
388
388
|
end
|
@@ -528,10 +528,10 @@ module Sequel
|
|
528
528
|
def bound_variable_array(arg)
|
529
529
|
case arg
|
530
530
|
when ArrayRow
|
531
|
-
"\"(#{arg.map{|v| bound_variable_array(v)}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
|
531
|
+
"\"(#{arg.map{|v| bound_variable_array(v) if v}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
|
532
532
|
when HashRow
|
533
533
|
arg.check_columns!
|
534
|
-
"\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v)}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
|
534
|
+
"\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
|
535
535
|
else
|
536
536
|
super
|
537
537
|
end
|
@@ -21,9 +21,6 @@ module Sequel
|
|
21
21
|
# not call any callbacks. If you have any association callbacks,
|
22
22
|
# you probably should not use the setter methods.
|
23
23
|
#
|
24
|
-
# This plugin only works with singular primary keys, it does not work
|
25
|
-
# with composite primary keys.
|
26
|
-
#
|
27
24
|
# Usage:
|
28
25
|
#
|
29
26
|
# # Make all model subclass *_to_many associations have association_pks
|
@@ -52,16 +49,85 @@ module Sequel
|
|
52
49
|
# a setter that deletes from or inserts into the join table.
|
53
50
|
def def_many_to_many(opts)
|
54
51
|
super
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
52
|
+
# Grab values from the reflection so that the hash lookup only needs to be
|
53
|
+
# done once instead of inside ever method call.
|
54
|
+
lk, lpk, rk = opts.values_at(:left_key, :left_primary_key, :right_key)
|
55
|
+
|
56
|
+
# Add 2 separate implementations of the getter method optimized for the
|
57
|
+
# composite and singular left key cases, and 4 separate implementations of the setter
|
58
|
+
# method optimized for each combination of composite and singular keys for both
|
59
|
+
# the left and right keys.
|
60
|
+
if lpk.is_a?(Array)
|
61
|
+
def_association_pks_getter(opts) do
|
62
|
+
h = {}
|
63
|
+
lk.zip(lpk).each{|k, pk| h[k] = send(pk)}
|
64
|
+
_join_table_dataset(opts).filter(h).select_map(rk)
|
65
|
+
end
|
66
|
+
|
67
|
+
if rk.is_a?(Array)
|
68
|
+
def_association_pks_setter(opts) do |pks|
|
69
|
+
pks = convert_cpk_array(opts, pks)
|
70
|
+
checked_transaction do
|
71
|
+
lpkv = lpk.map{|k| send(k)}
|
72
|
+
ds = _join_table_dataset(opts).filter(lk.zip(lpkv))
|
73
|
+
ds.exclude(rk=>pks).delete
|
74
|
+
pks -= ds.select_map(rk)
|
75
|
+
h = {}
|
76
|
+
lk.zip(lpkv).each{|k, v| h[k] = v}
|
77
|
+
pks.each do |pk|
|
78
|
+
ih = h.dup
|
79
|
+
rk.zip(pk).each{|k, v| ih[k] = v}
|
80
|
+
ds.insert(ih)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
def_association_pks_setter(opts) do |pks|
|
86
|
+
pks = convert_pk_array(opts, pks)
|
87
|
+
checked_transaction do
|
88
|
+
lpkv = lpk.map{|k| send(k)}
|
89
|
+
ds = _join_table_dataset(opts).filter(lk.zip(lpkv))
|
90
|
+
ds.exclude(rk=>pks).delete
|
91
|
+
pks -= ds.select_map(rk)
|
92
|
+
h = {}
|
93
|
+
lk.zip(lpkv).each{|k, v| h[k] = v}
|
94
|
+
pks.each do |pk|
|
95
|
+
ds.insert(h.merge(rk=>pk))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
else
|
101
|
+
def_association_pks_getter(opts) do
|
102
|
+
_join_table_dataset(opts).filter(lk=>send(lpk)).select_map(rk)
|
103
|
+
end
|
104
|
+
|
105
|
+
if rk.is_a?(Array)
|
106
|
+
def_association_pks_setter(opts) do |pks|
|
107
|
+
pks = convert_cpk_array(opts, pks)
|
108
|
+
checked_transaction do
|
109
|
+
lpkv = send(lpk)
|
110
|
+
ds = _join_table_dataset(opts).filter(lk=>lpkv)
|
111
|
+
ds.exclude(rk=>pks).delete
|
112
|
+
pks -= ds.select_map(rk)
|
113
|
+
pks.each do |pk|
|
114
|
+
h = {lk=>lpkv}
|
115
|
+
rk.zip(pk).each{|k, v| h[k] = v}
|
116
|
+
ds.insert(h)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
else
|
121
|
+
def_association_pks_setter(opts) do |pks|
|
122
|
+
pks = convert_pk_array(opts, pks)
|
123
|
+
checked_transaction do
|
124
|
+
lpkv = send(lpk)
|
125
|
+
ds = _join_table_dataset(opts).filter(lk=>lpkv)
|
126
|
+
ds.exclude(rk=>pks).delete
|
127
|
+
pks -= ds.select_map(rk)
|
128
|
+
pks.each{|pk| ds.insert(lk=>lpkv, rk=>pk)}
|
129
|
+
end
|
130
|
+
end
|
65
131
|
end
|
66
132
|
end
|
67
133
|
end
|
@@ -71,17 +137,40 @@ module Sequel
|
|
71
137
|
def def_one_to_many(opts)
|
72
138
|
super
|
73
139
|
return if opts[:type] == :one_to_one
|
140
|
+
|
141
|
+
key = opts[:key]
|
142
|
+
|
74
143
|
def_association_pks_getter(opts) do
|
75
144
|
send(opts.dataset_method).select_map(opts.associated_class.primary_key)
|
76
145
|
end
|
146
|
+
|
77
147
|
def_association_pks_setter(opts) do |pks|
|
78
|
-
|
148
|
+
primary_key = opts.associated_class.primary_key
|
149
|
+
|
150
|
+
pks = if primary_key.is_a?(Array)
|
151
|
+
convert_cpk_array(opts, pks)
|
152
|
+
else
|
153
|
+
convert_pk_array(opts, pks)
|
154
|
+
end
|
155
|
+
|
156
|
+
pkh = {primary_key=>pks}
|
157
|
+
|
158
|
+
if key.is_a?(Array)
|
159
|
+
h = {}
|
160
|
+
nh = {}
|
161
|
+
key.zip(pk).each do|k, v|
|
162
|
+
h[k] = v
|
163
|
+
nh[k] = nil
|
164
|
+
end
|
165
|
+
else
|
166
|
+
h = {key=>pk}
|
167
|
+
nh = {key=>nil}
|
168
|
+
end
|
169
|
+
|
79
170
|
checked_transaction do
|
80
171
|
ds = send(opts.dataset_method)
|
81
|
-
|
82
|
-
|
83
|
-
ds.unfiltered.filter(primary_key=>pks).update(key=>pk)
|
84
|
-
ds.exclude(primary_key=>pks).update(key=>nil)
|
172
|
+
ds.unfiltered.filter(pkh).update(h)
|
173
|
+
ds.exclude(pkh).update(nh)
|
85
174
|
end
|
86
175
|
end
|
87
176
|
end
|
@@ -90,6 +179,20 @@ module Sequel
|
|
90
179
|
module InstanceMethods
|
91
180
|
private
|
92
181
|
|
182
|
+
# If any of associated class's composite primary key column types is integer,
|
183
|
+
# typecast the appropriate values to integer before using them.
|
184
|
+
def convert_cpk_array(opts, cpks)
|
185
|
+
if klass = opts.associated_class and sch = klass.db_schema and (cols = sch.values_at(*klass.primary_key)).all? and (convs = cols.map{|c| c[:type] == :integer}).any?
|
186
|
+
cpks.map do |cpk|
|
187
|
+
cpk.zip(convs).map do |pk, conv|
|
188
|
+
conv ? model.db.typecast_value(:integer, pk) : pk
|
189
|
+
end
|
190
|
+
end
|
191
|
+
else
|
192
|
+
cpks
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
93
196
|
# If the associated class's primary key column type is integer,
|
94
197
|
# typecast all provided values to integer before using them.
|
95
198
|
def convert_pk_array(opts, pks)
|