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.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +9 -4
  3. data/lib/sequel/adapters/ado.rb +1 -1
  4. data/lib/sequel/adapters/ibmdb.rb +1 -0
  5. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/derby.rb +3 -3
  7. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  9. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  10. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  11. data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
  12. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  13. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
  15. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  16. data/lib/sequel/adapters/jdbc.rb +8 -8
  17. data/lib/sequel/adapters/mysql2.rb +8 -1
  18. data/lib/sequel/adapters/shared/access.rb +1 -0
  19. data/lib/sequel/adapters/shared/db2.rb +1 -1
  20. data/lib/sequel/adapters/shared/mssql.rb +18 -5
  21. data/lib/sequel/adapters/shared/mysql.rb +8 -4
  22. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  23. data/lib/sequel/adapters/shared/postgres.rb +106 -13
  24. data/lib/sequel/adapters/shared/sqlite.rb +4 -2
  25. data/lib/sequel/adapters/sqlite.rb +4 -0
  26. data/lib/sequel/adapters/trilogy.rb +1 -2
  27. data/lib/sequel/connection_pool/sharded_threaded.rb +26 -10
  28. data/lib/sequel/connection_pool/threaded.rb +26 -10
  29. data/lib/sequel/connection_pool.rb +2 -2
  30. data/lib/sequel/core.rb +15 -0
  31. data/lib/sequel/database/connecting.rb +20 -26
  32. data/lib/sequel/database/dataset_defaults.rb +3 -3
  33. data/lib/sequel/database/misc.rb +46 -10
  34. data/lib/sequel/database/query.rb +11 -11
  35. data/lib/sequel/database/schema_generator.rb +8 -0
  36. data/lib/sequel/database/schema_methods.rb +17 -1
  37. data/lib/sequel/dataset/actions.rb +9 -1
  38. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  39. data/lib/sequel/dataset/prepared_statements.rb +2 -1
  40. data/lib/sequel/dataset/query.rb +9 -5
  41. data/lib/sequel/dataset/sql.rb +25 -5
  42. data/lib/sequel/extensions/caller_logging.rb +2 -0
  43. data/lib/sequel/extensions/connection_validator.rb +15 -10
  44. data/lib/sequel/extensions/dataset_run.rb +41 -0
  45. data/lib/sequel/extensions/migration.rb +23 -3
  46. data/lib/sequel/extensions/null_dataset.rb +2 -2
  47. data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
  48. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
  49. data/lib/sequel/extensions/pg_enum.rb +3 -3
  50. data/lib/sequel/extensions/pg_json_ops.rb +642 -9
  51. data/lib/sequel/extensions/pg_row.rb +3 -1
  52. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  53. data/lib/sequel/extensions/provenance.rb +2 -0
  54. data/lib/sequel/extensions/query_blocker.rb +172 -0
  55. data/lib/sequel/extensions/schema_caching.rb +24 -9
  56. data/lib/sequel/extensions/schema_dumper.rb +16 -4
  57. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  58. data/lib/sequel/extensions/stdio_logger.rb +48 -0
  59. data/lib/sequel/extensions/string_agg.rb +17 -4
  60. data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
  61. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  62. data/lib/sequel/model/associations.rb +28 -3
  63. data/lib/sequel/model/base.rb +67 -18
  64. data/lib/sequel/plugins/association_pks.rb +1 -1
  65. data/lib/sequel/plugins/column_encryption.rb +1 -1
  66. data/lib/sequel/plugins/composition.rb +1 -1
  67. data/lib/sequel/plugins/defaults_setter.rb +16 -4
  68. data/lib/sequel/plugins/enum.rb +1 -1
  69. data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
  70. data/lib/sequel/plugins/input_transformer.rb +1 -1
  71. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  72. data/lib/sequel/plugins/instance_filters.rb +4 -1
  73. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  74. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  75. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  76. data/lib/sequel/plugins/optimistic_locking.rb +2 -0
  77. data/lib/sequel/plugins/paged_operations.rb +5 -2
  78. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  79. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  80. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  81. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  82. data/lib/sequel/plugins/serialization.rb +11 -5
  83. data/lib/sequel/plugins/sql_comments.rb +7 -2
  84. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  85. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  86. data/lib/sequel/plugins/subset_static_cache.rb +263 -0
  87. data/lib/sequel/plugins/tactical_eager_loading.rb +6 -2
  88. data/lib/sequel/plugins/validate_associated.rb +1 -1
  89. data/lib/sequel/sql.rb +16 -6
  90. data/lib/sequel/version.rb +1 -1
  91. metadata +12 -234
  92. data/CHANGELOG +0 -1355
  93. data/README.rdoc +0 -936
  94. data/doc/advanced_associations.rdoc +0 -884
  95. data/doc/association_basics.rdoc +0 -1859
  96. data/doc/bin_sequel.rdoc +0 -146
  97. data/doc/cheat_sheet.rdoc +0 -255
  98. data/doc/code_order.rdoc +0 -102
  99. data/doc/core_extensions.rdoc +0 -405
  100. data/doc/dataset_basics.rdoc +0 -96
  101. data/doc/dataset_filtering.rdoc +0 -222
  102. data/doc/extensions.rdoc +0 -77
  103. data/doc/fork_safety.rdoc +0 -84
  104. data/doc/mass_assignment.rdoc +0 -98
  105. data/doc/migration.rdoc +0 -660
  106. data/doc/model_dataset_method_design.rdoc +0 -129
  107. data/doc/model_hooks.rdoc +0 -254
  108. data/doc/model_plugins.rdoc +0 -270
  109. data/doc/mssql_stored_procedures.rdoc +0 -43
  110. data/doc/object_model.rdoc +0 -563
  111. data/doc/opening_databases.rdoc +0 -436
  112. data/doc/postgresql.rdoc +0 -611
  113. data/doc/prepared_statements.rdoc +0 -144
  114. data/doc/querying.rdoc +0 -1070
  115. data/doc/reflection.rdoc +0 -120
  116. data/doc/release_notes/5.0.0.txt +0 -159
  117. data/doc/release_notes/5.1.0.txt +0 -31
  118. data/doc/release_notes/5.10.0.txt +0 -84
  119. data/doc/release_notes/5.11.0.txt +0 -83
  120. data/doc/release_notes/5.12.0.txt +0 -141
  121. data/doc/release_notes/5.13.0.txt +0 -27
  122. data/doc/release_notes/5.14.0.txt +0 -63
  123. data/doc/release_notes/5.15.0.txt +0 -39
  124. data/doc/release_notes/5.16.0.txt +0 -110
  125. data/doc/release_notes/5.17.0.txt +0 -31
  126. data/doc/release_notes/5.18.0.txt +0 -69
  127. data/doc/release_notes/5.19.0.txt +0 -28
  128. data/doc/release_notes/5.2.0.txt +0 -33
  129. data/doc/release_notes/5.20.0.txt +0 -89
  130. data/doc/release_notes/5.21.0.txt +0 -87
  131. data/doc/release_notes/5.22.0.txt +0 -48
  132. data/doc/release_notes/5.23.0.txt +0 -56
  133. data/doc/release_notes/5.24.0.txt +0 -56
  134. data/doc/release_notes/5.25.0.txt +0 -32
  135. data/doc/release_notes/5.26.0.txt +0 -35
  136. data/doc/release_notes/5.27.0.txt +0 -21
  137. data/doc/release_notes/5.28.0.txt +0 -16
  138. data/doc/release_notes/5.29.0.txt +0 -22
  139. data/doc/release_notes/5.3.0.txt +0 -121
  140. data/doc/release_notes/5.30.0.txt +0 -20
  141. data/doc/release_notes/5.31.0.txt +0 -148
  142. data/doc/release_notes/5.32.0.txt +0 -46
  143. data/doc/release_notes/5.33.0.txt +0 -24
  144. data/doc/release_notes/5.34.0.txt +0 -40
  145. data/doc/release_notes/5.35.0.txt +0 -56
  146. data/doc/release_notes/5.36.0.txt +0 -60
  147. data/doc/release_notes/5.37.0.txt +0 -30
  148. data/doc/release_notes/5.38.0.txt +0 -28
  149. data/doc/release_notes/5.39.0.txt +0 -19
  150. data/doc/release_notes/5.4.0.txt +0 -80
  151. data/doc/release_notes/5.40.0.txt +0 -40
  152. data/doc/release_notes/5.41.0.txt +0 -25
  153. data/doc/release_notes/5.42.0.txt +0 -136
  154. data/doc/release_notes/5.43.0.txt +0 -98
  155. data/doc/release_notes/5.44.0.txt +0 -32
  156. data/doc/release_notes/5.45.0.txt +0 -34
  157. data/doc/release_notes/5.46.0.txt +0 -87
  158. data/doc/release_notes/5.47.0.txt +0 -59
  159. data/doc/release_notes/5.48.0.txt +0 -14
  160. data/doc/release_notes/5.49.0.txt +0 -59
  161. data/doc/release_notes/5.5.0.txt +0 -61
  162. data/doc/release_notes/5.50.0.txt +0 -78
  163. data/doc/release_notes/5.51.0.txt +0 -47
  164. data/doc/release_notes/5.52.0.txt +0 -87
  165. data/doc/release_notes/5.53.0.txt +0 -23
  166. data/doc/release_notes/5.54.0.txt +0 -27
  167. data/doc/release_notes/5.55.0.txt +0 -21
  168. data/doc/release_notes/5.56.0.txt +0 -51
  169. data/doc/release_notes/5.57.0.txt +0 -23
  170. data/doc/release_notes/5.58.0.txt +0 -31
  171. data/doc/release_notes/5.59.0.txt +0 -73
  172. data/doc/release_notes/5.6.0.txt +0 -31
  173. data/doc/release_notes/5.60.0.txt +0 -22
  174. data/doc/release_notes/5.61.0.txt +0 -43
  175. data/doc/release_notes/5.62.0.txt +0 -132
  176. data/doc/release_notes/5.63.0.txt +0 -33
  177. data/doc/release_notes/5.64.0.txt +0 -50
  178. data/doc/release_notes/5.65.0.txt +0 -21
  179. data/doc/release_notes/5.66.0.txt +0 -24
  180. data/doc/release_notes/5.67.0.txt +0 -32
  181. data/doc/release_notes/5.68.0.txt +0 -61
  182. data/doc/release_notes/5.69.0.txt +0 -26
  183. data/doc/release_notes/5.7.0.txt +0 -108
  184. data/doc/release_notes/5.70.0.txt +0 -35
  185. data/doc/release_notes/5.71.0.txt +0 -21
  186. data/doc/release_notes/5.72.0.txt +0 -33
  187. data/doc/release_notes/5.73.0.txt +0 -66
  188. data/doc/release_notes/5.74.0.txt +0 -45
  189. data/doc/release_notes/5.75.0.txt +0 -35
  190. data/doc/release_notes/5.76.0.txt +0 -86
  191. data/doc/release_notes/5.77.0.txt +0 -63
  192. data/doc/release_notes/5.78.0.txt +0 -67
  193. data/doc/release_notes/5.79.0.txt +0 -28
  194. data/doc/release_notes/5.8.0.txt +0 -170
  195. data/doc/release_notes/5.80.0.txt +0 -40
  196. data/doc/release_notes/5.9.0.txt +0 -99
  197. data/doc/schema_modification.rdoc +0 -679
  198. data/doc/security.rdoc +0 -443
  199. data/doc/sharding.rdoc +0 -286
  200. data/doc/sql.rdoc +0 -648
  201. data/doc/testing.rdoc +0 -190
  202. data/doc/thread_safety.rdoc +0 -15
  203. data/doc/transactions.rdoc +0 -250
  204. data/doc/validations.rdoc +0 -558
  205. 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
- apply_eager_limit_strategy(ds.where(predicate_key=>pl.arg), eager_limit_strategy)
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)
@@ -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.[] or Model.first instead of this method.
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
- instance_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
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
- define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
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
- singleton_class.send(:alias_method, meth, meth)
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
- include(@overridable_methods_module = Module.new) unless @overridable_methods_module
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
- "#<#{model.name} @values=#{inspect_values}>"
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 (naked) dataset that should return only this instance.
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 inspection output for the values hash, overwrite to change what #inspect displays.
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(*model.primary_key)}
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? && (convs = cols.map{|c| c[:type] == :integer}).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|
@@ -586,7 +586,7 @@ module Sequel
586
586
  end
587
587
  end
588
588
 
589
- def self.apply(model, opts=OPTS)
589
+ def self.apply(model, opts=OPTS, &_)
590
590
  model.plugin :serialization
591
591
  end
592
592
 
@@ -61,7 +61,7 @@ module Sequel
61
61
  def self.apply(model)
62
62
  model.instance_exec do
63
63
  @compositions = {}
64
- include(@composition_module ||= Module.new)
64
+ include(@composition_module ||= Sequel.set_temp_name(Module.new){"#{name}::@composition_module"})
65
65
  end
66
66
  end
67
67
 
@@ -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
- v = v.call if v.respond_to?(:call)
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
@@ -80,7 +80,7 @@ module Sequel
80
80
  inverted = values.invert.freeze
81
81
 
82
82
  unless @enum_methods
83
- @enum_methods = Module.new
83
+ @enum_methods = Sequel.set_temp_name(Module.new){"#{name}::@enum_methods"}
84
84
  include @enum_methods
85
85
  end
86
86
 
@@ -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 DatasetMethods
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){|ds1, i| ds1.where(*i[0], &i[1])}
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?