sequel 3.28.0 → 3.29.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 +119 -3
- data/Rakefile +5 -3
- data/bin/sequel +1 -5
- data/doc/model_hooks.rdoc +9 -1
- data/doc/opening_databases.rdoc +49 -40
- data/doc/prepared_statements.rdoc +27 -6
- data/doc/release_notes/3.28.0.txt +2 -2
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +18 -9
- data/doc/transactions.rdoc +41 -1
- data/lib/sequel/adapters/ado.rb +28 -17
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/amalgalite.rb +11 -7
- data/lib/sequel/adapters/db2.rb +122 -70
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do.rb +5 -36
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +3 -6
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/informix.rb +2 -4
- data/lib/sequel/adapters/jdbc.rb +47 -11
- data/lib/sequel/adapters/jdbc/as400.rb +5 -24
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +10 -12
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -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 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
- 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 +56 -7
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +64 -51
- data/lib/sequel/adapters/mysql2.rb +15 -9
- data/lib/sequel/adapters/odbc.rb +13 -6
- data/lib/sequel/adapters/odbc/db2.rb +0 -4
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +333 -51
- data/lib/sequel/adapters/postgres.rb +80 -27
- data/lib/sequel/adapters/shared/access.rb +0 -6
- data/lib/sequel/adapters/shared/db2.rb +13 -15
- data/lib/sequel/adapters/shared/firebird.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +23 -18
- data/lib/sequel/adapters/shared/mysql.rb +6 -6
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -30
- data/lib/sequel/adapters/shared/postgres.rb +35 -18
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +116 -37
- data/lib/sequel/adapters/sqlite.rb +16 -8
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/tinytds.rb +13 -10
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
- data/lib/sequel/core.rb +40 -0
- data/lib/sequel/database/connecting.rb +1 -2
- data/lib/sequel/database/dataset.rb +3 -3
- data/lib/sequel/database/dataset_defaults.rb +58 -0
- data/lib/sequel/database/misc.rb +62 -2
- data/lib/sequel/database/query.rb +113 -49
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/actions.rb +37 -19
- data/lib/sequel/dataset/features.rb +24 -0
- data/lib/sequel/dataset/graph.rb +7 -6
- data/lib/sequel/dataset/misc.rb +11 -3
- data/lib/sequel/dataset/mutation.rb +2 -3
- data/lib/sequel/dataset/prepared_statements.rb +6 -4
- data/lib/sequel/dataset/query.rb +46 -15
- data/lib/sequel/dataset/sql.rb +28 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +115 -33
- data/lib/sequel/model/base.rb +91 -31
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +6 -10
- data/lib/sequel/plugins/prepared_statements.rb +12 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/sharding.rb +0 -5
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/timezones.rb +42 -42
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +29 -29
- data/spec/adapters/mysql_spec.rb +86 -104
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +98 -33
- data/spec/adapters/spec_helper.rb +0 -5
- data/spec/adapters/sqlite_spec.rb +24 -21
- data/spec/core/connection_pool_spec.rb +9 -15
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +491 -227
- data/spec/core/dataset_spec.rb +638 -1051
- data/spec/core/expression_filters_spec.rb +0 -1
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +51 -114
- data/spec/core/spec_helper.rb +3 -90
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +3 -0
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/spec_helper.rb +4 -0
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/integration/associations_test.rb +123 -12
- data/spec/integration/dataset_test.rb +140 -47
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +179 -128
- data/spec/integration/prepared_statement_test.rb +92 -91
- data/spec/integration/schema_test.rb +42 -23
- data/spec/integration/spec_helper.rb +25 -31
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +161 -34
- data/spec/integration/type_test.rb +3 -3
- data/spec/model/association_reflection_spec.rb +83 -7
- data/spec/model/associations_spec.rb +393 -676
- data/spec/model/base_spec.rb +186 -116
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +343 -867
- data/spec/model/hooks_spec.rb +160 -79
- data/spec/model/model_spec.rb +118 -165
- data/spec/model/plugins_spec.rb +7 -13
- data/spec/model/record_spec.rb +138 -207
- data/spec/model/spec_helper.rb +10 -73
- metadata +14 -8
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
# Then, before data is stored in the database, it is converted to New
|
|
22
22
|
# York time. When data is retrieved from the database, it is
|
|
23
23
|
# converted to Los Angeles time.
|
|
24
|
+
#
|
|
25
|
+
# Note that typecasting from the database timezone to the application
|
|
26
|
+
# timezone when fetching rows is dependent on the database adapter,
|
|
27
|
+
# and only works on adapters where Sequel itself does the conversion.
|
|
28
|
+
# It should work on mysql, postgres, sqlite, ibmdb, and jdbc.
|
|
24
29
|
|
|
25
30
|
require 'tzinfo'
|
|
26
31
|
|
|
@@ -32,7 +32,7 @@ module Sequel
|
|
|
32
32
|
module ThreadLocalTimezones
|
|
33
33
|
%w'application database typecast'.each do |t|
|
|
34
34
|
class_eval("def thread_#{t}_timezone=(tz); Thread.current[:#{t}_timezone] = convert_timezone_setter_arg(tz); end", __FILE__, __LINE__)
|
|
35
|
-
class_eval(<<END, __FILE__, __LINE__)
|
|
35
|
+
class_eval(<<END, __FILE__, __LINE__ + 1)
|
|
36
36
|
def #{t}_timezone
|
|
37
37
|
if tz = Thread.current[:#{t}_timezone]
|
|
38
38
|
tz unless tz == :nil
|
data/lib/sequel/model.rb
CHANGED
|
@@ -79,7 +79,8 @@ module Sequel
|
|
|
79
79
|
|
|
80
80
|
# Hooks that are called after an action. When overriding these, it is recommended to call
|
|
81
81
|
# +super+ on the first line of your method, so later hooks are called after earlier hooks.
|
|
82
|
-
AFTER_HOOKS = [:after_initialize, :after_create, :after_update, :after_save, :after_destroy,
|
|
82
|
+
AFTER_HOOKS = [:after_initialize, :after_create, :after_update, :after_save, :after_destroy,
|
|
83
|
+
:after_validation, :after_commit, :after_rollback, :after_destroy_commit, :after_destroy_rollback]
|
|
83
84
|
|
|
84
85
|
# Hooks that are called around an action. If overridden, these methods must call super
|
|
85
86
|
# exactly once if the behavior they wrap is desired. The can be used to rescue exceptions
|
|
@@ -127,6 +127,22 @@ module Sequel
|
|
|
127
127
|
def need_associated_primary_key?
|
|
128
128
|
false
|
|
129
129
|
end
|
|
130
|
+
|
|
131
|
+
# Qualify +col+ with the given table name. If +col+ is an array of columns,
|
|
132
|
+
# return an array of qualified columns.
|
|
133
|
+
def qualify(table, col)
|
|
134
|
+
transform(col){|k| SQL::QualifiedIdentifier.new(table, k)}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Qualify col with the associated model's table name.
|
|
138
|
+
def qualify_assoc(col)
|
|
139
|
+
qualify(associated_class.table_name, col)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Qualify col with the current model's table name.
|
|
143
|
+
def qualify_cur(col)
|
|
144
|
+
qualify(self[:model].table_name, col)
|
|
145
|
+
end
|
|
130
146
|
|
|
131
147
|
# Returns the reciprocal association variable, if one exists. The reciprocal
|
|
132
148
|
# association is the association in the associated class that is the opposite
|
|
@@ -195,6 +211,14 @@ module Sequel
|
|
|
195
211
|
def setter_method
|
|
196
212
|
:"#{self[:name]}="
|
|
197
213
|
end
|
|
214
|
+
|
|
215
|
+
private
|
|
216
|
+
|
|
217
|
+
# If +s+ is an array, map +s+ over the block. Otherwise, just call the
|
|
218
|
+
# block with +s+.
|
|
219
|
+
def transform(s)
|
|
220
|
+
s.is_a?(Array) ? s.map(&Proc.new) : (yield s)
|
|
221
|
+
end
|
|
198
222
|
end
|
|
199
223
|
|
|
200
224
|
class ManyToOneAssociationReflection < AssociationReflection
|
|
@@ -243,6 +267,11 @@ module Sequel
|
|
|
243
267
|
self[:primary_keys] ||= Array(primary_key)
|
|
244
268
|
end
|
|
245
269
|
alias associated_object_keys primary_keys
|
|
270
|
+
|
|
271
|
+
# #primary_key qualified by the associated table
|
|
272
|
+
def qualified_primary_key
|
|
273
|
+
self[:qualified_primary_key] ||= self[:qualify] == false ? primary_key : qualify_assoc(primary_key)
|
|
274
|
+
end
|
|
246
275
|
|
|
247
276
|
# True only if the reciprocal is a one_to_many association.
|
|
248
277
|
def reciprocal_array?
|
|
@@ -297,12 +326,18 @@ module Sequel
|
|
|
297
326
|
|
|
298
327
|
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3))
|
|
299
328
|
def eager_loading_predicate_key
|
|
300
|
-
self[:eager_loading_predicate_key] ||=
|
|
329
|
+
self[:eager_loading_predicate_key] ||= qualify_assoc(self[:key])
|
|
301
330
|
end
|
|
331
|
+
alias qualified_key eager_loading_predicate_key
|
|
302
332
|
|
|
303
333
|
# The column in the current table that the key in the associated table references.
|
|
304
334
|
def primary_key
|
|
305
|
-
self[:primary_key]
|
|
335
|
+
self[:primary_key]
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# #primary_key qualified by the current table
|
|
339
|
+
def qualified_primary_key
|
|
340
|
+
self[:qualified_primary_key] ||= qualify_cur(primary_key)
|
|
306
341
|
end
|
|
307
342
|
|
|
308
343
|
# Whether the reciprocal of this association returns an array of objects instead of a single object,
|
|
@@ -381,13 +416,6 @@ module Sequel
|
|
|
381
416
|
self[:left_key]
|
|
382
417
|
end
|
|
383
418
|
|
|
384
|
-
# The table containing the column to use for the associated key when eagerly loading
|
|
385
|
-
def associated_key_table
|
|
386
|
-
self[:associated_key_table] ||= (
|
|
387
|
-
syms = associated_class.dataset.split_alias(self[:join_table]);
|
|
388
|
-
syms[1] || syms[0])
|
|
389
|
-
end
|
|
390
|
-
|
|
391
419
|
# Alias of right_primary_keys
|
|
392
420
|
def associated_object_keys
|
|
393
421
|
right_primary_keys
|
|
@@ -427,9 +455,16 @@ module Sequel
|
|
|
427
455
|
self[:eager_loader_key] ||= self[:left_primary_key]
|
|
428
456
|
end
|
|
429
457
|
|
|
430
|
-
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3))
|
|
458
|
+
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3)).
|
|
459
|
+
# The left key qualified by the join table.
|
|
431
460
|
def eager_loading_predicate_key
|
|
432
|
-
self[:eager_loading_predicate_key] ||=
|
|
461
|
+
self[:eager_loading_predicate_key] ||= qualify(join_table_alias, self[:left_key])
|
|
462
|
+
end
|
|
463
|
+
alias qualified_left_key eager_loading_predicate_key
|
|
464
|
+
|
|
465
|
+
# The right key qualified by the join table.
|
|
466
|
+
def qualified_right_key
|
|
467
|
+
self[:qualified_right_key] ||= qualify(join_table_alias, self[:right_key])
|
|
433
468
|
end
|
|
434
469
|
|
|
435
470
|
# many_to_many associations need to select a key in an associated table to eagerly load
|
|
@@ -437,6 +472,25 @@ module Sequel
|
|
|
437
472
|
true
|
|
438
473
|
end
|
|
439
474
|
|
|
475
|
+
# The source of the join table. This is the join table itself, unless it
|
|
476
|
+
# is aliased, in which case it is the unaliased part.
|
|
477
|
+
def join_table_source
|
|
478
|
+
fetch(:join_table_source) do
|
|
479
|
+
split_join_table_alias
|
|
480
|
+
self[:join_table_source]
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# The join table itself, unless it is aliased, in which case this
|
|
485
|
+
# is the alias.
|
|
486
|
+
def join_table_alias
|
|
487
|
+
fetch(:join_table_alias) do
|
|
488
|
+
split_join_table_alias
|
|
489
|
+
self[:join_table_alias]
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
alias associated_key_table join_table_alias
|
|
493
|
+
|
|
440
494
|
# Whether the associated object needs a primary key to be added/removed,
|
|
441
495
|
# true for many_to_many associations.
|
|
442
496
|
def need_associated_primary_key?
|
|
@@ -458,6 +512,11 @@ module Sequel
|
|
|
458
512
|
end
|
|
459
513
|
self[:reciprocal] = nil
|
|
460
514
|
end
|
|
515
|
+
|
|
516
|
+
# #right_primary_key qualified by the associated table
|
|
517
|
+
def qualified_right_primary_key
|
|
518
|
+
self[:qualified_right_primary_key] ||= qualify_assoc(right_primary_key)
|
|
519
|
+
end
|
|
461
520
|
|
|
462
521
|
# The primary key column(s) to use in the associated table (can be symbol or array).
|
|
463
522
|
def right_primary_key
|
|
@@ -474,6 +533,15 @@ module Sequel
|
|
|
474
533
|
return self[:select] if include?(:select)
|
|
475
534
|
self[:select] ||= Sequel::SQL::ColumnAll.new(associated_class.table_name)
|
|
476
535
|
end
|
|
536
|
+
|
|
537
|
+
private
|
|
538
|
+
|
|
539
|
+
# Split the join table into source and alias parts.
|
|
540
|
+
def split_join_table_alias
|
|
541
|
+
s, a = associated_class.dataset.split_alias(self[:join_table])
|
|
542
|
+
self[:join_table_source] = s
|
|
543
|
+
self[:join_table_alias] = a || s
|
|
544
|
+
end
|
|
477
545
|
end
|
|
478
546
|
|
|
479
547
|
# This module contains methods added to all association datasets
|
|
@@ -548,6 +616,22 @@ module Sequel
|
|
|
548
616
|
association_reflections.values
|
|
549
617
|
end
|
|
550
618
|
|
|
619
|
+
# Given an association reflection and a dataset, apply the
|
|
620
|
+
# :select, :conditions, :order, :eager, :distinct, and :eager_block
|
|
621
|
+
# association options to the given dataset and return the dataset
|
|
622
|
+
# or a modified copy of it.
|
|
623
|
+
def apply_association_dataset_opts(opts, ds)
|
|
624
|
+
ds = ds.select(*opts.select) if opts.select
|
|
625
|
+
if c = opts[:conditions]
|
|
626
|
+
ds = (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
|
627
|
+
end
|
|
628
|
+
ds = ds.order(*opts[:order]) if opts[:order]
|
|
629
|
+
ds = ds.eager(opts[:eager]) if opts[:eager]
|
|
630
|
+
ds = ds.distinct if opts[:distinct]
|
|
631
|
+
ds = opts[:eager_block].call(ds) if opts[:eager_block]
|
|
632
|
+
ds
|
|
633
|
+
end
|
|
634
|
+
|
|
551
635
|
# Associates a related model with the current model. The following types are
|
|
552
636
|
# supported:
|
|
553
637
|
#
|
|
@@ -676,6 +760,10 @@ module Sequel
|
|
|
676
760
|
# :primary_key :: column in the associated table that :key option references, as a symbol.
|
|
677
761
|
# Defaults to the primary key of the associated table. Can use an
|
|
678
762
|
# array of symbols for a composite key association.
|
|
763
|
+
# :qualify :: Whether to use qualifier primary keys when loading the association. The default
|
|
764
|
+
# is true, so you must set to false to not qualify. Qualification rarely causes
|
|
765
|
+
# problems, but it's necessary to disable in some cases, such as when you are doing
|
|
766
|
+
# a JOIN USING operation on the column on Oracle.
|
|
679
767
|
# === :one_to_many and :one_to_one
|
|
680
768
|
# :key :: foreign key in associated model's table that references
|
|
681
769
|
# current model's primary key, as a symbol. Defaults to
|
|
@@ -763,26 +851,19 @@ module Sequel
|
|
|
763
851
|
|
|
764
852
|
# Modify and return eager loading dataset based on association options.
|
|
765
853
|
def eager_loading_dataset(opts, ds, select, associations, eager_options={})
|
|
854
|
+
ds = apply_association_dataset_opts(opts, ds)
|
|
766
855
|
ds = ds.select(*select) if select
|
|
767
|
-
if c = opts[:conditions]
|
|
768
|
-
ds = (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
|
769
|
-
end
|
|
770
|
-
ds = ds.order(*opts[:order]) if opts[:order]
|
|
771
|
-
ds = ds.eager(opts[:eager]) if opts[:eager]
|
|
772
|
-
ds = ds.distinct if opts[:distinct]
|
|
773
856
|
if opts[:eager_graph]
|
|
774
857
|
raise(Error, "cannot eagerly load a #{opts[:type]} association that uses :eager_graph") if opts.eager_loading_use_associated_key?
|
|
775
858
|
ds = ds.eager_graph(opts[:eager_graph])
|
|
776
859
|
end
|
|
777
860
|
ds = ds.eager(associations) unless Array(associations).empty?
|
|
778
|
-
ds = opts[:eager_block].call(ds) if opts[:eager_block]
|
|
779
861
|
ds = eager_options[:eager_block].call(ds) if eager_options[:eager_block]
|
|
780
862
|
if opts.eager_loading_use_associated_key?
|
|
781
863
|
ds = if opts[:uses_left_composite_keys]
|
|
782
|
-
|
|
783
|
-
ds.select_append(*opts.associated_key_alias.zip(opts.associated_key_column).map{|a, c| SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(t, c), a)})
|
|
864
|
+
ds.select_append(*opts.associated_key_alias.zip(opts.eager_loading_predicate_key).map{|a, k| SQL::AliasedExpression.new(k, a)})
|
|
784
865
|
else
|
|
785
|
-
ds.select_append(SQL::AliasedExpression.new(
|
|
866
|
+
ds.select_append(SQL::AliasedExpression.new(opts.eager_loading_predicate_key, opts.associated_key_alias))
|
|
786
867
|
end
|
|
787
868
|
end
|
|
788
869
|
ds
|
|
@@ -963,9 +1044,9 @@ module Sequel
|
|
|
963
1044
|
h = eo[:key_hash][left_pk]
|
|
964
1045
|
rows = eo[:rows]
|
|
965
1046
|
rows.each{|object| object.associations[name] = []}
|
|
966
|
-
r =
|
|
967
|
-
l =
|
|
968
|
-
ds = model.eager_loading_dataset(opts, opts.associated_class.inner_join(join_table, r + l),
|
|
1047
|
+
r = rcks.zip(opts.right_primary_keys)
|
|
1048
|
+
l = [[opts.qualify(opts.join_table_alias, left), h.keys]]
|
|
1049
|
+
ds = model.eager_loading_dataset(opts, opts.associated_class.inner_join(join_table, r + l), nil, eo[:associations], eo)
|
|
969
1050
|
case opts.eager_limit_strategy
|
|
970
1051
|
when :window_function
|
|
971
1052
|
delete_rn = true
|
|
@@ -974,7 +1055,7 @@ module Sequel
|
|
|
974
1055
|
when :correlated_subquery
|
|
975
1056
|
ds = apply_correlated_subquery_eager_limit_strategy(ds, opts) do |xds|
|
|
976
1057
|
dsa = ds.send(:dataset_alias, 2)
|
|
977
|
-
xds.inner_join(join_table, r + lcks.map{|k| [k, SQL::QualifiedIdentifier.new(
|
|
1058
|
+
xds.inner_join(join_table, r + lcks.map{|k| [k, SQL::QualifiedIdentifier.new(opts.join_table_alias, k)]}, :table_alias=>dsa)
|
|
978
1059
|
end
|
|
979
1060
|
end
|
|
980
1061
|
ds.all do |assoc_record|
|
|
@@ -1005,7 +1086,7 @@ module Sequel
|
|
|
1005
1086
|
jt_graph_block = opts[:graph_join_table_block]
|
|
1006
1087
|
opts[:eager_grapher] ||= proc do |eo|
|
|
1007
1088
|
ds = eo[:self]
|
|
1008
|
-
ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(lcpks) + graph_jt_conds, :select=>false, :table_alias=>ds.unused_table_alias(join_table), :join_type=>jt_join_type, :implicit_qualifier=>eo[:implicit_qualifier], :from_self_alias=>ds.opts[:eager_graph][:master], &jt_graph_block)
|
|
1089
|
+
ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(lcpks) + graph_jt_conds, :select=>false, :table_alias=>ds.unused_table_alias(join_table, [eo[:table_alias]]), :join_type=>jt_join_type, :implicit_qualifier=>eo[:implicit_qualifier], :from_self_alias=>ds.opts[:eager_graph][:master], &jt_graph_block)
|
|
1009
1090
|
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.right_primary_keys.zip(rcks) + conditions, :select=>select, :table_alias=>eo[:table_alias], :join_type=>join_type, &graph_block)
|
|
1010
1091
|
end
|
|
1011
1092
|
|
|
@@ -1037,15 +1118,17 @@ module Sequel
|
|
|
1037
1118
|
opts[:key] = opts.default_key unless opts.include?(:key)
|
|
1038
1119
|
key = opts[:key]
|
|
1039
1120
|
cks = opts[:keys] = Array(opts[:key])
|
|
1121
|
+
opts[:qualified_key] = opts.qualify_cur(key)
|
|
1040
1122
|
if opts[:primary_key]
|
|
1041
1123
|
cpks = Array(opts[:primary_key])
|
|
1042
1124
|
raise(Error, "mismatched number of composite keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
|
|
1043
1125
|
end
|
|
1044
1126
|
uses_cks = opts[:uses_composite_keys] = cks.length > 1
|
|
1127
|
+
qualify = opts[:qualify] != false
|
|
1045
1128
|
opts[:cartesian_product_number] ||= 0
|
|
1046
1129
|
opts[:dataset] ||= proc do
|
|
1047
1130
|
klass = opts.associated_class
|
|
1048
|
-
klass.filter(opts.
|
|
1131
|
+
klass.filter(Array(opts.qualified_primary_key).zip(cks.map{|k| send(k)}))
|
|
1049
1132
|
end
|
|
1050
1133
|
opts[:eager_loader] ||= proc do |eo|
|
|
1051
1134
|
h = eo[:key_hash][key]
|
|
@@ -1056,7 +1139,7 @@ module Sequel
|
|
|
1056
1139
|
# Skip eager loading if no objects have a foreign key for this association
|
|
1057
1140
|
unless keys.empty?
|
|
1058
1141
|
klass = opts.associated_class
|
|
1059
|
-
model.eager_loading_dataset(opts, klass.filter(
|
|
1142
|
+
model.eager_loading_dataset(opts, klass.filter(opts.qualified_primary_key=>keys), nil, eo[:associations], eo).all do |assoc_record|
|
|
1060
1143
|
hash_key = uses_cks ? opts.primary_keys.map{|k| assoc_record.send(k)} : assoc_record.send(opts.primary_key)
|
|
1061
1144
|
next unless objects = h[hash_key]
|
|
1062
1145
|
objects.each{|object| object.associations[name] = assoc_record}
|
|
@@ -1095,8 +1178,7 @@ module Sequel
|
|
|
1095
1178
|
raise(Error, "mismatched number of composite keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
|
|
1096
1179
|
uses_cks = opts[:uses_composite_keys] = cks.length > 1
|
|
1097
1180
|
opts[:dataset] ||= proc do
|
|
1098
|
-
|
|
1099
|
-
klass.filter(cks.map{|k| SQL::QualifiedIdentifier.new(klass.table_name, k)}.zip(cpks.map{|k| send(k)}))
|
|
1181
|
+
opts.associated_class.filter(Array(opts.qualified_key).zip(cpks.map{|k| send(k)}))
|
|
1100
1182
|
end
|
|
1101
1183
|
opts[:eager_loader] ||= proc do |eo|
|
|
1102
1184
|
h = eo[:key_hash][primary_key]
|
|
@@ -1109,7 +1191,7 @@ module Sequel
|
|
|
1109
1191
|
reciprocal = opts.reciprocal
|
|
1110
1192
|
klass = opts.associated_class
|
|
1111
1193
|
filter_keys = opts.eager_loading_predicate_key
|
|
1112
|
-
ds = model.eager_loading_dataset(opts, klass.filter(filter_keys=>h.keys),
|
|
1194
|
+
ds = model.eager_loading_dataset(opts, klass.filter(filter_keys=>h.keys), nil, eo[:associations], eo)
|
|
1113
1195
|
case opts.eager_limit_strategy
|
|
1114
1196
|
when :distinct_on
|
|
1115
1197
|
ds = ds.distinct(*filter_keys).order_prepend(*filter_keys)
|
|
@@ -1277,7 +1359,7 @@ module Sequel
|
|
|
1277
1359
|
|
|
1278
1360
|
# Dataset for the join table of the given many to many association reflection
|
|
1279
1361
|
def _join_table_dataset(opts)
|
|
1280
|
-
ds = model.db.from(opts
|
|
1362
|
+
ds = model.db.from(opts.join_table_source)
|
|
1281
1363
|
opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
|
|
1282
1364
|
end
|
|
1283
1365
|
|
|
@@ -1845,7 +1927,7 @@ module Sequel
|
|
|
1845
1927
|
if exp == SQL::Constants::FALSE
|
|
1846
1928
|
association_filter_handle_inversion(op, exp, Array(lpks))
|
|
1847
1929
|
else
|
|
1848
|
-
association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>model.db
|
|
1930
|
+
association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>model.db.from(ref[:join_table]).select(*lks).where(exp).exclude(SQL::BooleanExpression.from_value_pairs(lks.zip([]), :OR))), Array(lpks))
|
|
1849
1931
|
end
|
|
1850
1932
|
end
|
|
1851
1933
|
|
data/lib/sequel/model/base.rb
CHANGED
|
@@ -105,6 +105,17 @@ module Sequel
|
|
|
105
105
|
args = args.first if (args.size == 1)
|
|
106
106
|
args.is_a?(Hash) ? dataset[args] : primary_key_lookup(args)
|
|
107
107
|
end
|
|
108
|
+
|
|
109
|
+
# Initializes a model instance as an existing record. This constructor is
|
|
110
|
+
# used by Sequel to initialize model instances when fetching records.
|
|
111
|
+
# Requires that values be a hash where all keys are symbols. It
|
|
112
|
+
# probably should not be used by external code.
|
|
113
|
+
def call(values)
|
|
114
|
+
o = allocate
|
|
115
|
+
o.set_values(values)
|
|
116
|
+
o.after_initialize
|
|
117
|
+
o
|
|
118
|
+
end
|
|
108
119
|
|
|
109
120
|
# Clear the setter_methods cache
|
|
110
121
|
def clear_setter_methods_cache
|
|
@@ -149,9 +160,13 @@ module Sequel
|
|
|
149
160
|
set_dataset(ds)
|
|
150
161
|
end
|
|
151
162
|
|
|
152
|
-
# Extend the dataset with
|
|
163
|
+
# Extend the dataset with a module, similar to adding
|
|
153
164
|
# a plugin with the methods defined in DatasetMethods. If a block
|
|
154
|
-
# is given,
|
|
165
|
+
# is given, an anonymous module is created and the module_evaled, otherwise
|
|
166
|
+
# the argument should be a module. Returns the module given or the anonymous
|
|
167
|
+
# module created.
|
|
168
|
+
#
|
|
169
|
+
# Artist.dataset_module Sequel::ColumnsIntrospection
|
|
155
170
|
#
|
|
156
171
|
# Artist.dataset_module do
|
|
157
172
|
# def foo
|
|
@@ -162,11 +177,17 @@ module Sequel
|
|
|
162
177
|
# # => :bar
|
|
163
178
|
# Artist.foo
|
|
164
179
|
# # => :bar
|
|
165
|
-
def dataset_module
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
def dataset_module(mod = nil)
|
|
181
|
+
if mod
|
|
182
|
+
raise Error, "can't provide both argument and block to Model.dataset_module" if block_given?
|
|
183
|
+
dataset_extend(mod)
|
|
184
|
+
mod
|
|
185
|
+
else
|
|
186
|
+
@dataset_module ||= Module.new
|
|
187
|
+
@dataset_module.module_eval(&Proc.new) if block_given?
|
|
188
|
+
dataset_extend(@dataset_module)
|
|
189
|
+
@dataset_module
|
|
190
|
+
end
|
|
170
191
|
end
|
|
171
192
|
|
|
172
193
|
# Returns the database associated with the Model class.
|
|
@@ -237,9 +258,30 @@ module Sequel
|
|
|
237
258
|
@dataset_methods[meth] = block
|
|
238
259
|
dataset.meta_def(meth, &block) if @dataset
|
|
239
260
|
end
|
|
240
|
-
args.each
|
|
261
|
+
args.each do |arg|
|
|
262
|
+
if arg.to_s =~ NORMAL_METHOD_NAME_REGEXP
|
|
263
|
+
instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg, true)
|
|
264
|
+
else
|
|
265
|
+
def_model_dataset_method_block(arg)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
241
268
|
end
|
|
242
|
-
|
|
269
|
+
|
|
270
|
+
module_eval(if RUBY_VERSION < '1.8.7'
|
|
271
|
+
<<-END
|
|
272
|
+
def def_model_dataset_method_block(arg)
|
|
273
|
+
meta_def(arg){|*args| dataset.send(arg, *args)}
|
|
274
|
+
end
|
|
275
|
+
END
|
|
276
|
+
else
|
|
277
|
+
<<-END
|
|
278
|
+
def def_model_dataset_method_block(arg)
|
|
279
|
+
meta_def(arg){|*args, &block| dataset.send(arg, *args, &block)}
|
|
280
|
+
end
|
|
281
|
+
END
|
|
282
|
+
end, __FILE__, __LINE__ - 4)
|
|
283
|
+
private :def_model_dataset_method_block
|
|
284
|
+
|
|
243
285
|
# Finds a single record according to the supplied filter.
|
|
244
286
|
# You are encouraged to use Model.[] or Model.first instead of this method.
|
|
245
287
|
#
|
|
@@ -318,12 +360,9 @@ module Sequel
|
|
|
318
360
|
pluralize(underscore(demodulize(name))).to_sym
|
|
319
361
|
end
|
|
320
362
|
|
|
321
|
-
#
|
|
322
|
-
# used by Sequel to initialize model instances when fetching records.
|
|
323
|
-
# +load+ requires that values be a hash where all keys are symbols. It
|
|
324
|
-
# probably should not be used by external code.
|
|
363
|
+
# Calls #call with the values hash. Only for backwards compatibility.
|
|
325
364
|
def load(values)
|
|
326
|
-
|
|
365
|
+
call(values)
|
|
327
366
|
end
|
|
328
367
|
|
|
329
368
|
# Clear the setter_methods cache when a setter method is added
|
|
@@ -447,13 +486,17 @@ module Sequel
|
|
|
447
486
|
@simple_table = db.literal(ds)
|
|
448
487
|
db.from(ds)
|
|
449
488
|
when Dataset
|
|
450
|
-
@simple_table =
|
|
489
|
+
@simple_table = if ds.send(:simple_select_all?)
|
|
490
|
+
ds.literal(ds.first_source_table)
|
|
491
|
+
else
|
|
492
|
+
nil
|
|
493
|
+
end
|
|
451
494
|
@db = ds.db
|
|
452
495
|
ds
|
|
453
496
|
else
|
|
454
497
|
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
|
455
498
|
end
|
|
456
|
-
@dataset.row_proc =
|
|
499
|
+
@dataset.row_proc = self
|
|
457
500
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
|
458
501
|
if inherited
|
|
459
502
|
@simple_table = superclass.simple_table
|
|
@@ -466,7 +509,7 @@ module Sequel
|
|
|
466
509
|
check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
|
|
467
510
|
self
|
|
468
511
|
end
|
|
469
|
-
|
|
512
|
+
|
|
470
513
|
# Sets the primary key for this model. You can use either a regular
|
|
471
514
|
# or a composite primary key. To not use a primary key, set to nil
|
|
472
515
|
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
|
@@ -484,7 +527,11 @@ module Sequel
|
|
|
484
527
|
def set_primary_key(*key)
|
|
485
528
|
clear_setter_methods_cache
|
|
486
529
|
key = key.flatten
|
|
487
|
-
@simple_pk = key.length == 1
|
|
530
|
+
@simple_pk = if key.length == 1
|
|
531
|
+
(@dataset || db).literal(key.first)
|
|
532
|
+
else
|
|
533
|
+
nil
|
|
534
|
+
end
|
|
488
535
|
@primary_key = (key.length == 1) ? key[0] : key
|
|
489
536
|
end
|
|
490
537
|
|
|
@@ -617,10 +664,9 @@ module Sequel
|
|
|
617
664
|
return nil unless @dataset
|
|
618
665
|
schema_hash = {}
|
|
619
666
|
ds_opts = dataset.opts
|
|
620
|
-
single_table = ds_opts[:from] && (ds_opts[:from].length == 1) \
|
|
621
|
-
&& !ds_opts.include?(:join) && !ds_opts.include?(:sql)
|
|
622
667
|
get_columns = proc{check_non_connection_error{columns} || []}
|
|
623
|
-
|
|
668
|
+
schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)}
|
|
669
|
+
if schema_array
|
|
624
670
|
schema_array.each{|k,v| schema_hash[k] = v}
|
|
625
671
|
if ds_opts.include?(:select)
|
|
626
672
|
# We don't remove the columns from the schema_hash,
|
|
@@ -628,6 +674,7 @@ module Sequel
|
|
|
628
674
|
# even if they are not selected.
|
|
629
675
|
cols = get_columns.call
|
|
630
676
|
cols.each{|c| schema_hash[c] ||= {}}
|
|
677
|
+
def_column_accessor(*schema_hash.keys)
|
|
631
678
|
else
|
|
632
679
|
# Dataset is for a single table with all columns,
|
|
633
680
|
# so set the columns based on the order they were
|
|
@@ -741,7 +788,7 @@ module Sequel
|
|
|
741
788
|
# * The following instance_methods all call the class method of the same
|
|
742
789
|
# name: columns, db, primary_key, db_schema.
|
|
743
790
|
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
|
744
|
-
# to set values for the attribute. It also creates
|
|
791
|
+
# to set values for the attribute. It also creates instance getters returning
|
|
745
792
|
# the value of the setting. If the value has not yet been set, it
|
|
746
793
|
# gets the default value from the class by calling the class method of the same name.
|
|
747
794
|
module InstanceMethods
|
|
@@ -784,7 +831,7 @@ module Sequel
|
|
|
784
831
|
#
|
|
785
832
|
# Arguments:
|
|
786
833
|
# values :: should be a hash to pass to set.
|
|
787
|
-
# from_db ::
|
|
834
|
+
# from_db :: only for backwards compatibility, forget it exists.
|
|
788
835
|
#
|
|
789
836
|
# Artist.new(:name=>'Bob')
|
|
790
837
|
#
|
|
@@ -793,7 +840,6 @@ module Sequel
|
|
|
793
840
|
# end
|
|
794
841
|
def initialize(values = {}, from_db = false)
|
|
795
842
|
if from_db
|
|
796
|
-
@new = false
|
|
797
843
|
set_values(values)
|
|
798
844
|
else
|
|
799
845
|
@values = {}
|
|
@@ -1034,7 +1080,7 @@ module Sequel
|
|
|
1034
1080
|
# Artist.new.new? # => true
|
|
1035
1081
|
# Artist[1].new? # => false
|
|
1036
1082
|
def new?
|
|
1037
|
-
@new
|
|
1083
|
+
defined?(@new) ? @new : (@new = false)
|
|
1038
1084
|
end
|
|
1039
1085
|
|
|
1040
1086
|
# Returns the primary key value identifying the model instance.
|
|
@@ -1179,6 +1225,13 @@ module Sequel
|
|
|
1179
1225
|
set_restricted(hash, only.flatten, false)
|
|
1180
1226
|
end
|
|
1181
1227
|
|
|
1228
|
+
# Replace the current values with hash. Should definitely not be
|
|
1229
|
+
# used with untrusted input, and should probably not be called
|
|
1230
|
+
# directly by user code.
|
|
1231
|
+
def set_values(hash)
|
|
1232
|
+
@values = hash
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1182
1235
|
# Clear the setter_methods cache when a method is added
|
|
1183
1236
|
def singleton_method_added(meth)
|
|
1184
1237
|
@singleton_setter_added = true if meth.to_s =~ SETTER_METHOD_REGEXP
|
|
@@ -1286,6 +1339,8 @@ module Sequel
|
|
|
1286
1339
|
# Internal destroy method, separted from destroy to
|
|
1287
1340
|
# allow running inside a transaction
|
|
1288
1341
|
def _destroy(opts)
|
|
1342
|
+
sh = {:server=>this_server}
|
|
1343
|
+
db.after_rollback(sh){after_destroy_rollback}
|
|
1289
1344
|
called = false
|
|
1290
1345
|
around_destroy do
|
|
1291
1346
|
called = true
|
|
@@ -1295,6 +1350,7 @@ module Sequel
|
|
|
1295
1350
|
true
|
|
1296
1351
|
end
|
|
1297
1352
|
raise_hook_failure(:destroy) unless called
|
|
1353
|
+
db.after_commit(sh){after_destroy_commit}
|
|
1298
1354
|
self
|
|
1299
1355
|
end
|
|
1300
1356
|
|
|
@@ -1355,6 +1411,8 @@ module Sequel
|
|
|
1355
1411
|
# Internal version of save, split from save to allow running inside
|
|
1356
1412
|
# it's own transaction.
|
|
1357
1413
|
def _save(columns, opts)
|
|
1414
|
+
sh = {:server=>this_server}
|
|
1415
|
+
db.after_rollback(sh){after_rollback}
|
|
1358
1416
|
was_new = false
|
|
1359
1417
|
pk = nil
|
|
1360
1418
|
called_save = false
|
|
@@ -1408,6 +1466,7 @@ module Sequel
|
|
|
1408
1466
|
@columns_updated = nil
|
|
1409
1467
|
end
|
|
1410
1468
|
@modified = false
|
|
1469
|
+
db.after_commit(sh){after_commit}
|
|
1411
1470
|
self
|
|
1412
1471
|
end
|
|
1413
1472
|
|
|
@@ -1506,7 +1565,7 @@ module Sequel
|
|
|
1506
1565
|
|
|
1507
1566
|
# If transactions should be used, wrap the yield in a transaction block.
|
|
1508
1567
|
def checked_transaction(opts={})
|
|
1509
|
-
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
|
1568
|
+
use_transaction?(opts) ? db.transaction({:server=>this_server}.merge(opts)){yield} : yield
|
|
1510
1569
|
end
|
|
1511
1570
|
|
|
1512
1571
|
# Set the columns with the given hash. By default, the same as +set+, but
|
|
@@ -1563,11 +1622,6 @@ module Sequel
|
|
|
1563
1622
|
self
|
|
1564
1623
|
end
|
|
1565
1624
|
|
|
1566
|
-
# Replace the current values with hash.
|
|
1567
|
-
def set_values(hash)
|
|
1568
|
-
@values = hash
|
|
1569
|
-
end
|
|
1570
|
-
|
|
1571
1625
|
# Returns all methods that can be used for attribute
|
|
1572
1626
|
# assignment (those that end with =), modified by the only
|
|
1573
1627
|
# and except arguments:
|
|
@@ -1596,6 +1650,12 @@ module Sequel
|
|
|
1596
1650
|
meths
|
|
1597
1651
|
end
|
|
1598
1652
|
end
|
|
1653
|
+
|
|
1654
|
+
# The server/shard that the model object's dataset uses, or :default if the
|
|
1655
|
+
# model object's dataset does not have an associated shard.
|
|
1656
|
+
def this_server
|
|
1657
|
+
primary_key ? (this.opts[:server] || :default) : (model.dataset.opts[:server] || :default)
|
|
1658
|
+
end
|
|
1599
1659
|
|
|
1600
1660
|
# Typecast the value to the column's type if typecasting. Calls the database's
|
|
1601
1661
|
# typecast_value method, so database adapters can override/augment the handling
|