sequel 5.80.0 → 5.92.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.
- checksums.yaml +4 -4
- data/bin/sequel +9 -4
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -0
- data/lib/sequel/adapters/jdbc/db2.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
- data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +8 -8
- data/lib/sequel/adapters/mysql2.rb +8 -1
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +18 -5
- data/lib/sequel/adapters/shared/mysql.rb +8 -4
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +106 -13
- data/lib/sequel/adapters/shared/sqlite.rb +4 -2
- data/lib/sequel/adapters/sqlite.rb +4 -0
- data/lib/sequel/adapters/trilogy.rb +1 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +26 -10
- data/lib/sequel/connection_pool/threaded.rb +26 -10
- data/lib/sequel/connection_pool.rb +2 -2
- data/lib/sequel/core.rb +15 -0
- data/lib/sequel/database/connecting.rb +20 -26
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +46 -10
- data/lib/sequel/database/query.rb +11 -11
- data/lib/sequel/database/schema_generator.rb +8 -0
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +9 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -1
- data/lib/sequel/dataset/query.rb +9 -5
- data/lib/sequel/dataset/sql.rb +25 -5
- data/lib/sequel/extensions/caller_logging.rb +2 -0
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/dataset_run.rb +41 -0
- data/lib/sequel/extensions/migration.rb +23 -3
- data/lib/sequel/extensions/null_dataset.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
- data/lib/sequel/extensions/pg_enum.rb +3 -3
- data/lib/sequel/extensions/pg_json_ops.rb +642 -9
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
- data/lib/sequel/extensions/provenance.rb +2 -0
- data/lib/sequel/extensions/query_blocker.rb +172 -0
- data/lib/sequel/extensions/schema_caching.rb +24 -9
- data/lib/sequel/extensions/schema_dumper.rb +16 -4
- data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
- data/lib/sequel/extensions/stdio_logger.rb +48 -0
- data/lib/sequel/extensions/string_agg.rb +17 -4
- data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +28 -3
- data/lib/sequel/model/base.rb +67 -18
- data/lib/sequel/plugins/association_pks.rb +1 -1
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/defaults_setter.rb +16 -4
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
- data/lib/sequel/plugins/input_transformer.rb +1 -1
- data/lib/sequel/plugins/inspect_pk.rb +44 -0
- data/lib/sequel/plugins/instance_filters.rb +4 -1
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +10 -5
- data/lib/sequel/plugins/optimistic_locking.rb +2 -0
- data/lib/sequel/plugins/paged_operations.rb +5 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
- data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +11 -5
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/static_cache_cache.rb +50 -13
- data/lib/sequel/plugins/subset_conditions.rb +85 -5
- data/lib/sequel/plugins/subset_static_cache.rb +263 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +6 -2
- data/lib/sequel/plugins/validate_associated.rb +1 -1
- data/lib/sequel/sql.rb +16 -6
- data/lib/sequel/version.rb +1 -1
- metadata +12 -234
- data/CHANGELOG +0 -1355
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -102
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -436
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -190
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- data/doc/virtual_rows.rdoc +0 -265
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The temporarily_release_connection extension adds support for temporarily
|
4
|
+
# releasing a checked out connection back to the connection pool. It is
|
5
|
+
# designed for use in multithreaded transactional integration tests, allowing
|
6
|
+
# a connection to start a transaction in one thread, but be temporarily
|
7
|
+
# released back to the connection pool, so it can be operated on safely
|
8
|
+
# by multiple threads inside a block. For example, the main thread could be
|
9
|
+
# running tests that send web requests, and a separate thread running a web
|
10
|
+
# server that is responding to those requests, and the same connection and
|
11
|
+
# transaction would be used for both.
|
12
|
+
#
|
13
|
+
# To load the extension into the database:
|
14
|
+
#
|
15
|
+
# DB.extension :temporarily_release_connection
|
16
|
+
#
|
17
|
+
# After the extension is loaded, call the +temporarily_release_connection+
|
18
|
+
# method with the connection object to temporarily release the connection
|
19
|
+
# back to the pool. Example:
|
20
|
+
#
|
21
|
+
# DB.transaction(rollback: :always, auto_savepoint: true) do |conn|
|
22
|
+
# DB.temporarily_release_connection(conn) do
|
23
|
+
# # Other threads can operate on connection safely inside the transaction
|
24
|
+
# yield
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# For sharded connection pools, the second argument to +temporarily_release_connection+
|
29
|
+
# is respected, and specifies the server on which to temporarily release the connection.
|
30
|
+
#
|
31
|
+
# The temporarily_release_connection extension is only supported with the
|
32
|
+
# threaded and timed_queue connection pools that ship with Sequel (and the sharded
|
33
|
+
# versions of each). To make sure that same connection object can be reacquired, it
|
34
|
+
# is only supported if the maximum connection pool size is 1, so set the Database
|
35
|
+
# :max_connections option to 1 if you plan to use this extension.
|
36
|
+
#
|
37
|
+
# If the +temporarily_release_connection+ method cannot reacquire the same connection
|
38
|
+
# it released to the pool, it will raise a Sequel::UnableToReacquireConnectionError
|
39
|
+
# exception. This should only happen if the connection has been disconnected
|
40
|
+
# while it was temporarily released. If this error is raised, Database#transaction
|
41
|
+
# will not rollback the transaction, since the connection object is likely no longer
|
42
|
+
# valid, and on poorly written database drivers, that could cause the process to crash.
|
43
|
+
#
|
44
|
+
# Related modules: Sequel::TemporarilyReleaseConnection,
|
45
|
+
# Sequel::UnableToReacquireConnectionError
|
46
|
+
|
47
|
+
#
|
48
|
+
module Sequel
|
49
|
+
# Error class raised if the connection pool does not provide the same connection
|
50
|
+
# object when checking a temporarily released connection out.
|
51
|
+
class UnableToReacquireConnectionError < Error
|
52
|
+
end
|
53
|
+
|
54
|
+
module TemporarilyReleaseConnection
|
55
|
+
module DatabaseMethods
|
56
|
+
# Temporarily release the connection back to the connection pool for the
|
57
|
+
# duration of the block.
|
58
|
+
def temporarily_release_connection(conn, server=:default, &block)
|
59
|
+
pool.temporarily_release_connection(conn, server, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Do nothing if UnableToReacquireConnectionError is raised, as it is
|
65
|
+
# likely the connection is not in a usable state.
|
66
|
+
def rollback_transaction(conn, opts)
|
67
|
+
return if UnableToReacquireConnectionError === $!
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module PoolMethods
|
73
|
+
# Temporarily release a currently checked out connection, then yield to the block. Reacquire the same
|
74
|
+
# connection upon the exit of the block.
|
75
|
+
def temporarily_release_connection(conn, server)
|
76
|
+
t = Sequel.current
|
77
|
+
raise Error, "connection not currently checked out" unless conn.equal?(trc_owned_connection(t, server))
|
78
|
+
|
79
|
+
begin
|
80
|
+
trc_release(t, conn, server)
|
81
|
+
yield
|
82
|
+
ensure
|
83
|
+
c = trc_acquire(t, server)
|
84
|
+
unless conn.equal?(c)
|
85
|
+
raise UnableToReacquireConnectionError, "reacquired connection not the same as initial connection"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
module TimedQueue
|
92
|
+
private
|
93
|
+
|
94
|
+
def trc_owned_connection(t, server)
|
95
|
+
owned_connection(t)
|
96
|
+
end
|
97
|
+
|
98
|
+
def trc_release(t, conn, server)
|
99
|
+
release(t)
|
100
|
+
end
|
101
|
+
|
102
|
+
def trc_acquire(t, server)
|
103
|
+
acquire(t)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module ShardedTimedQueue
|
108
|
+
# Normalize the server name for sharded connection pools
|
109
|
+
def temporarily_release_connection(conn, server)
|
110
|
+
server = pick_server(server)
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def trc_owned_connection(t, server)
|
117
|
+
owned_connection(t, server)
|
118
|
+
end
|
119
|
+
|
120
|
+
def trc_release(t, conn, server)
|
121
|
+
release(t, conn, server)
|
122
|
+
end
|
123
|
+
|
124
|
+
def trc_acquire(t, server)
|
125
|
+
acquire(t, server)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
module ThreadedBase
|
130
|
+
private
|
131
|
+
|
132
|
+
def trc_release(t, conn, server)
|
133
|
+
sync{super}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
module Threaded
|
138
|
+
include TimedQueue
|
139
|
+
include ThreadedBase
|
140
|
+
end
|
141
|
+
|
142
|
+
module ShardedThreaded
|
143
|
+
include ShardedTimedQueue
|
144
|
+
include ThreadedBase
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
trc = TemporarilyReleaseConnection
|
149
|
+
trc_map = {
|
150
|
+
:threaded => trc::Threaded,
|
151
|
+
:sharded_threaded => trc::ShardedThreaded,
|
152
|
+
:timed_queue => trc::TimedQueue,
|
153
|
+
:sharded_timed_queue => trc::ShardedTimedQueue,
|
154
|
+
}.freeze
|
155
|
+
|
156
|
+
Database.register_extension(:temporarily_release_connection) do |db|
|
157
|
+
unless pool_mod = trc_map[db.pool.pool_type]
|
158
|
+
raise(Error, "temporarily_release_connection extension not supported for connection pool type #{db.pool.pool_type}")
|
159
|
+
end
|
160
|
+
|
161
|
+
case db.pool.pool_type
|
162
|
+
when :threaded, :sharded_threaded
|
163
|
+
if db.opts[:connection_handling] == :disconnect
|
164
|
+
raise Error, "temporarily_release_connection extension not supported with connection_handling: :disconnect option"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
unless db.pool.max_size == 1
|
169
|
+
raise Error, "temporarily_release_connection extension not supported unless :max_connections option is 1"
|
170
|
+
end
|
171
|
+
|
172
|
+
db.extend(trc::DatabaseMethods)
|
173
|
+
db.pool.extend(trc::PoolMethods)
|
174
|
+
db.pool.extend(pool_mod)
|
175
|
+
end
|
176
|
+
|
177
|
+
private_constant :TemporarilyReleaseConnection
|
178
|
+
end
|
@@ -14,6 +14,7 @@ module Sequel
|
|
14
14
|
module SQL
|
15
15
|
class VirtualRow < BasicObject
|
16
16
|
include(Module.new do
|
17
|
+
Sequel.set_temp_name(self){"Sequel::SQL:VirtualRow::_MethodBlockMethodMissing"}
|
17
18
|
# Handle blocks passed to methods and change the behavior.
|
18
19
|
def method_missing(m, *args, &block)
|
19
20
|
if block
|
@@ -414,6 +414,12 @@ module Sequel
|
|
414
414
|
false
|
415
415
|
end
|
416
416
|
|
417
|
+
# Hash value for the association reflection. This is precomputed to avoid
|
418
|
+
# concurrency issues at runtime.
|
419
|
+
def hash
|
420
|
+
self[:_hash]
|
421
|
+
end
|
422
|
+
|
417
423
|
# Initialize the associations cache for the current association for the given objects.
|
418
424
|
def initialize_association_cache(objects)
|
419
425
|
name = self[:name]
|
@@ -693,6 +699,9 @@ module Sequel
|
|
693
699
|
|
694
700
|
# The predicate condition to use for the eager_loader.
|
695
701
|
def eager_loading_predicate_condition(keys)
|
702
|
+
if transform = self[:eager_loading_predicate_transform]
|
703
|
+
keys = transform.call(keys, self)
|
704
|
+
end
|
696
705
|
{predicate_key=>keys}
|
697
706
|
end
|
698
707
|
|
@@ -759,7 +768,15 @@ module Sequel
|
|
759
768
|
def placeholder_eager_loader
|
760
769
|
cached_fetch(:placeholder_eager_loader) do
|
761
770
|
eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
|
762
|
-
|
771
|
+
arg = pl.arg
|
772
|
+
|
773
|
+
if transform = self[:eager_loading_predicate_transform]
|
774
|
+
arg = arg.transform do |v|
|
775
|
+
transform.call(v, self)
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
|
763
780
|
end
|
764
781
|
end
|
765
782
|
end
|
@@ -1707,6 +1724,9 @@ module Sequel
|
|
1707
1724
|
# record should be populated.
|
1708
1725
|
# :eager_loader_key :: A symbol for the key column to use to populate the key_hash
|
1709
1726
|
# for the eager loader. Can be set to nil to not populate the key_hash.
|
1727
|
+
# :eager_loading_predicate_transform :: A callable object with which to transform the predicate key values used
|
1728
|
+
# when eager loading. Called with two arguments, the array of predicate key
|
1729
|
+
# values, and a the reflection for the association being eager loaded.
|
1710
1730
|
# :extend :: A module or array of modules to extend the dataset with.
|
1711
1731
|
# :filter_limit_strategy :: Determines the strategy used for enforcing limits and offsets when filtering by
|
1712
1732
|
# limited associations. Possible options are :window_function, :distinct_on, or
|
@@ -1769,6 +1789,9 @@ module Sequel
|
|
1769
1789
|
# Set to nil to not define a setter method for the association.
|
1770
1790
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
1771
1791
|
# loading limited associations using the default :union strategy.
|
1792
|
+
# :use_placeholder_loader :: Whether to use a placeholder loader when eager loading the
|
1793
|
+
# association. Can be set to false to disable the use of a placeholder
|
1794
|
+
# loader if one would be used by default.
|
1772
1795
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
1773
1796
|
# === :many_to_one
|
1774
1797
|
# :key :: foreign key in current model's table that references
|
@@ -1891,7 +1914,7 @@ module Sequel
|
|
1891
1914
|
raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
|
1892
1915
|
end
|
1893
1916
|
|
1894
|
-
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
1917
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph] unless opts.include?(:use_placeholder_loader)
|
1895
1918
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
1896
1919
|
opts[:graph_join_type] ||= :left_outer
|
1897
1920
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
@@ -1914,6 +1937,8 @@ module Sequel
|
|
1914
1937
|
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
1915
1938
|
opts.delete(:class) unless opts[:class]
|
1916
1939
|
|
1940
|
+
opts[:_hash] = [self, name].hash
|
1941
|
+
|
1917
1942
|
def_association(opts)
|
1918
1943
|
|
1919
1944
|
orig_opts.delete(:clone)
|
@@ -3591,7 +3616,7 @@ module Sequel
|
|
3591
3616
|
|
3592
3617
|
# Prepare a hash loaders and eager options which will be used to implement the eager loading.
|
3593
3618
|
def prepare_eager_load(a, reflections, eager_assoc)
|
3594
|
-
eager_load_data = {}
|
3619
|
+
eager_load_data = {}.compare_by_identity
|
3595
3620
|
|
3596
3621
|
# Key is foreign/primary key name symbol.
|
3597
3622
|
# Value is hash with keys being foreign/primary key values (generally integers)
|
data/lib/sequel/model/base.rb
CHANGED
@@ -87,7 +87,7 @@ module Sequel
|
|
87
87
|
attr_reader :simple_pk
|
88
88
|
|
89
89
|
# Should be the literal table name if this Model's dataset is a simple table (no select, order, join, etc.),
|
90
|
-
# or nil otherwise. This and simple_pk are used for an optimization in Model
|
90
|
+
# or nil otherwise. This and simple_pk are used for an optimization in Model[].
|
91
91
|
attr_reader :simple_table
|
92
92
|
|
93
93
|
# Whether mass assigning via .create/.new/#set/#update should raise an error
|
@@ -184,7 +184,7 @@ module Sequel
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
-
klass = Class.new(self)
|
187
|
+
klass = Sequel.set_temp_name(Class.new(self)){"Sequel::_Model(#{source.inspect})"}
|
188
188
|
|
189
189
|
if source.is_a?(::Sequel::Database)
|
190
190
|
klass.db = source
|
@@ -398,7 +398,7 @@ module Sequel
|
|
398
398
|
end
|
399
399
|
|
400
400
|
# Finds a single record according to the supplied filter.
|
401
|
-
# You are encouraged to use Model
|
401
|
+
# You are encouraged to use Model[] or Model.first instead of this method.
|
402
402
|
#
|
403
403
|
# Artist.find(name: 'Bob')
|
404
404
|
# # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
@@ -533,10 +533,28 @@ module Sequel
|
|
533
533
|
end
|
534
534
|
end
|
535
535
|
|
536
|
+
# Return a qualified identifier or array of qualified identifiers for
|
537
|
+
# the model's primary key. Uses the given qualifier if provided, or
|
538
|
+
# the table_name otherwise. If the model does not have a primary key,
|
539
|
+
# raises an +Error+.
|
540
|
+
#
|
541
|
+
# Artist.order(Artist.qualified_primary_key)
|
542
|
+
# # SELECT * FROM artists ORDER BY artists.id
|
543
|
+
def qualified_primary_key(qualifier=table_name)
|
544
|
+
case key = @primary_key
|
545
|
+
when Symbol
|
546
|
+
SQL::QualifiedIdentifier.new(qualifier, key)
|
547
|
+
when Array
|
548
|
+
key.map{|k| SQL::QualifiedIdentifier.new(qualifier, k)}
|
549
|
+
else
|
550
|
+
raise(Error, "#{self} does not have a primary key")
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
536
554
|
# Return a hash where the keys are qualified column references. Uses the given
|
537
555
|
# qualifier if provided, or the table_name otherwise. This is useful if you
|
538
556
|
# plan to join other tables to this table and you want the column references
|
539
|
-
# to be qualified.
|
557
|
+
# to be qualified. If the model does not have a primary key, raises an +Error+.
|
540
558
|
#
|
541
559
|
# Artist.where(Artist.qualified_primary_key_hash(1))
|
542
560
|
# # SELECT * FROM artists WHERE (artists.id = 1)
|
@@ -762,22 +780,36 @@ module Sequel
|
|
762
780
|
end
|
763
781
|
end
|
764
782
|
end
|
783
|
+
|
784
|
+
# Module that the class methods that call dataset methods are kept in.
|
785
|
+
# This allows the methods to be overridden and call super with the
|
786
|
+
# default behavior.
|
787
|
+
def dataset_methods_module
|
788
|
+
return @dataset_methods_module if defined?(@dataset_methods_module)
|
789
|
+
mod_name = "#{name}::@dataset_methods_module"
|
790
|
+
Sequel.synchronize{@dataset_methods_module ||= Sequel.set_temp_name(Module.new){mod_name}}
|
791
|
+
extend(@dataset_methods_module)
|
792
|
+
@dataset_methods_module
|
793
|
+
end
|
765
794
|
|
766
|
-
# Define a model method that calls the dataset method with the same name
|
767
|
-
# only used for methods with names that can't be represented directly in
|
768
|
-
# ruby code.
|
795
|
+
# Define a model method that calls the dataset method with the same name.
|
769
796
|
def def_model_dataset_method(meth)
|
770
797
|
return if respond_to?(meth, true)
|
771
798
|
|
799
|
+
mod = dataset_methods_module
|
800
|
+
|
772
801
|
if meth.to_s =~ /\A[A-Za-z_][A-Za-z0-9_]*\z/
|
773
|
-
|
802
|
+
mod.module_eval(<<END, __FILE__, __LINE__ + 1)
|
803
|
+
def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end
|
804
|
+
ruby2_keywords :#{meth} if respond_to?(:ruby2_keywords, true)
|
805
|
+
END
|
774
806
|
else
|
775
|
-
|
807
|
+
mod.send(:define_method, meth){|*args, &block| dataset.public_send(meth, *args, &block)}
|
808
|
+
# :nocov:
|
809
|
+
mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
810
|
+
# :nocov:
|
776
811
|
end
|
777
|
-
|
778
|
-
# :nocov:
|
779
|
-
singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
780
|
-
# :nocov:
|
812
|
+
mod.send(:alias_method, meth, meth)
|
781
813
|
end
|
782
814
|
|
783
815
|
# Get the schema from the database, fall back on checking the columns
|
@@ -943,7 +975,10 @@ module Sequel
|
|
943
975
|
# Module that the class includes that holds methods the class adds for column accessors and
|
944
976
|
# associations so that the methods can be overridden with +super+.
|
945
977
|
def overridable_methods_module
|
946
|
-
|
978
|
+
return @overridable_methods_module if defined?(@overridable_methods_module)
|
979
|
+
mod_name = "#{name}::@overridable_methods_module"
|
980
|
+
Sequel.synchronize{@overridable_methods_module ||= Sequel.set_temp_name(Module.new){mod_name}}
|
981
|
+
include(@overridable_methods_module)
|
947
982
|
@overridable_methods_module
|
948
983
|
end
|
949
984
|
|
@@ -1311,7 +1346,7 @@ module Sequel
|
|
1311
1346
|
# Returns a string representation of the model instance including
|
1312
1347
|
# the class name and values.
|
1313
1348
|
def inspect
|
1314
|
-
"#<#{
|
1349
|
+
"#<#{inspect_prefix} @values=#{inspect_values}>"
|
1315
1350
|
end
|
1316
1351
|
|
1317
1352
|
# Returns the keys in +values+. May not include all column names.
|
@@ -1597,7 +1632,7 @@ module Sequel
|
|
1597
1632
|
@skip_validation_on_next_save = true
|
1598
1633
|
end
|
1599
1634
|
|
1600
|
-
# Returns
|
1635
|
+
# Returns naked dataset that should return only the row related to this instance.
|
1601
1636
|
#
|
1602
1637
|
# Artist[1].this
|
1603
1638
|
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
@@ -1994,7 +2029,12 @@ module Sequel
|
|
1994
2029
|
set(h) unless h.empty?
|
1995
2030
|
end
|
1996
2031
|
|
1997
|
-
# Default
|
2032
|
+
# Default inspect output for the inspect, by default, just showing the class.
|
2033
|
+
def inspect_prefix
|
2034
|
+
model.name
|
2035
|
+
end
|
2036
|
+
|
2037
|
+
# Default inspect output for the values hash, overwrite to change what #inspect displays.
|
1998
2038
|
def inspect_values
|
1999
2039
|
@values.inspect
|
2000
2040
|
end
|
@@ -2238,7 +2278,7 @@ module Sequel
|
|
2238
2278
|
# Return the dataset ordered by the model's primary key. This should not
|
2239
2279
|
# be used if the model does not have a primary key.
|
2240
2280
|
def _force_primary_key_order
|
2241
|
-
cached_dataset(:_pk_order_ds){order(*
|
2281
|
+
cached_dataset(:_pk_order_ds){order(*unambiguous_primary_key)}
|
2242
2282
|
end
|
2243
2283
|
|
2244
2284
|
# If the dataset is not already ordered, and the model has a primary key,
|
@@ -2266,6 +2306,15 @@ module Sequel
|
|
2266
2306
|
end
|
2267
2307
|
end
|
2268
2308
|
|
2309
|
+
# The primary key for the dataset's model, qualified if the dataset is joined.
|
2310
|
+
def unambiguous_primary_key
|
2311
|
+
if joined_dataset?
|
2312
|
+
model.qualified_primary_key
|
2313
|
+
else
|
2314
|
+
model.primary_key
|
2315
|
+
end
|
2316
|
+
end
|
2317
|
+
|
2269
2318
|
def non_sql_option?(key)
|
2270
2319
|
super || key == :model
|
2271
2320
|
end
|
@@ -294,7 +294,7 @@ module Sequel
|
|
294
294
|
sch = klass.db_schema
|
295
295
|
|
296
296
|
if primary_key.is_a?(Array)
|
297
|
-
if (cols = sch.values_at(*klass.primary_key)).all? && (
|
297
|
+
if (cols = sch.values_at(*klass.primary_key)).all? && (cols.map{|c| c[:type] == :integer}).all?
|
298
298
|
db = model.db
|
299
299
|
pks.map do |cpk|
|
300
300
|
cpk.map do |pk|
|
@@ -26,6 +26,12 @@ module Sequel
|
|
26
26
|
# Album.default_values[:a] = lambda{Date.today}
|
27
27
|
# Album.new.a # => Date.today
|
28
28
|
#
|
29
|
+
# If the proc accepts a single argument, it is passed the instance, allowing
|
30
|
+
# default values to depend on instance-specific state:
|
31
|
+
#
|
32
|
+
# Album.default_values[:a] = lambda{|album| album.b + 1}
|
33
|
+
# Album.new(b: 10).a # => 11
|
34
|
+
#
|
29
35
|
# By default, default values returned are not cached:
|
30
36
|
#
|
31
37
|
# Album.new.a.equal?(Album.new.a) # => false
|
@@ -43,13 +49,13 @@ module Sequel
|
|
43
49
|
# album.values # => {}
|
44
50
|
# album.a
|
45
51
|
# album.values # => {:a => Date.today}
|
46
|
-
#
|
52
|
+
#
|
47
53
|
# Usage:
|
48
54
|
#
|
49
55
|
# # Make all model subclass instances set defaults (called before loading subclasses)
|
50
56
|
# Sequel::Model.plugin :defaults_setter
|
51
57
|
#
|
52
|
-
# # Make the Album class set defaults
|
58
|
+
# # Make the Album class set defaults
|
53
59
|
# Album.plugin :defaults_setter
|
54
60
|
module DefaultsSetter
|
55
61
|
# Set the default values based on the model schema. Options:
|
@@ -76,7 +82,7 @@ module Sequel
|
|
76
82
|
def cache_default_values?
|
77
83
|
@cache_default_values
|
78
84
|
end
|
79
|
-
|
85
|
+
|
80
86
|
# Freeze default values when freezing model class
|
81
87
|
def freeze
|
82
88
|
@default_values.freeze
|
@@ -133,7 +139,13 @@ module Sequel
|
|
133
139
|
def [](k)
|
134
140
|
if new? && !values.has_key?(k)
|
135
141
|
v = model.default_values.fetch(k){return}
|
136
|
-
|
142
|
+
if v.respond_to?(:call)
|
143
|
+
v = if v.respond_to?(:arity) && v.arity == 1
|
144
|
+
v.call(self)
|
145
|
+
else
|
146
|
+
v.call
|
147
|
+
end
|
148
|
+
end
|
137
149
|
values[k] = v if model.cache_default_values?
|
138
150
|
v
|
139
151
|
else
|
data/lib/sequel/plugins/enum.rb
CHANGED
@@ -39,6 +39,9 @@ module Sequel
|
|
39
39
|
#
|
40
40
|
# Album.first.artist # no error
|
41
41
|
#
|
42
|
+
# This behavior of enabling +forbid_lazy_load+ automatically from dataset
|
43
|
+
# methods can be disabled using the plugin's +:allow_by_default+ option.
|
44
|
+
#
|
42
45
|
# You can allow lazy loading associations for an instance that it
|
43
46
|
# was previously forbidden for:
|
44
47
|
#
|
@@ -98,7 +101,17 @@ module Sequel
|
|
98
101
|
#
|
99
102
|
# # Make the Album class support forbidding lazy load
|
100
103
|
# Album.plugin :forbid_lazy_load
|
104
|
+
#
|
105
|
+
# # Let lazy loading be forbidden by object, but not automatically for any
|
106
|
+
# # object loaded via dataset.
|
107
|
+
# Album.plugin :forbid_lazy_load, allow_by_default: true
|
101
108
|
module ForbidLazyLoad
|
109
|
+
def self.apply(model, opts=OPTS)
|
110
|
+
unless opts[:allow_by_default]
|
111
|
+
model.send(:dataset_extend, ForbidByDefault, :create_class_methods=>false)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
102
115
|
# Error raised when attempting to lazy load an association when
|
103
116
|
# lazy loading has been forbidden.
|
104
117
|
class Error < StandardError
|
@@ -179,7 +192,7 @@ module Sequel
|
|
179
192
|
end
|
180
193
|
end
|
181
194
|
|
182
|
-
module
|
195
|
+
module ForbidByDefault
|
183
196
|
# Mark model instances retrieved in this call as forbidding lazy loading.
|
184
197
|
def each
|
185
198
|
if row_proc
|
@@ -24,7 +24,7 @@ module Sequel
|
|
24
24
|
# # Make the Album class support input transformers
|
25
25
|
# Album.plugin :input_transformer
|
26
26
|
module InputTransformer
|
27
|
-
def self.apply(model,
|
27
|
+
def self.apply(model, *, &_)
|
28
28
|
model.instance_exec do
|
29
29
|
@input_transformers = {}
|
30
30
|
@skip_input_transformer_columns = {}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The inspect_pk plugin includes the pk right next to the
|
6
|
+
# model name in inspect, allowing for easily copying and
|
7
|
+
# pasting to retrieve a copy of the object:
|
8
|
+
#
|
9
|
+
# Album.with_pk(1).inspect
|
10
|
+
# # default: #<Album @values={...}>
|
11
|
+
# # with inspect_pk: #<Album[1] @values={...}>
|
12
|
+
#
|
13
|
+
# Usage:
|
14
|
+
#
|
15
|
+
# # Make all model instances include pk in inspect output
|
16
|
+
# Sequel::Model.plugin :inspect_pk
|
17
|
+
#
|
18
|
+
# # Make Album instances include pk in inspect output
|
19
|
+
# Album.plugin :inspect_pk
|
20
|
+
module InspectPk
|
21
|
+
module InstanceMethods
|
22
|
+
private
|
23
|
+
|
24
|
+
# The primary key value to include in the inspect output, if any.
|
25
|
+
# For composite primary keys, this only includes a value if all
|
26
|
+
# fields are present.
|
27
|
+
def inspect_pk
|
28
|
+
if primary_key && (pk = self.pk) && (!(Array === pk) || pk.all?)
|
29
|
+
pk
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Include the instance's primary key in the output.
|
34
|
+
def inspect_prefix
|
35
|
+
if v = inspect_pk
|
36
|
+
"#{super}[#{v.inspect}]"
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -102,7 +102,10 @@ module Sequel
|
|
102
102
|
|
103
103
|
# Apply the instance filters to the given dataset
|
104
104
|
def apply_instance_filters(ds)
|
105
|
-
instance_filters.inject(ds)
|
105
|
+
instance_filters.inject(ds) do |ds1, i|
|
106
|
+
block = i[1]
|
107
|
+
ds1.where(*i[0], &block)
|
108
|
+
end
|
106
109
|
end
|
107
110
|
|
108
111
|
# Clear the instance filters.
|
@@ -32,6 +32,7 @@ module Sequel
|
|
32
32
|
def self.apply(model, &block)
|
33
33
|
model.instance_exec do
|
34
34
|
@dataset_module_class = Class.new(@dataset_module_class) do
|
35
|
+
Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(InvertedSubsets)"}
|
35
36
|
include DatasetModuleMethods
|
36
37
|
if block
|
37
38
|
define_method(:inverted_subset_name, &block)
|
@@ -64,7 +64,7 @@ module Sequel
|
|
64
64
|
# :dataset :: The base dataset to use for the lazy attribute lookup
|
65
65
|
# :table :: The table name to use to qualify the attribute and primary key columns.
|
66
66
|
def define_lazy_attribute_getter(a, opts=OPTS)
|
67
|
-
include(@lazy_attributes_module ||= Module.new) unless @lazy_attributes_module
|
67
|
+
include(@lazy_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@lazy_attributes_module"}) unless @lazy_attributes_module
|
68
68
|
@lazy_attributes_module.class_eval do
|
69
69
|
define_method(a) do
|
70
70
|
if !values.has_key?(a) && !new?
|