sequel 5.33.0 → 5.58.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +318 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +40 -9
  5. data/doc/association_basics.rdoc +77 -13
  6. data/doc/cheat_sheet.rdoc +13 -5
  7. data/doc/code_order.rdoc +0 -12
  8. data/doc/dataset_filtering.rdoc +2 -2
  9. data/doc/fork_safety.rdoc +84 -0
  10. data/doc/migration.rdoc +12 -6
  11. data/doc/model_plugins.rdoc +1 -1
  12. data/doc/opening_databases.rdoc +15 -3
  13. data/doc/postgresql.rdoc +9 -1
  14. data/doc/querying.rdoc +7 -5
  15. data/doc/release_notes/5.34.0.txt +40 -0
  16. data/doc/release_notes/5.35.0.txt +56 -0
  17. data/doc/release_notes/5.36.0.txt +60 -0
  18. data/doc/release_notes/5.37.0.txt +30 -0
  19. data/doc/release_notes/5.38.0.txt +28 -0
  20. data/doc/release_notes/5.39.0.txt +19 -0
  21. data/doc/release_notes/5.40.0.txt +40 -0
  22. data/doc/release_notes/5.41.0.txt +25 -0
  23. data/doc/release_notes/5.42.0.txt +136 -0
  24. data/doc/release_notes/5.43.0.txt +98 -0
  25. data/doc/release_notes/5.44.0.txt +32 -0
  26. data/doc/release_notes/5.45.0.txt +34 -0
  27. data/doc/release_notes/5.46.0.txt +87 -0
  28. data/doc/release_notes/5.47.0.txt +59 -0
  29. data/doc/release_notes/5.48.0.txt +14 -0
  30. data/doc/release_notes/5.49.0.txt +59 -0
  31. data/doc/release_notes/5.50.0.txt +78 -0
  32. data/doc/release_notes/5.51.0.txt +47 -0
  33. data/doc/release_notes/5.52.0.txt +87 -0
  34. data/doc/release_notes/5.53.0.txt +23 -0
  35. data/doc/release_notes/5.54.0.txt +27 -0
  36. data/doc/release_notes/5.55.0.txt +21 -0
  37. data/doc/release_notes/5.56.0.txt +51 -0
  38. data/doc/release_notes/5.57.0.txt +23 -0
  39. data/doc/release_notes/5.58.0.txt +31 -0
  40. data/doc/sql.rdoc +14 -2
  41. data/doc/testing.rdoc +10 -1
  42. data/doc/transactions.rdoc +0 -8
  43. data/doc/validations.rdoc +1 -1
  44. data/doc/virtual_rows.rdoc +1 -1
  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/mysql.rb +4 -4
  53. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  54. data/lib/sequel/adapters/jdbc.rb +29 -19
  55. data/lib/sequel/adapters/mysql.rb +80 -67
  56. data/lib/sequel/adapters/mysql2.rb +54 -49
  57. data/lib/sequel/adapters/odbc.rb +8 -6
  58. data/lib/sequel/adapters/oracle.rb +5 -4
  59. data/lib/sequel/adapters/postgres.rb +27 -29
  60. data/lib/sequel/adapters/shared/access.rb +2 -0
  61. data/lib/sequel/adapters/shared/db2.rb +30 -0
  62. data/lib/sequel/adapters/shared/mssql.rb +84 -7
  63. data/lib/sequel/adapters/shared/mysql.rb +33 -2
  64. data/lib/sequel/adapters/shared/oracle.rb +82 -7
  65. data/lib/sequel/adapters/shared/postgres.rb +158 -20
  66. data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
  67. data/lib/sequel/adapters/shared/sqlite.rb +102 -10
  68. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  69. data/lib/sequel/adapters/sqlite.rb +60 -18
  70. data/lib/sequel/adapters/tinytds.rb +2 -1
  71. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  72. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
  73. data/lib/sequel/ast_transformer.rb +6 -0
  74. data/lib/sequel/connection_pool/sharded_single.rb +9 -8
  75. data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
  76. data/lib/sequel/connection_pool/single.rb +7 -9
  77. data/lib/sequel/connection_pool/threaded.rb +1 -1
  78. data/lib/sequel/core.rb +33 -24
  79. data/lib/sequel/database/connecting.rb +3 -4
  80. data/lib/sequel/database/misc.rb +37 -12
  81. data/lib/sequel/database/query.rb +3 -1
  82. data/lib/sequel/database/schema_generator.rb +50 -53
  83. data/lib/sequel/database/schema_methods.rb +45 -23
  84. data/lib/sequel/database/transactions.rb +9 -6
  85. data/lib/sequel/dataset/actions.rb +61 -8
  86. data/lib/sequel/dataset/features.rb +15 -0
  87. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
  88. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  89. data/lib/sequel/dataset/query.rb +114 -11
  90. data/lib/sequel/dataset/sql.rb +172 -46
  91. data/lib/sequel/deprecated.rb +3 -1
  92. data/lib/sequel/exceptions.rb +2 -0
  93. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  94. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  95. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  96. data/lib/sequel/extensions/blank.rb +8 -0
  97. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  98. data/lib/sequel/extensions/core_refinements.rb +38 -11
  99. data/lib/sequel/extensions/date_arithmetic.rb +36 -24
  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 +3 -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 +139 -0
  106. data/lib/sequel/extensions/migration.rb +13 -2
  107. data/lib/sequel/extensions/named_timezones.rb +5 -1
  108. data/lib/sequel/extensions/pagination.rb +1 -1
  109. data/lib/sequel/extensions/pg_array.rb +1 -0
  110. data/lib/sequel/extensions/pg_array_ops.rb +6 -2
  111. data/lib/sequel/extensions/pg_enum.rb +3 -1
  112. data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
  113. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  114. data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
  115. data/lib/sequel/extensions/pg_inet.rb +2 -0
  116. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  117. data/lib/sequel/extensions/pg_interval.rb +35 -8
  118. data/lib/sequel/extensions/pg_json.rb +3 -5
  119. data/lib/sequel/extensions/pg_json_ops.rb +119 -4
  120. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  121. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  122. data/lib/sequel/extensions/pg_range.rb +7 -19
  123. data/lib/sequel/extensions/pg_range_ops.rb +39 -9
  124. data/lib/sequel/extensions/pg_row.rb +1 -1
  125. data/lib/sequel/extensions/pg_row_ops.rb +25 -1
  126. data/lib/sequel/extensions/query.rb +3 -0
  127. data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
  128. data/lib/sequel/extensions/s.rb +4 -1
  129. data/lib/sequel/extensions/schema_dumper.rb +16 -5
  130. data/lib/sequel/extensions/server_block.rb +8 -12
  131. data/lib/sequel/extensions/sql_comments.rb +110 -3
  132. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  133. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  134. data/lib/sequel/extensions/string_agg.rb +1 -1
  135. data/lib/sequel/extensions/string_date_time.rb +19 -23
  136. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  137. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  138. data/lib/sequel/extensions/to_dot.rb +9 -3
  139. data/lib/sequel/model/associations.rb +342 -114
  140. data/lib/sequel/model/base.rb +45 -24
  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 +8 -3
  144. data/lib/sequel/model.rb +3 -1
  145. data/lib/sequel/plugins/association_pks.rb +60 -18
  146. data/lib/sequel/plugins/association_proxies.rb +3 -0
  147. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  148. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  149. data/lib/sequel/plugins/auto_validations.rb +39 -5
  150. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  151. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  152. data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
  153. data/lib/sequel/plugins/column_encryption.rb +728 -0
  154. data/lib/sequel/plugins/composition.rb +8 -2
  155. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  156. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  157. data/lib/sequel/plugins/csv_serializer.rb +2 -0
  158. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  159. data/lib/sequel/plugins/dirty.rb +44 -0
  160. data/lib/sequel/plugins/enum.rb +124 -0
  161. data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
  162. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  163. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  164. data/lib/sequel/plugins/json_serializer.rb +39 -24
  165. data/lib/sequel/plugins/lazy_attributes.rb +4 -1
  166. data/lib/sequel/plugins/many_through_many.rb +108 -9
  167. data/lib/sequel/plugins/nested_attributes.rb +8 -3
  168. data/lib/sequel/plugins/pg_array_associations.rb +58 -41
  169. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
  170. data/lib/sequel/plugins/prepared_statements.rb +15 -12
  171. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  172. data/lib/sequel/plugins/rcte_tree.rb +37 -35
  173. data/lib/sequel/plugins/serialization.rb +9 -3
  174. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  175. data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
  176. data/lib/sequel/plugins/sql_comments.rb +189 -0
  177. data/lib/sequel/plugins/static_cache.rb +1 -1
  178. data/lib/sequel/plugins/string_stripper.rb +1 -1
  179. data/lib/sequel/plugins/subclasses.rb +28 -11
  180. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
  181. data/lib/sequel/plugins/timestamps.rb +1 -1
  182. data/lib/sequel/plugins/tree.rb +9 -4
  183. data/lib/sequel/plugins/unused_associations.rb +521 -0
  184. data/lib/sequel/plugins/update_or_create.rb +1 -1
  185. data/lib/sequel/plugins/validation_class_methods.rb +5 -1
  186. data/lib/sequel/plugins/validation_helpers.rb +18 -11
  187. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  188. data/lib/sequel/sql.rb +1 -1
  189. data/lib/sequel/timezones.rb +20 -17
  190. data/lib/sequel/version.rb +1 -1
  191. metadata +93 -39
@@ -98,6 +98,11 @@ module Sequel
98
98
  # The conversion procs to use for this database
99
99
  attr_reader :conversion_procs
100
100
 
101
+ def initialize(opts = OPTS)
102
+ super
103
+ @allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
104
+ end
105
+
101
106
  # Connect to the database. Since SQLite is a file based database,
102
107
  # available options are limited:
103
108
  #
@@ -119,6 +124,12 @@ module Sequel
119
124
  end
120
125
 
121
126
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
127
+
128
+ if typecast_value_boolean(opts[:setup_regexp_function])
129
+ db.create_function("regexp", 2) do |func, regexp_str, string|
130
+ func.result = Regexp.new(regexp_str).match(string) ? 1 : 0
131
+ end
132
+ end
122
133
 
123
134
  class << db
124
135
  attr_reader :prepared_statements
@@ -128,6 +139,12 @@ module Sequel
128
139
  db
129
140
  end
130
141
 
142
+ # Whether this Database instance is setup to allow regexp matching.
143
+ # True if the :setup_regexp_function option was passed when creating the Database.
144
+ def allow_regexp?
145
+ @allow_regexp
146
+ end
147
+
131
148
  # Disconnect given connections from the database.
132
149
  def disconnect_connection(c)
133
150
  c.prepared_statements.each_value{|v| v.first.close}
@@ -189,26 +206,24 @@ module Sequel
189
206
  # Yield an available connection. Rescue
190
207
  # any SQLite3::Exceptions and turn them into DatabaseErrors.
191
208
  def _execute(type, sql, opts, &block)
192
- begin
193
- synchronize(opts[:server]) do |conn|
194
- return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
195
- log_args = opts[:arguments]
196
- args = {}
197
- opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
198
- case type
199
- when :select
200
- log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
201
- when :insert
202
- log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
203
- conn.last_insert_row_id
204
- when :update
205
- log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
206
- conn.changes
207
- end
209
+ synchronize(opts[:server]) do |conn|
210
+ return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
211
+ log_args = opts[:arguments]
212
+ args = {}
213
+ opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
214
+ case type
215
+ when :select
216
+ log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
217
+ when :insert
218
+ log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
219
+ conn.last_insert_row_id
220
+ when :update
221
+ log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
222
+ conn.changes
208
223
  end
209
- rescue SQLite3::Exception => e
210
- raise_error(e)
211
224
  end
225
+ rescue SQLite3::Exception => e
226
+ raise_error(e)
212
227
  end
213
228
 
214
229
  # The SQLite adapter does not need the pool to convert exceptions.
@@ -323,6 +338,28 @@ module Sequel
323
338
  BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
324
339
  PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
325
340
 
341
+ # Support regexp functions if using :setup_regexp_function Database option.
342
+ def complex_expression_sql_append(sql, op, args)
343
+ case op
344
+ when :~, :'!~', :'~*', :'!~*'
345
+ return super unless supports_regexp?
346
+
347
+ case_insensitive = [:'~*', :'!~*'].include?(op)
348
+ sql << 'NOT ' if [:'!~', :'!~*'].include?(op)
349
+ sql << '('
350
+ sql << 'LOWER(' if case_insensitive
351
+ literal_append(sql, args[0])
352
+ sql << ')' if case_insensitive
353
+ sql << ' REGEXP '
354
+ sql << 'LOWER(' if case_insensitive
355
+ literal_append(sql, args[1])
356
+ sql << ')' if case_insensitive
357
+ sql << ')'
358
+ else
359
+ super
360
+ end
361
+ end
362
+
326
363
  def fetch_rows(sql)
327
364
  execute(sql) do |result|
328
365
  cps = db.conversion_procs
@@ -346,6 +383,11 @@ module Sequel
346
383
  end
347
384
  end
348
385
  end
386
+
387
+ # Support regexp if using :setup_regexp_function Database option.
388
+ def supports_regexp?
389
+ db.allow_regexp?
390
+ end
349
391
 
350
392
  private
351
393
 
@@ -16,6 +16,7 @@ module Sequel
16
16
  c = TinyTds::Client.new(opts)
17
17
  c.query_options.merge!(:cache_rows=>false)
18
18
 
19
+ # SEQUEL6: Default to ansi: true
19
20
  if opts[:ansi]
20
21
  sql = %w(
21
22
  ANSI_NULLS
@@ -74,7 +75,7 @@ module Sequel
74
75
  return r.public_send(m) if m
75
76
  end
76
77
  end
77
- yield(r) if block_given?
78
+ yield(r) if defined?(yield)
78
79
  rescue TinyTds::Error => e
79
80
  raise_error(e, :disconnect=>!c.active?)
80
81
  ensure
@@ -0,0 +1,22 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ class Dataset
5
+ module ColumnsLimit1
6
+ COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
7
+
8
+ # Use a limit of 1 instead of a limit of 0 when
9
+ # getting the columns.
10
+ def columns!
11
+ ds = clone(COLUMNS_CLONE_OPTIONS)
12
+ ds.each{break}
13
+
14
+ if cols = ds.cache[:_columns]
15
+ self.columns = cols
16
+ else
17
+ []
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -18,6 +18,7 @@ module Sequel
18
18
  This connection is still waiting for a result, try again once you have the result
19
19
  closed MySQL connection
20
20
  The MySQL server is running with the --read-only option so it cannot execute this statement
21
+ Connection was killed
21
22
  END
22
23
  # Error messages for mysql and mysql2 that indicate the current connection should be disconnected
23
24
  MYSQL_DATABASE_DISCONNECT_ERRORS = /\A#{Regexp.union(disconnect_errors)}/
@@ -33,7 +34,7 @@ module Sequel
33
34
  def execute(sql, opts=OPTS, &block)
34
35
  if opts[:sproc]
35
36
  call_sproc(sql, opts, &block)
36
- elsif sql.is_a?(Symbol)
37
+ elsif sql.is_a?(Symbol) || sql.is_a?(Sequel::Dataset::ArgumentMapper)
37
38
  execute_prepared_statement(sql, opts, &block)
38
39
  else
39
40
  synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
@@ -80,6 +80,12 @@ module Sequel
80
80
  SQL::DelayedEvaluation.new(lambda{|ds| v(o.call(ds))})
81
81
  when SQL::Wrapper
82
82
  SQL::Wrapper.new(v(o.value))
83
+ when SQL::Expression
84
+ if o.respond_to?(:sequel_ast_transform)
85
+ o.sequel_ast_transform(method(:v))
86
+ else
87
+ o
88
+ end
83
89
  else
84
90
  o
85
91
  end
@@ -41,7 +41,10 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
41
41
  # :server :: Should be a symbol specifing the server to disconnect from,
42
42
  # or an array of symbols to specify multiple servers.
43
43
  def disconnect(opts=OPTS)
44
- (opts[:server] ? Array(opts[:server]) : servers).each{|s| disconnect_server(s)}
44
+ (opts[:server] ? Array(opts[:server]) : servers).each do |s|
45
+ raise Sequel::Error, "invalid server: #{s}" unless @servers.has_key?(s)
46
+ disconnect_server(s)
47
+ end
45
48
  end
46
49
 
47
50
  def freeze
@@ -52,13 +55,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
52
55
  # Yields the connection to the supplied block for the given server.
53
56
  # This method simulates the ConnectionPool#hold API.
54
57
  def hold(server=:default)
55
- begin
56
- server = pick_server(server)
57
- yield(@conns[server] ||= make_new(server))
58
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
59
- disconnect_server(server) if disconnect_error?(e)
60
- raise
61
- end
58
+ server = pick_server(server)
59
+ yield(@conns[server] ||= make_new(server))
60
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
61
+ disconnect_server(server) if disconnect_error?(e)
62
+ raise
62
63
  end
63
64
 
64
65
  # The ShardedSingleConnectionPool always has a maximum size of 1.
@@ -95,9 +95,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
95
95
  # or an array of symbols to specify multiple servers.
96
96
  def disconnect(opts=OPTS)
97
97
  (opts[:server] ? Array(opts[:server]) : sync{@servers.keys}).each do |s|
98
- if conns = sync{disconnect_server_connections(s)}
99
- disconnect_connections(conns)
100
- end
98
+ disconnect_connections(sync{disconnect_server_connections(s)})
101
99
  end
102
100
  end
103
101
 
@@ -203,9 +201,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
203
201
 
204
202
  until conn = assign_connection(thread, server)
205
203
  elapsed = Sequel.elapsed_seconds_since(timer)
204
+ # :nocov:
206
205
  raise_pool_timeout(elapsed, server) if elapsed > timeout
207
206
 
208
- # :nocov:
209
207
  # It's difficult to get to this point, it can only happen if there is a race condition
210
208
  # where a connection cannot be acquired even after the thread is signalled by the condition variable
211
209
  sync do
@@ -278,13 +276,15 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
278
276
  # Mark any allocated connections to be removed when they are checked back in. The calling
279
277
  # code should already have the mutex before calling this.
280
278
  def disconnect_server_connections(server)
281
- @connections_to_remove.concat(allocated(server).values)
279
+ remove_conns = allocated(server)
280
+ dis_conns = available_connections(server)
281
+ raise Sequel::Error, "invalid server: #{server}" unless remove_conns && dis_conns
282
282
 
283
- if dis_conns = available_connections(server)
284
- conns = dis_conns.dup
285
- dis_conns.clear
286
- @waiters[server].signal
287
- end
283
+ @connections_to_remove.concat(remove_conns.values)
284
+
285
+ conns = dis_conns.dup
286
+ dis_conns.clear
287
+ @waiters[server].signal
288
288
  conns
289
289
  end
290
290
 
@@ -11,7 +11,7 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
11
11
 
12
12
  # Yield the connection if one has been made.
13
13
  def all_connections
14
- yield @conn.first if @conn
14
+ yield @conn.first unless @conn.empty?
15
15
  end
16
16
 
17
17
  # Disconnect the connection from the database.
@@ -24,15 +24,13 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
24
24
 
25
25
  # Yield the connection to the block.
26
26
  def hold(server=nil)
27
- begin
28
- unless c = @conn.first
29
- @conn.replace([c = make_new(:default)])
30
- end
31
- yield c
32
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
33
- disconnect if disconnect_error?(e)
34
- raise
27
+ unless c = @conn.first
28
+ @conn.replace([c = make_new(:default)])
35
29
  end
30
+ yield c
31
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
32
+ disconnect if disconnect_error?(e)
33
+ raise
36
34
  end
37
35
 
38
36
  # The SingleConnectionPool always has a maximum size of 1.
@@ -152,9 +152,9 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
152
152
 
153
153
  until conn = assign_connection(thread)
154
154
  elapsed = Sequel.elapsed_seconds_since(timer)
155
+ # :nocov:
155
156
  raise_pool_timeout(elapsed) if elapsed > timeout
156
157
 
157
- # :nocov:
158
158
  # It's difficult to get to this point, it can only happen if there is a race condition
159
159
  # where a connection cannot be acquired even after the thread is signalled by the condition variable
160
160
  sync do
data/lib/sequel/core.rb CHANGED
@@ -52,13 +52,12 @@ module Sequel
52
52
  #
53
53
  # Sequel.datetime_class = DateTime
54
54
  #
55
- # Note that +Time+ and +DateTime+ objects
56
- # have a different API, and in cases where they implement the same methods,
57
- # they often implement them differently (e.g. + using seconds on +Time+ and
58
- # days on +DateTime+).
55
+ # Note that +Time+ and +DateTime+ objects have a different API, and in
56
+ # cases where they implement the same methods, they often implement them
57
+ # differently (e.g. + using seconds on +Time+ and days on +DateTime+).
59
58
  attr_accessor :datetime_class
60
59
 
61
- # Set whether Sequel is being used in single threaded mode. by default,
60
+ # Set whether Sequel is being used in single threaded mode. By default,
62
61
  # Sequel uses a thread-safe connection pool, which isn't as fast as the
63
62
  # single threaded connection pool, and also has some additional thread
64
63
  # safety checks. If your program will only have one thread,
@@ -67,7 +66,7 @@ module Sequel
67
66
  # Sequel.single_threaded = true
68
67
  attr_accessor :single_threaded
69
68
 
70
- # Alias of original require method, as Sequel.require is does a relative
69
+ # Alias of original require method, as Sequel.require does a relative
71
70
  # require for backwards compatibility.
72
71
  alias orig_require require
73
72
  private :orig_require
@@ -177,6 +176,17 @@ module Sequel
177
176
  JSON.parse(json, :create_additions=>false)
178
177
  end
179
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
+
180
190
  # Convert each item in the array to the correct type, handling multi-dimensional
181
191
  # arrays. For each element in the array or subarrays, call the converter,
182
192
  # unless the value is nil.
@@ -268,11 +278,9 @@ module Sequel
268
278
  #
269
279
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
270
280
  def string_to_date(string)
271
- begin
272
- Date.parse(string, Sequel.convert_two_digit_years)
273
- rescue => e
274
- raise convert_exception_class(e, InvalidValue)
275
- end
281
+ Date.parse(string, Sequel.convert_two_digit_years)
282
+ rescue => e
283
+ raise convert_exception_class(e, InvalidValue)
276
284
  end
277
285
 
278
286
  # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
@@ -280,15 +288,13 @@ module Sequel
280
288
  #
281
289
  # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
282
290
  def string_to_datetime(string)
283
- begin
284
- if datetime_class == DateTime
285
- DateTime.parse(string, convert_two_digit_years)
286
- else
287
- datetime_class.parse(string)
288
- end
289
- rescue => e
290
- 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)
291
295
  end
296
+ rescue => e
297
+ raise convert_exception_class(e, InvalidValue)
292
298
  end
293
299
 
294
300
  # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
@@ -296,11 +302,9 @@ module Sequel
296
302
  # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
297
303
  # DB.literal(v) # => '10:20:30'
298
304
  def string_to_time(string)
299
- begin
300
- SQLTime.parse(string)
301
- rescue => e
302
- raise convert_exception_class(e, InvalidValue)
303
- end
305
+ SQLTime.parse(string)
306
+ rescue => e
307
+ raise convert_exception_class(e, InvalidValue)
304
308
  end
305
309
 
306
310
  # Unless in single threaded mode, protects access to any mutable
@@ -390,6 +394,11 @@ module Sequel
390
394
 
391
395
  private
392
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
+
393
402
  # Helper method that the database adapter class methods that are added to Sequel via
394
403
  # metaprogramming use to parse arguments.
395
404
  def adapter_method(adapter, *args, &block)
@@ -36,7 +36,7 @@ module Sequel
36
36
  c = adapter_class(scheme)
37
37
  uri_options = c.send(:uri_to_options, uri)
38
38
  uri.query.split('&').map{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
39
- uri_options.to_a.each{|k,v| uri_options[k] = (defined?(URI::DEFAULT_PARSER) ? URI::DEFAULT_PARSER : URI).unescape(v) if v.is_a?(String)}
39
+ uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
40
40
  opts = uri_options.merge(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
41
41
  end
42
42
  when Hash
@@ -55,12 +55,11 @@ module Sequel
55
55
 
56
56
  begin
57
57
  db = c.new(opts)
58
- db.test_connection if db.send(:typecast_value_boolean, opts.fetch(:test, true))
59
- if block_given?
58
+ if defined?(yield)
60
59
  return yield(db)
61
60
  end
62
61
  ensure
63
- if block_given?
62
+ if defined?(yield)
64
63
  db.disconnect if db
65
64
  Sequel.synchronize{::Sequel::DATABASES.delete(db)}
66
65
  end
@@ -95,6 +95,8 @@ module Sequel
95
95
  # options hash.
96
96
  #
97
97
  # Accepts the following options:
98
+ # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
99
+ # but before any connections are created.
98
100
  # :cache_schema :: Whether schema should be cached for this Database instance
99
101
  # :default_string_column_size :: The default size of string columns, 255 by default.
100
102
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
@@ -153,19 +155,28 @@ module Sequel
153
155
  reset_default_dataset
154
156
  adapter_initialize
155
157
 
156
- unless typecast_value_boolean(@opts[:keep_reference]) == false
157
- Sequel.synchronize{::Sequel::DATABASES.push(self)}
158
- end
159
- Sequel::Database.run_after_initialize(self)
158
+ keep_reference = typecast_value_boolean(@opts[:keep_reference]) != false
159
+ begin
160
+ Sequel.synchronize{::Sequel::DATABASES.push(self)} if keep_reference
161
+ Sequel::Database.run_after_initialize(self)
160
162
 
161
- initialize_load_extensions(:preconnect_extensions)
163
+ initialize_load_extensions(:preconnect_extensions)
162
164
 
163
- if typecast_value_boolean(@opts[:preconnect]) && @pool.respond_to?(:preconnect, true)
164
- concurrent = typecast_value_string(@opts[:preconnect]) == "concurrently"
165
- @pool.send(:preconnect, concurrent)
166
- end
165
+ if before_preconnect = @opts[:before_preconnect]
166
+ before_preconnect.call(self)
167
+ end
168
+
169
+ if typecast_value_boolean(@opts[:preconnect]) && @pool.respond_to?(:preconnect, true)
170
+ concurrent = typecast_value_string(@opts[:preconnect]) == "concurrently"
171
+ @pool.send(:preconnect, concurrent)
172
+ end
167
173
 
168
- initialize_load_extensions(:extensions)
174
+ initialize_load_extensions(:extensions)
175
+ test_connection if typecast_value_boolean(@opts.fetch(:test, true)) && respond_to?(:connect, true)
176
+ rescue
177
+ Sequel.synchronize{::Sequel::DATABASES.delete(self)} if keep_reference
178
+ raise
179
+ end
169
180
  end
170
181
 
171
182
  # Freeze internal data structures for the Database instance.
@@ -185,7 +196,9 @@ module Sequel
185
196
 
186
197
  # Disallow dup/clone for Database instances
187
198
  undef_method :dup, :clone, :initialize_copy
199
+ # :nocov:
188
200
  if RUBY_VERSION >= '1.9.3'
201
+ # :nocov:
189
202
  undef_method :initialize_clone, :initialize_dup
190
203
  end
191
204
 
@@ -206,8 +219,7 @@ module Sequel
206
219
  Sequel.extension(*exts)
207
220
  exts.each do |ext|
208
221
  if pr = Sequel.synchronize{EXTENSIONS[ext]}
209
- unless Sequel.synchronize{@loaded_extensions.include?(ext)}
210
- Sequel.synchronize{@loaded_extensions << ext}
222
+ if Sequel.synchronize{@loaded_extensions.include?(ext) ? false : (@loaded_extensions << ext)}
211
223
  pr.call(self)
212
224
  end
213
225
  else
@@ -440,6 +452,19 @@ module Sequel
440
452
  end
441
453
  end
442
454
 
455
+ # Swallow database errors, unless they are connect/disconnect errors.
456
+ def swallow_database_error
457
+ yield
458
+ rescue Sequel::DatabaseDisconnectError, DatabaseConnectionError
459
+ # Always raise disconnect errors
460
+ raise
461
+ rescue Sequel::DatabaseError
462
+ # Don't raise other database errors.
463
+ nil
464
+ # else
465
+ # Don't rescue other exceptions, they will be raised normally.
466
+ end
467
+
443
468
  # Typecast the value to an SQL::Blob
444
469
  def typecast_value_blob(value)
445
470
  value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
@@ -236,7 +236,7 @@ module Sequel
236
236
  when :date
237
237
  Sequel.string_to_date(default)
238
238
  when :datetime
239
- DateTime.parse(default)
239
+ Sequel.string_to_datetime(default)
240
240
  when :time
241
241
  Sequel.string_to_time(default)
242
242
  when :decimal
@@ -344,7 +344,9 @@ module Sequel
344
344
 
345
345
  # Post process the schema values.
346
346
  def schema_post_process(cols)
347
+ # :nocov:
347
348
  if RUBY_VERSION >= '2.5'
349
+ # :nocov:
348
350
  cols.each do |_, h|
349
351
  db_type = h[:db_type]
350
352
  if db_type.is_a?(String)