sequel 5.58.0 → 5.78.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +288 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +24 -23
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +53 -17
  8. data/doc/cheat_sheet.rdoc +3 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +15 -0
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +20 -12
  14. data/doc/postgresql.rdoc +8 -8
  15. data/doc/querying.rdoc +1 -1
  16. data/doc/release_notes/5.59.0.txt +73 -0
  17. data/doc/release_notes/5.60.0.txt +22 -0
  18. data/doc/release_notes/5.61.0.txt +43 -0
  19. data/doc/release_notes/5.62.0.txt +132 -0
  20. data/doc/release_notes/5.63.0.txt +33 -0
  21. data/doc/release_notes/5.64.0.txt +50 -0
  22. data/doc/release_notes/5.65.0.txt +21 -0
  23. data/doc/release_notes/5.66.0.txt +24 -0
  24. data/doc/release_notes/5.67.0.txt +32 -0
  25. data/doc/release_notes/5.68.0.txt +61 -0
  26. data/doc/release_notes/5.69.0.txt +26 -0
  27. data/doc/release_notes/5.70.0.txt +35 -0
  28. data/doc/release_notes/5.71.0.txt +21 -0
  29. data/doc/release_notes/5.72.0.txt +33 -0
  30. data/doc/release_notes/5.73.0.txt +66 -0
  31. data/doc/release_notes/5.74.0.txt +45 -0
  32. data/doc/release_notes/5.75.0.txt +35 -0
  33. data/doc/release_notes/5.76.0.txt +86 -0
  34. data/doc/release_notes/5.77.0.txt +63 -0
  35. data/doc/release_notes/5.78.0.txt +67 -0
  36. data/doc/schema_modification.rdoc +3 -3
  37. data/doc/security.rdoc +9 -9
  38. data/doc/sharding.rdoc +3 -1
  39. data/doc/sql.rdoc +14 -14
  40. data/doc/testing.rdoc +16 -12
  41. data/doc/transactions.rdoc +6 -6
  42. data/doc/virtual_rows.rdoc +1 -1
  43. data/lib/sequel/adapters/ibmdb.rb +1 -1
  44. data/lib/sequel/adapters/jdbc/h2.rb +3 -0
  45. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
  46. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
  47. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  48. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  49. data/lib/sequel/adapters/jdbc.rb +10 -6
  50. data/lib/sequel/adapters/mysql.rb +19 -7
  51. data/lib/sequel/adapters/mysql2.rb +2 -2
  52. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  53. data/lib/sequel/adapters/oracle.rb +1 -0
  54. data/lib/sequel/adapters/postgres.rb +62 -16
  55. data/lib/sequel/adapters/shared/access.rb +9 -1
  56. data/lib/sequel/adapters/shared/db2.rb +12 -0
  57. data/lib/sequel/adapters/shared/mssql.rb +71 -9
  58. data/lib/sequel/adapters/shared/mysql.rb +80 -1
  59. data/lib/sequel/adapters/shared/oracle.rb +17 -7
  60. data/lib/sequel/adapters/shared/postgres.rb +494 -164
  61. data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
  62. data/lib/sequel/adapters/shared/sqlite.rb +40 -4
  63. data/lib/sequel/adapters/sqlite.rb +42 -3
  64. data/lib/sequel/adapters/trilogy.rb +117 -0
  65. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  66. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  67. data/lib/sequel/connection_pool/threaded.rb +14 -8
  68. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  69. data/lib/sequel/connection_pool.rb +57 -31
  70. data/lib/sequel/database/connecting.rb +25 -1
  71. data/lib/sequel/database/dataset.rb +16 -6
  72. data/lib/sequel/database/misc.rb +65 -14
  73. data/lib/sequel/database/query.rb +72 -1
  74. data/lib/sequel/database/schema_generator.rb +2 -1
  75. data/lib/sequel/database/schema_methods.rb +13 -3
  76. data/lib/sequel/database/transactions.rb +6 -0
  77. data/lib/sequel/dataset/actions.rb +60 -13
  78. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  79. data/lib/sequel/dataset/features.rb +15 -1
  80. data/lib/sequel/dataset/misc.rb +12 -2
  81. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  82. data/lib/sequel/dataset/query.rb +62 -37
  83. data/lib/sequel/dataset/sql.rb +58 -36
  84. data/lib/sequel/dataset.rb +4 -0
  85. data/lib/sequel/exceptions.rb +5 -0
  86. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  87. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  88. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  89. data/lib/sequel/extensions/async_thread_pool.rb +21 -13
  90. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  91. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  92. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  93. data/lib/sequel/extensions/connection_validator.rb +16 -11
  94. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  95. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  96. data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
  97. data/lib/sequel/extensions/index_caching.rb +5 -1
  98. data/lib/sequel/extensions/is_distinct_from.rb +3 -1
  99. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  100. data/lib/sequel/extensions/migration.rb +65 -15
  101. data/lib/sequel/extensions/named_timezones.rb +22 -6
  102. data/lib/sequel/extensions/pg_array.rb +33 -4
  103. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  104. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  105. data/lib/sequel/extensions/pg_enum.rb +1 -2
  106. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  107. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  108. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  109. data/lib/sequel/extensions/pg_inet.rb +10 -11
  110. data/lib/sequel/extensions/pg_interval.rb +10 -11
  111. data/lib/sequel/extensions/pg_json.rb +10 -10
  112. data/lib/sequel/extensions/pg_json_ops.rb +52 -0
  113. data/lib/sequel/extensions/pg_multirange.rb +6 -11
  114. data/lib/sequel/extensions/pg_range.rb +9 -14
  115. data/lib/sequel/extensions/pg_row.rb +20 -19
  116. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  117. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  118. data/lib/sequel/extensions/schema_caching.rb +1 -1
  119. data/lib/sequel/extensions/schema_dumper.rb +32 -9
  120. data/lib/sequel/extensions/server_block.rb +2 -1
  121. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  122. data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
  123. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  124. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  125. data/lib/sequel/model/associations.rb +50 -11
  126. data/lib/sequel/model/base.rb +45 -21
  127. data/lib/sequel/model/dataset_module.rb +3 -0
  128. data/lib/sequel/model/exceptions.rb +15 -3
  129. data/lib/sequel/plugins/auto_validations.rb +53 -15
  130. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  131. data/lib/sequel/plugins/column_encryption.rb +27 -6
  132. data/lib/sequel/plugins/composition.rb +2 -2
  133. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  134. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  135. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  136. data/lib/sequel/plugins/dirty.rb +1 -1
  137. data/lib/sequel/plugins/finder.rb +4 -2
  138. data/lib/sequel/plugins/list.rb +8 -3
  139. data/lib/sequel/plugins/many_through_many.rb +1 -1
  140. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  141. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  142. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  143. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  144. data/lib/sequel/plugins/paged_operations.rb +181 -0
  145. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  146. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  147. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  148. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  149. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  150. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  151. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  152. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  153. data/lib/sequel/plugins/sql_comments.rb +5 -5
  154. data/lib/sequel/plugins/static_cache.rb +38 -0
  155. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  156. data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
  157. data/lib/sequel/plugins/validate_associated.rb +22 -12
  158. data/lib/sequel/plugins/validation_helpers.rb +29 -2
  159. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  160. data/lib/sequel/version.rb +1 -1
  161. metadata +76 -6
@@ -37,7 +37,7 @@ module Sequel
37
37
  row[:auto_increment] = auto_increment == 1 || auto_increment == true
38
38
  row[:primary_key] = row.delete(:pkey) == 'Y'
39
39
  row[:allow_null] = row[:nulls_allowed].is_a?(Integer) ? row.delete(:nulls_allowed) == 1 : row.delete(:nulls_allowed)
40
- row[:db_type] = row.delete(:domain_name)
40
+ row[:db_type] = row.delete(:domain_name_with_size)
41
41
  row[:type] = if row[:db_type] =~ /numeric/i and (row[:scale].is_a?(Integer) ? row[:scale] == 0 : !row[:scale])
42
42
  :integer
43
43
  else
@@ -195,6 +195,11 @@ module Sequel
195
195
  end
196
196
  end
197
197
 
198
+ # SQLAnywhere tinyint types are unsigned.
199
+ def column_schema_tinyint_type_is_unsigned?
200
+ true
201
+ end
202
+
198
203
  # SqlAnywhere doesn't support CREATE TABLE AS, it only supports SELECT INTO.
199
204
  # Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
200
205
  # is given as the argument, it can't work with a string, so raise an
@@ -213,6 +218,8 @@ module Sequel
213
218
  def schema_column_type(db_type)
214
219
  if convert_smallint_to_bool && db_type =~ /smallint/i
215
220
  :boolean
221
+ elsif db_type =~ /unsigned (big)?int/i
222
+ :integer
216
223
  else
217
224
  super
218
225
  end
@@ -274,10 +281,6 @@ module Sequel
274
281
  false
275
282
  end
276
283
 
277
- def supports_timestamp_usecs?
278
- false
279
- end
280
-
281
284
  def supports_window_clause?
282
285
  true
283
286
  end
@@ -371,6 +374,16 @@ module Sequel
371
374
 
372
375
  private
373
376
 
377
+ # SQLAnywhere only supports 3 digits after the decimal point for times.
378
+ def default_time_format
379
+ "'%H:%M:%S.%3N'"
380
+ end
381
+
382
+ # SQLAnywhere only supports 3 digits after the decimal point for timestamps.
383
+ def default_timestamp_format
384
+ "'%Y-%m-%d %H:%M:%S.%3N'"
385
+ end
386
+
374
387
  # Use 1 for true on Sybase
375
388
  def literal_true
376
389
  '1'
@@ -145,6 +145,11 @@ module Sequel
145
145
  sqlite_version >= 30608
146
146
  end
147
147
 
148
+ # SQLite 3.8.2+ supports the without rowid table constraint
149
+ def support_without_rowid?
150
+ sqlite_version >= 30802
151
+ end
152
+
148
153
  # Override the default setting for whether to use timezones in timestamps.
149
154
  # It is set to +false+ by default, as SQLite's date/time methods do not
150
155
  # support timezones in timestamps.
@@ -169,6 +174,7 @@ module Sequel
169
174
  # DB.values([[1, 2], [3, 4]])
170
175
  # # VALUES ((1, 2), (3, 4))
171
176
  def values(v)
177
+ raise Error, "Cannot provide an empty array for values" if v.empty?
172
178
  @default_dataset.clone(:values=>v)
173
179
  end
174
180
 
@@ -320,6 +326,12 @@ module Sequel
320
326
  end
321
327
  end
322
328
 
329
+ # SQLite does not restrict the integer or decimal type to a specific range.
330
+ def column_schema_integer_min_max_values(column)
331
+ nil
332
+ end
333
+ alias column_schema_decimal_min_max_values column_schema_integer_min_max_values
334
+
323
335
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
324
336
  # new connections.
325
337
  def connection_pragmas
@@ -337,9 +349,17 @@ module Sequel
337
349
  ps
338
350
  end
339
351
 
340
- # Support creating STRICT tables via :strict option
352
+ # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
341
353
  def create_table_sql(name, generator, options)
342
- "#{super}#{' STRICT' if options[:strict]}"
354
+ if options[:strict] && options[:without_rowid]
355
+ "#{super} STRICT, WITHOUT ROWID"
356
+ elsif options[:strict]
357
+ "#{super} STRICT"
358
+ elsif options[:without_rowid]
359
+ "#{super} WITHOUT ROWID"
360
+ else
361
+ super
362
+ end
343
363
  end
344
364
 
345
365
  # SQLite support creating temporary views.
@@ -350,6 +370,7 @@ module Sequel
350
370
  DATABASE_ERROR_REGEXPS = {
351
371
  /(is|are) not unique\z|PRIMARY KEY must be unique\z|UNIQUE constraint failed: .+\z/ => UniqueConstraintViolation,
352
372
  /foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
373
+ /\ASQLITE ERROR 3091/ => CheckConstraintViolation,
353
374
  /\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
354
375
  /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
355
376
  /\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
@@ -496,7 +517,6 @@ module Sequel
496
517
  # table_xinfo PRAGMA used, remove hidden columns
497
518
  # that are not generated columns
498
519
  if row[:generated] = (row.delete(:hidden) != 0)
499
- next unless row[:type].end_with?(' GENERATED ALWAYS')
500
520
  row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
501
521
  end
502
522
  end
@@ -649,6 +669,12 @@ module Sequel
649
669
  @opts[:where] ? super : where(1=>1).delete(&block)
650
670
  end
651
671
 
672
+ # Always return false when using VALUES
673
+ def empty?
674
+ return false if @opts[:values]
675
+ super
676
+ end
677
+
652
678
  # Return an array of strings specifying a query explanation for a SELECT of the
653
679
  # current dataset. Currently, the options are ignored, but it accepts options
654
680
  # to be compatible with other adapters.
@@ -663,7 +689,7 @@ module Sequel
663
689
 
664
690
  # HAVING requires GROUP BY on SQLite
665
691
  def having(*cond)
666
- raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") unless @opts[:group]
692
+ raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
667
693
  super
668
694
  end
669
695
 
@@ -862,6 +888,11 @@ module Sequel
862
888
  end
863
889
  end
864
890
 
891
+ # Use from_self for aggregate dataset using VALUES.
892
+ def aggreate_dataset_use_from_self?
893
+ super || @opts[:values]
894
+ end
895
+
865
896
  # SQLite uses string literals instead of identifiers in AS clauses.
866
897
  def as_sql_append(sql, aliaz, column_aliases=nil)
867
898
  raise Error, "sqlite does not support derived column lists" if column_aliases
@@ -899,6 +930,11 @@ module Sequel
899
930
  500
900
931
  end
901
932
 
933
+ # The strftime format to use when literalizing the time.
934
+ def default_timestamp_format
935
+ db.use_timestamp_timezones? ? "'%Y-%m-%d %H:%M:%S.%6N%z'" : super
936
+ end
937
+
902
938
  # SQL fragment specifying a list of identifiers
903
939
  def identifier_list(columns)
904
940
  columns.map{|i| quote_identifier(i)}.join(', ')
@@ -111,6 +111,18 @@ module Sequel
111
111
  # static data that you do not want to modify
112
112
  # :timeout :: how long to wait for the database to be available if it
113
113
  # is locked, given in milliseconds (default is 5000)
114
+ # :setup_regexp_function :: enable use of Regexp objects with SQL
115
+ # 'REGEXP' operator. If the value is :cached or "cached",
116
+ # caches the generated regexps, which can result in a memory
117
+ # leak if dynamic regexps are used. If the value is a Proc,
118
+ # it will be called with a string for the regexp and a string
119
+ # for the value to compare, and should return whether the regexp
120
+ # matches.
121
+ # :regexp_function_cache :: If setting +setup_regexp_function+ to +cached+, this
122
+ # determines the cache to use. It should either be a proc or a class, and it
123
+ # defaults to +Hash+. You can use +ObjectSpace::WeakKeyMap+ on Ruby 3.3+ to
124
+ # have the VM automatically remove regexps from the cache after they
125
+ # are no longer used.
114
126
  def connect(server)
115
127
  opts = server_opts(server)
116
128
  opts[:database] = ':memory:' if blank_object?(opts[:database])
@@ -126,9 +138,7 @@ module Sequel
126
138
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
127
139
 
128
140
  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
141
+ setup_regexp_function(db, opts[:setup_regexp_function])
132
142
  end
133
143
 
134
144
  class << db
@@ -202,6 +212,35 @@ module Sequel
202
212
  @conversion_procs['datetime'] = @conversion_procs['timestamp'] = method(:to_application_timestamp)
203
213
  set_integer_booleans
204
214
  end
215
+
216
+ def setup_regexp_function(db, how)
217
+ case how
218
+ when Proc
219
+ # nothing
220
+ when :cached, "cached"
221
+ cache = @opts[:regexp_function_cache] || Hash
222
+ cache = cache.is_a?(Proc) ? cache.call : cache.new
223
+ how = if RUBY_VERSION >= '2.4'
224
+ lambda do |regexp_str, str|
225
+ (cache[regexp_str] ||= Regexp.new(regexp_str)).match?(str)
226
+ end
227
+ else
228
+ lambda do |regexp_str, str|
229
+ (cache[regexp_str] ||= Regexp.new(regexp_str)).match(str)
230
+ end
231
+ end
232
+ else
233
+ how = if RUBY_VERSION >= '2.4'
234
+ lambda{|regexp_str, str| Regexp.new(regexp_str).match?(str)}
235
+ else
236
+ lambda{|regexp_str, str| Regexp.new(regexp_str).match(str)}
237
+ end
238
+ end
239
+
240
+ db.create_function("regexp", 2) do |func, regexp_str, str|
241
+ func.result = how.call(regexp_str, str) ? 1 : 0
242
+ end
243
+ end
205
244
 
206
245
  # Yield an available connection. Rescue
207
246
  # any SQLite3::Exceptions and turn them into DatabaseErrors.
@@ -0,0 +1,117 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'trilogy'
4
+ require_relative 'shared/mysql'
5
+
6
+ module Sequel
7
+ module Trilogy
8
+ class Database < Sequel::Database
9
+ include Sequel::MySQL::DatabaseMethods
10
+
11
+ QUERY_FLAGS = ::Trilogy::QUERY_FLAGS_CAST | ::Trilogy::QUERY_FLAGS_CAST_BOOLEANS
12
+ LOCAL_TIME_QUERY_FLAGS = QUERY_FLAGS | ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
13
+
14
+ set_adapter_scheme :trilogy
15
+
16
+ # Connect to the database. See Trilogy documentation for options.
17
+ def connect(server)
18
+ opts = server_opts(server)
19
+ opts[:username] ||= opts.delete(:user)
20
+ opts[:found_rows] = true
21
+ conn = ::Trilogy.new(opts)
22
+ mysql_connection_setting_sqls.each{|sql| log_connection_yield(sql, conn){conn.query(sql)}}
23
+ conn
24
+ end
25
+
26
+ def disconnect_connection(c)
27
+ c.discard!
28
+ rescue ::Trilogy::Error
29
+ nil
30
+ end
31
+
32
+ # Execute the given SQL on the given connection and yield the result.
33
+ def execute(sql, opts)
34
+ r = synchronize(opts[:server]) do |conn|
35
+ log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn) do
36
+ conn.query_with_flags(sql, timezone.nil? || timezone == :local ? LOCAL_TIME_QUERY_FLAGS : QUERY_FLAGS)
37
+ end
38
+ end
39
+ yield r
40
+ rescue ::Trilogy::Error => e
41
+ raise_error(e)
42
+ end
43
+
44
+ def execute_dui(sql, opts=OPTS)
45
+ execute(sql, opts, &:affected_rows)
46
+ end
47
+
48
+ def execute_insert(sql, opts=OPTS)
49
+ execute(sql, opts, &:last_insert_id)
50
+ end
51
+
52
+ def freeze
53
+ server_version
54
+ super
55
+ end
56
+
57
+ # Return the version of the MySQL server to which we are connecting.
58
+ def server_version(_server=nil)
59
+ @server_version ||= super()
60
+ end
61
+
62
+ private
63
+
64
+ def database_specific_error_class(exception, opts)
65
+ case exception.message
66
+ when /1205 - Lock wait timeout exceeded; try restarting transaction\z/
67
+ DatabaseLockTimeout
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ def connection_execute_method
74
+ :query
75
+ end
76
+
77
+ def database_error_classes
78
+ [::Trilogy::Error]
79
+ end
80
+
81
+ def dataset_class_default
82
+ Dataset
83
+ end
84
+
85
+ # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
86
+ def schema_column_type(db_type)
87
+ db_type =~ /\Atinyint\(1\)/ ? :boolean : super
88
+ end
89
+ end
90
+
91
+ class Dataset < Sequel::Dataset
92
+ include Sequel::MySQL::DatasetMethods
93
+
94
+ def fetch_rows(sql)
95
+ execute(sql) do |r|
96
+ self.columns = r.fields.map!{|c| output_identifier(c.to_s)}
97
+ r.each_hash{|h| yield h}
98
+ end
99
+ self
100
+ end
101
+
102
+ private
103
+
104
+ def execute(sql, opts=OPTS)
105
+ opts = Hash[opts]
106
+ opts[:type] = :select
107
+ super
108
+ end
109
+
110
+ # Handle correct quoting of strings using ::Trilogy#escape.
111
+ def literal_string_append(sql, v)
112
+ sql << "'" << db.synchronize(@opts[:server]){|c| c.escape(v)} << "'"
113
+ end
114
+ end
115
+ end
116
+ end
117
+
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'threaded'
4
4
 
5
- # The slowest and most advanced connection, dealing with both multi-threaded
5
+ # The slowest and most advanced connection pool, dealing with both multi-threaded
6
6
  # access and configurations with multiple shards/servers.
7
7
  #
8
8
  # In addition, this pool subclass also handles scheduling in-use connections
@@ -22,6 +22,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
22
22
  @connections_to_disconnect = []
23
23
  @servers = opts.fetch(:servers_hash, Hash.new(:default))
24
24
  remove_instance_variable(:@waiter)
25
+ remove_instance_variable(:@allocated)
26
+ @allocated = {}
25
27
  @waiters = {}
26
28
 
27
29
  add_servers([:default])
@@ -36,7 +38,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
36
38
  unless @servers.has_key?(server)
37
39
  @servers[server] = server
38
40
  @available_connections[server] = []
39
- @allocated[server] = {}
41
+ allocated = {}
42
+ allocated.compare_by_identity
43
+ @allocated[server] = allocated
40
44
  @waiters[server] = ConditionVariable.new
41
45
  end
42
46
  end
@@ -108,7 +112,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
108
112
  # available, creates a new connection. Passes the connection to the supplied
109
113
  # block:
110
114
  #
111
- # pool.hold {|conn| conn.execute('DROP TABLE posts')}
115
+ # pool.hold(:server1) {|conn| conn.execute('DROP TABLE posts')}
112
116
  #
113
117
  # Pool#hold is re-entrant, meaning it can be called recursively in
114
118
  # the same thread without blocking.
@@ -141,12 +145,13 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
141
145
  # except that after it is used, future requests for the server will use the
142
146
  # :default server instead.
143
147
  def remove_servers(servers)
144
- conns = nil
148
+ conns = []
149
+ raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
150
+
145
151
  sync do
146
- raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
147
152
  servers.each do |server|
148
153
  if @servers.include?(server)
149
- conns = disconnect_server_connections(server)
154
+ conns.concat(disconnect_server_connections(server))
150
155
  @waiters.delete(server)
151
156
  @available_connections.delete(server)
152
157
  @allocated.delete(server)
@@ -155,9 +160,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
155
160
  end
156
161
  end
157
162
 
158
- if conns
159
- disconnect_connections(conns)
160
- end
163
+ nil
164
+ ensure
165
+ disconnect_connections(conns)
161
166
  end
162
167
 
163
168
  # Return an array of symbols for servers in the connection pool.
@@ -182,7 +187,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
182
187
  # is available. The calling code should NOT already have the mutex when
183
188
  # calling this.
184
189
  #
185
- # This should return a connection is one is available within the timeout,
190
+ # This should return a connection if one is available within the timeout,
186
191
  # or nil if a connection could not be acquired within the timeout.
187
192
  def acquire(thread, server)
188
193
  if conn = assign_connection(thread, server)
@@ -321,7 +326,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
321
326
  # Create the maximum number of connections immediately. The calling code should
322
327
  # NOT have the mutex before calling this.
323
328
  def preconnect(concurrent = false)
324
- conn_servers = @servers.keys.map!{|s| Array.new(max_size - _size(s), s)}.flatten!
329
+ conn_servers = sync{@servers.keys}.map!{|s| Array.new(max_size - _size(s), s)}.flatten!
325
330
 
326
331
  if concurrent
327
332
  conn_servers.map!{|s| Thread.new{[s, make_new(s)]}}.map!(&:value)