sequel 3.34.0 → 3.35.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 +56 -0
- data/README.rdoc +3 -1
- data/Rakefile +2 -10
- data/doc/active_record.rdoc +1 -0
- data/doc/migration.rdoc +18 -7
- data/doc/model_hooks.rdoc +6 -0
- data/doc/opening_databases.rdoc +3 -0
- data/doc/prepared_statements.rdoc +0 -1
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/schema_modification.rdoc +16 -1
- data/doc/thread_safety.rdoc +17 -0
- data/lib/sequel/adapters/do/postgres.rb +1 -52
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +2 -2
- data/lib/sequel/adapters/firebird.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +29 -2
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +23 -19
- data/lib/sequel/adapters/mysql2.rb +1 -1
- data/lib/sequel/adapters/odbc/mssql.rb +14 -1
- data/lib/sequel/adapters/odbc.rb +3 -3
- data/lib/sequel/adapters/oracle.rb +6 -18
- data/lib/sequel/adapters/postgres.rb +36 -53
- data/lib/sequel/adapters/shared/db2.rb +16 -2
- data/lib/sequel/adapters/shared/mssql.rb +40 -9
- data/lib/sequel/adapters/shared/mysql.rb +16 -4
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +2 -0
- data/lib/sequel/adapters/shared/postgres.rb +135 -211
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/swift/postgres.rb +1 -71
- data/lib/sequel/adapters/swift.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +3 -3
- data/lib/sequel/core.rb +27 -4
- data/lib/sequel/database/connecting.rb +7 -8
- data/lib/sequel/database/logging.rb +6 -1
- data/lib/sequel/database/misc.rb +20 -4
- data/lib/sequel/database/query.rb +38 -18
- data/lib/sequel/database/schema_generator.rb +5 -2
- data/lib/sequel/database/schema_methods.rb +34 -8
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/sql.rb +18 -24
- data/lib/sequel/extensions/core_extensions.rb +0 -23
- data/lib/sequel/extensions/migration.rb +22 -8
- data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/model/associations.rb +95 -70
- data/lib/sequel/model/base.rb +19 -20
- data/lib/sequel/model.rb +2 -2
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +16 -1
- data/lib/sequel/plugins/many_through_many.rb +22 -32
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
- data/lib/sequel/plugins/prepared_statements.rb +22 -8
- data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +12 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +30 -79
- data/spec/core/database_spec.rb +46 -2
- data/spec/core/dataset_spec.rb +28 -22
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +51 -0
- data/spec/extensions/arbitrary_servers_spec.rb +0 -4
- data/spec/extensions/association_autoreloading_spec.rb +17 -0
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/core_extensions_spec.rb +1 -24
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/json_serializer_spec.rb +13 -0
- data/spec/extensions/migration_spec.rb +28 -15
- data/spec/extensions/named_timezones_spec.rb +6 -8
- data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
- data/spec/extensions/schema_dumper_spec.rb +3 -1
- data/spec/extensions/single_table_inheritance_spec.rb +5 -0
- data/spec/extensions/xml_serializer_spec.rb +13 -0
- data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
- data/spec/integration/associations_test.rb +5 -7
- data/spec/integration/dataset_test.rb +25 -7
- data/spec/integration/plugin_test.rb +1 -1
- data/spec/integration/schema_test.rb +16 -1
- data/spec/model/associations_spec.rb +2 -2
- metadata +14 -9
- data/lib/sequel/adapters/odbc/db2.rb +0 -17
- /data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
- /data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
- /data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
|
@@ -21,20 +21,6 @@ class Array
|
|
|
21
21
|
Sequel.~(self)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# +true+ if the array is not empty and all of its elements are
|
|
25
|
-
# arrays of size 2, +false+ otherwise. This is used to determine if the array
|
|
26
|
-
# could be a specifier of conditions, used similarly to a hash
|
|
27
|
-
# but allowing for duplicate keys and a specific order.
|
|
28
|
-
#
|
|
29
|
-
# [].to_a.all_two_pairs? # => false
|
|
30
|
-
# [:a].to_a.all_two_pairs? # => false
|
|
31
|
-
# [[:b]].to_a.all_two_pairs? # => false
|
|
32
|
-
# [[:a, 1]].to_a.all_two_pairs? # => true
|
|
33
|
-
def all_two_pairs?
|
|
34
|
-
warn('Array#all_two_pairs? is deprecated and will be removed in Sequel 3.35.0')
|
|
35
|
-
!empty? && all?{|i| (Array === i) && (i.length == 2)}
|
|
36
|
-
end
|
|
37
|
-
|
|
38
24
|
# Return a <tt>Sequel::SQL::CaseExpression</tt> with this array as the conditions and the given
|
|
39
25
|
# default value and expression.
|
|
40
26
|
#
|
|
@@ -103,15 +89,6 @@ class Array
|
|
|
103
89
|
def sql_string_join(joiner=nil)
|
|
104
90
|
Sequel.join(self, joiner)
|
|
105
91
|
end
|
|
106
|
-
|
|
107
|
-
private
|
|
108
|
-
|
|
109
|
-
# Raise an error if this array is not made up all two element arrays, otherwise create a <tt>Sequel::SQL::BooleanExpression</tt> from this array.
|
|
110
|
-
def sql_expr_if_all_two_pairs(*args)
|
|
111
|
-
warn('Array#sql_expr_if_all_two_pairs? is deprecated and will be removed in Sequel 3.35.0')
|
|
112
|
-
raise(Sequel::Error, 'Not all elements of the array are arrays of size 2, so it cannot be converted to an SQL expression') unless all_two_pairs?
|
|
113
|
-
::Sequel::SQL::BooleanExpression.from_value_pairs(self, *args)
|
|
114
|
-
end
|
|
115
92
|
end
|
|
116
93
|
|
|
117
94
|
# Sequel extends +Hash+ to add methods to implement the SQL DSL.
|
|
@@ -44,9 +44,9 @@ module Sequel
|
|
|
44
44
|
descendants << base
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
#
|
|
47
|
+
# Don't allow transaction overriding in old migrations.
|
|
48
48
|
def self.use_transactions
|
|
49
|
-
|
|
49
|
+
nil
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# The default down action does nothing
|
|
@@ -74,12 +74,13 @@ module Sequel
|
|
|
74
74
|
# Proc used for the up action
|
|
75
75
|
attr_accessor :up
|
|
76
76
|
|
|
77
|
-
# Whether to use transactions for this migration,
|
|
77
|
+
# Whether to use transactions for this migration, default depends on the
|
|
78
|
+
# database.
|
|
78
79
|
attr_accessor :use_transactions
|
|
79
80
|
|
|
80
|
-
#
|
|
81
|
+
# Don't set transaction use by default.
|
|
81
82
|
def initialize
|
|
82
|
-
@use_transactions =
|
|
83
|
+
@use_transactions = nil
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
# Apply the appropriate block on the +Database+
|
|
@@ -118,6 +119,11 @@ module Sequel
|
|
|
118
119
|
migration.use_transactions = false
|
|
119
120
|
end
|
|
120
121
|
|
|
122
|
+
# Enable the use of transactions for the related migration
|
|
123
|
+
def transaction
|
|
124
|
+
migration.use_transactions = true
|
|
125
|
+
end
|
|
126
|
+
|
|
121
127
|
# Defines the migration's up action.
|
|
122
128
|
def up(&block)
|
|
123
129
|
migration.up = block
|
|
@@ -414,9 +420,17 @@ module Sequel
|
|
|
414
420
|
# If transactions should be used for the migration, yield to the block
|
|
415
421
|
# inside a transaction. Otherwise, just yield to the block.
|
|
416
422
|
def checked_transaction(migration, &block)
|
|
417
|
-
if @use_transactions
|
|
418
|
-
|
|
419
|
-
|
|
423
|
+
use_trans = if @use_transactions.nil?
|
|
424
|
+
if migration.use_transactions.nil?
|
|
425
|
+
@db.supports_transactional_ddl?
|
|
426
|
+
else
|
|
427
|
+
migration.use_transactions
|
|
428
|
+
end
|
|
429
|
+
else
|
|
430
|
+
@use_transactions
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
if use_trans
|
|
420
434
|
db.transaction(&block)
|
|
421
435
|
else
|
|
422
436
|
yield
|
|
@@ -175,7 +175,7 @@ END_MIG
|
|
|
175
175
|
{:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
|
|
176
176
|
when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/o
|
|
177
177
|
{:type=>File, :size=>($1.to_i if $1)}
|
|
178
|
-
when
|
|
178
|
+
when /\A(?:year|(?:int )?identity)\z/o
|
|
179
179
|
{:type=>Integer}
|
|
180
180
|
else
|
|
181
181
|
{:type=>String}
|
|
@@ -54,7 +54,7 @@ module Sequel
|
|
|
54
54
|
|
|
55
55
|
# The class associated to the current model class via this association
|
|
56
56
|
def associated_class
|
|
57
|
-
|
|
57
|
+
cached_fetch(:class){constantize(self[:class_name])}
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# Whether this association can have associated objects, given the current
|
|
@@ -81,9 +81,9 @@ module Sequel
|
|
|
81
81
|
|
|
82
82
|
# The eager limit strategy to use for this dataset.
|
|
83
83
|
def eager_limit_strategy
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
case s =
|
|
84
|
+
cached_fetch(:_eager_limit_strategy) do
|
|
85
|
+
if self[:limit]
|
|
86
|
+
case s = cached_fetch(:eager_limit_strategy){self[:model].default_eager_limit_strategy || :ruby}
|
|
87
87
|
when true
|
|
88
88
|
ds = associated_class.dataset
|
|
89
89
|
if ds.supports_window_functions?
|
|
@@ -151,16 +151,19 @@ module Sequel
|
|
|
151
151
|
# to populate reciprocal associations. For example, when you do this_artist.add_album(album)
|
|
152
152
|
# it sets album.artist to this_artist.
|
|
153
153
|
def reciprocal
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
154
|
+
cached_fetch(:reciprocal) do
|
|
155
|
+
r_types = Array(reciprocal_type)
|
|
156
|
+
keys = self[:keys]
|
|
157
|
+
recip = nil
|
|
158
|
+
associated_class.all_association_reflections.each do |assoc_reflect|
|
|
159
|
+
if r_types.include?(assoc_reflect[:type]) && assoc_reflect[:keys] == keys && assoc_reflect.associated_class == self[:model]
|
|
160
|
+
cached_set(:reciprocal_type, assoc_reflect[:type])
|
|
161
|
+
recip = assoc_reflect[:name]
|
|
162
|
+
break
|
|
163
|
+
end
|
|
161
164
|
end
|
|
165
|
+
recip
|
|
162
166
|
end
|
|
163
|
-
self[:reciprocal] = nil
|
|
164
167
|
end
|
|
165
168
|
|
|
166
169
|
# Whether the reciprocal of this association returns an array of objects instead of a single object,
|
|
@@ -214,6 +217,39 @@ module Sequel
|
|
|
214
217
|
|
|
215
218
|
private
|
|
216
219
|
|
|
220
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE != 'ruby'
|
|
221
|
+
# On non-GVL rubies, assume the need to synchronize access. Store the key
|
|
222
|
+
# in a special sub-hash that always uses this method to synchronize access.
|
|
223
|
+
def cached_fetch(key)
|
|
224
|
+
fetch(key) do
|
|
225
|
+
h = self[:cache]
|
|
226
|
+
Sequel.synchronize{return h[key] if h.has_key?(key)}
|
|
227
|
+
value = yield
|
|
228
|
+
Sequel.synchronize{h[key] = value}
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Cache the value at the given key, synchronizing access.
|
|
233
|
+
def cached_set(key, value)
|
|
234
|
+
h = self[:cache]
|
|
235
|
+
Sequel.synchronize{h[key] = value}
|
|
236
|
+
end
|
|
237
|
+
else
|
|
238
|
+
# On MRI, use a plain fetch, since the GVL will synchronize access.
|
|
239
|
+
def cached_fetch(key)
|
|
240
|
+
fetch(key) do
|
|
241
|
+
h = self[:cache]
|
|
242
|
+
h.fetch(key){h[key] = yield}
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# On MRI, just set the value at the key in the cache, since the GVL
|
|
247
|
+
# will synchronize access.
|
|
248
|
+
def cached_set(key, value)
|
|
249
|
+
self[:cache][key] = value
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
217
253
|
# If +s+ is an array, map +s+ over the block. Otherwise, just call the
|
|
218
254
|
# block with +s+.
|
|
219
255
|
def transform(s)
|
|
@@ -254,35 +290,35 @@ module Sequel
|
|
|
254
290
|
|
|
255
291
|
# The key to use for the key hash when eager loading
|
|
256
292
|
def eager_loader_key
|
|
257
|
-
|
|
293
|
+
cached_fetch(:eager_loader_key){self[:key]}
|
|
258
294
|
end
|
|
259
295
|
|
|
260
296
|
# The column(s) in the associated table that the key in the current table references (either a symbol or an array).
|
|
261
297
|
def primary_key
|
|
262
|
-
|
|
298
|
+
cached_fetch(:primary_key){associated_class.primary_key}
|
|
263
299
|
end
|
|
264
300
|
|
|
265
301
|
# The columns in the associated table that the key in the current table references (always an array).
|
|
266
302
|
def primary_keys
|
|
267
|
-
|
|
303
|
+
cached_fetch(:primary_keys){Array(primary_key)}
|
|
268
304
|
end
|
|
269
305
|
alias associated_object_keys primary_keys
|
|
270
306
|
|
|
271
307
|
# The method symbol or array of method symbols to call on the associated object
|
|
272
308
|
# to get the value to use for the foreign keys.
|
|
273
309
|
def primary_key_method
|
|
274
|
-
|
|
310
|
+
cached_fetch(:primary_key_method){primary_key}
|
|
275
311
|
end
|
|
276
312
|
|
|
277
313
|
# The array of method symbols to call on the associated object
|
|
278
314
|
# to get the value to use for the foreign keys.
|
|
279
315
|
def primary_key_methods
|
|
280
|
-
|
|
316
|
+
cached_fetch(:primary_key_methods){Array(primary_key_method)}
|
|
281
317
|
end
|
|
282
318
|
|
|
283
319
|
# #primary_key qualified by the associated table
|
|
284
320
|
def qualified_primary_key
|
|
285
|
-
|
|
321
|
+
cached_fetch(:qualified_primary_key){self[:qualify] == false ? primary_key : qualify_assoc(primary_key)}
|
|
286
322
|
end
|
|
287
323
|
|
|
288
324
|
# True only if the reciprocal is a one_to_many association.
|
|
@@ -299,7 +335,7 @@ module Sequel
|
|
|
299
335
|
# True only if the reciprocal is a one_to_one association.
|
|
300
336
|
def set_reciprocal_to_self?
|
|
301
337
|
reciprocal
|
|
302
|
-
|
|
338
|
+
reciprocal_type == :one_to_one
|
|
303
339
|
end
|
|
304
340
|
|
|
305
341
|
private
|
|
@@ -307,7 +343,7 @@ module Sequel
|
|
|
307
343
|
# The reciprocal type of a many_to_one association is either
|
|
308
344
|
# a one_to_many or a one_to_one association.
|
|
309
345
|
def reciprocal_type
|
|
310
|
-
|
|
346
|
+
cached_fetch(:reciprocal_type){[:one_to_many, :one_to_one]}
|
|
311
347
|
end
|
|
312
348
|
end
|
|
313
349
|
|
|
@@ -333,12 +369,12 @@ module Sequel
|
|
|
333
369
|
|
|
334
370
|
# The key to use for the key hash when eager loading
|
|
335
371
|
def eager_loader_key
|
|
336
|
-
|
|
372
|
+
cached_fetch(:eager_loader_key){primary_key}
|
|
337
373
|
end
|
|
338
374
|
|
|
339
375
|
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3))
|
|
340
376
|
def eager_loading_predicate_key
|
|
341
|
-
|
|
377
|
+
cached_fetch(:eager_loading_predicate_key){qualify_assoc(self[:key])}
|
|
342
378
|
end
|
|
343
379
|
alias qualified_key eager_loading_predicate_key
|
|
344
380
|
|
|
@@ -349,7 +385,7 @@ module Sequel
|
|
|
349
385
|
|
|
350
386
|
# #primary_key qualified by the current table
|
|
351
387
|
def qualified_primary_key
|
|
352
|
-
|
|
388
|
+
cached_fetch(:qualified_primary_key){qualify_cur(primary_key)}
|
|
353
389
|
end
|
|
354
390
|
|
|
355
391
|
# Whether the reciprocal of this association returns an array of objects instead of a single object,
|
|
@@ -387,8 +423,8 @@ module Sequel
|
|
|
387
423
|
# one_to_one associations don't use an eager limit strategy by default, but
|
|
388
424
|
# support both DISTINCT ON and window functions as strategies.
|
|
389
425
|
def eager_limit_strategy
|
|
390
|
-
|
|
391
|
-
|
|
426
|
+
cached_fetch(:_eager_limit_strategy) do
|
|
427
|
+
case s = self[:eager_limit_strategy]
|
|
392
428
|
when Symbol
|
|
393
429
|
s
|
|
394
430
|
when true
|
|
@@ -464,19 +500,19 @@ module Sequel
|
|
|
464
500
|
|
|
465
501
|
# The key to use for the key hash when eager loading
|
|
466
502
|
def eager_loader_key
|
|
467
|
-
|
|
503
|
+
cached_fetch(:eager_loader_key){self[:left_primary_key]}
|
|
468
504
|
end
|
|
469
505
|
|
|
470
506
|
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3)).
|
|
471
507
|
# The left key qualified by the join table.
|
|
472
508
|
def eager_loading_predicate_key
|
|
473
|
-
|
|
509
|
+
cached_fetch(:eager_loading_predicate_key){qualify(join_table_alias, self[:left_key])}
|
|
474
510
|
end
|
|
475
511
|
alias qualified_left_key eager_loading_predicate_key
|
|
476
512
|
|
|
477
513
|
# The right key qualified by the join table.
|
|
478
514
|
def qualified_right_key
|
|
479
|
-
|
|
515
|
+
cached_fetch(:qualified_right_key){qualify(join_table_alias, self[:right_key])}
|
|
480
516
|
end
|
|
481
517
|
|
|
482
518
|
# many_to_many associations need to select a key in an associated table to eagerly load
|
|
@@ -487,18 +523,15 @@ module Sequel
|
|
|
487
523
|
# The source of the join table. This is the join table itself, unless it
|
|
488
524
|
# is aliased, in which case it is the unaliased part.
|
|
489
525
|
def join_table_source
|
|
490
|
-
|
|
491
|
-
split_join_table_alias
|
|
492
|
-
self[:join_table_source]
|
|
493
|
-
end
|
|
526
|
+
cached_fetch(:join_table_source){split_join_table_alias[0]}
|
|
494
527
|
end
|
|
495
528
|
|
|
496
529
|
# The join table itself, unless it is aliased, in which case this
|
|
497
530
|
# is the alias.
|
|
498
531
|
def join_table_alias
|
|
499
|
-
|
|
500
|
-
split_join_table_alias
|
|
501
|
-
|
|
532
|
+
cached_fetch(:join_table_alias) do
|
|
533
|
+
s, a = split_join_table_alias
|
|
534
|
+
a || s
|
|
502
535
|
end
|
|
503
536
|
end
|
|
504
537
|
alias associated_key_table join_table_alias
|
|
@@ -511,60 +544,60 @@ module Sequel
|
|
|
511
544
|
|
|
512
545
|
# Returns the reciprocal association symbol, if one exists.
|
|
513
546
|
def reciprocal
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
547
|
+
cached_fetch(:reciprocal) do
|
|
548
|
+
left_keys = self[:left_keys]
|
|
549
|
+
right_keys = self[:right_keys]
|
|
550
|
+
join_table = self[:join_table]
|
|
551
|
+
recip = nil
|
|
552
|
+
associated_class.all_association_reflections.each do |assoc_reflect|
|
|
553
|
+
if assoc_reflect[:type] == :many_to_many && assoc_reflect[:left_keys] == right_keys &&
|
|
554
|
+
assoc_reflect[:right_keys] == left_keys && assoc_reflect[:join_table] == join_table &&
|
|
555
|
+
assoc_reflect.associated_class == self[:model]
|
|
556
|
+
recip = assoc_reflect[:name]
|
|
557
|
+
break
|
|
558
|
+
end
|
|
523
559
|
end
|
|
560
|
+
recip
|
|
524
561
|
end
|
|
525
|
-
self[:reciprocal] = nil
|
|
526
562
|
end
|
|
527
563
|
|
|
528
564
|
# #right_primary_key qualified by the associated table
|
|
529
565
|
def qualified_right_primary_key
|
|
530
|
-
|
|
566
|
+
cached_fetch(:qualified_right_primary_key){qualify_assoc(right_primary_key)}
|
|
531
567
|
end
|
|
532
568
|
|
|
533
569
|
# The primary key column(s) to use in the associated table (can be symbol or array).
|
|
534
570
|
def right_primary_key
|
|
535
|
-
|
|
571
|
+
cached_fetch(:right_primary_key){associated_class.primary_key}
|
|
536
572
|
end
|
|
537
573
|
|
|
538
574
|
# The primary key columns to use in the associated table (always array).
|
|
539
575
|
def right_primary_keys
|
|
540
|
-
|
|
576
|
+
cached_fetch(:right_primary_keys){Array(right_primary_key)}
|
|
541
577
|
end
|
|
542
578
|
|
|
543
579
|
# The method symbol or array of method symbols to call on the associated objects
|
|
544
580
|
# to get the foreign key values for the join table.
|
|
545
581
|
def right_primary_key_method
|
|
546
|
-
|
|
582
|
+
cached_fetch(:right_primary_key_method){right_primary_key}
|
|
547
583
|
end
|
|
548
584
|
|
|
549
585
|
# The array of method symbols to call on the associated objects
|
|
550
586
|
# to get the foreign key values for the join table.
|
|
551
587
|
def right_primary_key_methods
|
|
552
|
-
|
|
588
|
+
cached_fetch(:right_primary_key_methods){Array(right_primary_key_method)}
|
|
553
589
|
end
|
|
554
590
|
|
|
555
591
|
# The columns to select when loading the association, associated_class.table_name.* by default.
|
|
556
592
|
def select
|
|
557
|
-
|
|
558
|
-
self[:select] ||= Sequel::SQL::ColumnAll.new(associated_class.table_name)
|
|
593
|
+
cached_fetch(:select){Sequel::SQL::ColumnAll.new(associated_class.table_name)}
|
|
559
594
|
end
|
|
560
595
|
|
|
561
596
|
private
|
|
562
597
|
|
|
563
598
|
# Split the join table into source and alias parts.
|
|
564
599
|
def split_join_table_alias
|
|
565
|
-
|
|
566
|
-
self[:join_table_source] = s
|
|
567
|
-
self[:join_table_alias] = a || s
|
|
600
|
+
associated_class.dataset.split_alias(self[:join_table])
|
|
568
601
|
end
|
|
569
602
|
end
|
|
570
603
|
|
|
@@ -862,7 +895,7 @@ module Sequel
|
|
|
862
895
|
# dup early so we don't modify opts
|
|
863
896
|
orig_opts = opts.dup
|
|
864
897
|
orig_opts = association_reflection(opts[:clone])[:orig_opts].merge(orig_opts) if opts[:clone]
|
|
865
|
-
opts = orig_opts.merge(:type => type, :name => name, :cache
|
|
898
|
+
opts = orig_opts.merge(:type => type, :name => name, :cache=>{}, :model => self)
|
|
866
899
|
opts[:block] = block if block
|
|
867
900
|
opts = assoc_class.new.merge!(opts)
|
|
868
901
|
opts[:eager_block] = block unless opts.include?(:eager_block)
|
|
@@ -878,6 +911,9 @@ module Sequel
|
|
|
878
911
|
end
|
|
879
912
|
late_binding_class_option(opts, opts.returns_array? ? singularize(name) : name)
|
|
880
913
|
|
|
914
|
+
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
|
915
|
+
opts.delete(:class) unless opts[:class]
|
|
916
|
+
|
|
881
917
|
send(:"def_#{type}", opts)
|
|
882
918
|
|
|
883
919
|
orig_opts.delete(:clone)
|
|
@@ -1047,20 +1083,9 @@ module Sequel
|
|
|
1047
1083
|
def_association_method(opts)
|
|
1048
1084
|
end
|
|
1049
1085
|
|
|
1050
|
-
# Adds the association method to the association methods module.
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
class_eval <<-END, __FILE__, __LINE__+1
|
|
1054
|
-
def def_association_method(opts)
|
|
1055
|
-
association_module_def(opts.association_method, opts){|*dynamic_opts, &block| load_associated_objects(opts, dynamic_opts[0], &block)}
|
|
1056
|
-
end
|
|
1057
|
-
END
|
|
1058
|
-
else
|
|
1059
|
-
class_eval <<-END, __FILE__, __LINE__+1
|
|
1060
|
-
def def_association_method(opts)
|
|
1061
|
-
association_module_def(opts.association_method, opts){|*dynamic_opts| load_associated_objects(opts, dynamic_opts[0])}
|
|
1062
|
-
end
|
|
1063
|
-
END
|
|
1086
|
+
# Adds the association method to the association methods module.
|
|
1087
|
+
def def_association_method(opts)
|
|
1088
|
+
association_module_def(opts.association_method, opts){|*dynamic_opts, &block| load_associated_objects(opts, dynamic_opts[0], &block)}
|
|
1064
1089
|
end
|
|
1065
1090
|
|
|
1066
1091
|
# Configures many_to_many association reflection and adds the related association methods
|
data/lib/sequel/model/base.rb
CHANGED
|
@@ -220,7 +220,7 @@ module Sequel
|
|
|
220
220
|
# end # COMMIT
|
|
221
221
|
def db
|
|
222
222
|
return @db if @db
|
|
223
|
-
@db = self == Model ? DATABASES.first : superclass.db
|
|
223
|
+
@db = self == Model ? Sequel.synchronize{DATABASES.first} : superclass.db
|
|
224
224
|
raise(Error, "No database associated with #{self}: have you called Sequel.connect or #{self}.db= ?") unless @db
|
|
225
225
|
@db
|
|
226
226
|
end
|
|
@@ -295,21 +295,6 @@ module Sequel
|
|
|
295
295
|
end
|
|
296
296
|
end
|
|
297
297
|
|
|
298
|
-
module_eval(if RUBY_VERSION < '1.8.7'
|
|
299
|
-
<<-END
|
|
300
|
-
def def_model_dataset_method_block(arg)
|
|
301
|
-
meta_def(arg){|*args| dataset.send(arg, *args)}
|
|
302
|
-
end
|
|
303
|
-
END
|
|
304
|
-
else
|
|
305
|
-
<<-END
|
|
306
|
-
def def_model_dataset_method_block(arg)
|
|
307
|
-
meta_def(arg){|*args, &block| dataset.send(arg, *args, &block)}
|
|
308
|
-
end
|
|
309
|
-
END
|
|
310
|
-
end, __FILE__, __LINE__ - 4)
|
|
311
|
-
private :def_model_dataset_method_block
|
|
312
|
-
|
|
313
298
|
# Finds a single record according to the supplied filter.
|
|
314
299
|
# You are encouraged to use Model.[] or Model.first instead of this method.
|
|
315
300
|
#
|
|
@@ -685,6 +670,13 @@ module Sequel
|
|
|
685
670
|
end
|
|
686
671
|
end
|
|
687
672
|
|
|
673
|
+
# Define a model method that calls the dataset method with the same name,
|
|
674
|
+
# only used for methods with names that can't be presented directly in
|
|
675
|
+
# ruby code.
|
|
676
|
+
def def_model_dataset_method_block(arg)
|
|
677
|
+
meta_def(arg){|*args, &block| dataset.send(arg, *args, &block)}
|
|
678
|
+
end
|
|
679
|
+
|
|
688
680
|
# Get the schema from the database, fall back on checking the columns
|
|
689
681
|
# via the database if that will return inaccurate results or if
|
|
690
682
|
# it raises an error.
|
|
@@ -790,8 +782,9 @@ module Sequel
|
|
|
790
782
|
def primary_key_lookup(pk)
|
|
791
783
|
if sql = @fast_pk_lookup_sql
|
|
792
784
|
sql = sql.dup
|
|
793
|
-
dataset
|
|
794
|
-
|
|
785
|
+
ds = dataset
|
|
786
|
+
ds.literal_append(sql, pk)
|
|
787
|
+
ds.fetch_rows(sql){|r| return ds.row_proc.call(r)}
|
|
795
788
|
nil
|
|
796
789
|
else
|
|
797
790
|
dataset[primary_key_hash(pk)]
|
|
@@ -934,8 +927,7 @@ module Sequel
|
|
|
934
927
|
v = typecast_value(column, value)
|
|
935
928
|
vals = @values
|
|
936
929
|
if new? || !vals.include?(column) || v != (c = vals[column]) || v.class != c.class
|
|
937
|
-
|
|
938
|
-
vals[column] = v
|
|
930
|
+
change_column_value(column, v)
|
|
939
931
|
end
|
|
940
932
|
end
|
|
941
933
|
|
|
@@ -1709,6 +1701,13 @@ module Sequel
|
|
|
1709
1701
|
use_transaction?(opts) ? db.transaction({:server=>this_server}.merge(opts)){yield} : yield
|
|
1710
1702
|
end
|
|
1711
1703
|
|
|
1704
|
+
# Change the value of the column to given value, recording the change.
|
|
1705
|
+
def change_column_value(column, value)
|
|
1706
|
+
cc = changed_columns
|
|
1707
|
+
cc << column unless cc.include?(column)
|
|
1708
|
+
@values[column] = value
|
|
1709
|
+
end
|
|
1710
|
+
|
|
1712
1711
|
# Set the columns with the given hash. By default, the same as +set+, but
|
|
1713
1712
|
# exists so it can be overridden. This is called only for new records, before
|
|
1714
1713
|
# changed_columns is cleared.
|
data/lib/sequel/model.rb
CHANGED
|
@@ -35,7 +35,7 @@ module Sequel
|
|
|
35
35
|
# dataset # => DB1[:comments]
|
|
36
36
|
# end
|
|
37
37
|
def self.Model(source)
|
|
38
|
-
if Sequel::Model.cache_anonymous_models && (klass = Model::ANONYMOUS_MODEL_CLASSES[source])
|
|
38
|
+
if Sequel::Model.cache_anonymous_models && (klass = Sequel.synchronize{Model::ANONYMOUS_MODEL_CLASSES[source]})
|
|
39
39
|
return klass
|
|
40
40
|
end
|
|
41
41
|
klass = if source.is_a?(Database)
|
|
@@ -45,7 +45,7 @@ module Sequel
|
|
|
45
45
|
else
|
|
46
46
|
Class.new(Model).set_dataset(source)
|
|
47
47
|
end
|
|
48
|
-
Model::ANONYMOUS_MODEL_CLASSES[source] = klass if Sequel::Model.cache_anonymous_models
|
|
48
|
+
Sequel.synchronize{Model::ANONYMOUS_MODEL_CLASSES[source] = klass} if Sequel::Model.cache_anonymous_models
|
|
49
49
|
klass
|
|
50
50
|
end
|
|
51
51
|
|