sequel 5.39.0 → 5.63.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +308 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +57 -25
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +13 -13
  7. data/doc/association_basics.rdoc +89 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/migration.rdoc +12 -6
  10. data/doc/model_hooks.rdoc +1 -1
  11. data/doc/object_model.rdoc +8 -8
  12. data/doc/opening_databases.rdoc +18 -11
  13. data/doc/postgresql.rdoc +16 -8
  14. data/doc/querying.rdoc +5 -3
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/release_notes/5.41.0.txt +25 -0
  17. data/doc/release_notes/5.42.0.txt +136 -0
  18. data/doc/release_notes/5.43.0.txt +98 -0
  19. data/doc/release_notes/5.44.0.txt +32 -0
  20. data/doc/release_notes/5.45.0.txt +34 -0
  21. data/doc/release_notes/5.46.0.txt +87 -0
  22. data/doc/release_notes/5.47.0.txt +59 -0
  23. data/doc/release_notes/5.48.0.txt +14 -0
  24. data/doc/release_notes/5.49.0.txt +59 -0
  25. data/doc/release_notes/5.50.0.txt +78 -0
  26. data/doc/release_notes/5.51.0.txt +47 -0
  27. data/doc/release_notes/5.52.0.txt +87 -0
  28. data/doc/release_notes/5.53.0.txt +23 -0
  29. data/doc/release_notes/5.54.0.txt +27 -0
  30. data/doc/release_notes/5.55.0.txt +21 -0
  31. data/doc/release_notes/5.56.0.txt +51 -0
  32. data/doc/release_notes/5.57.0.txt +23 -0
  33. data/doc/release_notes/5.58.0.txt +31 -0
  34. data/doc/release_notes/5.59.0.txt +73 -0
  35. data/doc/release_notes/5.60.0.txt +22 -0
  36. data/doc/release_notes/5.61.0.txt +43 -0
  37. data/doc/release_notes/5.62.0.txt +132 -0
  38. data/doc/release_notes/5.63.0.txt +33 -0
  39. data/doc/schema_modification.rdoc +1 -1
  40. data/doc/security.rdoc +9 -9
  41. data/doc/sql.rdoc +27 -15
  42. data/doc/testing.rdoc +22 -11
  43. data/doc/transactions.rdoc +6 -6
  44. data/doc/virtual_rows.rdoc +2 -2
  45. data/lib/sequel/adapters/ado/access.rb +1 -1
  46. data/lib/sequel/adapters/ado.rb +17 -17
  47. data/lib/sequel/adapters/amalgalite.rb +3 -5
  48. data/lib/sequel/adapters/ibmdb.rb +2 -2
  49. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  50. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  51. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  52. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  53. data/lib/sequel/adapters/jdbc.rb +16 -18
  54. data/lib/sequel/adapters/mysql.rb +80 -67
  55. data/lib/sequel/adapters/mysql2.rb +54 -49
  56. data/lib/sequel/adapters/odbc.rb +6 -2
  57. data/lib/sequel/adapters/oracle.rb +3 -3
  58. data/lib/sequel/adapters/postgres.rb +83 -40
  59. data/lib/sequel/adapters/shared/access.rb +11 -1
  60. data/lib/sequel/adapters/shared/db2.rb +30 -0
  61. data/lib/sequel/adapters/shared/mssql.rb +58 -7
  62. data/lib/sequel/adapters/shared/mysql.rb +40 -2
  63. data/lib/sequel/adapters/shared/oracle.rb +76 -0
  64. data/lib/sequel/adapters/shared/postgres.rb +418 -174
  65. data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -0
  66. data/lib/sequel/adapters/shared/sqlite.rb +102 -11
  67. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  68. data/lib/sequel/adapters/sqlite.rb +60 -18
  69. data/lib/sequel/adapters/tinytds.rb +1 -1
  70. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  71. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  72. data/lib/sequel/ast_transformer.rb +6 -0
  73. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  74. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  75. data/lib/sequel/connection_pool/single.rb +6 -8
  76. data/lib/sequel/connection_pool/threaded.rb +8 -8
  77. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  78. data/lib/sequel/connection_pool.rb +47 -30
  79. data/lib/sequel/core.rb +28 -18
  80. data/lib/sequel/database/connecting.rb +26 -2
  81. data/lib/sequel/database/misc.rb +69 -14
  82. data/lib/sequel/database/query.rb +38 -1
  83. data/lib/sequel/database/schema_generator.rb +45 -52
  84. data/lib/sequel/database/schema_methods.rb +17 -1
  85. data/lib/sequel/dataset/actions.rb +107 -13
  86. data/lib/sequel/dataset/features.rb +20 -0
  87. data/lib/sequel/dataset/misc.rb +1 -1
  88. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  89. data/lib/sequel/dataset/query.rb +118 -16
  90. data/lib/sequel/dataset/sql.rb +177 -47
  91. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  92. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  93. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  94. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  95. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  96. data/lib/sequel/extensions/blank.rb +8 -0
  97. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  98. data/lib/sequel/extensions/core_refinements.rb +36 -11
  99. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  100. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  101. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  102. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  103. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  104. data/lib/sequel/extensions/inflector.rb +9 -1
  105. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  106. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  107. data/lib/sequel/extensions/migration.rb +7 -2
  108. data/lib/sequel/extensions/named_timezones.rb +26 -6
  109. data/lib/sequel/extensions/pagination.rb +1 -1
  110. data/lib/sequel/extensions/pg_array.rb +23 -3
  111. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  112. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  113. data/lib/sequel/extensions/pg_enum.rb +1 -1
  114. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  115. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  116. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  117. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  118. data/lib/sequel/extensions/pg_inet.rb +10 -11
  119. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  120. data/lib/sequel/extensions/pg_interval.rb +45 -19
  121. data/lib/sequel/extensions/pg_json.rb +13 -15
  122. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  123. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  124. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  125. data/lib/sequel/extensions/pg_range.rb +10 -23
  126. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  127. data/lib/sequel/extensions/pg_row.rb +19 -13
  128. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  129. data/lib/sequel/extensions/query.rb +2 -0
  130. data/lib/sequel/extensions/s.rb +2 -1
  131. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  132. data/lib/sequel/extensions/server_block.rb +8 -12
  133. data/lib/sequel/extensions/sql_comments.rb +110 -3
  134. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  135. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  136. data/lib/sequel/extensions/string_agg.rb +1 -1
  137. data/lib/sequel/extensions/string_date_time.rb +19 -23
  138. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  139. data/lib/sequel/model/associations.rb +325 -96
  140. data/lib/sequel/model/base.rb +51 -27
  141. data/lib/sequel/model/errors.rb +10 -1
  142. data/lib/sequel/model/inflections.rb +1 -1
  143. data/lib/sequel/model/plugins.rb +5 -0
  144. data/lib/sequel/plugins/association_proxies.rb +2 -0
  145. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  146. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  147. data/lib/sequel/plugins/auto_validations.rb +87 -15
  148. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  149. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  150. data/lib/sequel/plugins/column_encryption.rb +728 -0
  151. data/lib/sequel/plugins/composition.rb +10 -4
  152. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  153. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  154. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  155. data/lib/sequel/plugins/dirty.rb +1 -1
  156. data/lib/sequel/plugins/enum.rb +124 -0
  157. data/lib/sequel/plugins/finder.rb +3 -1
  158. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  159. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  160. data/lib/sequel/plugins/json_serializer.rb +39 -24
  161. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  162. data/lib/sequel/plugins/list.rb +3 -1
  163. data/lib/sequel/plugins/many_through_many.rb +108 -9
  164. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  165. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  166. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  167. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  168. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  169. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  170. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  171. data/lib/sequel/plugins/serialization.rb +9 -3
  172. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  173. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  174. data/lib/sequel/plugins/sql_comments.rb +189 -0
  175. data/lib/sequel/plugins/static_cache.rb +1 -1
  176. data/lib/sequel/plugins/subclasses.rb +28 -11
  177. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  178. data/lib/sequel/plugins/timestamps.rb +1 -1
  179. data/lib/sequel/plugins/unused_associations.rb +521 -0
  180. data/lib/sequel/plugins/update_or_create.rb +1 -1
  181. data/lib/sequel/plugins/validate_associated.rb +22 -12
  182. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  183. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  184. data/lib/sequel/sql.rb +1 -1
  185. data/lib/sequel/timezones.rb +12 -14
  186. data/lib/sequel/version.rb +1 -1
  187. metadata +97 -43
@@ -0,0 +1,257 @@
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 = sync{@allocated[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
+ # Create the maximum number of connections immediately. This should not be called
227
+ # with a true argument unles no code is currently operating on the database.
228
+ #
229
+ # Calling code should not have the mutex when calling this.
230
+ def preconnect(concurrent = false)
231
+ if concurrent
232
+ if times = sync{@max_size > (size = @size[0]) ? @max_size - size : false}
233
+ times.times.map{Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
234
+ end
235
+ else
236
+ while conn = try_make_new
237
+ @queue.push(conn)
238
+ end
239
+ end
240
+
241
+ nil
242
+ end
243
+
244
+ # Releases the connection assigned to the supplied thread back to the pool.
245
+ #
246
+ # Calling code should not have the mutex when calling this.
247
+ def release(thread)
248
+ @queue.push(sync{@allocated.delete(thread)})
249
+ end
250
+
251
+ # Yield to the block while inside the mutex.
252
+ #
253
+ # Calling code should not have the mutex when calling this.
254
+ def sync
255
+ @mutex.synchronize{yield}
256
+ end
257
+ end
@@ -30,8 +30,11 @@ 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
+ }
36
+ POOL_CLASS_MAP.to_a.each{|k, v| POOL_CLASS_MAP[k.to_s] = v}
37
+ POOL_CLASS_MAP.freeze
35
38
 
36
39
  # Class methods used to return an appropriate pool subclass, separated
37
40
  # into a module for easier overridding by extensions.
@@ -74,26 +77,35 @@ class Sequel::ConnectionPool
74
77
 
75
78
  # The after_connect proc used for this pool. This is called with each new
76
79
  # connection made, and is usually used to set custom per-connection settings.
77
- attr_accessor :after_connect
80
+ # Deprecated.
81
+ attr_reader :after_connect # SEQUEL6: Remove
78
82
 
79
- # An array of sql strings to execute on each new connection.
80
- attr_accessor :connect_sqls
83
+ # Override the after_connect proc for the connection pool. Deprecated.
84
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
85
+ def after_connect=(v) # SEQUEL6: Remove
86
+ @use_old_connect_api = true
87
+ @after_connect = v
88
+ end
89
+
90
+ # An array of sql strings to execute on each new connection. Deprecated.
91
+ attr_reader :connect_sqls # SEQUEL6: Remove
92
+
93
+ # Override the connect_sqls for the connection pool. Deprecated.
94
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
95
+ def connect_sqls=(v) # SEQUEL6: Remove
96
+ @use_old_connect_api = true
97
+ @connect_sqls = v
98
+ end
81
99
 
82
100
  # The Sequel::Database object tied to this connection pool.
83
101
  attr_accessor :db
84
102
 
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)
103
+ # Instantiates a connection pool with the given Database and options.
104
+ def initialize(db, opts=OPTS) # SEQUEL6: Remove second argument, always use db.opts
94
105
  @db = db
95
- @after_connect = opts[:after_connect]
96
- @connect_sqls = opts[:connect_sqls]
106
+ @use_old_connect_api = false # SEQUEL6: Remove
107
+ @after_connect = opts[:after_connect] # SEQUEL6: Remove
108
+ @connect_sqls = opts[:connect_sqls] # SEQUEL6: Remove
97
109
  @error_classes = db.send(:database_error_classes).dup.freeze
98
110
  end
99
111
 
@@ -119,25 +131,30 @@ class Sequel::ConnectionPool
119
131
  # and checking for connection errors.
120
132
  def make_new(server)
121
133
  begin
122
- conn = @db.connect(server)
134
+ if @use_old_connect_api
135
+ # SEQUEL6: Remove block
136
+ conn = @db.connect(server)
123
137
 
124
- if ac = @after_connect
125
- if ac.arity == 2
126
- ac.call(conn, server)
127
- else
128
- ac.call(conn)
138
+ if ac = @after_connect
139
+ if ac.arity == 2
140
+ ac.call(conn, server)
141
+ else
142
+ ac.call(conn)
143
+ end
129
144
  end
130
- end
131
-
132
- if cs = @connect_sqls
133
- cs.each do |sql|
134
- db.send(:log_connection_execute, conn, sql)
145
+
146
+ if cs = @connect_sqls
147
+ cs.each do |sql|
148
+ db.send(:log_connection_execute, conn, sql)
149
+ end
135
150
  end
151
+
152
+ conn
153
+ else
154
+ @db.new_connection(server)
136
155
  end
137
156
  rescue Exception=>exception
138
157
  raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
139
- end
140
- raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
141
- conn
158
+ end || raise(Sequel::DatabaseConnectionError, "Connection parameters not valid")
142
159
  end
143
160
  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)
@@ -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
@@ -91,11 +91,23 @@ module Sequel
91
91
  # The specific default size of string columns for this Sequel::Database, usually 255 by default.
92
92
  attr_accessor :default_string_column_size
93
93
 
94
+ # Whether to check the bytesize of strings before typecasting (to avoid typecasting strings that
95
+ # would be too long for the given type), true by default. Strings that are too long will raise
96
+ # a typecasting error.
97
+ attr_accessor :check_string_typecast_bytesize
98
+
94
99
  # Constructs a new instance of a database connection with the specified
95
100
  # options hash.
96
101
  #
97
102
  # Accepts the following options:
103
+ # :after_connect :: A callable object called after each new connection is made, with the
104
+ # connection object (and server argument if the callable accepts 2 arguments),
105
+ # useful for customizations that you want to apply to all connections.
106
+ # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
107
+ # but before any connections are created.
98
108
  # :cache_schema :: Whether schema should be cached for this Database instance
109
+ # :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting.
110
+ # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
99
111
  # :default_string_column_size :: The default size of string columns, 255 by default.
100
112
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
101
113
  # or string with extensions separated by columns. These extensions are loaded after
@@ -105,7 +117,7 @@ module Sequel
105
117
  # :loggers :: An array of loggers to use.
106
118
  # :log_connection_info :: Whether connection information should be logged when logging queries.
107
119
  # :log_warn_duration :: The number of elapsed seconds after which queries should be logged at warn level.
108
- # :name :: A name to use for the Database object, displayed in PoolTimeout .
120
+ # :name :: A name to use for the Database object, displayed in PoolTimeout.
109
121
  # :preconnect :: Automatically create the maximum number of connections, so that they don't
110
122
  # need to be created as needed. This is useful when connecting takes a long time
111
123
  # and you want to avoid possible latency during runtime.
@@ -114,13 +126,15 @@ module Sequel
114
126
  # :preconnect_extensions :: Similar to the :extensions option, but loads the extensions before the
115
127
  # connections are made by the :preconnect option.
116
128
  # :quote_identifiers :: Whether to quote identifiers.
117
- # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol .
129
+ # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol.
118
130
  # :single_threaded :: Whether to use a single-threaded connection pool.
119
131
  # :sql_log_level :: Method to use to log SQL to a logger, :info by default.
120
132
  #
133
+ # For sharded connection pools, :after_connect and :connect_sqls can be specified per-shard.
134
+ #
121
135
  # All options given are also passed to the connection pool. Additional options respected by
122
- # the connection pool are :after_connect, :connect_sqls, :max_connections, :pool_timeout,
123
- # :servers, and :servers_hash. See the connection pool documentation for details.
136
+ # the connection pool are :max_connections, :pool_timeout, :servers, and :servers_hash. See the
137
+ # connection pool documentation for details.
124
138
  def initialize(opts = OPTS)
125
139
  @opts ||= opts
126
140
  @opts = connection_pool_default_options.merge(@opts)
@@ -130,6 +144,7 @@ module Sequel
130
144
  @opts[:adapter_class] = self.class
131
145
  @opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, Sequel.single_threaded))
132
146
  @default_string_column_size = @opts[:default_string_column_size] || DEFAULT_STRING_COLUMN_SIZE
147
+ @check_string_typecast_bytesize = typecast_value_boolean(@opts.fetch(:check_string_typecast_bytesize, true))
133
148
 
134
149
  @schemas = {}
135
150
  @prepared_statements = {}
@@ -160,6 +175,10 @@ module Sequel
160
175
 
161
176
  initialize_load_extensions(:preconnect_extensions)
162
177
 
178
+ if before_preconnect = @opts[:before_preconnect]
179
+ before_preconnect.call(self)
180
+ end
181
+
163
182
  if typecast_value_boolean(@opts[:preconnect]) && @pool.respond_to?(:preconnect, true)
164
183
  concurrent = typecast_value_string(@opts[:preconnect]) == "concurrently"
165
184
  @pool.send(:preconnect, concurrent)
@@ -213,8 +232,7 @@ module Sequel
213
232
  Sequel.extension(*exts)
214
233
  exts.each do |ext|
215
234
  if pr = Sequel.synchronize{EXTENSIONS[ext]}
216
- unless Sequel.synchronize{@loaded_extensions.include?(ext)}
217
- Sequel.synchronize{@loaded_extensions << ext}
235
+ if Sequel.synchronize{@loaded_extensions.include?(ext) ? false : (@loaded_extensions << ext)}
218
236
  pr.call(self)
219
237
  end
220
238
  else
@@ -460,6 +478,21 @@ module Sequel
460
478
  # Don't rescue other exceptions, they will be raised normally.
461
479
  end
462
480
 
481
+ # Check the bytesize of a string before conversion. There is no point
482
+ # trying to typecast strings that would be way too long.
483
+ def typecast_check_string_length(string, max_size)
484
+ if @check_string_typecast_bytesize && string.bytesize > max_size
485
+ raise InvalidValue, "string too long to typecast (bytesize: #{string.bytesize}, max: #{max_size})"
486
+ end
487
+ string
488
+ end
489
+
490
+ # Check the bytesize of the string value, if value is a string.
491
+ def typecast_check_length(value, max_size)
492
+ typecast_check_string_length(value, max_size) if String === value
493
+ value
494
+ end
495
+
463
496
  # Typecast the value to an SQL::Blob
464
497
  def typecast_value_blob(value)
465
498
  value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
@@ -483,9 +516,9 @@ module Sequel
483
516
  when Date
484
517
  value
485
518
  when String
486
- Sequel.string_to_date(value)
519
+ Sequel.string_to_date(typecast_check_string_length(value, 100))
487
520
  when Hash
488
- Date.new(*[:year, :month, :day].map{|x| (value[x] || value[x.to_s]).to_i})
521
+ Date.new(*[:year, :month, :day].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
489
522
  else
490
523
  raise InvalidValue, "invalid value for Date: #{value.inspect}"
491
524
  end
@@ -493,7 +526,17 @@ module Sequel
493
526
 
494
527
  # Typecast the value to a DateTime or Time depending on Sequel.datetime_class
495
528
  def typecast_value_datetime(value)
496
- Sequel.typecast_to_application_timestamp(value)
529
+ case value
530
+ when String
531
+ Sequel.typecast_to_application_timestamp(typecast_check_string_length(value, 100))
532
+ when Hash
533
+ [:year, :month, :day, :hour, :minute, :second, :nanos, :offset].each do |x|
534
+ typecast_check_length(value[x] || value[x.to_s], 100)
535
+ end
536
+ Sequel.typecast_to_application_timestamp(value)
537
+ else
538
+ Sequel.typecast_to_application_timestamp(value)
539
+ end
497
540
  end
498
541
 
499
542
  if RUBY_VERSION >= '2.4'
@@ -526,18 +569,30 @@ module Sequel
526
569
  when Numeric
527
570
  BigDecimal(value.to_s)
528
571
  when String
529
- _typecast_value_string_to_decimal(value)
572
+ _typecast_value_string_to_decimal(typecast_check_string_length(value, 1000))
530
573
  else
531
574
  raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
532
575
  end
533
576
  end
534
577
 
535
578
  # Typecast the value to a Float
536
- alias typecast_value_float Float
579
+ def typecast_value_float(value)
580
+ Float(typecast_check_length(value, 1000))
581
+ end
537
582
 
538
583
  # Typecast the value to an Integer
539
584
  def typecast_value_integer(value)
540
- (value.is_a?(String) && value =~ /\A0+(\d)/) ? Integer(value, 10) : Integer(value)
585
+ case value
586
+ when String
587
+ typecast_check_string_length(value, 100)
588
+ if value =~ /\A-?0+(\d)/
589
+ Integer(value, 10)
590
+ else
591
+ Integer(value)
592
+ end
593
+ else
594
+ Integer(value)
595
+ end
541
596
  end
542
597
 
543
598
  # Typecast the value to a String
@@ -560,9 +615,9 @@ module Sequel
560
615
  SQLTime.create(value.hour, value.min, value.sec, value.nsec/1000.0)
561
616
  end
562
617
  when String
563
- Sequel.string_to_time(value)
618
+ Sequel.string_to_time(typecast_check_string_length(value, 100))
564
619
  when Hash
565
- SQLTime.create(*[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
620
+ SQLTime.create(*[:hour, :minute, :second].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
566
621
  else
567
622
  raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
568
623
  end