sequel 5.58.0 → 5.78.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -82,7 +82,7 @@ module Sequel
|
|
82
82
|
when DateTime
|
83
83
|
literal_datetime_append(sql, v)
|
84
84
|
when Date
|
85
|
-
sql
|
85
|
+
literal_date_append(sql, v)
|
86
86
|
when Dataset
|
87
87
|
literal_dataset_append(sql, v)
|
88
88
|
else
|
@@ -115,6 +115,33 @@ module Sequel
|
|
115
115
|
sql
|
116
116
|
end
|
117
117
|
|
118
|
+
# Literalize a date or time value, as a SQL string value with no
|
119
|
+
# typecasting. If +raw+ is true, remove the surrounding single
|
120
|
+
# quotes. This is designed for usage by bound argument code that
|
121
|
+
# can work even if the auto_cast_date_and_time extension is
|
122
|
+
# used (either manually or implicitly in the related adapter).
|
123
|
+
def literal_date_or_time(dt, raw=false)
|
124
|
+
value = case dt
|
125
|
+
when SQLTime
|
126
|
+
literal_sqltime(dt)
|
127
|
+
when Time
|
128
|
+
literal_time(dt)
|
129
|
+
when DateTime
|
130
|
+
literal_datetime(dt)
|
131
|
+
when Date
|
132
|
+
literal_date(dt)
|
133
|
+
else
|
134
|
+
raise TypeError, "unsupported type: #{dt.inspect}"
|
135
|
+
end
|
136
|
+
|
137
|
+
if raw
|
138
|
+
value.sub!(/\A'/, '')
|
139
|
+
value.sub!(/'\z/, '')
|
140
|
+
end
|
141
|
+
|
142
|
+
value
|
143
|
+
end
|
144
|
+
|
118
145
|
# Returns an array of insert statements for inserting multiple records.
|
119
146
|
# This method is used by +multi_insert+ to format insert statements and
|
120
147
|
# expects a keys array and and an array of value arrays.
|
@@ -977,7 +1004,12 @@ module Sequel
|
|
977
1004
|
# order if not. Also removes the row_proc, which isn't needed
|
978
1005
|
# for aggregate calculations.
|
979
1006
|
def aggregate_dataset
|
980
|
-
(
|
1007
|
+
(aggreate_dataset_use_from_self? ? from_self : unordered).naked
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
# Whether to use from_self for an aggregate dataset.
|
1011
|
+
def aggreate_dataset_use_from_self?
|
1012
|
+
options_overlap(COUNT_FROM_SELF_OPTS)
|
981
1013
|
end
|
982
1014
|
|
983
1015
|
# Append aliasing expression to SQL string.
|
@@ -1099,9 +1131,14 @@ module Sequel
|
|
1099
1131
|
:"t#{number}"
|
1100
1132
|
end
|
1101
1133
|
|
1102
|
-
# The strftime format to use when literalizing
|
1134
|
+
# The strftime format to use when literalizing time (Sequel::SQLTime) values.
|
1135
|
+
def default_time_format
|
1136
|
+
"'%H:%M:%S.%6N'"
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
# The strftime format to use when literalizing timestamp (Time/DateTime) values.
|
1103
1140
|
def default_timestamp_format
|
1104
|
-
|
1141
|
+
"'%Y-%m-%d %H:%M:%S.%6N'"
|
1105
1142
|
end
|
1106
1143
|
|
1107
1144
|
def delete_delete_sql(sql)
|
@@ -1164,43 +1201,23 @@ module Sequel
|
|
1164
1201
|
{1 => ((op == :IN) ? 0 : 1)}
|
1165
1202
|
end
|
1166
1203
|
|
1167
|
-
# Format the timestamp based on the default_timestamp_format
|
1168
|
-
# of modifiers. First, allow %N to be used for fractions seconds (if the
|
1169
|
-
# database supports them), and override %z to always use a numeric offset
|
1170
|
-
# of hours and minutes.
|
1204
|
+
# Format the timestamp based on the default_timestamp_format.
|
1171
1205
|
def format_timestamp(v)
|
1172
|
-
|
1173
|
-
fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
|
1174
|
-
if m == '%N'
|
1175
|
-
# Ruby 1.9 supports %N in timestamp formats, but Sequel has supported %N
|
1176
|
-
# for longer in a different way, where the . is already appended and only 6
|
1177
|
-
# decimal places are used by default.
|
1178
|
-
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(1000000) : v.usec) if supports_timestamp_usecs?
|
1179
|
-
else
|
1180
|
-
if supports_timestamp_timezones?
|
1181
|
-
# Would like to just use %z format, but it doesn't appear to work on Windows
|
1182
|
-
# Instead, the offset fragment is constructed manually
|
1183
|
-
minutes = (v2.is_a?(DateTime) ? v2.offset * 1440 : v2.utc_offset/60).to_i
|
1184
|
-
format_timestamp_offset(*minutes.divmod(60))
|
1185
|
-
end
|
1186
|
-
end
|
1187
|
-
end
|
1188
|
-
v2.strftime(fmt)
|
1206
|
+
db.from_application_timestamp(v).strftime(default_timestamp_format)
|
1189
1207
|
end
|
1190
1208
|
|
1191
|
-
#
|
1192
|
-
def format_timestamp_offset(hour, minute)
|
1193
|
-
sprintf("%+03i%02i", hour, minute)
|
1194
|
-
end
|
1209
|
+
# :nocov:
|
1195
1210
|
|
1196
1211
|
# Return the SQL timestamp fragment to use for the fractional time part.
|
1197
1212
|
# Should start with the decimal point. Uses 6 decimal places by default.
|
1198
1213
|
def format_timestamp_usec(usec, ts=timestamp_precision)
|
1214
|
+
# SEQUEL6: Remove
|
1199
1215
|
unless ts == 6
|
1200
1216
|
usec = usec/(10 ** (6 - ts))
|
1201
1217
|
end
|
1202
1218
|
sprintf(".%0#{ts}d", usec)
|
1203
1219
|
end
|
1220
|
+
# :nocov:
|
1204
1221
|
|
1205
1222
|
# Append literalization of identifier to SQL string, considering regular strings
|
1206
1223
|
# as SQL identifiers instead of SQL strings.
|
@@ -1342,11 +1359,12 @@ module Sequel
|
|
1342
1359
|
|
1343
1360
|
# SQL fragment for Date, using the ISO8601 format.
|
1344
1361
|
def literal_date(v)
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1362
|
+
v.strftime("'%Y-%m-%d'")
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
# Append literalization of date to SQL string.
|
1366
|
+
def literal_date_append(sql, v)
|
1367
|
+
sql << literal_date(v)
|
1350
1368
|
end
|
1351
1369
|
|
1352
1370
|
# SQL fragment for DateTime
|
@@ -1409,7 +1427,7 @@ module Sequel
|
|
1409
1427
|
|
1410
1428
|
# SQL fragment for Sequel::SQLTime, containing just the time part
|
1411
1429
|
def literal_sqltime(v)
|
1412
|
-
v.strftime(
|
1430
|
+
v.strftime(default_time_format)
|
1413
1431
|
end
|
1414
1432
|
|
1415
1433
|
# Append literalization of Sequel::SQLTime to SQL string.
|
@@ -1725,7 +1743,7 @@ module Sequel
|
|
1725
1743
|
# Append literalization of the subselect to SQL string.
|
1726
1744
|
def subselect_sql_append(sql, ds)
|
1727
1745
|
sds = subselect_sql_dataset(sql, ds)
|
1728
|
-
sds
|
1746
|
+
subselect_sql_append_sql(sql, sds)
|
1729
1747
|
unless sds.send(:cache_sql?)
|
1730
1748
|
# If subquery dataset does not allow caching SQL,
|
1731
1749
|
# then this dataset should not allow caching SQL.
|
@@ -1737,6 +1755,10 @@ module Sequel
|
|
1737
1755
|
ds.clone(:append_sql=>sql)
|
1738
1756
|
end
|
1739
1757
|
|
1758
|
+
def subselect_sql_append_sql(sql, ds)
|
1759
|
+
ds.sql
|
1760
|
+
end
|
1761
|
+
|
1740
1762
|
# The number of decimal digits of precision to use in timestamps.
|
1741
1763
|
def timestamp_precision
|
1742
1764
|
supports_timestamp_usecs? ? 6 : 0
|
data/lib/sequel/dataset.rb
CHANGED
@@ -53,4 +53,8 @@ module Sequel
|
|
53
53
|
require_relative "dataset/sql"
|
54
54
|
require_relative "dataset/placeholder_literalizer"
|
55
55
|
require_relative "dataset/dataset_module"
|
56
|
+
|
57
|
+
# :nocov:
|
58
|
+
require_relative "dataset/deprecated_singleton_class_methods" if Dataset::TRUE_FREEZE
|
59
|
+
# :nocov:
|
56
60
|
end
|
data/lib/sequel/exceptions.rb
CHANGED
@@ -18,6 +18,11 @@ module Sequel
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
(
|
23
|
+
# Error raised when there is a failed attempt to acquire an advisory lock.
|
24
|
+
AdvisoryLockError = Class.new(Error)
|
25
|
+
).name
|
21
26
|
|
22
27
|
(
|
23
28
|
# Error raised when the adapter requested doesn't exist or can't be loaded.
|
@@ -23,18 +23,6 @@ module Sequel
|
|
23
23
|
super
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
# Handle Sequel::Model instances in bound variable arrays.
|
30
|
-
def bound_variable_array(arg)
|
31
|
-
case arg
|
32
|
-
when Sequel::Model
|
33
|
-
"\"(#{arg.values.values_at(*arg.columns).map{|v| bound_variable_array(v)}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
|
34
|
-
else
|
35
|
-
super
|
36
|
-
end
|
37
|
-
end
|
38
26
|
end
|
39
27
|
end
|
40
28
|
end
|
@@ -5,9 +5,9 @@
|
|
5
5
|
# code
|
6
6
|
#
|
7
7
|
# DB.extension :async_thread_pool
|
8
|
-
# foos = DB[:foos].async.where
|
8
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
9
9
|
# bar_names = DB[:bar].async.select_order_map(:name)
|
10
|
-
# baz_1 = DB[:bazes].async.first(:
|
10
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
11
11
|
#
|
12
12
|
# All 3 queries will be run in separate threads. +foos+, +bar_names+
|
13
13
|
# and +baz_1+ will be proxy objects. Calling a method on the proxy
|
@@ -15,9 +15,9 @@
|
|
15
15
|
# of calling that method on the result of the query method. For example,
|
16
16
|
# if you run:
|
17
17
|
#
|
18
|
-
# foos = DB[:foos].async.where
|
18
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
19
19
|
# bar_names = DB[:bars].async.select_order_map(:name)
|
20
|
-
# baz_1 = DB[:bazes].async.first(:
|
20
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
21
21
|
# sleep(1)
|
22
22
|
# foos.size
|
23
23
|
# bar_names.first
|
@@ -26,9 +26,9 @@
|
|
26
26
|
# These three queries will generally be run concurrently in separate
|
27
27
|
# threads. If you instead run:
|
28
28
|
#
|
29
|
-
# DB[:foos].async.where
|
29
|
+
# DB[:foos].async.where(name: 'A'..'M').all.size
|
30
30
|
# DB[:bars].async.select_order_map(:name).first
|
31
|
-
# DB[:bazes].async.first(:
|
31
|
+
# DB[:bazes].async.first(id: 1).name
|
32
32
|
#
|
33
33
|
# Then will run each query sequentially, since you need the result of
|
34
34
|
# one query before running the next query. The queries will still be
|
@@ -37,11 +37,11 @@
|
|
37
37
|
# What is run in the separate thread is the entire method call that
|
38
38
|
# returns results. So with the original example:
|
39
39
|
#
|
40
|
-
# foos = DB[:foos].async.where
|
40
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
41
41
|
# bar_names = DB[:bars].async.select_order_map(:name)
|
42
|
-
# baz_1 = DB[:bazes].async.first(:
|
42
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
43
43
|
#
|
44
|
-
# The +all+, <tt>select_order_map(:name)</tt>, and <tt>first(:
|
44
|
+
# The +all+, <tt>select_order_map(:name)</tt>, and <tt>first(id: 1)</tt>
|
45
45
|
# calls are run in separate threads. If a block is passed to a method
|
46
46
|
# such as +all+ or +each+, the block is also run in that thread. If you
|
47
47
|
# have code such as:
|
@@ -156,10 +156,10 @@
|
|
156
156
|
# so that the query will run in the current thread instead of waiting
|
157
157
|
# for an async thread to become available. With the following code:
|
158
158
|
#
|
159
|
-
# foos = DB[:foos].async.where
|
159
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
160
160
|
# bar_names = DB[:bar].async.select_order_map(:name)
|
161
161
|
# if foos.length > 4
|
162
|
-
# baz_1 = DB[:bazes].async.first(:
|
162
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
163
163
|
# end
|
164
164
|
#
|
165
165
|
# Whether you need the +baz_1+ variable depends on the value of foos.
|
@@ -176,6 +176,13 @@
|
|
176
176
|
# +:preempt_async_thread+ Database option before loading the
|
177
177
|
# async_thread_pool extension.
|
178
178
|
#
|
179
|
+
# Note that the async_thread_pool extension creates the thread pool
|
180
|
+
# when it is loaded into the Database. If you fork after loading
|
181
|
+
# the extension, the extension will not work, as fork does not
|
182
|
+
# copy the thread pools. If you are using a forking webserver
|
183
|
+
# (or any other system that forks worker processes), load this
|
184
|
+
# extension in each child process, do not load it before forking.
|
185
|
+
#
|
179
186
|
# Related module: Sequel::Database::AsyncThreadPool::DatasetMethods
|
180
187
|
|
181
188
|
|
@@ -338,8 +345,9 @@ module Sequel
|
|
338
345
|
module DatabaseMethods
|
339
346
|
def self.extended(db)
|
340
347
|
db.instance_exec do
|
341
|
-
|
342
|
-
|
348
|
+
case pool.pool_type
|
349
|
+
when :single, :sharded_single
|
350
|
+
raise Error, "cannot load async_thread_pool extension if using single or sharded_single connection pool"
|
343
351
|
end
|
344
352
|
|
345
353
|
num_async_threads = opts[:num_async_threads] ? typecast_value_integer(opts[:num_async_threads]) : (Integer(opts[:max_connections] || 4))
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The auto_cast_date_and_time extension uses SQL standard type casting
|
4
|
+
# when literalizing date, time, and timestamp values:
|
5
|
+
#
|
6
|
+
# DB.literal(Time.now)
|
7
|
+
# # => "TIMESTAMP '...'"
|
8
|
+
#
|
9
|
+
# DB.literal(Date.today)
|
10
|
+
# # => "DATE '...'"
|
11
|
+
#
|
12
|
+
# DB.literal(Sequel::SQLTime.create(10, 20, 30))
|
13
|
+
# # => "TIME '10:20:30.000000'"
|
14
|
+
#
|
15
|
+
# The default behavior of Sequel on adapters that do not require the
|
16
|
+
# SQL standard behavior is to format the date or time value without:
|
17
|
+
# casting
|
18
|
+
#
|
19
|
+
# DB.literal(Sequel::SQLTime.create(10, 20, 30))
|
20
|
+
# # => "'10:20:30.000000'"
|
21
|
+
#
|
22
|
+
# However, then the database cannot determine the type of the string,
|
23
|
+
# and must perform some implicit casting. If implicit casting cannot
|
24
|
+
# be used, it will probably treat the value as a string:
|
25
|
+
#
|
26
|
+
# DB.get(Time.now).class
|
27
|
+
# # Without auto_cast_date_and_time: String
|
28
|
+
# # With auto_cast_date_and_time: Time
|
29
|
+
#
|
30
|
+
# Note that not all databases support this extension. PostgreSQL and
|
31
|
+
# MySQL support it, but SQLite and Microsoft SQL Server do not.
|
32
|
+
#
|
33
|
+
# You can load this extension into specific datasets:
|
34
|
+
#
|
35
|
+
# ds = DB[:table]
|
36
|
+
# ds = ds.extension(:auto_cast_date_and_time)
|
37
|
+
#
|
38
|
+
# Or you can load it into all of a database's datasets, which
|
39
|
+
# is probably the desired behavior if you are using this extension:
|
40
|
+
#
|
41
|
+
# DB.extension(:auto_cast_date_and_time)
|
42
|
+
#
|
43
|
+
# Related module: Sequel::AutoCastDateAndTime
|
44
|
+
|
45
|
+
#
|
46
|
+
module Sequel
|
47
|
+
module AutoCastDateAndTime
|
48
|
+
# :nocov:
|
49
|
+
|
50
|
+
# Mark the datasets as requiring sql standard date times. This is only needed
|
51
|
+
# for backwards compatibility.
|
52
|
+
def requires_sql_standard_datetimes?
|
53
|
+
# SEQUEL6: Remove
|
54
|
+
true
|
55
|
+
end
|
56
|
+
# :nocov:
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Explicitly cast SQLTime objects to TIME.
|
61
|
+
def literal_sqltime_append(sql, v)
|
62
|
+
sql << "TIME "
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
# Explicitly cast Time objects to TIMESTAMP.
|
67
|
+
def literal_time_append(sql, v)
|
68
|
+
sql << literal_datetime_timestamp_cast
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
72
|
+
# Explicitly cast DateTime objects to TIMESTAMP.
|
73
|
+
def literal_datetime_append(sql, v)
|
74
|
+
sql << literal_datetime_timestamp_cast
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
# Explicitly cast Date objects to DATE.
|
79
|
+
def literal_date_append(sql, v)
|
80
|
+
sql << "DATE "
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
# The default cast string to use for Time/DateTime objects.
|
85
|
+
# Respects existing method if already defined.
|
86
|
+
def literal_datetime_timestamp_cast
|
87
|
+
return super if defined?(super)
|
88
|
+
'TIMESTAMP '
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Dataset.register_extension(:auto_cast_date_and_time, AutoCastDateAndTime)
|
93
|
+
end
|
94
|
+
|
@@ -22,7 +22,7 @@
|
|
22
22
|
#
|
23
23
|
# Named placeholders can also be used with a hash:
|
24
24
|
#
|
25
|
-
# ds.where("name > :a", :
|
25
|
+
# ds.where("name > :a", a: "A")
|
26
26
|
# # SELECT * FROM table WHERE (name > 'A')
|
27
27
|
#
|
28
28
|
# This extension also allows the use of a plain string passed to Dataset#update:
|
@@ -15,16 +15,16 @@
|
|
15
15
|
#
|
16
16
|
# DB.pool.connection_expiration_timeout = 3600 # 1 hour
|
17
17
|
#
|
18
|
-
# Note that this extension
|
19
|
-
# and
|
20
|
-
#
|
21
|
-
# not affected. As the only reason to use the single threaded
|
18
|
+
# Note that this extension does not work with the single
|
19
|
+
# threaded and sharded single threaded connection pools.
|
20
|
+
# As the only reason to use the single threaded
|
22
21
|
# pools is for speed, and this extension makes the connection
|
23
22
|
# pool slower, there's not much point in modifying this
|
24
23
|
# extension to work with the single threaded pools. The
|
25
|
-
# threaded pools work fine even in single threaded
|
26
|
-
# you are currently using a single threaded pool
|
27
|
-
# use this extension, switch to using
|
24
|
+
# non-single threaded pools work fine even in single threaded
|
25
|
+
# code, so if you are currently using a single threaded pool
|
26
|
+
# and want to use this extension, switch to using another
|
27
|
+
# pool.
|
28
28
|
#
|
29
29
|
# Related module: Sequel::ConnectionExpiration
|
30
30
|
|
@@ -45,6 +45,11 @@ module Sequel
|
|
45
45
|
|
46
46
|
# Initialize the data structures used by this extension.
|
47
47
|
def self.extended(pool)
|
48
|
+
case pool.pool_type
|
49
|
+
when :single, :sharded_single
|
50
|
+
raise Error, "cannot load connection_expiration extension if using single or sharded_single connection pool"
|
51
|
+
end
|
52
|
+
|
48
53
|
pool.instance_exec do
|
49
54
|
sync do
|
50
55
|
@connection_expiration_timestamps ||= {}
|
@@ -79,8 +84,9 @@ module Sequel
|
|
79
84
|
(cet = sync{@connection_expiration_timestamps[conn]}) &&
|
80
85
|
Sequel.elapsed_seconds_since(cet[0]) > cet[1]
|
81
86
|
|
82
|
-
|
83
|
-
|
87
|
+
case pool_type
|
88
|
+
when :sharded_threaded, :sharded_timed_queue
|
89
|
+
sync{@allocated[a.last].delete(Sequel.current)}
|
84
90
|
else
|
85
91
|
sync{@allocated.delete(Sequel.current)}
|
86
92
|
end
|
@@ -28,22 +28,22 @@
|
|
28
28
|
# connections on every checkout without setting up coarse
|
29
29
|
# connection checkouts will hurt performance, in some cases
|
30
30
|
# significantly. Note that setting up coarse connection
|
31
|
-
# checkouts reduces the concurrency level
|
31
|
+
# checkouts reduces the concurrency level achievable. For
|
32
32
|
# example, in a web application, using Database#synchronize
|
33
33
|
# in a rack middleware will limit the number of concurrent
|
34
34
|
# web requests to the number to connections in the database
|
35
35
|
# connection pool.
|
36
36
|
#
|
37
|
-
# Note that this extension
|
38
|
-
# and
|
39
|
-
#
|
40
|
-
# not affected. As the only reason to use the single threaded
|
37
|
+
# Note that this extension does not work with the single
|
38
|
+
# threaded and sharded single threaded connection pools.
|
39
|
+
# As the only reason to use the single threaded
|
41
40
|
# pools is for speed, and this extension makes the connection
|
42
41
|
# pool slower, there's not much point in modifying this
|
43
42
|
# extension to work with the single threaded pools. The
|
44
|
-
# threaded pools work fine even in single threaded
|
45
|
-
# you are currently using a single threaded pool
|
46
|
-
# use this extension, switch to using
|
43
|
+
# non-single threaded pools work fine even in single threaded
|
44
|
+
# code, so if you are currently using a single threaded pool
|
45
|
+
# and want to use this extension, switch to using another
|
46
|
+
# pool.
|
47
47
|
#
|
48
48
|
# Related module: Sequel::ConnectionValidator
|
49
49
|
|
@@ -61,6 +61,11 @@ module Sequel
|
|
61
61
|
|
62
62
|
# Initialize the data structures used by this extension.
|
63
63
|
def self.extended(pool)
|
64
|
+
case pool.pool_type
|
65
|
+
when :single, :sharded_single
|
66
|
+
raise Error, "cannot load connection_validator extension if using single or sharded_single connection pool"
|
67
|
+
end
|
68
|
+
|
64
69
|
pool.instance_exec do
|
65
70
|
sync do
|
66
71
|
@connection_timestamps ||= {}
|
@@ -103,8 +108,9 @@ module Sequel
|
|
103
108
|
Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout &&
|
104
109
|
!db.valid_connection?(conn)
|
105
110
|
|
106
|
-
|
107
|
-
|
111
|
+
case pool_type
|
112
|
+
when :sharded_threaded, :sharded_timed_queue
|
113
|
+
sync{@allocated[a.last].delete(Sequel.current)}
|
108
114
|
else
|
109
115
|
sync{@allocated.delete(Sequel.current)}
|
110
116
|
end
|
@@ -120,4 +126,3 @@ module Sequel
|
|
120
126
|
|
121
127
|
Database.register_extension(:connection_validator){|db| db.pool.extend(ConnectionValidator)}
|
122
128
|
end
|
123
|
-
|
@@ -126,7 +126,7 @@
|
|
126
126
|
# be emulated by dropping the table and recreating it with the constraints.
|
127
127
|
# If you want to use this plugin on SQLite with an alter_table block,
|
128
128
|
# you should drop all constraint validation metadata using
|
129
|
-
# <tt>drop_constraint_validations_for(:
|
129
|
+
# <tt>drop_constraint_validations_for(table: 'table')</tt>, and then
|
130
130
|
# readd all constraints you want to use inside the alter table block,
|
131
131
|
# making no other changes inside the alter_table block.
|
132
132
|
#
|
@@ -25,13 +25,17 @@
|
|
25
25
|
# By default, values are casted to the generic timestamp type for the
|
26
26
|
# database. You can override the cast type using the :cast option:
|
27
27
|
#
|
28
|
-
# add = Sequel.date_add(:date_column, {years: 1, months: 2, days: 3}, :
|
28
|
+
# add = Sequel.date_add(:date_column, {years: 1, months: 2, days: 3}, cast: :timestamptz)
|
29
29
|
#
|
30
30
|
# These expressions can be used in your datasets, or anywhere else that
|
31
31
|
# Sequel expressions are allowed:
|
32
32
|
#
|
33
33
|
# DB[:table].select(add.as(:d)).where(sub > Sequel::CURRENT_TIMESTAMP)
|
34
34
|
#
|
35
|
+
# On most databases, the values you provide for years/months/days/etc. must
|
36
|
+
# be numeric values and not arbitrary SQL expressions. However, on PostgreSQL
|
37
|
+
# 9.4+, use of arbitrary SQL expressions is supported.
|
38
|
+
#
|
35
39
|
# Related module: Sequel::SQL::DateAdd
|
36
40
|
|
37
41
|
#
|
@@ -54,7 +58,16 @@ module Sequel
|
|
54
58
|
interval = interval.parts
|
55
59
|
end
|
56
60
|
parts = {}
|
57
|
-
interval.each
|
61
|
+
interval.each do |k,v|
|
62
|
+
case v
|
63
|
+
when nil
|
64
|
+
# ignore
|
65
|
+
when Numeric
|
66
|
+
parts[k] = -v
|
67
|
+
else
|
68
|
+
parts[k] = Sequel::SQL::NumericExpression.new(:*, v, -1)
|
69
|
+
end
|
70
|
+
end
|
58
71
|
DateAdd.new(expr, parts, opts)
|
59
72
|
end
|
60
73
|
end
|
@@ -68,6 +81,7 @@ module Sequel
|
|
68
81
|
module DatasetMethods
|
69
82
|
DURATION_UNITS = [:years, :months, :days, :hours, :minutes, :seconds].freeze
|
70
83
|
DEF_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s.freeze}).freeze
|
84
|
+
POSTGRES_DURATION_UNITS = DURATION_UNITS.zip([:years, :months, :days, :hours, :mins, :secs].map{|s| s.to_s.freeze}).freeze
|
71
85
|
MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
|
72
86
|
MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
|
73
87
|
H2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s[0...-1].freeze}).freeze
|
@@ -87,14 +101,28 @@ module Sequel
|
|
87
101
|
|
88
102
|
cast = case db_type = db.database_type
|
89
103
|
when :postgres
|
90
|
-
|
91
|
-
|
92
|
-
|
104
|
+
casted = Sequel.cast(expr, cast_type)
|
105
|
+
|
106
|
+
if db.server_version >= 90400
|
107
|
+
placeholder = []
|
108
|
+
vals = []
|
109
|
+
each_valid_interval_unit(h, POSTGRES_DURATION_UNITS) do |value, sql_unit|
|
110
|
+
placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
|
111
|
+
vals << value
|
112
|
+
end
|
113
|
+
interval = Sequel.function(:make_interval, Sequel.lit(placeholder, *vals)) unless vals.empty?
|
114
|
+
else
|
115
|
+
parts = String.new
|
116
|
+
each_valid_interval_unit(h, DEF_DURATION_UNITS) do |value, sql_unit|
|
117
|
+
parts << "#{value} #{sql_unit} "
|
118
|
+
end
|
119
|
+
interval = Sequel.cast(parts, :interval) unless parts.empty?
|
93
120
|
end
|
94
|
-
|
95
|
-
|
121
|
+
|
122
|
+
if interval
|
123
|
+
return complex_expression_sql_append(sql, :+, [casted, interval])
|
96
124
|
else
|
97
|
-
return
|
125
|
+
return literal_append(sql, casted)
|
98
126
|
end
|
99
127
|
when :sqlite
|
100
128
|
args = [expr]
|
@@ -14,12 +14,12 @@
|
|
14
14
|
#
|
15
15
|
# ds = DB[:items].extension(:duplicate_columns_handler)
|
16
16
|
#
|
17
|
-
#
|
18
|
-
# or any object that responds to :call.
|
17
|
+
# If the Database option :on_duplicate_columns is set, it configures how this
|
18
|
+
# extension works. The value should be # or any object that responds to :call.
|
19
19
|
#
|
20
|
-
# on_duplicate_columns: :raise
|
21
|
-
# on_duplicate_columns: :warn
|
22
|
-
# on_duplicate_columns: :ignore
|
20
|
+
# on_duplicate_columns: :raise # or 'raise'
|
21
|
+
# on_duplicate_columns: :warn # or 'warn'
|
22
|
+
# on_duplicate_columns: :ignore # or anything unrecognized
|
23
23
|
# on_duplicate_columns: lambda{|columns| arbitrary_condition? ? :raise : :warn}
|
24
24
|
#
|
25
25
|
# You may also configure duplicate columns handling for a specific dataset:
|
@@ -30,9 +30,10 @@
|
|
30
30
|
# ds.on_duplicate_columns{|columns| arbitrary_condition? ? :raise : :warn}
|
31
31
|
# ds.on_duplicate_columns(lambda{|columns| arbitrary_condition? ? :raise : :warn})
|
32
32
|
#
|
33
|
-
# If :raise is specified, a Sequel::DuplicateColumnError is raised.
|
34
|
-
# If :warn is specified, you will receive a warning via +warn+.
|
33
|
+
# If :raise or 'raise' is specified, a Sequel::DuplicateColumnError is raised.
|
34
|
+
# If :warn or 'warn' is specified, you will receive a warning via +warn+.
|
35
35
|
# If a callable is specified, it will be called.
|
36
|
+
# For other values, duplicate columns are ignored (Sequel's default behavior)
|
36
37
|
# If no on_duplicate_columns is specified, the default is :warn.
|
37
38
|
#
|
38
39
|
# Related module: Sequel::DuplicateColumnsHandler
|
@@ -64,9 +65,9 @@ module Sequel
|
|
64
65
|
message = "#{caller(*CALLER_ARGS).first}: One or more duplicate columns present in #{cols.inspect}"
|
65
66
|
|
66
67
|
case duplicate_columns_handler_type(cols)
|
67
|
-
when :raise
|
68
|
+
when :raise, 'raise'
|
68
69
|
raise DuplicateColumnError, message
|
69
|
-
when :warn
|
70
|
+
when :warn, 'warn'
|
70
71
|
warn message
|
71
72
|
end
|
72
73
|
end
|