sequel 5.39.0 → 5.72.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +408 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +13 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +26 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.40.0.txt +40 -0
  17. data/doc/release_notes/5.41.0.txt +25 -0
  18. data/doc/release_notes/5.42.0.txt +136 -0
  19. data/doc/release_notes/5.43.0.txt +98 -0
  20. data/doc/release_notes/5.44.0.txt +32 -0
  21. data/doc/release_notes/5.45.0.txt +34 -0
  22. data/doc/release_notes/5.46.0.txt +87 -0
  23. data/doc/release_notes/5.47.0.txt +59 -0
  24. data/doc/release_notes/5.48.0.txt +14 -0
  25. data/doc/release_notes/5.49.0.txt +59 -0
  26. data/doc/release_notes/5.50.0.txt +78 -0
  27. data/doc/release_notes/5.51.0.txt +47 -0
  28. data/doc/release_notes/5.52.0.txt +87 -0
  29. data/doc/release_notes/5.53.0.txt +23 -0
  30. data/doc/release_notes/5.54.0.txt +27 -0
  31. data/doc/release_notes/5.55.0.txt +21 -0
  32. data/doc/release_notes/5.56.0.txt +51 -0
  33. data/doc/release_notes/5.57.0.txt +23 -0
  34. data/doc/release_notes/5.58.0.txt +31 -0
  35. data/doc/release_notes/5.59.0.txt +73 -0
  36. data/doc/release_notes/5.60.0.txt +22 -0
  37. data/doc/release_notes/5.61.0.txt +43 -0
  38. data/doc/release_notes/5.62.0.txt +132 -0
  39. data/doc/release_notes/5.63.0.txt +33 -0
  40. data/doc/release_notes/5.64.0.txt +50 -0
  41. data/doc/release_notes/5.65.0.txt +21 -0
  42. data/doc/release_notes/5.66.0.txt +24 -0
  43. data/doc/release_notes/5.67.0.txt +32 -0
  44. data/doc/release_notes/5.68.0.txt +61 -0
  45. data/doc/release_notes/5.69.0.txt +26 -0
  46. data/doc/release_notes/5.70.0.txt +35 -0
  47. data/doc/release_notes/5.71.0.txt +21 -0
  48. data/doc/release_notes/5.72.0.txt +33 -0
  49. data/doc/schema_modification.rdoc +1 -1
  50. data/doc/security.rdoc +9 -9
  51. data/doc/sharding.rdoc +3 -1
  52. data/doc/sql.rdoc +28 -16
  53. data/doc/testing.rdoc +22 -11
  54. data/doc/transactions.rdoc +6 -6
  55. data/doc/virtual_rows.rdoc +2 -2
  56. data/lib/sequel/adapters/ado/access.rb +1 -1
  57. data/lib/sequel/adapters/ado.rb +17 -17
  58. data/lib/sequel/adapters/amalgalite.rb +3 -5
  59. data/lib/sequel/adapters/ibmdb.rb +2 -2
  60. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  61. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  62. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  64. data/lib/sequel/adapters/jdbc.rb +16 -18
  65. data/lib/sequel/adapters/mysql.rb +92 -67
  66. data/lib/sequel/adapters/mysql2.rb +54 -49
  67. data/lib/sequel/adapters/odbc.rb +6 -2
  68. data/lib/sequel/adapters/oracle.rb +4 -3
  69. data/lib/sequel/adapters/postgres.rb +83 -40
  70. data/lib/sequel/adapters/shared/access.rb +11 -1
  71. data/lib/sequel/adapters/shared/db2.rb +30 -0
  72. data/lib/sequel/adapters/shared/mssql.rb +90 -9
  73. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  74. data/lib/sequel/adapters/shared/oracle.rb +82 -1
  75. data/lib/sequel/adapters/shared/postgres.rb +496 -178
  76. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  77. data/lib/sequel/adapters/shared/sqlite.rb +116 -11
  78. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  79. data/lib/sequel/adapters/sqlite.rb +60 -18
  80. data/lib/sequel/adapters/tinytds.rb +1 -1
  81. data/lib/sequel/adapters/trilogy.rb +117 -0
  82. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  83. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  84. data/lib/sequel/ast_transformer.rb +6 -0
  85. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  86. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  87. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  88. data/lib/sequel/connection_pool/single.rb +6 -8
  89. data/lib/sequel/connection_pool/threaded.rb +14 -8
  90. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  91. data/lib/sequel/connection_pool.rb +55 -31
  92. data/lib/sequel/core.rb +28 -18
  93. data/lib/sequel/database/connecting.rb +27 -3
  94. data/lib/sequel/database/dataset.rb +16 -6
  95. data/lib/sequel/database/misc.rb +69 -14
  96. data/lib/sequel/database/query.rb +73 -2
  97. data/lib/sequel/database/schema_generator.rb +46 -53
  98. data/lib/sequel/database/schema_methods.rb +18 -2
  99. data/lib/sequel/dataset/actions.rb +108 -14
  100. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  101. data/lib/sequel/dataset/features.rb +20 -0
  102. data/lib/sequel/dataset/misc.rb +12 -2
  103. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  104. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  105. data/lib/sequel/dataset/query.rb +171 -44
  106. data/lib/sequel/dataset/sql.rb +182 -47
  107. data/lib/sequel/dataset.rb +4 -0
  108. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  109. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  110. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  111. data/lib/sequel/extensions/async_thread_pool.rb +439 -0
  112. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  113. data/lib/sequel/extensions/blank.rb +8 -0
  114. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  115. data/lib/sequel/extensions/connection_validator.rb +16 -11
  116. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  117. data/lib/sequel/extensions/core_refinements.rb +36 -11
  118. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  119. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  120. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  121. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  122. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  123. data/lib/sequel/extensions/index_caching.rb +5 -1
  124. data/lib/sequel/extensions/inflector.rb +9 -1
  125. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  126. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  127. data/lib/sequel/extensions/migration.rb +11 -2
  128. data/lib/sequel/extensions/named_timezones.rb +26 -6
  129. data/lib/sequel/extensions/pagination.rb +1 -1
  130. data/lib/sequel/extensions/pg_array.rb +32 -4
  131. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  132. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  133. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  134. data/lib/sequel/extensions/pg_enum.rb +2 -3
  135. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  136. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  137. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  138. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  139. data/lib/sequel/extensions/pg_inet.rb +10 -11
  140. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  141. data/lib/sequel/extensions/pg_interval.rb +45 -19
  142. data/lib/sequel/extensions/pg_json.rb +13 -15
  143. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  144. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +11 -24
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +21 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/query.rb +2 -0
  151. data/lib/sequel/extensions/s.rb +2 -1
  152. data/lib/sequel/extensions/schema_caching.rb +1 -1
  153. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  154. data/lib/sequel/extensions/server_block.rb +10 -13
  155. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  156. data/lib/sequel/extensions/sql_comments.rb +110 -3
  157. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  158. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  159. data/lib/sequel/extensions/string_agg.rb +1 -1
  160. data/lib/sequel/extensions/string_date_time.rb +19 -23
  161. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  162. data/lib/sequel/model/associations.rb +345 -101
  163. data/lib/sequel/model/base.rb +51 -27
  164. data/lib/sequel/model/dataset_module.rb +3 -0
  165. data/lib/sequel/model/errors.rb +10 -1
  166. data/lib/sequel/model/inflections.rb +1 -1
  167. data/lib/sequel/model/plugins.rb +5 -0
  168. data/lib/sequel/plugins/association_proxies.rb +2 -0
  169. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +87 -15
  172. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  173. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  174. data/lib/sequel/plugins/column_encryption.rb +728 -0
  175. data/lib/sequel/plugins/composition.rb +10 -4
  176. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  177. data/lib/sequel/plugins/constraint_validations.rb +10 -6
  178. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  179. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  180. data/lib/sequel/plugins/dirty.rb +1 -1
  181. data/lib/sequel/plugins/enum.rb +124 -0
  182. data/lib/sequel/plugins/finder.rb +4 -2
  183. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  184. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  185. data/lib/sequel/plugins/json_serializer.rb +39 -24
  186. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  187. data/lib/sequel/plugins/list.rb +3 -1
  188. data/lib/sequel/plugins/many_through_many.rb +109 -10
  189. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  190. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  191. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  192. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  193. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  194. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
  195. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  196. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  197. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  198. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  199. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  200. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  201. data/lib/sequel/plugins/serialization.rb +9 -3
  202. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  203. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  204. data/lib/sequel/plugins/sql_comments.rb +189 -0
  205. data/lib/sequel/plugins/static_cache.rb +39 -1
  206. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  207. data/lib/sequel/plugins/subclasses.rb +28 -11
  208. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  209. data/lib/sequel/plugins/timestamps.rb +1 -1
  210. data/lib/sequel/plugins/unused_associations.rb +521 -0
  211. data/lib/sequel/plugins/update_or_create.rb +1 -1
  212. data/lib/sequel/plugins/validate_associated.rb +22 -12
  213. data/lib/sequel/plugins/validation_helpers.rb +46 -12
  214. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  215. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  216. data/lib/sequel/sql.rb +1 -1
  217. data/lib/sequel/timezones.rb +12 -14
  218. data/lib/sequel/version.rb +1 -1
  219. metadata +132 -38
@@ -0,0 +1,270 @@
1
+ # frozen-string-literal: true
2
+
3
+ # :nocov:
4
+ raise LoadError, "Sequel::TimedQueueConnectionPool is only available on Ruby 3.2+" unless RUBY_VERSION >= '3.2'
5
+ # :nocov:
6
+
7
+ # A connection pool allowing multi-threaded access to a pool of connections,
8
+ # using a timed queue (only available in Ruby 3.2+).
9
+ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
10
+ # The maximum number of connections this pool will create.
11
+ attr_reader :max_size
12
+
13
+ # The following additional options are respected:
14
+ # :max_connections :: The maximum number of connections the connection pool
15
+ # will open (default 4)
16
+ # :pool_timeout :: The amount of seconds to wait to acquire a connection
17
+ # before raising a PoolTimeout (default 5)
18
+ def initialize(db, opts = OPTS)
19
+ super
20
+ @max_size = Integer(opts[:max_connections] || 4)
21
+ raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
22
+ @mutex = Mutex.new
23
+ # Size inside array so this still works while the pool is frozen.
24
+ @size = [0]
25
+ @allocated = {}
26
+ @allocated.compare_by_identity
27
+ @timeout = Float(opts[:pool_timeout] || 5)
28
+ @queue = Queue.new
29
+ end
30
+
31
+ # Yield all of the available connections, and the one currently allocated to
32
+ # this thread. This will not yield connections currently allocated to other
33
+ # threads, as it is not safe to operate on them.
34
+ def all_connections
35
+ hold do |conn|
36
+ yield conn
37
+
38
+ # Use a hash to record all connections already seen. As soon as we
39
+ # come across a connection we've already seen, we stop the loop.
40
+ conns = {}
41
+ conns.compare_by_identity
42
+ while true
43
+ conn = nil
44
+ begin
45
+ break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
46
+ conns[conn] = true
47
+ yield conn
48
+ ensure
49
+ @queue.push(conn) if conn
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # Removes all connections currently in the pool's queue. This method has the effect of
56
+ # disconnecting from the database, assuming that no connections are currently
57
+ # being used.
58
+ #
59
+ # Once a connection is requested using #hold, the connection pool
60
+ # creates new connections to the database.
61
+ def disconnect(opts=OPTS)
62
+ while conn = @queue.pop(timeout: 0)
63
+ disconnect_connection(conn)
64
+ end
65
+ fill_queue
66
+ nil
67
+ end
68
+
69
+ # Chooses the first available connection, or if none are
70
+ # available, creates a new connection. Passes the connection to the supplied
71
+ # block:
72
+ #
73
+ # pool.hold {|conn| conn.execute('DROP TABLE posts')}
74
+ #
75
+ # Pool#hold is re-entrant, meaning it can be called recursively in
76
+ # the same thread without blocking.
77
+ #
78
+ # If no connection is immediately available and the pool is already using the maximum
79
+ # number of connections, Pool#hold will block until a connection
80
+ # is available or the timeout expires. If the timeout expires before a
81
+ # connection can be acquired, a Sequel::PoolTimeout is raised.
82
+ def hold(server=nil)
83
+ t = Sequel.current
84
+ if conn = owned_connection(t)
85
+ return yield(conn)
86
+ end
87
+
88
+ begin
89
+ conn = acquire(t)
90
+ yield conn
91
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
92
+ if disconnect_error?(e)
93
+ oconn = conn
94
+ conn = nil
95
+ disconnect_connection(oconn) if oconn
96
+ sync{@allocated.delete(t)}
97
+ fill_queue
98
+ end
99
+ raise
100
+ ensure
101
+ release(t) if conn
102
+ end
103
+ end
104
+
105
+ def pool_type
106
+ :timed_queue
107
+ end
108
+
109
+ # The total number of connections in the pool.
110
+ def size
111
+ sync{@size[0]}
112
+ end
113
+
114
+ private
115
+
116
+ # Create a new connection, after the pool's current size has already
117
+ # been updated to account for the new connection. If there is an exception
118
+ # when creating the connection, decrement the current size.
119
+ #
120
+ # This should only be called after can_make_new?. If there is an exception
121
+ # between when can_make_new? is called and when preallocated_make_new
122
+ # is called, it has the effect of reducing the maximum size of the
123
+ # connection pool by 1, since the current size of the pool will show a
124
+ # higher number than the number of connections allocated or
125
+ # in the queue.
126
+ #
127
+ # Calling code should not have the mutex when calling this.
128
+ def preallocated_make_new
129
+ make_new(:default)
130
+ rescue Exception
131
+ sync{@size[0] -= 1}
132
+ raise
133
+ end
134
+
135
+ # Decrement the current size of the pool when disconnecting connections.
136
+ #
137
+ # Calling code should not have the mutex when calling this.
138
+ def disconnect_connection(conn)
139
+ sync{@size[0] -= 1}
140
+ super
141
+ end
142
+
143
+ # If there are any threads waiting on the queue, try to create
144
+ # new connections in a separate thread if the pool is not yet at the
145
+ # maximum size.
146
+ #
147
+ # The reason for this method is to handle cases where acquire
148
+ # could not retrieve a connection immediately, and the pool
149
+ # was already at the maximum size. In that case, the acquire will
150
+ # wait on the queue until the timeout. This method is called
151
+ # after disconnecting to potentially add new connections to the
152
+ # pool, so the threads that are currently waiting for connections
153
+ # do not timeout after the pool is no longer full.
154
+ def fill_queue
155
+ if @queue.num_waiting > 0
156
+ Thread.new do
157
+ while @queue.num_waiting > 0 && (conn = try_make_new)
158
+ @queue.push(conn)
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ # Whether the given size is less than the maximum size of the pool.
165
+ # In that case, the pool's current size is incremented. If this
166
+ # method returns true, space in the pool for the connection is
167
+ # preallocated, and preallocated_make_new should be called to
168
+ # create the connection.
169
+ #
170
+ # Calling code should have the mutex when calling this.
171
+ def can_make_new?(current_size)
172
+ if @max_size > current_size
173
+ @size[0] += 1
174
+ end
175
+ end
176
+
177
+ # Try to make a new connection if there is space in the pool.
178
+ # If the pool is already full, look for dead threads/fibers and
179
+ # disconnect the related connections.
180
+ #
181
+ # Calling code should not have the mutex when calling this.
182
+ def try_make_new
183
+ return preallocated_make_new if sync{can_make_new?(@size[0])}
184
+
185
+ to_disconnect = nil
186
+ do_make_new = false
187
+
188
+ sync do
189
+ current_size = @size[0]
190
+ @allocated.keys.each do |t|
191
+ unless t.alive?
192
+ (to_disconnect ||= []) << @allocated.delete(t)
193
+ current_size -= 1
194
+ end
195
+ end
196
+
197
+ do_make_new = true if can_make_new?(current_size)
198
+ end
199
+
200
+ begin
201
+ preallocated_make_new if do_make_new
202
+ ensure
203
+ if to_disconnect
204
+ to_disconnect.each{|conn| disconnect_connection(conn)}
205
+ fill_queue
206
+ end
207
+ end
208
+ end
209
+
210
+ # Assigns a connection to the supplied thread, if one
211
+ # is available.
212
+ #
213
+ # This should return a connection is one is available within the timeout,
214
+ # or raise PoolTimeout if a connection could not be acquired within the timeout.
215
+ #
216
+ # Calling code should not have the mutex when calling this.
217
+ def acquire(thread)
218
+ if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
219
+ sync{@allocated[thread] = conn}
220
+ else
221
+ name = db.opts[:name]
222
+ raise ::Sequel::PoolTimeout, "timeout: #{@timeout}#{", database name: #{name}" if name}"
223
+ end
224
+ end
225
+
226
+ # Returns the connection owned by the supplied thread,
227
+ # if any. The calling code should NOT already have the mutex before calling this.
228
+ def owned_connection(thread)
229
+ sync{@allocated[thread]}
230
+ end
231
+
232
+ # Create the maximum number of connections immediately. This should not be called
233
+ # with a true argument unless no code is currently operating on the database.
234
+ #
235
+ # Calling code should not have the mutex when calling this.
236
+ def preconnect(concurrent = false)
237
+ if concurrent
238
+ if times = sync{@max_size > (size = @size[0]) ? @max_size - size : false}
239
+ times.times.map{Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
240
+ end
241
+ else
242
+ while conn = try_make_new
243
+ @queue.push(conn)
244
+ end
245
+ end
246
+
247
+ nil
248
+ end
249
+
250
+ # Releases the connection assigned to the supplied thread back to the pool.
251
+ #
252
+ # Calling code should not have the mutex when calling this.
253
+ def release(thread)
254
+ checkin_connection(sync{@allocated.delete(thread)})
255
+ nil
256
+ end
257
+
258
+ # Adds a connection to the queue of available connections, returns the connection.
259
+ def checkin_connection(conn)
260
+ @queue.push(conn)
261
+ conn
262
+ end
263
+
264
+ # Yield to the block while inside the mutex.
265
+ #
266
+ # Calling code should not have the mutex when calling this.
267
+ def sync
268
+ @mutex.synchronize{yield}
269
+ end
270
+ end
@@ -30,8 +30,12 @@ class Sequel::ConnectionPool
30
30
  :threaded => :ThreadedConnectionPool,
31
31
  :single => :SingleConnectionPool,
32
32
  :sharded_threaded => :ShardedThreadedConnectionPool,
33
- :sharded_single => :ShardedSingleConnectionPool
34
- }.freeze
33
+ :sharded_single => :ShardedSingleConnectionPool,
34
+ :timed_queue => :TimedQueueConnectionPool,
35
+ :sharded_timed_queue => :ShardedTimedQueueConnectionPool,
36
+ }
37
+ POOL_CLASS_MAP.to_a.each{|k, v| POOL_CLASS_MAP[k.to_s] = v}
38
+ POOL_CLASS_MAP.freeze
35
39
 
36
40
  # Class methods used to return an appropriate pool subclass, separated
37
41
  # into a module for easier overridding by extensions.
@@ -39,7 +43,8 @@ class Sequel::ConnectionPool
39
43
  # Return a pool subclass instance based on the given options. If a <tt>:pool_class</tt>
40
44
  # option is provided is provided, use that pool class, otherwise
41
45
  # use a new instance of an appropriate pool subclass based on the
42
- # <tt>:single_threaded</tt> and <tt>:servers</tt> options.
46
+ # +SEQUEL_DEFAULT_CONNECTION_POOL+ environment variable if set, or
47
+ # the <tt>:single_threaded</tt> and <tt>:servers</tt> options, otherwise.
43
48
  def get_pool(db, opts = OPTS)
44
49
  connection_pool_class(opts).new(db, opts)
45
50
  end
@@ -59,9 +64,14 @@ class Sequel::ConnectionPool
59
64
  end
60
65
 
61
66
  pc
67
+ elsif pc = ENV['SEQUEL_DEFAULT_CONNECTION_POOL']
68
+ pc = "sharded_#{pc}" if opts[:servers] && !pc.start_with?('sharded_')
69
+ connection_pool_class(:pool_class=>pc)
62
70
  else
63
71
  pc = if opts[:single_threaded]
64
72
  opts[:servers] ? :sharded_single : :single
73
+ #elsif RUBY_VERSION >= '3.2' # SEQUEL6 or maybe earlier
74
+ # opts[:servers] ? :sharded_timed_queue : :timed_queue
65
75
  else
66
76
  opts[:servers] ? :sharded_threaded : :threaded
67
77
  end
@@ -74,26 +84,35 @@ class Sequel::ConnectionPool
74
84
 
75
85
  # The after_connect proc used for this pool. This is called with each new
76
86
  # connection made, and is usually used to set custom per-connection settings.
77
- attr_accessor :after_connect
87
+ # Deprecated.
88
+ attr_reader :after_connect # SEQUEL6: Remove
89
+
90
+ # Override the after_connect proc for the connection pool. Deprecated.
91
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
92
+ def after_connect=(v) # SEQUEL6: Remove
93
+ @use_old_connect_api = true
94
+ @after_connect = v
95
+ end
78
96
 
79
- # An array of sql strings to execute on each new connection.
80
- attr_accessor :connect_sqls
97
+ # An array of sql strings to execute on each new connection. Deprecated.
98
+ attr_reader :connect_sqls # SEQUEL6: Remove
99
+
100
+ # Override the connect_sqls for the connection pool. Deprecated.
101
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
102
+ def connect_sqls=(v) # SEQUEL6: Remove
103
+ @use_old_connect_api = true
104
+ @connect_sqls = v
105
+ end
81
106
 
82
107
  # The Sequel::Database object tied to this connection pool.
83
108
  attr_accessor :db
84
109
 
85
- # Instantiates a connection pool with the given options. The block is called
86
- # with a single symbol (specifying the server/shard to use) every time a new
87
- # connection is needed. The following options are respected for all connection
88
- # pools:
89
- # :after_connect :: A callable object called after each new connection is made, with the
90
- # connection object (and server argument if the callable accepts 2 arguments),
91
- # useful for customizations that you want to apply to all connections.
92
- # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
93
- def initialize(db, opts=OPTS)
110
+ # Instantiates a connection pool with the given Database and options.
111
+ def initialize(db, opts=OPTS) # SEQUEL6: Remove second argument, always use db.opts
94
112
  @db = db
95
- @after_connect = opts[:after_connect]
96
- @connect_sqls = opts[:connect_sqls]
113
+ @use_old_connect_api = false # SEQUEL6: Remove
114
+ @after_connect = opts[:after_connect] # SEQUEL6: Remove
115
+ @connect_sqls = opts[:connect_sqls] # SEQUEL6: Remove
97
116
  @error_classes = db.send(:database_error_classes).dup.freeze
98
117
  end
99
118
 
@@ -119,25 +138,30 @@ class Sequel::ConnectionPool
119
138
  # and checking for connection errors.
120
139
  def make_new(server)
121
140
  begin
122
- conn = @db.connect(server)
141
+ if @use_old_connect_api
142
+ # SEQUEL6: Remove block
143
+ conn = @db.connect(server)
123
144
 
124
- if ac = @after_connect
125
- if ac.arity == 2
126
- ac.call(conn, server)
127
- else
128
- ac.call(conn)
145
+ if ac = @after_connect
146
+ if ac.arity == 2
147
+ ac.call(conn, server)
148
+ else
149
+ ac.call(conn)
150
+ end
129
151
  end
130
- end
131
-
132
- if cs = @connect_sqls
133
- cs.each do |sql|
134
- db.send(:log_connection_execute, conn, sql)
152
+
153
+ if cs = @connect_sqls
154
+ cs.each do |sql|
155
+ db.send(:log_connection_execute, conn, sql)
156
+ end
135
157
  end
158
+
159
+ conn
160
+ else
161
+ @db.new_connection(server)
136
162
  end
137
163
  rescue Exception=>exception
138
164
  raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
139
- end
140
- raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
141
- conn
165
+ end || raise(Sequel::DatabaseConnectionError, "Connection parameters not valid")
142
166
  end
143
167
  end
data/lib/sequel/core.rb CHANGED
@@ -176,6 +176,17 @@ module Sequel
176
176
  JSON.parse(json, :create_additions=>false)
177
177
  end
178
178
 
179
+ # If a mutex is given, synchronize access using it. If nil is given, just
180
+ # yield to the block. This is designed for cases where a mutex may or may
181
+ # not be provided.
182
+ def synchronize_with(mutex)
183
+ if mutex
184
+ mutex.synchronize{yield}
185
+ else
186
+ yield
187
+ end
188
+ end
189
+
179
190
  # Convert each item in the array to the correct type, handling multi-dimensional
180
191
  # arrays. For each element in the array or subarrays, call the converter,
181
192
  # unless the value is nil.
@@ -267,11 +278,9 @@ module Sequel
267
278
  #
268
279
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
269
280
  def string_to_date(string)
270
- begin
271
- Date.parse(string, Sequel.convert_two_digit_years)
272
- rescue => e
273
- raise convert_exception_class(e, InvalidValue)
274
- end
281
+ Date.parse(string, Sequel.convert_two_digit_years)
282
+ rescue => e
283
+ raise convert_exception_class(e, InvalidValue)
275
284
  end
276
285
 
277
286
  # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
@@ -279,15 +288,13 @@ module Sequel
279
288
  #
280
289
  # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
281
290
  def string_to_datetime(string)
282
- begin
283
- if datetime_class == DateTime
284
- DateTime.parse(string, convert_two_digit_years)
285
- else
286
- datetime_class.parse(string)
287
- end
288
- rescue => e
289
- raise convert_exception_class(e, InvalidValue)
291
+ if datetime_class == DateTime
292
+ DateTime.parse(string, convert_two_digit_years)
293
+ else
294
+ datetime_class.parse(string)
290
295
  end
296
+ rescue => e
297
+ raise convert_exception_class(e, InvalidValue)
291
298
  end
292
299
 
293
300
  # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
@@ -295,11 +302,9 @@ module Sequel
295
302
  # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
296
303
  # DB.literal(v) # => '10:20:30'
297
304
  def string_to_time(string)
298
- begin
299
- SQLTime.parse(string)
300
- rescue => e
301
- raise convert_exception_class(e, InvalidValue)
302
- end
305
+ SQLTime.parse(string)
306
+ rescue => e
307
+ raise convert_exception_class(e, InvalidValue)
303
308
  end
304
309
 
305
310
  # Unless in single threaded mode, protects access to any mutable
@@ -389,6 +394,11 @@ module Sequel
389
394
 
390
395
  private
391
396
 
397
+ # Return a hash of date information parsed from the given string.
398
+ def _date_parse(string)
399
+ Date._parse(string)
400
+ end
401
+
392
402
  # Helper method that the database adapter class methods that are added to Sequel via
393
403
  # metaprogramming use to parse arguments.
394
404
  def adapter_method(adapter, *args, &block)
@@ -8,7 +8,7 @@ module Sequel
8
8
  # ---------------------
9
9
 
10
10
  # Array of supported database adapters
11
- ADAPTERS = %w'ado amalgalite ibmdb jdbc mock mysql mysql2 odbc oracle postgres sqlanywhere sqlite tinytds'.map(&:to_sym)
11
+ ADAPTERS = %w'ado amalgalite ibmdb jdbc mock mysql mysql2 odbc oracle postgres sqlanywhere sqlite tinytds trilogy'.map(&:to_sym)
12
12
 
13
13
  # The Database subclass for the given adapter scheme.
14
14
  # Raises Sequel::AdapterNotFound if the adapter
@@ -55,11 +55,11 @@ module Sequel
55
55
 
56
56
  begin
57
57
  db = c.new(opts)
58
- if block_given?
58
+ if defined?(yield)
59
59
  return yield(db)
60
60
  end
61
61
  ensure
62
- if block_given?
62
+ if defined?(yield)
63
63
  db.disconnect if db
64
64
  Sequel.synchronize{::Sequel::DATABASES.delete(db)}
65
65
  end
@@ -241,6 +241,30 @@ module Sequel
241
241
  pool.servers
242
242
  end
243
243
 
244
+ # Connect to the given server/shard. Handles database-generic post-connection
245
+ # setup not handled by #connect, using the :after_connect and :connect_sqls
246
+ # options.
247
+ def new_connection(server)
248
+ conn = connect(server)
249
+ opts = server_opts(server)
250
+
251
+ if ac = opts[:after_connect]
252
+ if ac.arity == 2
253
+ ac.call(conn, server)
254
+ else
255
+ ac.call(conn)
256
+ end
257
+ end
258
+
259
+ if cs = opts[:connect_sqls]
260
+ cs.each do |sql|
261
+ log_connection_execute(conn, sql)
262
+ end
263
+ end
264
+
265
+ conn
266
+ end
267
+
244
268
  # Returns true if the database is using a single-threaded connection pool.
245
269
  def single_threaded?
246
270
  @single_threaded
@@ -30,19 +30,29 @@ module Sequel
30
30
  @dataset_class.new(self)
31
31
  end
32
32
 
33
- # Fetches records for an arbitrary SQL statement. If a block is given,
34
- # it is used to iterate over the records:
33
+ # Returns a dataset instance for the given SQL string:
35
34
  #
36
- # DB.fetch('SELECT * FROM items'){|r| p r}
35
+ # ds = DB.fetch('SELECT * FROM items')
36
+ #
37
+ # You can then call methods on the dataset to retrieve results:
37
38
  #
38
- # The +fetch+ method returns a dataset instance:
39
+ # ds.all
40
+ # # SELECT * FROM items
41
+ # # => [{:column=>value, ...}, ...]
39
42
  #
40
- # DB.fetch('SELECT * FROM items').all
43
+ # If a block is given, it is passed to #each on the resulting dataset to
44
+ # iterate over the records returned by the query:
45
+ #
46
+ # DB.fetch('SELECT * FROM items'){|r| p r}
47
+ # # {:column=>value, ...}
48
+ # # ...
41
49
  #
42
50
  # +fetch+ can also perform parameterized queries for protection against SQL
43
51
  # injection:
44
52
  #
45
- # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).all
53
+ # ds = DB.fetch('SELECT * FROM items WHERE name = ?', "my name")
54
+ # ds.all
55
+ # # SELECT * FROM items WHERE name = 'my name'
46
56
  #
47
57
  # See caveats listed in Dataset#with_sql regarding datasets using custom
48
58
  # SQL and the methods that can be called on them.