sequel 5.83.0 → 5.106.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/lib/sequel/adapters/ado/access.rb +2 -2
  4. data/lib/sequel/adapters/ado.rb +2 -2
  5. data/lib/sequel/adapters/amalgalite.rb +16 -1
  6. data/lib/sequel/adapters/ibmdb.rb +2 -1
  7. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/derby.rb +4 -2
  9. data/lib/sequel/adapters/jdbc/h2.rb +4 -4
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  11. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
  13. data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  15. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
  18. data/lib/sequel/adapters/jdbc.rb +29 -15
  19. data/lib/sequel/adapters/mysql.rb +1 -1
  20. data/lib/sequel/adapters/mysql2.rb +10 -3
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +16 -0
  23. data/lib/sequel/adapters/postgres.rb +2 -1
  24. data/lib/sequel/adapters/shared/access.rb +1 -0
  25. data/lib/sequel/adapters/shared/db2.rb +8 -3
  26. data/lib/sequel/adapters/shared/mssql.rb +9 -8
  27. data/lib/sequel/adapters/shared/mysql.rb +16 -11
  28. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  29. data/lib/sequel/adapters/shared/postgres.rb +438 -47
  30. data/lib/sequel/adapters/shared/sqlite.rb +9 -7
  31. data/lib/sequel/adapters/sqlite.rb +20 -2
  32. data/lib/sequel/adapters/tinytds.rb +2 -2
  33. data/lib/sequel/adapters/trilogy.rb +2 -3
  34. data/lib/sequel/ast_transformer.rb +2 -0
  35. data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
  36. data/lib/sequel/connection_pool/timed_queue.rb +33 -10
  37. data/lib/sequel/connection_pool.rb +8 -2
  38. data/lib/sequel/core.rb +31 -2
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/dataset_defaults.rb +3 -3
  41. data/lib/sequel/database/misc.rb +29 -11
  42. data/lib/sequel/database/query.rb +12 -12
  43. data/lib/sequel/database/schema_generator.rb +117 -16
  44. data/lib/sequel/database/schema_methods.rb +85 -30
  45. data/lib/sequel/dataset/actions.rb +83 -11
  46. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  47. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
  48. data/lib/sequel/dataset/prepared_statements.rb +76 -28
  49. data/lib/sequel/dataset/query.rb +26 -11
  50. data/lib/sequel/dataset/sql.rb +40 -17
  51. data/lib/sequel/extensions/async_thread_pool.rb +1 -1
  52. data/lib/sequel/extensions/caller_logging.rb +1 -3
  53. data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
  54. data/lib/sequel/extensions/connection_expiration.rb +44 -5
  55. data/lib/sequel/extensions/connection_validator.rb +15 -10
  56. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  57. data/lib/sequel/extensions/dataset_run.rb +41 -0
  58. data/lib/sequel/extensions/date_arithmetic.rb +6 -6
  59. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  60. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  61. data/lib/sequel/extensions/inflector.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
  63. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  64. data/lib/sequel/extensions/migration.rb +38 -21
  65. data/lib/sequel/extensions/null_dataset.rb +2 -2
  66. data/lib/sequel/extensions/pg_array_ops.rb +41 -0
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
  68. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  69. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
  70. data/lib/sequel/extensions/pg_enum.rb +4 -4
  71. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  73. data/lib/sequel/extensions/pg_json_ops.rb +674 -18
  74. data/lib/sequel/extensions/pg_multirange.rb +1 -1
  75. data/lib/sequel/extensions/pg_range.rb +14 -2
  76. data/lib/sequel/extensions/pg_row.rb +5 -3
  77. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  78. data/lib/sequel/extensions/provenance.rb +1 -3
  79. data/lib/sequel/extensions/query_blocker.rb +172 -0
  80. data/lib/sequel/extensions/schema_caching.rb +24 -9
  81. data/lib/sequel/extensions/schema_dumper.rb +17 -5
  82. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  83. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  84. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  85. data/lib/sequel/extensions/string_agg.rb +2 -2
  86. data/lib/sequel/extensions/to_dot.rb +5 -0
  87. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  88. data/lib/sequel/model/associations.rb +215 -15
  89. data/lib/sequel/model/base.rb +198 -35
  90. data/lib/sequel/model/plugins.rb +12 -1
  91. data/lib/sequel/model.rb +1 -0
  92. data/lib/sequel/plugins/accessed_columns.rb +4 -0
  93. data/lib/sequel/plugins/active_model.rb +5 -6
  94. data/lib/sequel/plugins/association_pks.rb +2 -0
  95. data/lib/sequel/plugins/auto_validations.rb +2 -0
  96. data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
  97. data/lib/sequel/plugins/columns_updated.rb +4 -0
  98. data/lib/sequel/plugins/composition.rb +3 -1
  99. data/lib/sequel/plugins/constraint_validations.rb +15 -10
  100. data/lib/sequel/plugins/dataset_associations.rb +20 -1
  101. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  102. data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
  103. data/lib/sequel/plugins/dirty.rb +9 -2
  104. data/lib/sequel/plugins/enum.rb +1 -1
  105. data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
  106. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  107. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  108. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  109. data/lib/sequel/plugins/instance_filters.rb +8 -1
  110. data/lib/sequel/plugins/instance_hooks.rb +4 -0
  111. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  112. data/lib/sequel/plugins/json_serializer.rb +6 -10
  113. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  114. data/lib/sequel/plugins/many_through_many.rb +21 -0
  115. data/lib/sequel/plugins/modification_detection.rb +2 -0
  116. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  117. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  118. data/lib/sequel/plugins/paged_operations.rb +5 -2
  119. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  120. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  121. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  122. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  123. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
  124. data/lib/sequel/plugins/rcte_tree.rb +6 -6
  125. data/lib/sequel/plugins/serialization.rb +23 -6
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
  127. data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
  128. data/lib/sequel/plugins/split_values.rb +12 -0
  129. data/lib/sequel/plugins/sql_comments.rb +7 -2
  130. data/lib/sequel/plugins/static_cache.rb +13 -0
  131. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  132. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  133. data/lib/sequel/plugins/subset_static_cache.rb +278 -0
  134. data/lib/sequel/plugins/table_select.rb +7 -0
  135. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
  136. data/lib/sequel/plugins/unused_associations.rb +11 -3
  137. data/lib/sequel/plugins/update_primary_key.rb +4 -0
  138. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  139. data/lib/sequel/plugins/validation_contexts.rb +4 -0
  140. data/lib/sequel/sql.rb +27 -12
  141. data/lib/sequel/version.rb +1 -1
  142. metadata +17 -240
  143. data/CHANGELOG +0 -1393
  144. data/README.rdoc +0 -936
  145. data/doc/advanced_associations.rdoc +0 -884
  146. data/doc/association_basics.rdoc +0 -1859
  147. data/doc/bin_sequel.rdoc +0 -146
  148. data/doc/cheat_sheet.rdoc +0 -255
  149. data/doc/code_order.rdoc +0 -104
  150. data/doc/core_extensions.rdoc +0 -405
  151. data/doc/dataset_basics.rdoc +0 -96
  152. data/doc/dataset_filtering.rdoc +0 -222
  153. data/doc/extensions.rdoc +0 -77
  154. data/doc/fork_safety.rdoc +0 -84
  155. data/doc/mass_assignment.rdoc +0 -98
  156. data/doc/migration.rdoc +0 -660
  157. data/doc/model_dataset_method_design.rdoc +0 -129
  158. data/doc/model_hooks.rdoc +0 -254
  159. data/doc/model_plugins.rdoc +0 -270
  160. data/doc/mssql_stored_procedures.rdoc +0 -43
  161. data/doc/object_model.rdoc +0 -563
  162. data/doc/opening_databases.rdoc +0 -439
  163. data/doc/postgresql.rdoc +0 -611
  164. data/doc/prepared_statements.rdoc +0 -144
  165. data/doc/querying.rdoc +0 -1070
  166. data/doc/reflection.rdoc +0 -120
  167. data/doc/release_notes/5.0.0.txt +0 -159
  168. data/doc/release_notes/5.1.0.txt +0 -31
  169. data/doc/release_notes/5.10.0.txt +0 -84
  170. data/doc/release_notes/5.11.0.txt +0 -83
  171. data/doc/release_notes/5.12.0.txt +0 -141
  172. data/doc/release_notes/5.13.0.txt +0 -27
  173. data/doc/release_notes/5.14.0.txt +0 -63
  174. data/doc/release_notes/5.15.0.txt +0 -39
  175. data/doc/release_notes/5.16.0.txt +0 -110
  176. data/doc/release_notes/5.17.0.txt +0 -31
  177. data/doc/release_notes/5.18.0.txt +0 -69
  178. data/doc/release_notes/5.19.0.txt +0 -28
  179. data/doc/release_notes/5.2.0.txt +0 -33
  180. data/doc/release_notes/5.20.0.txt +0 -89
  181. data/doc/release_notes/5.21.0.txt +0 -87
  182. data/doc/release_notes/5.22.0.txt +0 -48
  183. data/doc/release_notes/5.23.0.txt +0 -56
  184. data/doc/release_notes/5.24.0.txt +0 -56
  185. data/doc/release_notes/5.25.0.txt +0 -32
  186. data/doc/release_notes/5.26.0.txt +0 -35
  187. data/doc/release_notes/5.27.0.txt +0 -21
  188. data/doc/release_notes/5.28.0.txt +0 -16
  189. data/doc/release_notes/5.29.0.txt +0 -22
  190. data/doc/release_notes/5.3.0.txt +0 -121
  191. data/doc/release_notes/5.30.0.txt +0 -20
  192. data/doc/release_notes/5.31.0.txt +0 -148
  193. data/doc/release_notes/5.32.0.txt +0 -46
  194. data/doc/release_notes/5.33.0.txt +0 -24
  195. data/doc/release_notes/5.34.0.txt +0 -40
  196. data/doc/release_notes/5.35.0.txt +0 -56
  197. data/doc/release_notes/5.36.0.txt +0 -60
  198. data/doc/release_notes/5.37.0.txt +0 -30
  199. data/doc/release_notes/5.38.0.txt +0 -28
  200. data/doc/release_notes/5.39.0.txt +0 -19
  201. data/doc/release_notes/5.4.0.txt +0 -80
  202. data/doc/release_notes/5.40.0.txt +0 -40
  203. data/doc/release_notes/5.41.0.txt +0 -25
  204. data/doc/release_notes/5.42.0.txt +0 -136
  205. data/doc/release_notes/5.43.0.txt +0 -98
  206. data/doc/release_notes/5.44.0.txt +0 -32
  207. data/doc/release_notes/5.45.0.txt +0 -34
  208. data/doc/release_notes/5.46.0.txt +0 -87
  209. data/doc/release_notes/5.47.0.txt +0 -59
  210. data/doc/release_notes/5.48.0.txt +0 -14
  211. data/doc/release_notes/5.49.0.txt +0 -59
  212. data/doc/release_notes/5.5.0.txt +0 -61
  213. data/doc/release_notes/5.50.0.txt +0 -78
  214. data/doc/release_notes/5.51.0.txt +0 -47
  215. data/doc/release_notes/5.52.0.txt +0 -87
  216. data/doc/release_notes/5.53.0.txt +0 -23
  217. data/doc/release_notes/5.54.0.txt +0 -27
  218. data/doc/release_notes/5.55.0.txt +0 -21
  219. data/doc/release_notes/5.56.0.txt +0 -51
  220. data/doc/release_notes/5.57.0.txt +0 -23
  221. data/doc/release_notes/5.58.0.txt +0 -31
  222. data/doc/release_notes/5.59.0.txt +0 -73
  223. data/doc/release_notes/5.6.0.txt +0 -31
  224. data/doc/release_notes/5.60.0.txt +0 -22
  225. data/doc/release_notes/5.61.0.txt +0 -43
  226. data/doc/release_notes/5.62.0.txt +0 -132
  227. data/doc/release_notes/5.63.0.txt +0 -33
  228. data/doc/release_notes/5.64.0.txt +0 -50
  229. data/doc/release_notes/5.65.0.txt +0 -21
  230. data/doc/release_notes/5.66.0.txt +0 -24
  231. data/doc/release_notes/5.67.0.txt +0 -32
  232. data/doc/release_notes/5.68.0.txt +0 -61
  233. data/doc/release_notes/5.69.0.txt +0 -26
  234. data/doc/release_notes/5.7.0.txt +0 -108
  235. data/doc/release_notes/5.70.0.txt +0 -35
  236. data/doc/release_notes/5.71.0.txt +0 -21
  237. data/doc/release_notes/5.72.0.txt +0 -33
  238. data/doc/release_notes/5.73.0.txt +0 -66
  239. data/doc/release_notes/5.74.0.txt +0 -45
  240. data/doc/release_notes/5.75.0.txt +0 -35
  241. data/doc/release_notes/5.76.0.txt +0 -86
  242. data/doc/release_notes/5.77.0.txt +0 -63
  243. data/doc/release_notes/5.78.0.txt +0 -67
  244. data/doc/release_notes/5.79.0.txt +0 -28
  245. data/doc/release_notes/5.8.0.txt +0 -170
  246. data/doc/release_notes/5.80.0.txt +0 -40
  247. data/doc/release_notes/5.81.0.txt +0 -31
  248. data/doc/release_notes/5.82.0.txt +0 -61
  249. data/doc/release_notes/5.83.0.txt +0 -56
  250. data/doc/release_notes/5.9.0.txt +0 -99
  251. data/doc/schema_modification.rdoc +0 -679
  252. data/doc/security.rdoc +0 -443
  253. data/doc/sharding.rdoc +0 -286
  254. data/doc/sql.rdoc +0 -648
  255. data/doc/testing.rdoc +0 -204
  256. data/doc/thread_safety.rdoc +0 -15
  257. data/doc/transactions.rdoc +0 -250
  258. data/doc/validations.rdoc +0 -558
  259. data/doc/virtual_rows.rdoc +0 -265
@@ -95,14 +95,14 @@ module Sequel
95
95
  metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
96
96
  if opts[:only_autocreated]
97
97
  # If specifically asked for only autocreated indexes, then return those an only those
98
- next unless r[:name] =~ /\Asqlite_autoindex_/
98
+ next unless r[:name].start_with?('sqlite_autoindex_')
99
99
  elsif r.has_key?(:origin)
100
100
  # If origin is set, then only exclude primary key indexes and partial indexes
101
101
  next if r[:origin] == 'pk'
102
102
  next if r[:partial].to_i == 1
103
103
  else
104
104
  # When :origin key not present, assume any autoindex could be a primary key one and exclude it
105
- next if r[:name] =~ /\Asqlite_autoindex_/
105
+ next if r[:name].start_with?('sqlite_autoindex_')
106
106
  end
107
107
 
108
108
  indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
@@ -195,7 +195,7 @@ module Sequel
195
195
 
196
196
  # Dataset used for parsing schema
197
197
  def _parse_pragma_ds(table_name, opts)
198
- metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
198
+ metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)".freeze, input_identifier_meth(opts[:dataset]).call(table_name))
199
199
  end
200
200
 
201
201
  # Run all alter_table commands in a transaction. This is technically only
@@ -302,7 +302,7 @@ module Sequel
302
302
 
303
303
  # A name to use for the backup table
304
304
  def backup_table_name(table, opts=OPTS)
305
- table = table.gsub('`', '')
305
+ table = table.delete('`')
306
306
  (opts[:times]||1000).times do |i|
307
307
  table_name = "#{table}_backup#{i}"
308
308
  return table_name unless table_exists?(table_name)
@@ -349,7 +349,7 @@ module Sequel
349
349
  ps
350
350
  end
351
351
 
352
- # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
352
+ # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options, and VIRTUAL tables with :using option.
353
353
  def create_table_sql(name, generator, options)
354
354
  if options[:strict] && options[:without_rowid]
355
355
  "#{super} STRICT, WITHOUT ROWID"
@@ -357,6 +357,8 @@ module Sequel
357
357
  "#{super} STRICT"
358
358
  elsif options[:without_rowid]
359
359
  "#{super} WITHOUT ROWID"
360
+ elsif options[:using]
361
+ "CREATE VIRTUAL TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)} USING #{options[:using]}"
360
362
  else
361
363
  super
362
364
  end
@@ -405,7 +407,7 @@ module Sequel
405
407
  def defined_columns_for(table)
406
408
  cols = parse_pragma(table, OPTS)
407
409
  cols.each do |c|
408
- c[:default] = LiteralString.new(c[:default]) if c[:default]
410
+ c[:default] = LiteralString.new(c[:default]).freeze if c[:default]
409
411
  c[:type] = c[:db_type]
410
412
  end
411
413
  cols
@@ -682,7 +684,7 @@ module Sequel
682
684
  # Load the PrettyTable class, needed for explain output
683
685
  Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
684
686
 
685
- ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
687
+ ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
686
688
  rows = ds.all
687
689
  Sequel::PrettyTable.string(rows, ds.columns)
688
690
  end
@@ -89,8 +89,22 @@ module Sequel
89
89
 
90
90
  # Mimic the file:// uri, by having 2 preceding slashes specify a relative
91
91
  # path, and 3 preceding slashes specify an absolute path.
92
+ # Also support no preceding slashes to specify a relative path.
92
93
  def self.uri_to_options(uri) # :nodoc:
93
- { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
94
+ database = if uri.host.nil?
95
+ case uri.path
96
+ when '/'
97
+ nil
98
+ when nil
99
+ uri.opaque
100
+ else
101
+ uri.path
102
+ end
103
+ else
104
+ "#{uri.host}#{uri.path}"
105
+ end
106
+
107
+ { :database => database }
94
108
  end
95
109
 
96
110
  private_class_method :uri_to_options
@@ -109,6 +123,8 @@ module Sequel
109
123
  # :database :: database name (filename or ':memory:' or file: URI)
110
124
  # :readonly :: open database in read-only mode; useful for reading
111
125
  # static data that you do not want to modify
126
+ # :disable_dqs :: disable double quoted strings in DDL and DML statements
127
+ # (requires SQLite 3.29.0+ and sqlite3 gem version 1.4.3+).
112
128
  # :timeout :: how long to wait for the database to be available if it
113
129
  # is locked, given in milliseconds (default is 5000)
114
130
  # :setup_regexp_function :: enable use of Regexp objects with SQL
@@ -128,6 +144,8 @@ module Sequel
128
144
  opts[:database] = ':memory:' if blank_object?(opts[:database])
129
145
  sqlite3_opts = {}
130
146
  sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
147
+ # SEQUEL6: Make strict: true the default behavior
148
+ sqlite3_opts[:strict] = typecast_value_boolean(opts[:disable_dqs]) if opts.has_key?(:disable_dqs)
131
149
  db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
132
150
  db.busy_timeout(typecast_value_integer(opts.fetch(:timeout, 5000)))
133
151
 
@@ -370,7 +388,7 @@ module Sequel
370
388
  # SQLite uses a : before the name of the argument for named
371
389
  # arguments.
372
390
  def prepared_arg(k)
373
- LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
391
+ LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}".freeze)
374
392
  end
375
393
  end
376
394
 
@@ -14,7 +14,7 @@ module Sequel
14
14
  opts = server_opts(server)
15
15
  opts[:username] = opts[:user]
16
16
  c = TinyTds::Client.new(opts)
17
- c.query_options.merge!(:cache_rows=>false)
17
+ c.query_options[:cache_rows] = false
18
18
 
19
19
  # SEQUEL6: Default to ansi: true
20
20
  if opts[:ansi]
@@ -205,7 +205,7 @@ module Sequel
205
205
  private
206
206
 
207
207
  def prepared_arg(k)
208
- LiteralString.new("@#{k.to_s.gsub('.', '__')}")
208
+ LiteralString.new("@#{k.to_s.gsub('.', '__')}".freeze)
209
209
  end
210
210
  end
211
211
 
@@ -62,8 +62,7 @@ module Sequel
62
62
  private
63
63
 
64
64
  def database_specific_error_class(exception, opts)
65
- case exception.message
66
- when /1205 - Lock wait timeout exceeded; try restarting transaction\z/
65
+ if exception.error_code == 1205
67
66
  DatabaseLockTimeout
68
67
  else
69
68
  super
@@ -84,7 +83,7 @@ module Sequel
84
83
 
85
84
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
86
85
  def schema_column_type(db_type)
87
- db_type =~ /\Atinyint\(1\)/ ? :boolean : super
86
+ db_type.start_with?("tinyint(1)") ? :boolean : super
88
87
  end
89
88
  end
90
89
 
@@ -26,6 +26,8 @@ module Sequel
26
26
  h = {}
27
27
  o.each{|k, val| h[v(k)] = v(val)}
28
28
  h
29
+ when Set
30
+ Set.new(o.map{|x| v(x)})
29
31
  when SQL::NumericExpression
30
32
  if o.op == :extract
31
33
  o.class.new(o.op, o.args[0], v(o.args[1]))
@@ -71,7 +71,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
71
71
  while true
72
72
  conn = nil
73
73
  begin
74
- break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
74
+ break unless (conn = available(queue, server)) && !conns[conn]
75
75
  conns[conn] = true
76
76
  yield conn
77
77
  ensure
@@ -91,14 +91,16 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
91
91
  # creates new connections to the database.
92
92
  #
93
93
  # If the :server option is provided, it should be a symbol or array of symbols,
94
- # and then the method will only disconnect connectsion from those specified shards.
94
+ # and then the method will only disconnect connections from those specified shards.
95
95
  def disconnect(opts=OPTS)
96
96
  (opts[:server] ? Array(opts[:server]) : sync{@servers.keys}).each do |server|
97
97
  raise Sequel::Error, "invalid server" unless queue = sync{@queues[server]}
98
- while conn = queue.pop(timeout: 0)
98
+ nconns = 0
99
+ while conn = available(queue, server)
100
+ nconns += 1
99
101
  disconnect_pool_connection(conn, server)
100
102
  end
101
- fill_queue(server)
103
+ fill_queue(server, nconns)
102
104
  end
103
105
  nil
104
106
  end
@@ -132,7 +134,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
132
134
  conn = nil
133
135
  disconnect_pool_connection(oconn, server) if oconn
134
136
  sync{@allocated[server].delete(t)}
135
- fill_queue(server)
137
+ fill_queue(server, 1)
136
138
  end
137
139
  raise
138
140
  ensure
@@ -140,6 +142,12 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
140
142
  end
141
143
  end
142
144
 
145
+ # The number of threads waiting to check out a connection for the given
146
+ # server.
147
+ def num_waiting(server=:default)
148
+ @queues[pick_server(server)].num_waiting
149
+ end
150
+
143
151
  # The total number of connections in the pool. Using a non-existant server will return nil.
144
152
  def size(server=:default)
145
153
  sync{@sizes[server]}
@@ -161,7 +169,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
161
169
 
162
170
  queue = @queues[server]
163
171
 
164
- while conn = queue.pop(timeout: 0)
172
+ while conn = available(queue, server)
165
173
  @sizes[server] -= 1
166
174
  conns << conn
167
175
  end
@@ -219,6 +227,12 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
219
227
  conns.each{|conn| disconnect_connection(conn)}
220
228
  end
221
229
 
230
+ # Only for use by extension that need to disconnect a connection inside acquire.
231
+ # Takes the connection and any arguments accepted by acquire.
232
+ def disconnect_acquired_connection(conn, _, server)
233
+ disconnect_pool_connection(conn, server)
234
+ end
235
+
222
236
  # Decrement the current size of the pool for the server when disconnecting connections.
223
237
  #
224
238
  # Calling code should not have the mutex when calling this.
@@ -238,11 +252,15 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
238
252
  # after disconnecting to potentially add new connections to the
239
253
  # pool, so the threads that are currently waiting for connections
240
254
  # do not timeout after the pool is no longer full.
241
- def fill_queue(server)
255
+ #
256
+ # nconns specifies the maximum number of connections to add, which should
257
+ # be the number of connections that were disconnected.
258
+ def fill_queue(server, nconns)
242
259
  queue = sync{@queues[server]}
243
- if queue.num_waiting > 0
260
+ if nconns > 0 && queue.num_waiting > 0
244
261
  Thread.new do
245
- while queue.num_waiting > 0 && (conn = try_make_new(server))
262
+ while nconns > 0 && queue.num_waiting > 0 && (conn = try_make_new(server))
263
+ nconns -= 1
246
264
  queue.push(conn)
247
265
  end
248
266
  end
@@ -291,7 +309,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
291
309
  ensure
292
310
  if to_disconnect
293
311
  to_disconnect.each{|conn| disconnect_pool_connection(conn, server)}
294
- fill_queue(server)
312
+ fill_queue(server, to_disconnect.size)
295
313
  end
296
314
  end
297
315
  end
@@ -305,7 +323,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
305
323
  # Calling code should not have the mutex when calling this.
306
324
  def acquire(thread, server)
307
325
  queue = sync{@queues[server]}
308
- if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
326
+ if conn = available(queue, server) || try_make_new(server) || wait_until_available(queue, server)
309
327
  sync{@allocated[server][thread] = conn}
310
328
  else
311
329
  name = db.opts[:name]
@@ -313,6 +331,19 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
313
331
  end
314
332
  end
315
333
 
334
+ # Return the next connection in the pool if there is one available. Returns nil
335
+ # if no connection is currently available.
336
+ def available(queue, _server)
337
+ queue.pop(timeout: 0)
338
+ end
339
+
340
+ # Return the next connection in the pool if there is one available. If not, wait
341
+ # until the timeout for a connection to become available. If there is still no
342
+ # available connection, return nil.
343
+ def wait_until_available(queue, _server)
344
+ queue.pop(timeout: @timeout)
345
+ end
346
+
316
347
  # Returns the connection owned by the supplied thread for the given server,
317
348
  # if any. The calling code should NOT already have the mutex before calling this.
318
349
  def owned_connection(thread, server)
@@ -42,7 +42,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
42
42
  while true
43
43
  conn = nil
44
44
  begin
45
- break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
45
+ break unless (conn = available) && !conns[conn]
46
46
  conns[conn] = true
47
47
  yield conn
48
48
  ensure
@@ -59,10 +59,12 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
59
59
  # Once a connection is requested using #hold, the connection pool
60
60
  # creates new connections to the database.
61
61
  def disconnect(opts=OPTS)
62
- while conn = @queue.pop(timeout: 0)
62
+ nconns = 0
63
+ while conn = available
64
+ nconns += 1
63
65
  disconnect_connection(conn)
64
66
  end
65
- fill_queue
67
+ fill_queue(nconns)
66
68
  nil
67
69
  end
68
70
 
@@ -94,7 +96,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
94
96
  conn = nil
95
97
  disconnect_connection(oconn) if oconn
96
98
  sync{@allocated.delete(t)}
97
- fill_queue
99
+ fill_queue(1)
98
100
  end
99
101
  raise
100
102
  ensure
@@ -102,6 +104,11 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
102
104
  end
103
105
  end
104
106
 
107
+ # The number of threads waiting to check out a connection.
108
+ def num_waiting(_server=:default)
109
+ @queue.num_waiting
110
+ end
111
+
105
112
  def pool_type
106
113
  :timed_queue
107
114
  end
@@ -151,10 +158,13 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
151
158
  # after disconnecting to potentially add new connections to the
152
159
  # pool, so the threads that are currently waiting for connections
153
160
  # do not timeout after the pool is no longer full.
154
- def fill_queue
155
- if @queue.num_waiting > 0
161
+ #
162
+ # nconns specifies the maximum number of connections to add, which should
163
+ # be the number of connections that were disconnected.
164
+ def fill_queue(nconns)
165
+ if nconns > 0 && @queue.num_waiting > 0
156
166
  Thread.new do
157
- while @queue.num_waiting > 0 && (conn = try_make_new)
167
+ while nconns > 0 && @queue.num_waiting > 0 && (conn = try_make_new)
158
168
  @queue.push(conn)
159
169
  end
160
170
  end
@@ -202,7 +212,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
202
212
  ensure
203
213
  if to_disconnect
204
214
  to_disconnect.each{|conn| disconnect_connection(conn)}
205
- fill_queue
215
+ fill_queue(to_disconnect.size)
206
216
  end
207
217
  end
208
218
  end
@@ -215,7 +225,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
215
225
  #
216
226
  # Calling code should not have the mutex when calling this.
217
227
  def acquire(thread)
218
- if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
228
+ if conn = available || try_make_new || wait_until_available
219
229
  sync{@allocated[thread] = conn}
220
230
  else
221
231
  name = db.opts[:name]
@@ -223,6 +233,19 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
223
233
  end
224
234
  end
225
235
 
236
+ # Return the next connection in the pool if there is one available. Returns nil
237
+ # if no connection is currently available.
238
+ def available
239
+ @queue.pop(timeout: 0)
240
+ end
241
+
242
+ # Return the next connection in the pool if there is one available. If not, wait
243
+ # until the timeout for a connection to become available. If there is still no
244
+ # available connection, return nil.
245
+ def wait_until_available
246
+ @queue.pop(timeout: @timeout)
247
+ end
248
+
226
249
  # Returns the connection owned by the supplied thread,
227
250
  # if any. The calling code should NOT already have the mutex before calling this.
228
251
  def owned_connection(thread)
@@ -236,7 +259,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
236
259
  def preconnect(concurrent = false)
237
260
  if concurrent
238
261
  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)
262
+ Array.new(times){Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
240
263
  end
241
264
  else
242
265
  while conn = try_make_new
@@ -70,13 +70,13 @@ class Sequel::ConnectionPool
70
70
  else
71
71
  pc = if opts[:single_threaded]
72
72
  opts[:servers] ? :sharded_single : :single
73
- # :nocov:
74
- elsif RUBY_VERSION >= '3.4' # SEQUEL6 or maybe earlier switch to 3.2
73
+ elsif RUBY_VERSION >= '3.2'
75
74
  opts[:servers] ? :sharded_timed_queue : :timed_queue
76
75
  # :nocov:
77
76
  else
78
77
  opts[:servers] ? :sharded_threaded : :threaded
79
78
  end
79
+ # :nocov:
80
80
 
81
81
  connection_pool_class(:pool_class=>pc)
82
82
  end
@@ -131,6 +131,12 @@ class Sequel::ConnectionPool
131
131
  db.disconnect_connection(conn)
132
132
  end
133
133
 
134
+ # Only for use by extension that need to disconnect a connection inside acquire.
135
+ # Takes the connection and any arguments accepted by acquire.
136
+ def disconnect_acquired_connection(conn, *)
137
+ disconnect_connection(conn)
138
+ end
139
+
134
140
  # Whether the given exception is a disconnect exception.
135
141
  def disconnect_error?(exception)
136
142
  exception.is_a?(Sequel::DatabaseDisconnectError) || db.send(:disconnect_error?, exception, OPTS)
data/lib/sequel/core.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- %w'bigdecimal date thread time uri'.each{|f| require f}
3
+ %w'bigdecimal date set thread time uri'.each{|f| require f}
4
4
 
5
5
  # Top level module for Sequel
6
6
  #
@@ -164,6 +164,35 @@ module Sequel
164
164
  JSON::ParserError
165
165
  end
166
166
 
167
+ if RUBY_VERSION >= '3'
168
+ # Join the array or set.
169
+ def array_or_set_join(obj, arg)
170
+ obj.join(arg)
171
+ end
172
+ # :nocov:
173
+ else
174
+ def array_or_set_join(obj, arg)
175
+ obj = obj.to_a if obj.is_a?(Set)
176
+ obj.join(arg)
177
+ end
178
+ end
179
+ # :nocov:
180
+
181
+ if RUBY_VERSION >= '3.3'
182
+ # Create a new module using the block, and set the temporary name
183
+ # on it using the given a containing module and name.
184
+ def set_temp_name(mod)
185
+ mod.set_temporary_name(yield)
186
+ mod
187
+ end
188
+ # :nocov:
189
+ else
190
+ def set_temp_name(mod)
191
+ mod
192
+ end
193
+ end
194
+ # :nocov:
195
+
167
196
  # Convert given object to json and return the result.
168
197
  # This can be overridden to use an alternative json implementation.
169
198
  def object_to_json(obj, *args, &block)
@@ -388,7 +417,7 @@ module Sequel
388
417
  when -1, 0
389
418
  vr.instance_exec(&block)
390
419
  else
391
- block.call(vr)
420
+ yield(vr)
392
421
  end
393
422
  end
394
423
 
@@ -34,7 +34,7 @@ module Sequel
34
34
  uri = URI.parse(conn_string)
35
35
  scheme = uri.scheme
36
36
  c = adapter_class(scheme)
37
- opts = c.send(:uri_to_options, uri).merge!(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
37
+ opts = c.send(:options_from_uri, uri).merge!(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
38
38
  end
39
39
  when Hash
40
40
  opts = conn_string.merge(opts)
@@ -17,7 +17,7 @@ module Sequel
17
17
  # as the dataset class.
18
18
  def dataset_class=(c)
19
19
  unless @dataset_modules.empty?
20
- c = Class.new(c)
20
+ c = Sequel.set_temp_name(Class.new(c)){"Sequel::Dataset::_Subclass"}
21
21
  @dataset_modules.each{|m| c.send(:include, m)}
22
22
  end
23
23
  @dataset_class = c
@@ -61,10 +61,10 @@ module Sequel
61
61
  # # SELECT id, name FROM table WHERE active ORDER BY id
62
62
  def extend_datasets(mod=nil, &block)
63
63
  raise(Error, "must provide either mod or block, not both") if mod && block
64
- mod = Dataset::DatasetModule.new(&block) if block
64
+ mod = Sequel.set_temp_name(Dataset::DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location[0,2].join(':')})"} if block
65
65
  if @dataset_modules.empty?
66
66
  @dataset_modules = [mod]
67
- @dataset_class = Class.new(@dataset_class)
67
+ @dataset_class = Sequel.set_temp_name(Class.new(@dataset_class)){"Sequel::Dataset::_Subclass"}
68
68
  else
69
69
  @dataset_modules << mod
70
70
  end
@@ -26,18 +26,23 @@ module Sequel
26
26
  :time=>Sequel::SQLTime, :boolean=>[TrueClass, FalseClass].freeze, :float=>Float, :decimal=>BigDecimal,
27
27
  :blob=>Sequel::SQL::Blob}.freeze
28
28
 
29
+ # :nocov:
30
+ URI_PARSER = defined?(::URI::RFC2396_PARSER) ? ::URI::RFC2396_PARSER : ::URI::DEFAULT_PARSER
31
+ # :nocov:
32
+ private_constant :URI_PARSER
33
+
29
34
  # Nested hook Proc; each new hook Proc just wraps the previous one.
30
35
  @initialize_hook = proc{|db| }
31
36
 
32
37
  # Register a hook that will be run when a new Database is instantiated. It is
33
38
  # called with the new database handle.
34
- def self.after_initialize(&block)
35
- raise Error, "must provide block to after_initialize" unless block
39
+ def self.after_initialize
40
+ raise Error, "must provide block to after_initialize" unless defined?(yield)
36
41
  Sequel.synchronize do
37
42
  previous = @initialize_hook
38
43
  @initialize_hook = proc do |db|
39
44
  previous.call(db)
40
- block.call(db)
45
+ yield(db)
41
46
  end
42
47
  end
43
48
  end
@@ -72,18 +77,23 @@ module Sequel
72
77
  # Converts a uri to an options hash. These options are then passed
73
78
  # to a newly created database object.
74
79
  def self.uri_to_options(uri)
75
- uri_options = {
80
+ {
76
81
  :user => uri.user,
77
82
  :password => uri.password,
78
83
  :port => uri.port,
79
84
  :host => uri.hostname,
80
85
  :database => (m = /\/(.*)/.match(uri.path)) && (m[1])
81
86
  }
87
+ end
88
+ private_class_method :uri_to_options
89
+
90
+ def self.options_from_uri(uri)
91
+ uri_options = uri_to_options(uri)
82
92
  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?
83
- uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
93
+ uri_options.to_a.each{|k,v| uri_options[k] = URI_PARSER.unescape(v) if v.is_a?(String)}
84
94
  uri_options
85
95
  end
86
- private_class_method :uri_to_options
96
+ private_class_method :options_from_uri
87
97
 
88
98
  # The options hash for this database
89
99
  attr_reader :opts
@@ -106,6 +116,10 @@ module Sequel
106
116
  # :after_connect :: A callable object called after each new connection is made, with the
107
117
  # connection object (and server argument if the callable accepts 2 arguments),
108
118
  # useful for customizations that you want to apply to all connections.
119
+ # :compare_connections_by_identity :: Whether to use compare_by_identity on hashes that use
120
+ # connection objects as keys. Defaults to true. This should only
121
+ # be set to false to work around bugs in libraries or
122
+ # ruby implementations.
109
123
  # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
110
124
  # but before any connections are created.
111
125
  # :cache_schema :: Whether schema should be cached for this Database instance
@@ -155,7 +169,7 @@ module Sequel
155
169
  @schemas = {}
156
170
  @prepared_statements = {}
157
171
  @transactions = {}
158
- @transactions.compare_by_identity
172
+ @transactions.compare_by_identity if typecast_value_boolean(@opts.fetch(:compare_connections_by_identity, true))
159
173
  @symbol_literal_cache = {}
160
174
 
161
175
  @timezone = nil
@@ -236,9 +250,13 @@ module Sequel
236
250
  # extension does not have specific support for Database objects, an Error will be raised.
237
251
  # Returns self.
238
252
  def extension(*exts)
239
- Sequel.extension(*exts)
240
253
  exts.each do |ext|
241
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
254
+ unless pr = Sequel.synchronize{EXTENSIONS[ext]}
255
+ Sequel.extension(ext)
256
+ pr = Sequel.synchronize{EXTENSIONS[ext]}
257
+ end
258
+
259
+ if pr
242
260
  if Sequel.synchronize{@loaded_extensions.include?(ext) ? false : (@loaded_extensions << ext)}
243
261
  pr.call(self)
244
262
  end
@@ -266,7 +284,7 @@ module Sequel
266
284
  keys = [:host, :database, :user]
267
285
  opts = self.opts
268
286
  if !keys.any?{|k| opts[k]} && opts[:uri]
269
- opts = self.class.send(:uri_to_options, URI.parse(opts[:uri]))
287
+ opts = self.class.send(:options_from_uri, URI.parse(opts[:uri]))
270
288
  end
271
289
 
272
290
  keys.each do |key|
@@ -617,7 +635,7 @@ module Sequel
617
635
  # Typecast the value to a String
618
636
  def typecast_value_string(value)
619
637
  case value
620
- when Hash, Array
638
+ when Hash, Array, Set
621
639
  raise Sequel::InvalidValue, "invalid value for String: #{value.inspect}"
622
640
  else
623
641
  value.to_s