sequel 5.58.0 → 5.78.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.
- checksums.yaml +4 -4
- data/CHANGELOG +288 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +24 -23
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +20 -12
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +1 -1
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/release_notes/5.78.0.txt +67 -0
- data/doc/schema_modification.rdoc +3 -3
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +14 -14
- data/doc/testing.rdoc +16 -12
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +3 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/mysql.rb +19 -7
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/postgres.rb +62 -16
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +71 -9
- data/lib/sequel/adapters/shared/mysql.rb +80 -1
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +494 -164
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +40 -4
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/database/connecting.rb +25 -1
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +65 -14
- data/lib/sequel/database/query.rb +72 -1
- data/lib/sequel/database/schema_generator.rb +2 -1
- data/lib/sequel/database/schema_methods.rb +13 -3
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +62 -37
- data/lib/sequel/dataset/sql.rb +58 -36
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/exceptions.rb +5 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +21 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/is_distinct_from.rb +3 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +65 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +5 -0
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_interval.rb +10 -11
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/extensions/pg_multirange.rb +6 -11
- data/lib/sequel/extensions/pg_range.rb +9 -14
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/server_block.rb +2 -1
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +50 -11
- data/lib/sequel/model/base.rb +45 -21
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +27 -6
- data/lib/sequel/plugins/composition.rb +2 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +5 -5
- data/lib/sequel/plugins/static_cache.rb +38 -0
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +29 -2
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/version.rb +1 -1
- 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(:
|
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
|
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
|
-
|
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")
|
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
|
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
|
-
|
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 =
|
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
|
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
|
-
|
159
|
-
|
160
|
-
|
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
|
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)
|