sequel 5.35.0 → 5.40.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 +68 -0
- data/README.rdoc +2 -2
- data/doc/cheat_sheet.rdoc +5 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/fork_safety.rdoc +84 -0
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +5 -1
- data/doc/postgresql.rdoc +1 -1
- data/doc/querying.rdoc +3 -3
- 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/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/lib/sequel/adapters/jdbc.rb +15 -3
- data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
- data/lib/sequel/adapters/odbc.rb +4 -6
- data/lib/sequel/adapters/shared/mssql.rb +35 -5
- data/lib/sequel/adapters/shared/oracle.rb +13 -7
- data/lib/sequel/adapters/shared/postgres.rb +40 -2
- data/lib/sequel/adapters/shared/sqlite.rb +35 -1
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
- data/lib/sequel/core.rb +5 -6
- data/lib/sequel/database/connecting.rb +0 -1
- data/lib/sequel/database/misc.rb +14 -0
- data/lib/sequel/database/schema_generator.rb +6 -0
- data/lib/sequel/database/schema_methods.rb +16 -6
- data/lib/sequel/database/transactions.rb +2 -2
- data/lib/sequel/dataset/actions.rb +10 -6
- data/lib/sequel/dataset/features.rb +10 -0
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/sql.rb +32 -10
- data/lib/sequel/extensions/blank.rb +6 -0
- data/lib/sequel/extensions/date_arithmetic.rb +6 -3
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +6 -0
- data/lib/sequel/extensions/migration.rb +10 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_interval.rb +22 -6
- data/lib/sequel/extensions/pg_json_ops.rb +44 -2
- data/lib/sequel/extensions/pg_row.rb +1 -0
- data/lib/sequel/extensions/pg_row_ops.rb +24 -0
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -3
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/associations.rb +1 -0
- data/lib/sequel/model/base.rb +23 -4
- data/lib/sequel/model/plugins.rb +6 -0
- data/lib/sequel/plugins/association_proxies.rb +3 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +0 -5
- data/lib/sequel/plugins/composition.rb +5 -1
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/nested_attributes.rb +3 -1
- data/lib/sequel/plugins/pg_array_associations.rb +4 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/timezones.rb +8 -3
- data/lib/sequel/version.rb +1 -1
- metadata +33 -21
|
@@ -240,7 +240,7 @@ module Sequel
|
|
|
240
240
|
if supports_create_or_replace_view?
|
|
241
241
|
options = options.merge(:replace=>true)
|
|
242
242
|
else
|
|
243
|
-
drop_view(name)
|
|
243
|
+
swallow_database_error{drop_view(name)}
|
|
244
244
|
end
|
|
245
245
|
|
|
246
246
|
create_view(name, source, options)
|
|
@@ -580,14 +580,14 @@ module Sequel
|
|
|
580
580
|
sql << ' NULL'
|
|
581
581
|
end
|
|
582
582
|
end
|
|
583
|
-
|
|
583
|
+
|
|
584
584
|
# Add primary key SQL fragment to column creation SQL.
|
|
585
585
|
def column_definition_primary_key_sql(sql, column)
|
|
586
586
|
if column[:primary_key]
|
|
587
587
|
if name = column[:primary_key_constraint_name]
|
|
588
588
|
sql << " CONSTRAINT #{quote_identifier(name)}"
|
|
589
589
|
end
|
|
590
|
-
sql <<
|
|
590
|
+
sql << " " << primary_key_constraint_sql_fragment(column)
|
|
591
591
|
constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
|
|
592
592
|
end
|
|
593
593
|
end
|
|
@@ -608,7 +608,7 @@ module Sequel
|
|
|
608
608
|
if name = column[:unique_constraint_name]
|
|
609
609
|
sql << " CONSTRAINT #{quote_identifier(name)}"
|
|
610
610
|
end
|
|
611
|
-
sql << '
|
|
611
|
+
sql << ' ' << unique_constraint_sql_fragment(column)
|
|
612
612
|
constraint_deferrable_sql_append(sql, column[:unique_deferrable])
|
|
613
613
|
end
|
|
614
614
|
end
|
|
@@ -656,11 +656,11 @@ module Sequel
|
|
|
656
656
|
check = "(#{check})" unless check[0..0] == '(' && check[-1..-1] == ')'
|
|
657
657
|
sql << "CHECK #{check}"
|
|
658
658
|
when :primary_key
|
|
659
|
-
sql << "
|
|
659
|
+
sql << "#{primary_key_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
|
|
660
660
|
when :foreign_key
|
|
661
661
|
sql << column_references_table_constraint_sql(constraint.merge(:deferrable=>nil))
|
|
662
662
|
when :unique
|
|
663
|
-
sql << "
|
|
663
|
+
sql << "#{unique_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
|
|
664
664
|
else
|
|
665
665
|
raise Error, "Invalid constraint type #{constraint[:type]}, should be :check, :primary_key, :foreign_key, or :unique"
|
|
666
666
|
end
|
|
@@ -892,6 +892,11 @@ module Sequel
|
|
|
892
892
|
on_delete_clause(action)
|
|
893
893
|
end
|
|
894
894
|
|
|
895
|
+
# Add fragment for primary key specification, separated for easier overridding.
|
|
896
|
+
def primary_key_constraint_sql_fragment(_)
|
|
897
|
+
'PRIMARY KEY'
|
|
898
|
+
end
|
|
899
|
+
|
|
895
900
|
# Proxy the quote_schema_table method to the dataset
|
|
896
901
|
def quote_schema_table(table)
|
|
897
902
|
schema_utility_dataset.quote_schema_table(table)
|
|
@@ -1047,6 +1052,11 @@ module Sequel
|
|
|
1047
1052
|
"#{type}#{literal(Array(elements)) if elements}#{' UNSIGNED' if column[:unsigned]}"
|
|
1048
1053
|
end
|
|
1049
1054
|
|
|
1055
|
+
# Add fragment for unique specification, separated for easier overridding.
|
|
1056
|
+
def unique_constraint_sql_fragment(_)
|
|
1057
|
+
'UNIQUE'
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1050
1060
|
# Whether clob should be used for String text: true columns.
|
|
1051
1061
|
def uses_clob_for_text?
|
|
1052
1062
|
false
|
|
@@ -82,7 +82,7 @@ module Sequel
|
|
|
82
82
|
# :server :: The server/shard the transaction is being executed on.
|
|
83
83
|
def rollback_on_exit(opts=OPTS)
|
|
84
84
|
synchronize(opts[:server]) do |conn|
|
|
85
|
-
raise Error, "Cannot call Sequel:: Database#rollback_on_exit
|
|
85
|
+
raise Error, "Cannot call Sequel:: Database#rollback_on_exit unless inside a transaction" unless h = _trans(conn)
|
|
86
86
|
rollback = !opts[:cancel]
|
|
87
87
|
|
|
88
88
|
if supports_savepoints?
|
|
@@ -154,7 +154,7 @@ module Sequel
|
|
|
154
154
|
# Note that this should not be used unless the entire transaction
|
|
155
155
|
# block is idempotent, as otherwise it can cause non-idempotent
|
|
156
156
|
# behavior to execute multiple times.
|
|
157
|
-
# :rollback :: Can
|
|
157
|
+
# :rollback :: Can be set to :reraise to reraise any Sequel::Rollback exceptions
|
|
158
158
|
# raised, or :always to always rollback even if no exceptions occur
|
|
159
159
|
# (useful for testing).
|
|
160
160
|
# :server :: The server to use for the transaction. Set to :default, :read_only, or
|
|
@@ -607,14 +607,16 @@ module Sequel
|
|
|
607
607
|
# as_hash, it accepts an optional :hash parameter, into which entries will
|
|
608
608
|
# be merged.
|
|
609
609
|
#
|
|
610
|
-
# DB[:table].select_hash(:id, :name)
|
|
610
|
+
# DB[:table].select_hash(:id, :name)
|
|
611
|
+
# # SELECT id, name FROM table
|
|
611
612
|
# # => {1=>'a', 2=>'b', ...}
|
|
612
613
|
#
|
|
613
614
|
# You can also provide an array of column names for either the key_column,
|
|
614
615
|
# the value column, or both:
|
|
615
616
|
#
|
|
616
|
-
# DB[:table].select_hash([:id, :foo], [:name, :bar])
|
|
617
|
-
# #
|
|
617
|
+
# DB[:table].select_hash([:id, :foo], [:name, :bar])
|
|
618
|
+
# # SELECT id, foo, name, bar FROM table
|
|
619
|
+
# # => {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
|
|
618
620
|
#
|
|
619
621
|
# When using this method, you must be sure that each expression has an alias
|
|
620
622
|
# that Sequel can determine.
|
|
@@ -626,14 +628,16 @@ module Sequel
|
|
|
626
628
|
# Similar to to_hash_groups, but only selects the columns given. Like to_hash_groups,
|
|
627
629
|
# it accepts an optional :hash parameter, into which entries will be merged.
|
|
628
630
|
#
|
|
629
|
-
# DB[:table].select_hash_groups(:name, :id)
|
|
631
|
+
# DB[:table].select_hash_groups(:name, :id)
|
|
632
|
+
# # SELECT id, name FROM table
|
|
630
633
|
# # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
|
|
631
634
|
#
|
|
632
635
|
# You can also provide an array of column names for either the key_column,
|
|
633
636
|
# the value column, or both:
|
|
634
637
|
#
|
|
635
|
-
# DB[:table].select_hash_groups([:first, :middle], [:last, :id])
|
|
636
|
-
# #
|
|
638
|
+
# DB[:table].select_hash_groups([:first, :middle], [:last, :id])
|
|
639
|
+
# # SELECT first, middle, last, id FROM table
|
|
640
|
+
# # => {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
|
|
637
641
|
#
|
|
638
642
|
# When using this method, you must be sure that each expression has an alias
|
|
639
643
|
# that Sequel can determine.
|
|
@@ -51,6 +51,11 @@ module Sequel
|
|
|
51
51
|
false
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
# Whether deleting from joined datasets is supported, false by default.
|
|
55
|
+
def supports_deleting_joins?
|
|
56
|
+
supports_modifying_joins?
|
|
57
|
+
end
|
|
58
|
+
|
|
54
59
|
# Whether the database supports derived column lists (e.g.
|
|
55
60
|
# "table_expr AS table_alias(column_alias1, column_alias2, ...)"), true by
|
|
56
61
|
# default.
|
|
@@ -178,6 +183,11 @@ module Sequel
|
|
|
178
183
|
true
|
|
179
184
|
end
|
|
180
185
|
|
|
186
|
+
# Whether updating joined datasets is supported, false by default.
|
|
187
|
+
def supports_updating_joins?
|
|
188
|
+
supports_modifying_joins?
|
|
189
|
+
end
|
|
190
|
+
|
|
181
191
|
# Whether the dataset supports the WINDOW clause to define windows used by multiple
|
|
182
192
|
# window functions, false by default.
|
|
183
193
|
def supports_window_clause?
|
|
@@ -201,7 +201,9 @@ module Sequel
|
|
|
201
201
|
when :insert_pk
|
|
202
202
|
fetch_rows(prepared_sql){|r| return r.values.first}
|
|
203
203
|
when Array
|
|
204
|
+
# :nocov:
|
|
204
205
|
case prepared_type[0]
|
|
206
|
+
# :nocov:
|
|
205
207
|
when :map, :as_hash, :to_hash, :to_hash_groups
|
|
206
208
|
public_send(*prepared_type, &block)
|
|
207
209
|
end
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Sequel
|
|
|
22
22
|
def insert_sql(*values)
|
|
23
23
|
return static_sql(@opts[:sql]) if @opts[:sql]
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
check_insert_allowed!
|
|
26
26
|
|
|
27
27
|
columns = []
|
|
28
28
|
|
|
@@ -172,7 +172,7 @@ module Sequel
|
|
|
172
172
|
# than one table.
|
|
173
173
|
def update_sql(values = OPTS)
|
|
174
174
|
return static_sql(opts[:sql]) if opts[:sql]
|
|
175
|
-
|
|
175
|
+
check_update_allowed!
|
|
176
176
|
check_not_limited!(:update)
|
|
177
177
|
|
|
178
178
|
case values
|
|
@@ -215,7 +215,7 @@ module Sequel
|
|
|
215
215
|
lines << "def #{'_' if priv}#{type}_sql"
|
|
216
216
|
lines << 'if sql = opts[:sql]; return static_sql(sql) end' unless priv
|
|
217
217
|
lines << "if sql = cache_get(:_#{type}_sql); return sql end" if cacheable
|
|
218
|
-
lines << '
|
|
218
|
+
lines << 'check_delete_allowed!' << 'check_not_limited!(:delete)' if type == :delete
|
|
219
219
|
lines << 'sql = @opts[:append_sql] || sql_string_origin'
|
|
220
220
|
|
|
221
221
|
if clauses.all?{|c| c.is_a?(Array)}
|
|
@@ -918,10 +918,35 @@ module Sequel
|
|
|
918
918
|
!@opts[:no_cache_sql] && !cache_get(:_no_cache_sql)
|
|
919
919
|
end
|
|
920
920
|
|
|
921
|
-
# Raise an InvalidOperation exception if
|
|
921
|
+
# Raise an InvalidOperation exception if modification is not allowed for this dataset.
|
|
922
|
+
# Check whether it is allowed to insert into this dataset.
|
|
923
|
+
# Only for backwards compatibility with older external adapters.
|
|
922
924
|
def check_modification_allowed!
|
|
925
|
+
# SEQUEL6: Remove
|
|
926
|
+
Sequel::Deprecation.deprecate("Dataset#check_modification_allowed!", "Use check_{insert,delete,update,truncation}_allowed! instead")
|
|
927
|
+
_check_modification_allowed!(supports_modifying_joins?)
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
# Check whether it is allowed to insert into this dataset.
|
|
931
|
+
def check_insert_allowed!
|
|
932
|
+
_check_modification_allowed!(false)
|
|
933
|
+
end
|
|
934
|
+
alias check_truncation_allowed! check_insert_allowed!
|
|
935
|
+
|
|
936
|
+
# Check whether it is allowed to delete from this dataset.
|
|
937
|
+
def check_delete_allowed!
|
|
938
|
+
_check_modification_allowed!(supports_deleting_joins?)
|
|
939
|
+
end
|
|
940
|
+
|
|
941
|
+
# Check whether it is allowed to update this dataset.
|
|
942
|
+
def check_update_allowed!
|
|
943
|
+
_check_modification_allowed!(supports_updating_joins?)
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
# Internals of the check_*_allowed! methods
|
|
947
|
+
def _check_modification_allowed!(modifying_joins_supported)
|
|
923
948
|
raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
|
|
924
|
-
raise(InvalidOperation, "Joined datasets cannot be modified") if !
|
|
949
|
+
raise(InvalidOperation, "Joined datasets cannot be modified") if !modifying_joins_supported && joined_dataset?
|
|
925
950
|
end
|
|
926
951
|
|
|
927
952
|
# Raise error if the dataset uses limits or offsets.
|
|
@@ -930,11 +955,6 @@ module Sequel
|
|
|
930
955
|
raise InvalidOperation, "Dataset##{type} not supported on datasets with limits or offsets" if opts[:limit] || opts[:offset]
|
|
931
956
|
end
|
|
932
957
|
|
|
933
|
-
# Alias of check_modification_allowed!
|
|
934
|
-
def check_truncation_allowed!
|
|
935
|
-
check_modification_allowed!
|
|
936
|
-
end
|
|
937
|
-
|
|
938
958
|
# Append column list to SQL string.
|
|
939
959
|
# If the column list is empty, a wildcard (*) is appended.
|
|
940
960
|
def column_list_append(sql, columns)
|
|
@@ -971,7 +991,9 @@ module Sequel
|
|
|
971
991
|
# operators unsupported by some databases. Used by adapters for databases
|
|
972
992
|
# that don't support the operators natively.
|
|
973
993
|
def complex_expression_emulate_append(sql, op, args)
|
|
994
|
+
# :nocov:
|
|
974
995
|
case op
|
|
996
|
+
# :nocov:
|
|
975
997
|
when :%
|
|
976
998
|
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:MOD, a, b)}
|
|
977
999
|
when :>>
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
#
|
|
7
7
|
# Sequel.extension :blank
|
|
8
8
|
|
|
9
|
+
[FalseClass, Object, NilClass, Numeric, String, TrueClass].each do |klass|
|
|
10
|
+
if klass.method_defined?(:blank?)
|
|
11
|
+
klass.send(:alias_method, :blank?, :blank?)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
9
15
|
class FalseClass
|
|
10
16
|
# false is always blank
|
|
11
17
|
def blank?
|
|
@@ -49,7 +49,10 @@ module Sequel
|
|
|
49
49
|
# Options:
|
|
50
50
|
# :cast :: Cast to the specified type instead of the default if casting
|
|
51
51
|
def date_sub(expr, interval, opts=OPTS)
|
|
52
|
-
|
|
52
|
+
if defined?(ActiveSupport::Duration) && interval.is_a?(ActiveSupport::Duration)
|
|
53
|
+
interval = interval.parts
|
|
54
|
+
end
|
|
55
|
+
interval = if interval.is_a?(Enumerable)
|
|
53
56
|
h = {}
|
|
54
57
|
interval.each{|k,v| h[k] = -v unless v.nil?}
|
|
55
58
|
h
|
|
@@ -113,12 +116,12 @@ module Sequel
|
|
|
113
116
|
end
|
|
114
117
|
when :mssql, :h2, :access, :sqlanywhere
|
|
115
118
|
units = case db_type
|
|
116
|
-
when :mssql, :sqlanywhere
|
|
117
|
-
MSSQL_DURATION_UNITS
|
|
118
119
|
when :h2
|
|
119
120
|
H2_DURATION_UNITS
|
|
120
121
|
when :access
|
|
121
122
|
ACCESS_DURATION_UNITS
|
|
123
|
+
else
|
|
124
|
+
MSSQL_DURATION_UNITS
|
|
122
125
|
end
|
|
123
126
|
each_valid_interval_unit(h, units) do |value, sql_unit|
|
|
124
127
|
expr = Sequel.function(:DATEADD, sql_unit, value, expr)
|
|
@@ -105,6 +105,12 @@ class String
|
|
|
105
105
|
yield Inflections if block_given?
|
|
106
106
|
Inflections
|
|
107
107
|
end
|
|
108
|
+
|
|
109
|
+
%w'classify constantize dasherize demodulize foreign_key humanize pluralize singularize tableize underscore'.each do |m|
|
|
110
|
+
if method_defined?(m)
|
|
111
|
+
alias_method(m, m)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
108
114
|
|
|
109
115
|
# By default, camelize converts the string to UpperCamelCase. If the argument to camelize
|
|
110
116
|
# is set to :lower then camelize produces lowerCamelCase.
|
|
@@ -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:
|
|
@@ -537,6 +541,11 @@ module Sequel
|
|
|
537
541
|
end
|
|
538
542
|
|
|
539
543
|
@direction = current < target ? :up : :down
|
|
544
|
+
|
|
545
|
+
if @direction == :down && @current >= @files.length
|
|
546
|
+
raise Migrator::Error, "Missing migration version(s) needed to migrate down to target version (current: #{current}, target: #{target})"
|
|
547
|
+
end
|
|
548
|
+
|
|
540
549
|
@migrations = get_migrations
|
|
541
550
|
end
|
|
542
551
|
|
|
@@ -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
|
|
@@ -34,6 +34,13 @@
|
|
|
34
34
|
|
|
35
35
|
require 'active_support/duration'
|
|
36
36
|
|
|
37
|
+
# :nocov:
|
|
38
|
+
begin
|
|
39
|
+
require 'active_support/version'
|
|
40
|
+
rescue LoadError
|
|
41
|
+
end
|
|
42
|
+
# :nocov:
|
|
43
|
+
|
|
37
44
|
module Sequel
|
|
38
45
|
module Postgres
|
|
39
46
|
module IntervalDatabaseMethods
|
|
@@ -61,34 +68,37 @@ module Sequel
|
|
|
61
68
|
|
|
62
69
|
# Creates callable objects that convert strings into ActiveSupport::Duration instances.
|
|
63
70
|
class Parser
|
|
71
|
+
# Whether ActiveSupport::Duration.new takes parts as array instead of hash
|
|
72
|
+
USE_PARTS_ARRAY = !defined?(ActiveSupport::VERSION::STRING) || ActiveSupport::VERSION::STRING < '5.1'
|
|
73
|
+
|
|
64
74
|
# Parse the interval input string into an ActiveSupport::Duration instance.
|
|
65
75
|
def call(string)
|
|
66
76
|
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
77
|
|
|
68
78
|
value = 0
|
|
69
|
-
parts =
|
|
79
|
+
parts = {}
|
|
70
80
|
|
|
71
81
|
if v = matches[1]
|
|
72
82
|
v = v.to_i
|
|
73
83
|
value += 31557600 * v
|
|
74
|
-
parts
|
|
84
|
+
parts[:years] = v
|
|
75
85
|
end
|
|
76
86
|
if v = matches[2]
|
|
77
87
|
v = v.to_i
|
|
78
88
|
value += 2592000 * v
|
|
79
|
-
parts
|
|
89
|
+
parts[:months] = v
|
|
80
90
|
end
|
|
81
91
|
if v = matches[3]
|
|
82
92
|
v = v.to_i
|
|
83
93
|
value += 86400 * v
|
|
84
|
-
parts
|
|
94
|
+
parts[:days] = v
|
|
85
95
|
end
|
|
86
96
|
if matches[5]
|
|
87
97
|
seconds = matches[5].to_i * 3600 + matches[6].to_i * 60
|
|
88
98
|
seconds += matches[8] ? matches[7].to_f : matches[7].to_i
|
|
89
99
|
seconds *= -1 if matches[4] == '-'
|
|
90
100
|
value += seconds
|
|
91
|
-
parts
|
|
101
|
+
parts[:seconds] = seconds
|
|
92
102
|
elsif matches[9] || matches[10] || matches[11]
|
|
93
103
|
seconds = 0
|
|
94
104
|
if v = matches[9]
|
|
@@ -101,8 +111,14 @@ module Sequel
|
|
|
101
111
|
seconds += matches[12] ? v.to_f : v.to_i
|
|
102
112
|
end
|
|
103
113
|
value += seconds
|
|
104
|
-
parts
|
|
114
|
+
parts[:seconds] = seconds
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# :nocov:
|
|
118
|
+
if USE_PARTS_ARRAY
|
|
119
|
+
parts = parts.to_a
|
|
105
120
|
end
|
|
121
|
+
# :nocov:
|
|
106
122
|
|
|
107
123
|
ActiveSupport::Duration.new(value, parts)
|
|
108
124
|
end
|
|
@@ -73,7 +73,10 @@
|
|
|
73
73
|
# j.pretty # jsonb_pretty(jsonb_column)
|
|
74
74
|
# j.set(%w'0 a', :h) # jsonb_set(jsonb_column, ARRAY['0','a'], h, true)
|
|
75
75
|
#
|
|
76
|
-
#
|
|
76
|
+
# j.set_lax(%w'0 a', :h, false, 'raise_exception')
|
|
77
|
+
# # jsonb_set_lax(jsonb_column, ARRAY['0','a'], h, false, 'raise_exception')
|
|
78
|
+
#
|
|
79
|
+
# On PostgreSQL 12+ SQL/JSON path functions and operators are supported:
|
|
77
80
|
#
|
|
78
81
|
# j.path_exists('$.foo') # (jsonb_column @? '$.foo')
|
|
79
82
|
# j.path_match('$.foo') # (jsonb_column @@ '$.foo')
|
|
@@ -84,7 +87,15 @@
|
|
|
84
87
|
# j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo')
|
|
85
88
|
# j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo')
|
|
86
89
|
#
|
|
87
|
-
#
|
|
90
|
+
# On PostgreSQL 13+ timezone-aware SQL/JSON path functions and operators are supported:
|
|
91
|
+
#
|
|
92
|
+
# j.path_exists_tz!('$.foo') # jsonb_path_exists_tz(jsonb_column, '$.foo')
|
|
93
|
+
# j.path_match_tz!('$.foo') # jsonb_path_match_tz(jsonb_column, '$.foo')
|
|
94
|
+
# j.path_query_tz('$.foo') # jsonb_path_query_tz(jsonb_column, '$.foo')
|
|
95
|
+
# j.path_query_array_tz('$.foo') # jsonb_path_query_array_tz(jsonb_column, '$.foo')
|
|
96
|
+
# j.path_query_first_tz('$.foo') # jsonb_path_query_first_tz(jsonb_column, '$.foo')
|
|
97
|
+
#
|
|
98
|
+
# For the PostgreSQL 12+ SQL/JSON path functions, one argument is required (+path+) and
|
|
88
99
|
# two more arguments are optional (+vars+ and +silent+). +path+ specifies the JSON path.
|
|
89
100
|
# +vars+ specifies a hash or a string in JSON format of named variables to be
|
|
90
101
|
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
|
@@ -402,6 +413,11 @@ module Sequel
|
|
|
402
413
|
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists, path, vars, silent))
|
|
403
414
|
end
|
|
404
415
|
|
|
416
|
+
# The same as #path_exists!, except that timezone-aware conversions are used for date/time values.
|
|
417
|
+
def path_exists_tz!(path, vars=nil, silent=nil)
|
|
418
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists_tz, path, vars, silent))
|
|
419
|
+
end
|
|
420
|
+
|
|
405
421
|
# Returns the first item of the result of JSON path predicate check for the json object.
|
|
406
422
|
# Returns nil if the first item is not true or false.
|
|
407
423
|
#
|
|
@@ -425,6 +441,11 @@ module Sequel
|
|
|
425
441
|
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match, path, vars, silent))
|
|
426
442
|
end
|
|
427
443
|
|
|
444
|
+
# The same as #path_match!, except that timezone-aware conversions are used for date/time values.
|
|
445
|
+
def path_match_tz!(path, vars=nil, silent=nil)
|
|
446
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match_tz, path, vars, silent))
|
|
447
|
+
end
|
|
448
|
+
|
|
428
449
|
# Returns a set of all jsonb values specified by the JSON path
|
|
429
450
|
# for the json object.
|
|
430
451
|
#
|
|
@@ -440,6 +461,11 @@ module Sequel
|
|
|
440
461
|
_path_function(:jsonb_path_query, path, vars, silent)
|
|
441
462
|
end
|
|
442
463
|
|
|
464
|
+
# The same as #path_query, except that timezone-aware conversions are used for date/time values.
|
|
465
|
+
def path_query_tz(path, vars=nil, silent=nil)
|
|
466
|
+
_path_function(:jsonb_path_query_tz, path, vars, silent)
|
|
467
|
+
end
|
|
468
|
+
|
|
443
469
|
# Returns a jsonb array of all values specified by the JSON path
|
|
444
470
|
# for the json object.
|
|
445
471
|
#
|
|
@@ -455,6 +481,11 @@ module Sequel
|
|
|
455
481
|
JSONBOp.new(_path_function(:jsonb_path_query_array, path, vars, silent))
|
|
456
482
|
end
|
|
457
483
|
|
|
484
|
+
# The same as #path_query_array, except that timezone-aware conversions are used for date/time values.
|
|
485
|
+
def path_query_array_tz(path, vars=nil, silent=nil)
|
|
486
|
+
JSONBOp.new(_path_function(:jsonb_path_query_array_tz, path, vars, silent))
|
|
487
|
+
end
|
|
488
|
+
|
|
458
489
|
# Returns the first item of the result specified by the JSON path
|
|
459
490
|
# for the json object.
|
|
460
491
|
#
|
|
@@ -470,6 +501,11 @@ module Sequel
|
|
|
470
501
|
JSONBOp.new(_path_function(:jsonb_path_query_first, path, vars, silent))
|
|
471
502
|
end
|
|
472
503
|
|
|
504
|
+
# The same as #path_query_first, except that timezone-aware conversions are used for date/time values.
|
|
505
|
+
def path_query_first_tz(path, vars=nil, silent=nil)
|
|
506
|
+
JSONBOp.new(_path_function(:jsonb_path_query_first_tz, path, vars, silent))
|
|
507
|
+
end
|
|
508
|
+
|
|
473
509
|
# Return the receiver, since it is already a JSONBOp.
|
|
474
510
|
def pg_jsonb
|
|
475
511
|
self
|
|
@@ -492,6 +528,12 @@ module Sequel
|
|
|
492
528
|
self.class.new(function(:set, wrap_input_array(path), wrap_input_jsonb(other), create_missing))
|
|
493
529
|
end
|
|
494
530
|
|
|
531
|
+
# The same as #set, except if +other+ is +nil+, then behaves according to +null_value_treatment+,
|
|
532
|
+
# which can be one of 'raise_exception', 'use_json_null' (default), 'delete_key', or 'return_target'.
|
|
533
|
+
def set_lax(path, other, create_missing=true, null_value_treatment='use_json_null')
|
|
534
|
+
self.class.new(function(:set_lax, wrap_input_array(path), wrap_input_jsonb(other), create_missing, null_value_treatment))
|
|
535
|
+
end
|
|
536
|
+
|
|
495
537
|
private
|
|
496
538
|
|
|
497
539
|
# Internals of the jsonb SQL/JSON path functions.
|