sequel 5.60.1 → 5.81.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 +280 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +27 -24
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/code_order.rdoc +5 -3
- data/doc/dataset_basics.rdoc +1 -1
- 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 +18 -7
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +7 -2
- 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/release_notes/5.79.0.txt +28 -0
- data/doc/release_notes/5.80.0.txt +40 -0
- data/doc/release_notes/5.81.0.txt +31 -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 +10 -5
- 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 +82 -3
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +469 -160
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +39 -3
- 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 +1 -1
- data/lib/sequel/database/schema_methods.rb +12 -5
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/dataset_module.rb +1 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/graph.rb +1 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +119 -45
- 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/caller_logging.rb +4 -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 +1 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -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 +3 -3
- 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/provenance.rb +110 -0
- 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/temporarily_release_connection.rb +178 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +38 -11
- data/lib/sequel/model/base.rb +32 -18
- 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 +28 -7
- 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/input_transformer.rb +1 -1
- data/lib/sequel/plugins/list.rb +5 -2
- 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/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +1 -1
- 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 +14 -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 +2 -2
- metadata +79 -6
|
@@ -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:
|
|
@@ -36,6 +36,8 @@ require 'rbconfig'
|
|
|
36
36
|
module Sequel
|
|
37
37
|
module CallerLogging
|
|
38
38
|
SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
|
|
39
|
+
RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
|
|
40
|
+
INTERNAL = '<internal'
|
|
39
41
|
|
|
40
42
|
# A regexp of caller lines to ignore, in addition to internal Sequel and Ruby code.
|
|
41
43
|
attr_accessor :caller_logging_ignore
|
|
@@ -59,7 +61,8 @@ module Sequel
|
|
|
59
61
|
ignore = caller_logging_ignore
|
|
60
62
|
c = caller.find do |line|
|
|
61
63
|
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
|
62
|
-
line.start_with?(
|
|
64
|
+
line.start_with?(RUBY_STDLIB) ||
|
|
65
|
+
line.start_with?(INTERNAL) ||
|
|
63
66
|
(ignore && line =~ ignore))
|
|
64
67
|
end
|
|
65
68
|
|
|
@@ -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,7 +25,7 @@
|
|
|
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:
|
|
@@ -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
|
|
@@ -56,7 +56,11 @@ module Sequel
|
|
|
56
56
|
|
|
57
57
|
# Dump the index cache to the filename given in Marshal format.
|
|
58
58
|
def dump_index_cache(file)
|
|
59
|
-
|
|
59
|
+
indexes = {}
|
|
60
|
+
@indexes.sort.each do |k, v|
|
|
61
|
+
indexes[k] = v
|
|
62
|
+
end
|
|
63
|
+
File.open(file, 'wb'){|f| f.write(Marshal.dump(indexes))}
|
|
60
64
|
nil
|
|
61
65
|
end
|
|
62
66
|
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
# :decimal :: use 0.0 for unsupported strings
|
|
9
9
|
# :string :: silently allow hash and array conversion to string
|
|
10
10
|
#
|
|
11
|
+
# This also removes bytesize checks for string inputs for float, integer
|
|
12
|
+
# and decimal conversions.
|
|
13
|
+
#
|
|
11
14
|
# To load the extension into the database:
|
|
12
15
|
#
|
|
13
16
|
# DB.extension :looser_typecasting
|
|
@@ -159,6 +159,19 @@ module Sequel
|
|
|
159
159
|
migration.up = block
|
|
160
160
|
migration.down = MigrationReverser.new.reverse(&block)
|
|
161
161
|
end
|
|
162
|
+
|
|
163
|
+
# Creates a revert migration. This is the same as creating
|
|
164
|
+
# the same block with +down+, but it also calls the block and attempts
|
|
165
|
+
# to create a +up+ block that will reverse the changes made by
|
|
166
|
+
# the block. This is designed to revert the changes in the
|
|
167
|
+
# provided block.
|
|
168
|
+
#
|
|
169
|
+
# There are no guarantees that this will work perfectly
|
|
170
|
+
# in all cases, but it works for some simple cases.
|
|
171
|
+
def revert(&block)
|
|
172
|
+
migration.down = block
|
|
173
|
+
migration.up = MigrationReverser.new.reverse(&block)
|
|
174
|
+
end
|
|
162
175
|
end
|
|
163
176
|
|
|
164
177
|
# Handles the reversing of reversible migrations. Basically records
|
|
@@ -270,6 +283,10 @@ module Sequel
|
|
|
270
283
|
def rename_column(name, new_name)
|
|
271
284
|
@actions << [:rename_column, new_name, name]
|
|
272
285
|
end
|
|
286
|
+
|
|
287
|
+
def set_column_allow_null(name, allow_null=true)
|
|
288
|
+
@actions << [:set_column_allow_null, name, !allow_null]
|
|
289
|
+
end
|
|
273
290
|
end
|
|
274
291
|
|
|
275
292
|
# The preferred method for writing Sequel migrations, using a DSL:
|
|
@@ -377,7 +394,7 @@ module Sequel
|
|
|
377
394
|
# Raise a NotCurrentError unless the migrator is current, takes the same
|
|
378
395
|
# arguments as #run.
|
|
379
396
|
def self.check_current(*args)
|
|
380
|
-
raise(NotCurrentError, '
|
|
397
|
+
raise(NotCurrentError, 'current migration version does not match latest available version') unless is_current?(*args)
|
|
381
398
|
end
|
|
382
399
|
|
|
383
400
|
# Return whether the migrator is current (i.e. it does not need to make
|
|
@@ -386,6 +403,11 @@ module Sequel
|
|
|
386
403
|
migrator_class(directory).new(db, directory, opts).is_current?
|
|
387
404
|
end
|
|
388
405
|
|
|
406
|
+
# Lock ID to use for advisory locks when running migrations
|
|
407
|
+
# "sequel-migration".codepoints.reduce(:*) % (2**63)
|
|
408
|
+
MIGRATION_ADVISORY_LOCK_ID = 4966325471869609408
|
|
409
|
+
private_constant :MIGRATION_ADVISORY_LOCK_ID
|
|
410
|
+
|
|
389
411
|
# Migrates the supplied database using the migration files in the specified directory. Options:
|
|
390
412
|
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
|
391
413
|
# It is very risky to use this option, since it can result in
|
|
@@ -399,6 +421,8 @@ module Sequel
|
|
|
399
421
|
# :table :: The table containing the schema version (default: :schema_info for integer migrations and
|
|
400
422
|
# :schema_migrations for timestamped migrations).
|
|
401
423
|
# :target :: The target version to which to migrate. If not given, migrates to the maximum version.
|
|
424
|
+
# :use_advisory_lock :: Use advisory locks in migrations (only use this if Sequel supports advisory
|
|
425
|
+
# locks for the database).
|
|
402
426
|
#
|
|
403
427
|
# Examples:
|
|
404
428
|
# Sequel::Migrator.run(DB, "migrations")
|
|
@@ -406,7 +430,11 @@ module Sequel
|
|
|
406
430
|
# Sequel::Migrator.run(DB, "app1/migrations", column: :app2_version)
|
|
407
431
|
# Sequel::Migrator.run(DB, "app2/migrations", column: :app2_version, table: :schema_info2)
|
|
408
432
|
def self.run(db, directory, opts=OPTS)
|
|
409
|
-
|
|
433
|
+
if opts[:use_advisory_lock]
|
|
434
|
+
db.with_advisory_lock(MIGRATION_ADVISORY_LOCK_ID){run(db, directory, opts.merge(:use_advisory_lock=>false))}
|
|
435
|
+
else
|
|
436
|
+
migrator_class(directory).new(db, directory, opts).run
|
|
437
|
+
end
|
|
410
438
|
end
|
|
411
439
|
|
|
412
440
|
# Choose the Migrator subclass to use. Uses the TimestampMigrator
|
|
@@ -478,11 +506,7 @@ module Sequel
|
|
|
478
506
|
@use_transactions
|
|
479
507
|
end
|
|
480
508
|
|
|
481
|
-
|
|
482
|
-
db.transaction(&block)
|
|
483
|
-
else
|
|
484
|
-
yield
|
|
485
|
-
end
|
|
509
|
+
db.transaction(:skip_transaction=>use_trans == false, &block)
|
|
486
510
|
end
|
|
487
511
|
|
|
488
512
|
# Load the migration file, raising an exception if the file does not define
|
|
@@ -680,6 +704,13 @@ module Sequel
|
|
|
680
704
|
@migration_tuples = get_migration_tuples
|
|
681
705
|
end
|
|
682
706
|
|
|
707
|
+
# Apply the migration in the given file path. See Migrator.run for the
|
|
708
|
+
# available options. Additionally, this method supports the :direction
|
|
709
|
+
# option for whether to run the migration up (default) or down.
|
|
710
|
+
def self.run_single(db, path, opts=OPTS)
|
|
711
|
+
new(db, File.dirname(path), opts).run_single(path, opts[:direction] || :up)
|
|
712
|
+
end
|
|
713
|
+
|
|
683
714
|
# The timestamp migrator is current if there are no migrations to apply
|
|
684
715
|
# in either direction.
|
|
685
716
|
def is_current?
|
|
@@ -689,20 +720,39 @@ module Sequel
|
|
|
689
720
|
# Apply all migration tuples on the database
|
|
690
721
|
def run
|
|
691
722
|
migration_tuples.each do |m, f, direction|
|
|
692
|
-
|
|
693
|
-
db.log_info("Begin applying migration #{f}, direction: #{direction}")
|
|
694
|
-
checked_transaction(m) do
|
|
695
|
-
m.apply(db, direction)
|
|
696
|
-
fi = f.downcase
|
|
697
|
-
direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
|
|
698
|
-
end
|
|
699
|
-
db.log_info("Finished applying migration #{f}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
|
|
723
|
+
apply_migration(m, f, direction)
|
|
700
724
|
end
|
|
701
725
|
nil
|
|
702
726
|
end
|
|
703
727
|
|
|
728
|
+
# Apply single migration tuple at the given path with the given direction
|
|
729
|
+
# on the database.
|
|
730
|
+
def run_single(path, direction)
|
|
731
|
+
migration = load_migration_file(path)
|
|
732
|
+
file_name = File.basename(path)
|
|
733
|
+
already_applied = applied_migrations.include?(file_name.downcase)
|
|
734
|
+
|
|
735
|
+
return if direction == :up ? already_applied : !already_applied
|
|
736
|
+
|
|
737
|
+
apply_migration(migration, file_name, direction)
|
|
738
|
+
nil
|
|
739
|
+
end
|
|
740
|
+
|
|
704
741
|
private
|
|
705
742
|
|
|
743
|
+
# Apply a single migration with the given filename in the given direction.
|
|
744
|
+
def apply_migration(migration, file_name, direction)
|
|
745
|
+
fi = file_name.downcase
|
|
746
|
+
t = Time.now
|
|
747
|
+
|
|
748
|
+
db.log_info("Begin applying migration #{file_name}, direction: #{direction}")
|
|
749
|
+
checked_transaction(migration) do
|
|
750
|
+
migration.apply(db, direction)
|
|
751
|
+
direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
|
|
752
|
+
end
|
|
753
|
+
db.log_info("Finished applying migration #{file_name}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
|
|
754
|
+
end
|
|
755
|
+
|
|
706
756
|
# Convert the schema_info table to the new schema_migrations table format,
|
|
707
757
|
# using the version of the schema_info table and the current migration files.
|
|
708
758
|
def convert_from_schema_info
|
|
@@ -68,6 +68,10 @@ module Sequel
|
|
|
68
68
|
private
|
|
69
69
|
|
|
70
70
|
if RUBY_VERSION >= '2.6'
|
|
71
|
+
# Whether Time.at with :nsec and :in is broken. True on JRuby < 9.3.9.0.
|
|
72
|
+
BROKEN_TIME_AT_WITH_NSEC = defined?(JRUBY_VERSION) && (JRUBY_VERSION < '9.3' || (JRUBY_VERSION < '9.4' && JRUBY_VERSION.split('.')[2].to_i < 9))
|
|
73
|
+
private_constant :BROKEN_TIME_AT_WITH_NSEC
|
|
74
|
+
|
|
71
75
|
# Convert the given input Time (which must be in UTC) to the given input timezone,
|
|
72
76
|
# which should be a TZInfo::Timezone instance.
|
|
73
77
|
def convert_input_time_other(v, input_timezone)
|
|
@@ -76,33 +80,45 @@ module Sequel
|
|
|
76
80
|
raise unless disamb = tzinfo_disambiguator_for(v)
|
|
77
81
|
period = input_timezone.period_for_local(v, &disamb)
|
|
78
82
|
offset = period.utc_total_offset
|
|
79
|
-
|
|
83
|
+
# :nocov:
|
|
84
|
+
if BROKEN_TIME_AT_WITH_NSEC
|
|
85
|
+
Time.at(v.to_i - offset, :in => input_timezone) + v.nsec/1000000000.0
|
|
86
|
+
# :nocov:
|
|
87
|
+
else
|
|
88
|
+
Time.at(v.to_i - offset, v.nsec, :nsec, :in => input_timezone)
|
|
89
|
+
end
|
|
80
90
|
end
|
|
81
91
|
|
|
82
92
|
# Convert the given input Time to the given output timezone,
|
|
83
93
|
# which should be a TZInfo::Timezone instance.
|
|
84
94
|
def convert_output_time_other(v, output_timezone)
|
|
85
|
-
|
|
95
|
+
# :nocov:
|
|
96
|
+
if BROKEN_TIME_AT_WITH_NSEC
|
|
97
|
+
Time.at(v.to_i, :in => output_timezone) + v.nsec/1000000000.0
|
|
98
|
+
# :nocov:
|
|
99
|
+
else
|
|
100
|
+
Time.at(v.to_i, v.nsec, :nsec, :in => output_timezone)
|
|
101
|
+
end
|
|
86
102
|
end
|
|
87
103
|
# :nodoc:
|
|
88
104
|
# :nocov:
|
|
89
105
|
else
|
|
90
106
|
def convert_input_time_other(v, input_timezone)
|
|
91
107
|
local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
|
92
|
-
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
|
108
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + v.nsec/1000000000.0
|
|
93
109
|
end
|
|
94
110
|
|
|
95
111
|
if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
|
|
96
112
|
def convert_output_time_other(v, output_timezone)
|
|
97
113
|
v = output_timezone.utc_to_local(v.getutc)
|
|
98
114
|
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
|
99
|
-
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + local_offset
|
|
115
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + v.nsec/1000000000.0 + local_offset
|
|
100
116
|
end
|
|
101
117
|
else
|
|
102
118
|
def convert_output_time_other(v, output_timezone)
|
|
103
119
|
v = output_timezone.utc_to_local(v.getutc)
|
|
104
120
|
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
|
105
|
-
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
|
121
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + v.nsec/1000000000.0
|
|
106
122
|
end
|
|
107
123
|
end
|
|
108
124
|
# :nodoc:
|
|
@@ -120,7 +136,7 @@ module Sequel
|
|
|
120
136
|
v = output_timezone.utc_to_local(v.new_offset(0))
|
|
121
137
|
|
|
122
138
|
# Force DateTime output instead of TZInfo::DateTimeWithOffset
|
|
123
|
-
DateTime.
|
|
139
|
+
DateTime.civil(v.year, v.month, v.day, v.hour, v.minute, v.second + v.sec_fraction, v.offset, v.start)
|
|
124
140
|
end
|
|
125
141
|
# :nodoc:
|
|
126
142
|
# :nocov:
|
|
@@ -228,16 +228,37 @@ module Sequel
|
|
|
228
228
|
when Array
|
|
229
229
|
"{#{a.map{|i| bound_variable_array(i)}.join(',')}}"
|
|
230
230
|
when Sequel::SQL::Blob
|
|
231
|
-
|
|
231
|
+
bound_variable_array_string(literal(a)[BLOB_RANGE].gsub("''", "'"))
|
|
232
232
|
when Sequel::LiteralString
|
|
233
233
|
a
|
|
234
234
|
when String
|
|
235
|
-
|
|
235
|
+
bound_variable_array_string(a)
|
|
236
|
+
when Float
|
|
237
|
+
if a.infinite?
|
|
238
|
+
a > 0 ? '"Infinity"' : '"-Infinity"'
|
|
239
|
+
elsif a.nan?
|
|
240
|
+
'"NaN"'
|
|
241
|
+
else
|
|
242
|
+
literal(a)
|
|
243
|
+
end
|
|
244
|
+
when Time, Date
|
|
245
|
+
@default_dataset.literal_date_or_time(a)
|
|
236
246
|
else
|
|
237
|
-
|
|
247
|
+
if (s = bound_variable_arg(a, nil)).is_a?(String)
|
|
248
|
+
bound_variable_array_string(s)
|
|
249
|
+
else
|
|
250
|
+
literal(a)
|
|
251
|
+
end
|
|
238
252
|
end
|
|
239
253
|
end
|
|
240
254
|
|
|
255
|
+
# Escape strings used as array members in bound variables. Most complex
|
|
256
|
+
# will create a regular string with bound_variable_arg, and then use this
|
|
257
|
+
# escaping to format it as an array member.
|
|
258
|
+
def bound_variable_array_string(s)
|
|
259
|
+
"\"#{s.gsub(/("|\\)/, '\\\\\1')}\""
|
|
260
|
+
end
|
|
261
|
+
|
|
241
262
|
# Look into both the current database's array schema types and the global
|
|
242
263
|
# array schema types to get the type symbol for the given database type
|
|
243
264
|
# string.
|
|
@@ -246,7 +267,7 @@ module Sequel
|
|
|
246
267
|
end
|
|
247
268
|
|
|
248
269
|
# Make the column type detection handle registered array types.
|
|
249
|
-
def
|
|
270
|
+
def schema_array_type(db_type)
|
|
250
271
|
if (db_type =~ /\A([^(]+)(?:\([^(]+\))?\[\]\z/io) && (type = pg_array_schema_type($1))
|
|
251
272
|
type
|
|
252
273
|
else
|
|
@@ -457,6 +478,14 @@ module Sequel
|
|
|
457
478
|
end
|
|
458
479
|
end
|
|
459
480
|
|
|
481
|
+
# Allow automatic parameterization of the receiver if all elements can be
|
|
482
|
+
# can be automatically parameterized.
|
|
483
|
+
def sequel_auto_param_type(ds)
|
|
484
|
+
if array_type && all?{|x| nil == x || ds.send(:auto_param_type, x)}
|
|
485
|
+
"::#{array_type}[]"
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
460
489
|
private
|
|
461
490
|
|
|
462
491
|
# Recursive method that handles multi-dimensional
|