sequel 5.33.0 → 5.58.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 +318 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +40 -9
- data/doc/association_basics.rdoc +77 -13
- data/doc/cheat_sheet.rdoc +13 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/fork_safety.rdoc +84 -0
- data/doc/migration.rdoc +12 -6
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +15 -3
- data/doc/postgresql.rdoc +9 -1
- data/doc/querying.rdoc +7 -5
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/release_notes/5.39.0.txt +19 -0
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/sql.rdoc +14 -2
- data/doc/testing.rdoc +10 -1
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +29 -19
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +8 -6
- data/lib/sequel/adapters/oracle.rb +5 -4
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +84 -7
- data/lib/sequel/adapters/shared/mysql.rb +33 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -7
- data/lib/sequel/adapters/shared/postgres.rb +158 -20
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -10
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +2 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +9 -8
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +7 -9
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +33 -24
- data/lib/sequel/database/connecting.rb +3 -4
- data/lib/sequel/database/misc.rb +37 -12
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +50 -53
- data/lib/sequel/database/schema_methods.rb +45 -23
- data/lib/sequel/database/transactions.rb +9 -6
- data/lib/sequel/dataset/actions.rb +61 -8
- data/lib/sequel/dataset/features.rb +15 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +114 -11
- data/lib/sequel/dataset/sql.rb +172 -46
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +38 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -24
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +3 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +139 -0
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/named_timezones.rb +5 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_array_ops.rb +6 -2
- data/lib/sequel/extensions/pg_enum.rb +3 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +35 -8
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +119 -4
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +7 -19
- data/lib/sequel/extensions/pg_range_ops.rb +39 -9
- data/lib/sequel/extensions/pg_row.rb +1 -1
- data/lib/sequel/extensions/pg_row_ops.rb +25 -1
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +4 -1
- data/lib/sequel/extensions/schema_dumper.rb +16 -5
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model/associations.rb +342 -114
- data/lib/sequel/model/base.rb +45 -24
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +8 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +3 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +39 -5
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +8 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +8 -3
- data/lib/sequel/plugins/pg_array_associations.rb +58 -41
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/prepared_statements.rb +15 -12
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +37 -35
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/plugins/validation_helpers.rb +18 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +20 -17
- data/lib/sequel/version.rb +1 -1
- metadata +93 -39
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The date_parse_input_handler extension allows for configuring how input
|
|
4
|
+
# to date parsing methods should be handled. By default, the
|
|
5
|
+
# extension does not change behavior. However, you can use the
|
|
6
|
+
# +Sequel.date_parse_input_handler+ method to support custom handling
|
|
7
|
+
# of input strings to the date parsing methods. For example, if you want
|
|
8
|
+
# to implement a length check to prevent denial of service vulnerabilities
|
|
9
|
+
# in older versions of Ruby, you can do:
|
|
10
|
+
#
|
|
11
|
+
# Sequel.extension :date_parse_input_handler
|
|
12
|
+
# Sequel.date_parse_input_handler do |string|
|
|
13
|
+
# raise Sequel::InvalidValue, "string length (200) exceeds the limit 128" if string.bytesize > 128
|
|
14
|
+
# string
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# You can also use +Sequel.date_parse_input_handler+ to modify the string
|
|
18
|
+
# that will be passed to the parsing methods. For example, you could
|
|
19
|
+
# truncate it:
|
|
20
|
+
#
|
|
21
|
+
# Sequel.date_parse_input_handler do |string|
|
|
22
|
+
# string.b[0, 128]
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# Be aware that modern versions of Ruby will raise an exception if
|
|
26
|
+
# date parsing input exceeds 128 bytes.
|
|
27
|
+
|
|
28
|
+
module Sequel
|
|
29
|
+
module DateParseInputHandler
|
|
30
|
+
def date_parse_input_handler(&block)
|
|
31
|
+
singleton_class.class_eval do
|
|
32
|
+
define_method(:handle_date_parse_input, &block)
|
|
33
|
+
private :handle_date_parse_input
|
|
34
|
+
alias handle_date_parse_input handle_date_parse_input
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Call date parse input handler with input string.
|
|
39
|
+
def string_to_date(string)
|
|
40
|
+
super(handle_date_parse_input(string))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Call date parse input handler with input string.
|
|
44
|
+
def string_to_datetime(string)
|
|
45
|
+
super(handle_date_parse_input(string))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Call date parse input handler with input string.
|
|
49
|
+
def string_to_time(string)
|
|
50
|
+
super(handle_date_parse_input(string))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Call date parse input handler with input string.
|
|
56
|
+
def _date_parse(string)
|
|
57
|
+
super(handle_date_parse_input(string))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Return string as-is by default, so by default behavior does not change.
|
|
61
|
+
def handle_date_parse_input(string)
|
|
62
|
+
string
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
extend DateParseInputHandler
|
|
67
|
+
end
|
|
@@ -19,7 +19,11 @@ module Sequel::DateTimeParseToTime
|
|
|
19
19
|
# Use DateTime.parse.to_time to do the conversion if the input a string and is assumed to
|
|
20
20
|
# be in UTC and there is no offset information in the string.
|
|
21
21
|
def convert_input_timestamp(v, input_timezone)
|
|
22
|
-
if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !
|
|
22
|
+
if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !_date_parse(v).has_key?(:offset)
|
|
23
|
+
# :nocov:
|
|
24
|
+
# Whether this is fully branch covered depends on the order in which the specs are run.
|
|
25
|
+
v = handle_date_parse_input(v) if respond_to?(:handle_date_parse_input, true)
|
|
26
|
+
# :nocov:
|
|
23
27
|
t = DateTime.parse(v).to_time
|
|
24
28
|
case application_timezone
|
|
25
29
|
when nil, :local
|
|
@@ -39,10 +39,12 @@
|
|
|
39
39
|
|
|
40
40
|
module Sequel
|
|
41
41
|
module DuplicateColumnsHandler
|
|
42
|
+
# :nocov:
|
|
42
43
|
CALLER_ARGS = (RUBY_VERSION >= '2.0' ? [0,1] : [0]).freeze
|
|
44
|
+
# :nocov:
|
|
43
45
|
|
|
44
46
|
# Customize handling of duplicate columns for this dataset.
|
|
45
|
-
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless
|
|
47
|
+
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless defined?(yield); nil), &block)
|
|
46
48
|
raise Error, "Cannot provide both an argument and a block to on_duplicate_columns" if handler && block
|
|
47
49
|
clone(:on_duplicate_columns=>handler||block)
|
|
48
50
|
end
|
|
@@ -102,9 +102,17 @@ class String
|
|
|
102
102
|
# Yield the Inflections module if a block is given, and return
|
|
103
103
|
# the Inflections module.
|
|
104
104
|
def self.inflections
|
|
105
|
-
yield Inflections if
|
|
105
|
+
yield Inflections if defined?(yield)
|
|
106
106
|
Inflections
|
|
107
107
|
end
|
|
108
|
+
|
|
109
|
+
%w'classify constantize dasherize demodulize foreign_key humanize pluralize singularize tableize underscore'.each do |m|
|
|
110
|
+
# :nocov:
|
|
111
|
+
if method_defined?(m)
|
|
112
|
+
alias_method(m, m)
|
|
113
|
+
end
|
|
114
|
+
# :nocov:
|
|
115
|
+
end
|
|
108
116
|
|
|
109
117
|
# By default, camelize converts the string to UpperCamelCase. If the argument to camelize
|
|
110
118
|
# is set to :lower then camelize produces lowerCamelCase.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The is_distinct_from extension adds the ability to use the
|
|
4
|
+
# SQL standard IS DISTINCT FROM operator, which is similar to the
|
|
5
|
+
# not equals operator, except that NULL values are considered
|
|
6
|
+
# equal. Only PostgreSQL and H2 currently support this operator. On
|
|
7
|
+
# other databases, support is emulated.
|
|
8
|
+
#
|
|
9
|
+
# First, you need to load the extension into the database:
|
|
10
|
+
#
|
|
11
|
+
# DB.extension :is_distinct_from
|
|
12
|
+
#
|
|
13
|
+
# Then you can use the Sequel.is_distinct_from to create the expression
|
|
14
|
+
# objects:
|
|
15
|
+
#
|
|
16
|
+
# expr = Sequel.is_distinct_from(:column_a, :column_b)
|
|
17
|
+
# # (column_a IS DISTINCT FROM column_b)
|
|
18
|
+
#
|
|
19
|
+
# You can also use the +is_distinct_from+ method on most Sequel expressions:
|
|
20
|
+
#
|
|
21
|
+
# expr = Sequel[:column_a].is_distinct_from(:column_b)
|
|
22
|
+
# # (column_a IS DISTINCT FROM column_b)
|
|
23
|
+
#
|
|
24
|
+
# These expressions can be used in your datasets, or anywhere else that
|
|
25
|
+
# Sequel expressions are allowed:
|
|
26
|
+
#
|
|
27
|
+
# DB[:table].where(expr)
|
|
28
|
+
#
|
|
29
|
+
# Related module: Sequel::SQL::IsDistinctFrom
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
module Sequel
|
|
33
|
+
module SQL
|
|
34
|
+
module Builders
|
|
35
|
+
# Return a IsDistinctFrom expression object, using the IS DISTINCT FROM operator
|
|
36
|
+
# with the given left hand side and right hand side.
|
|
37
|
+
def is_distinct_from(lhs, rhs)
|
|
38
|
+
BooleanExpression.new(:NOOP, IsDistinctFrom.new(lhs, rhs))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Represents an SQL expression using the IS DISTINCT FROM operator.
|
|
43
|
+
class IsDistinctFrom < GenericExpression
|
|
44
|
+
# These methods are added to expressions, allowing them to return IS DISTINCT
|
|
45
|
+
# FROM expressions based on the receiving expression.
|
|
46
|
+
module Methods
|
|
47
|
+
# Return a IsDistinctFrom expression, using the IS DISTINCT FROM operator,
|
|
48
|
+
# with the receiver as the left hand side and the argument as the right hand side.
|
|
49
|
+
def is_distinct_from(rhs)
|
|
50
|
+
BooleanExpression.new(:NOOP, IsDistinctFrom.new(self, rhs))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# These methods are added to datasets using the is_distinct_from extension
|
|
55
|
+
# extension, for the purposes of correctly literalizing IsDistinctFrom
|
|
56
|
+
# expressions for the appropriate database type.
|
|
57
|
+
module DatasetMethods
|
|
58
|
+
# Append the SQL fragment for the IS DISTINCT FROM expression to the SQL query.
|
|
59
|
+
def is_distinct_from_sql_append(sql, idf)
|
|
60
|
+
lhs = idf.lhs
|
|
61
|
+
rhs = idf.rhs
|
|
62
|
+
|
|
63
|
+
if supports_is_distinct_from?
|
|
64
|
+
sql << "("
|
|
65
|
+
literal_append(sql, lhs)
|
|
66
|
+
sql << " IS DISTINCT FROM "
|
|
67
|
+
literal_append(sql, rhs)
|
|
68
|
+
sql << ")"
|
|
69
|
+
elsif db.database_type == :derby && (lhs == nil || rhs == nil)
|
|
70
|
+
if lhs == nil && rhs == nil
|
|
71
|
+
sql << literal_false
|
|
72
|
+
elsif lhs == nil
|
|
73
|
+
literal_append(sql, ~Sequel.expr(rhs=>nil))
|
|
74
|
+
else
|
|
75
|
+
literal_append(sql, ~Sequel.expr(lhs=>nil))
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
literal_append(sql, Sequel.case({(Sequel.expr(lhs=>rhs) | [[lhs, nil], [rhs, nil]]) => 0}, 1) => 1)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# Whether the database supports IS DISTINCT FROM.
|
|
85
|
+
def supports_is_distinct_from?
|
|
86
|
+
if defined?(super)
|
|
87
|
+
return super
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
case db.database_type
|
|
91
|
+
when :postgres, :h2
|
|
92
|
+
true
|
|
93
|
+
else
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The left hand side of the IS DISTINCT FROM expression.
|
|
100
|
+
attr_reader :lhs
|
|
101
|
+
|
|
102
|
+
# The right hand side of the IS DISTINCT FROM expression.
|
|
103
|
+
attr_reader :rhs
|
|
104
|
+
|
|
105
|
+
def initialize(lhs, rhs)
|
|
106
|
+
@lhs = lhs
|
|
107
|
+
@rhs = rhs
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
to_s_method :is_distinct_from_sql
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class SQL::GenericExpression
|
|
115
|
+
include SQL::IsDistinctFrom::Methods
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class LiteralString
|
|
119
|
+
include SQL::IsDistinctFrom::Methods
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
Dataset.register_extension(:is_distinct_from, SQL::IsDistinctFrom::DatasetMethods)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# :nocov:
|
|
126
|
+
if Sequel.core_extensions?
|
|
127
|
+
class Symbol
|
|
128
|
+
include Sequel::SQL::IsDistinctFrom::Methods
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
if defined?(Sequel::CoreRefinements)
|
|
133
|
+
module Sequel::CoreRefinements
|
|
134
|
+
refine Symbol do
|
|
135
|
+
send INCLUDE_METH, Sequel::SQL::IsDistinctFrom::Methods
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
# :nocov:
|
|
@@ -68,6 +68,9 @@ module Sequel
|
|
|
68
68
|
# Allow calling private methods for backwards compatibility
|
|
69
69
|
@db.send(method_sym, *args, &block)
|
|
70
70
|
end
|
|
71
|
+
# :nocov:
|
|
72
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
|
73
|
+
# :nocov:
|
|
71
74
|
|
|
72
75
|
# This object responds to all methods the database responds to.
|
|
73
76
|
def respond_to_missing?(meth, include_private)
|
|
@@ -329,7 +332,8 @@ module Sequel
|
|
|
329
332
|
# schema_migrations for timestamped migrations). in the database to keep track
|
|
330
333
|
# of the current migration version. If no migration version is stored in the
|
|
331
334
|
# database, the version is considered to be 0. If no target version is
|
|
332
|
-
# specified, the
|
|
335
|
+
# specified, or the target version specified is greater than the latest
|
|
336
|
+
# version available, the database is migrated to the latest version available in the
|
|
333
337
|
# migration directory.
|
|
334
338
|
#
|
|
335
339
|
# For example, to migrate the database to the latest version:
|
|
@@ -384,6 +388,9 @@ module Sequel
|
|
|
384
388
|
|
|
385
389
|
# Migrates the supplied database using the migration files in the specified directory. Options:
|
|
386
390
|
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
|
391
|
+
# It is very risky to use this option, since it can result in
|
|
392
|
+
# the database schema version number not matching the expected
|
|
393
|
+
# database schema.
|
|
387
394
|
# :column :: The column in the :table argument storing the migration version (default: :version).
|
|
388
395
|
# :current :: The current version of the database. If not given, it is retrieved from the database
|
|
389
396
|
# using the :table and :column options.
|
|
@@ -518,7 +525,6 @@ module Sequel
|
|
|
518
525
|
def initialize(db, directory, opts=OPTS)
|
|
519
526
|
super
|
|
520
527
|
@current = opts[:current] || current_migration_version
|
|
521
|
-
raise(Error, "No current version available") unless current
|
|
522
528
|
|
|
523
529
|
latest_version = latest_migration_version
|
|
524
530
|
@target = if opts[:target]
|
|
@@ -538,6 +544,11 @@ module Sequel
|
|
|
538
544
|
end
|
|
539
545
|
|
|
540
546
|
@direction = current < target ? :up : :down
|
|
547
|
+
|
|
548
|
+
if @direction == :down && @current >= @files.length && !@allow_missing_migration_files
|
|
549
|
+
raise Migrator::Error, "Missing migration version(s) needed to migrate down to target version (current: #{current}, target: #{target})"
|
|
550
|
+
end
|
|
551
|
+
|
|
541
552
|
@migrations = get_migrations
|
|
542
553
|
end
|
|
543
554
|
|
|
@@ -84,9 +84,9 @@ module Sequel
|
|
|
84
84
|
def convert_output_time_other(v, output_timezone)
|
|
85
85
|
Time.at(v.to_i, :in => output_timezone)
|
|
86
86
|
end
|
|
87
|
-
else
|
|
88
87
|
# :nodoc:
|
|
89
88
|
# :nocov:
|
|
89
|
+
else
|
|
90
90
|
def convert_input_time_other(v, input_timezone)
|
|
91
91
|
local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
|
92
92
|
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
|
@@ -105,6 +105,8 @@ module Sequel
|
|
|
105
105
|
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
|
+
# :nodoc:
|
|
109
|
+
# :nocov:
|
|
108
110
|
end
|
|
109
111
|
|
|
110
112
|
# Handle both TZInfo 1 and TZInfo 2
|
|
@@ -142,6 +144,8 @@ module Sequel
|
|
|
142
144
|
# Convert timezone offset from UTC to the offset for the output_timezone
|
|
143
145
|
(v - local_offset).new_offset(local_offset)
|
|
144
146
|
end
|
|
147
|
+
# :nodoc:
|
|
148
|
+
# :nocov:
|
|
145
149
|
end
|
|
146
150
|
|
|
147
151
|
# Returns TZInfo::Timezone instance if given a String.
|
|
@@ -54,7 +54,7 @@ module Sequel
|
|
|
54
54
|
# an enumerator if no block is given.
|
|
55
55
|
def each_page(page_size)
|
|
56
56
|
raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
|
|
57
|
-
return to_enum(:each_page, page_size) unless
|
|
57
|
+
return to_enum(:each_page, page_size) unless defined?(yield)
|
|
58
58
|
record_count = count
|
|
59
59
|
total_pages = (record_count / page_size.to_f).ceil
|
|
60
60
|
(1..total_pages).each{|page_no| yield paginate(page_no, page_size, record_count)}
|
|
@@ -213,6 +213,7 @@ module Sequel
|
|
|
213
213
|
scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, type)}"
|
|
214
214
|
define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)}
|
|
215
215
|
private meth
|
|
216
|
+
alias_method(meth, meth)
|
|
216
217
|
end
|
|
217
218
|
|
|
218
219
|
@schema_type_classes[:"#{type}_array"] = PGArray
|
|
@@ -108,7 +108,7 @@ module Sequel
|
|
|
108
108
|
|
|
109
109
|
# Call the ANY function:
|
|
110
110
|
#
|
|
111
|
-
# array_op.
|
|
111
|
+
# array_op.any # ANY(array)
|
|
112
112
|
#
|
|
113
113
|
# Usually used like:
|
|
114
114
|
#
|
|
@@ -157,7 +157,9 @@ module Sequel
|
|
|
157
157
|
else
|
|
158
158
|
Sequel.function(:hstore, self, wrap_array(arg))
|
|
159
159
|
end
|
|
160
|
+
# :nocov:
|
|
160
161
|
if Sequel.respond_to?(:hstore_op)
|
|
162
|
+
# :nocov:
|
|
161
163
|
v = Sequel.hstore_op(v)
|
|
162
164
|
end
|
|
163
165
|
v
|
|
@@ -283,7 +285,9 @@ module Sequel
|
|
|
283
285
|
end
|
|
284
286
|
end
|
|
285
287
|
|
|
288
|
+
# :nocov:
|
|
286
289
|
if defined?(PGArray)
|
|
290
|
+
# :nocov:
|
|
287
291
|
class PGArray
|
|
288
292
|
# Wrap the PGArray instance in an ArrayOp, allowing you to easily use
|
|
289
293
|
# the PostgreSQL array functions and operators with literal arrays.
|
|
@@ -325,7 +329,7 @@ end
|
|
|
325
329
|
if defined?(Sequel::CoreRefinements)
|
|
326
330
|
module Sequel::CoreRefinements
|
|
327
331
|
refine Symbol do
|
|
328
|
-
|
|
332
|
+
send INCLUDE_METH, Sequel::Postgres::ArrayOpMethods
|
|
329
333
|
end
|
|
330
334
|
end
|
|
331
335
|
end
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
#
|
|
43
43
|
# This extension integrates with the pg_array extension. If you plan
|
|
44
44
|
# to use arrays of enum types, load the pg_array extension before the
|
|
45
|
-
#
|
|
45
|
+
# pg_enum extension:
|
|
46
46
|
#
|
|
47
47
|
# DB.extension :pg_array, :pg_enum
|
|
48
48
|
#
|
|
@@ -181,7 +181,9 @@ module Sequel
|
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
# support reversible create_enum statements if the migration extension is loaded
|
|
184
|
+
# :nocov:
|
|
184
185
|
if defined?(MigrationReverser)
|
|
186
|
+
# :nocov:
|
|
185
187
|
class MigrationReverser
|
|
186
188
|
private
|
|
187
189
|
def create_enum(name, _)
|
|
@@ -83,7 +83,7 @@ module Sequel
|
|
|
83
83
|
# If convert_infinite_timestamps is true and the value is infinite, return an appropriate
|
|
84
84
|
# value based on the convert_infinite_timestamps setting.
|
|
85
85
|
def to_application_timestamp(value)
|
|
86
|
-
if value.is_a?(String) && (m =
|
|
86
|
+
if value.is_a?(String) && (m = /((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/.match(value)) && (m[2] || m[3])
|
|
87
87
|
if m[3]
|
|
88
88
|
value = value.sub(' BC', '').sub(' ', ' BC ')
|
|
89
89
|
conv = defined?(JRUBY_VERSION) && JRUBY_VERSION == '9.2.0.0'
|
|
@@ -189,7 +189,7 @@ module Sequel
|
|
|
189
189
|
if date < DATETIME_YEAR_1
|
|
190
190
|
date <<= ((date.year) * 24 - 12)
|
|
191
191
|
date = db.from_application_timestamp(date)
|
|
192
|
-
minutes = (date.
|
|
192
|
+
minutes = (date.offset * 1440).to_i
|
|
193
193
|
date.strftime("'%Y-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*minutes.divmod(60))} BC'")
|
|
194
194
|
else
|
|
195
195
|
super
|
|
@@ -62,6 +62,19 @@
|
|
|
62
62
|
# # Delete a key
|
|
63
63
|
# DB[:tab].update(h: Sequel.hstore_op(:h).delete('k1'))
|
|
64
64
|
#
|
|
65
|
+
# On PostgreSQL 14+, The hstore <tt>[]</tt> method will use subscripts instead of being
|
|
66
|
+
# the same as +get+, if the value being wrapped is an identifer:
|
|
67
|
+
#
|
|
68
|
+
# Sequel.hstore_op(:hstore_column)['a'] # hstore_column['a']
|
|
69
|
+
# Sequel.hstore_op(Sequel[:h][:s])['a'] # h.s['a']
|
|
70
|
+
#
|
|
71
|
+
# This support allows you to use hstore subscripts in UPDATE statements to update only
|
|
72
|
+
# part of a column:
|
|
73
|
+
#
|
|
74
|
+
# h = Sequel.hstore_op(:h)
|
|
75
|
+
# DB[:t].update(h['key1'] => 'val1', h['key2'] => 'val2')
|
|
76
|
+
# # UPDATE "t" SET "h"['key1'] = 'val1', "h"['key2'] = 'val2'
|
|
77
|
+
#
|
|
65
78
|
# See the PostgreSQL hstore function and operator documentation for more
|
|
66
79
|
# details on what these functions and operators do.
|
|
67
80
|
#
|
|
@@ -114,10 +127,15 @@ module Sequel
|
|
|
114
127
|
#
|
|
115
128
|
# hstore_op['a'] # (hstore -> 'a')
|
|
116
129
|
def [](key)
|
|
117
|
-
v = Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)])
|
|
118
130
|
if key.is_a?(Array) || (defined?(Sequel::Postgres::PGArray) && key.is_a?(Sequel::Postgres::PGArray)) || (defined?(Sequel::Postgres::ArrayOp) && key.is_a?(Sequel::Postgres::ArrayOp))
|
|
119
|
-
wrap_output_array(
|
|
131
|
+
wrap_output_array(Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)]))
|
|
120
132
|
else
|
|
133
|
+
v = case @value
|
|
134
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
|
|
135
|
+
HStoreSubscriptOp.new(self, key)
|
|
136
|
+
else
|
|
137
|
+
Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, key])
|
|
138
|
+
end
|
|
121
139
|
Sequel::SQL::StringExpression.new(:NOOP, v)
|
|
122
140
|
end
|
|
123
141
|
end
|
|
@@ -304,6 +322,38 @@ module Sequel
|
|
|
304
322
|
end
|
|
305
323
|
end
|
|
306
324
|
|
|
325
|
+
# Represents hstore subscripts. This is abstracted because the
|
|
326
|
+
# subscript support depends on the database version.
|
|
327
|
+
class HStoreSubscriptOp < SQL::Expression
|
|
328
|
+
SUBSCRIPT = ["".freeze, "[".freeze, "]".freeze].freeze
|
|
329
|
+
|
|
330
|
+
# The expression being subscripted
|
|
331
|
+
attr_reader :expression
|
|
332
|
+
|
|
333
|
+
# The subscript to use
|
|
334
|
+
attr_reader :sub
|
|
335
|
+
|
|
336
|
+
# Set the expression and subscript to the given arguments
|
|
337
|
+
def initialize(expression, sub)
|
|
338
|
+
@expression = expression
|
|
339
|
+
@sub = sub
|
|
340
|
+
freeze
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Use subscripts instead of -> operator on PostgreSQL 14+
|
|
344
|
+
def to_s_append(ds, sql)
|
|
345
|
+
server_version = ds.db.server_version
|
|
346
|
+
frag = server_version && server_version >= 140000 ? SUBSCRIPT : HStoreOp::LOOKUP
|
|
347
|
+
ds.literal_append(sql, Sequel::SQL::PlaceholderLiteralString.new(frag, [@expression, @sub]))
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Support transforming of hstore subscripts
|
|
351
|
+
def sequel_ast_transform(transformer)
|
|
352
|
+
self.class.new(transformer.call(@expression), transformer.call(@sub))
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
|
|
307
357
|
module HStoreOpMethods
|
|
308
358
|
# Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
|
|
309
359
|
# hstore functions and operators with it.
|
|
@@ -312,7 +362,9 @@ module Sequel
|
|
|
312
362
|
end
|
|
313
363
|
end
|
|
314
364
|
|
|
365
|
+
# :nocov:
|
|
315
366
|
if defined?(HStore)
|
|
367
|
+
# :nocov:
|
|
316
368
|
class HStore
|
|
317
369
|
# Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
|
|
318
370
|
# hstore functions and operators with it.
|
|
@@ -354,7 +406,7 @@ end
|
|
|
354
406
|
if defined?(Sequel::CoreRefinements)
|
|
355
407
|
module Sequel::CoreRefinements
|
|
356
408
|
refine Symbol do
|
|
357
|
-
|
|
409
|
+
send INCLUDE_METH, Sequel::Postgres::HStoreOpMethods
|
|
358
410
|
end
|
|
359
411
|
end
|
|
360
412
|
end
|
|
@@ -41,7 +41,9 @@ module Sequel
|
|
|
41
41
|
db.instance_exec do
|
|
42
42
|
extend_datasets(InetDatasetMethods)
|
|
43
43
|
|
|
44
|
+
# :nocov:
|
|
44
45
|
if !defined?(SEQUEL_PG_VERSION_INTEGER) || SEQUEL_PG_VERSION_INTEGER >= 11300
|
|
46
|
+
# :nocov:
|
|
45
47
|
# sequel_pg 1.13.0+ will use inet/cidr conversion procs, but doing so is
|
|
46
48
|
# slower, so don't add the conversion procs if using sequel_pg 1.13.0+.
|
|
47
49
|
meth = IPAddr.method(:new)
|
|
@@ -32,8 +32,16 @@
|
|
|
32
32
|
#
|
|
33
33
|
# Related module: Sequel::Postgres::IntervalDatabaseMethods
|
|
34
34
|
|
|
35
|
+
require 'active_support'
|
|
35
36
|
require 'active_support/duration'
|
|
36
37
|
|
|
38
|
+
# :nocov:
|
|
39
|
+
begin
|
|
40
|
+
require 'active_support/version'
|
|
41
|
+
rescue LoadError
|
|
42
|
+
end
|
|
43
|
+
# :nocov:
|
|
44
|
+
|
|
37
45
|
module Sequel
|
|
38
46
|
module Postgres
|
|
39
47
|
module IntervalDatabaseMethods
|
|
@@ -61,34 +69,47 @@ module Sequel
|
|
|
61
69
|
|
|
62
70
|
# Creates callable objects that convert strings into ActiveSupport::Duration instances.
|
|
63
71
|
class Parser
|
|
72
|
+
# Whether ActiveSupport::Duration.new takes parts as array instead of hash
|
|
73
|
+
USE_PARTS_ARRAY = !defined?(ActiveSupport::VERSION::STRING) || ActiveSupport::VERSION::STRING < '5.1'
|
|
74
|
+
|
|
75
|
+
if defined?(ActiveSupport::Duration::SECONDS_PER_MONTH)
|
|
76
|
+
SECONDS_PER_MONTH = ActiveSupport::Duration::SECONDS_PER_MONTH
|
|
77
|
+
SECONDS_PER_YEAR = ActiveSupport::Duration::SECONDS_PER_YEAR
|
|
78
|
+
# :nocov:
|
|
79
|
+
else
|
|
80
|
+
SECONDS_PER_MONTH = 2592000
|
|
81
|
+
SECONDS_PER_YEAR = 31557600
|
|
82
|
+
# :nocov:
|
|
83
|
+
end
|
|
84
|
+
|
|
64
85
|
# Parse the interval input string into an ActiveSupport::Duration instance.
|
|
65
86
|
def call(string)
|
|
66
87
|
raise(InvalidValue, "invalid or unhandled interval format: #{string.inspect}") unless matches = /\A([+-]?\d+ years?\s?)?([+-]?\d+ mons?\s?)?([+-]?\d+ days?\s?)?(?:(?:([+-])?(\d{2,10}):(\d\d):(\d\d(\.\d+)?))|([+-]?\d+ hours?\s?)?([+-]?\d+ mins?\s?)?([+-]?\d+(\.\d+)? secs?\s?)?)?\z/.match(string)
|
|
67
88
|
|
|
68
89
|
value = 0
|
|
69
|
-
parts =
|
|
90
|
+
parts = {}
|
|
70
91
|
|
|
71
92
|
if v = matches[1]
|
|
72
93
|
v = v.to_i
|
|
73
|
-
value +=
|
|
74
|
-
parts
|
|
94
|
+
value += SECONDS_PER_YEAR * v
|
|
95
|
+
parts[:years] = v
|
|
75
96
|
end
|
|
76
97
|
if v = matches[2]
|
|
77
98
|
v = v.to_i
|
|
78
|
-
value +=
|
|
79
|
-
parts
|
|
99
|
+
value += SECONDS_PER_MONTH * v
|
|
100
|
+
parts[:months] = v
|
|
80
101
|
end
|
|
81
102
|
if v = matches[3]
|
|
82
103
|
v = v.to_i
|
|
83
104
|
value += 86400 * v
|
|
84
|
-
parts
|
|
105
|
+
parts[:days] = v
|
|
85
106
|
end
|
|
86
107
|
if matches[5]
|
|
87
108
|
seconds = matches[5].to_i * 3600 + matches[6].to_i * 60
|
|
88
109
|
seconds += matches[8] ? matches[7].to_f : matches[7].to_i
|
|
89
110
|
seconds *= -1 if matches[4] == '-'
|
|
90
111
|
value += seconds
|
|
91
|
-
parts
|
|
112
|
+
parts[:seconds] = seconds
|
|
92
113
|
elsif matches[9] || matches[10] || matches[11]
|
|
93
114
|
seconds = 0
|
|
94
115
|
if v = matches[9]
|
|
@@ -101,8 +122,14 @@ module Sequel
|
|
|
101
122
|
seconds += matches[12] ? v.to_f : v.to_i
|
|
102
123
|
end
|
|
103
124
|
value += seconds
|
|
104
|
-
parts
|
|
125
|
+
parts[:seconds] = seconds
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# :nocov:
|
|
129
|
+
if USE_PARTS_ARRAY
|
|
130
|
+
parts = parts.to_a
|
|
105
131
|
end
|
|
132
|
+
# :nocov:
|
|
106
133
|
|
|
107
134
|
ActiveSupport::Duration.new(value, parts)
|
|
108
135
|
end
|
|
@@ -387,11 +387,9 @@ module Sequel
|
|
|
387
387
|
# argument is true), or a String, Numeric, true, false, or nil
|
|
388
388
|
# if the json library used supports that.
|
|
389
389
|
def _parse_json(s)
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
|
|
394
|
-
end
|
|
390
|
+
Sequel.parse_json(s)
|
|
391
|
+
rescue Sequel.json_parser_error_class => e
|
|
392
|
+
raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
|
|
395
393
|
end
|
|
396
394
|
|
|
397
395
|
# Wrap the parsed JSON value in the appropriate JSON wrapper class.
|