sequel 5.60.1 → 5.62.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +44 -0
  3. data/README.rdoc +20 -19
  4. data/doc/advanced_associations.rdoc +13 -13
  5. data/doc/association_basics.rdoc +21 -15
  6. data/doc/cheat_sheet.rdoc +3 -3
  7. data/doc/model_hooks.rdoc +1 -1
  8. data/doc/object_model.rdoc +8 -8
  9. data/doc/opening_databases.rdoc +4 -4
  10. data/doc/postgresql.rdoc +8 -8
  11. data/doc/querying.rdoc +1 -1
  12. data/doc/release_notes/5.61.0.txt +43 -0
  13. data/doc/release_notes/5.62.0.txt +132 -0
  14. data/doc/schema_modification.rdoc +1 -1
  15. data/doc/security.rdoc +9 -9
  16. data/doc/sql.rdoc +13 -13
  17. data/doc/testing.rdoc +13 -11
  18. data/doc/transactions.rdoc +6 -6
  19. data/doc/virtual_rows.rdoc +1 -1
  20. data/lib/sequel/adapters/postgres.rb +4 -0
  21. data/lib/sequel/adapters/shared/access.rb +9 -1
  22. data/lib/sequel/adapters/shared/mssql.rb +9 -5
  23. data/lib/sequel/adapters/shared/mysql.rb +7 -0
  24. data/lib/sequel/adapters/shared/oracle.rb +7 -0
  25. data/lib/sequel/adapters/shared/postgres.rb +275 -152
  26. data/lib/sequel/adapters/shared/sqlanywhere.rb +7 -0
  27. data/lib/sequel/adapters/shared/sqlite.rb +5 -0
  28. data/lib/sequel/connection_pool.rb +42 -28
  29. data/lib/sequel/database/connecting.rb +24 -0
  30. data/lib/sequel/database/misc.rb +62 -12
  31. data/lib/sequel/database/query.rb +37 -0
  32. data/lib/sequel/dataset/actions.rb +31 -11
  33. data/lib/sequel/dataset/features.rb +5 -0
  34. data/lib/sequel/dataset/misc.rb +1 -1
  35. data/lib/sequel/dataset/query.rb +9 -9
  36. data/lib/sequel/dataset/sql.rb +5 -1
  37. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  38. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  39. data/lib/sequel/extensions/async_thread_pool.rb +11 -11
  40. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  41. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  42. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  43. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  44. data/lib/sequel/extensions/migration.rb +1 -1
  45. data/lib/sequel/extensions/named_timezones.rb +17 -5
  46. data/lib/sequel/extensions/pg_array.rb +22 -3
  47. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  48. data/lib/sequel/extensions/pg_extended_date_support.rb +27 -24
  49. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  50. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  51. data/lib/sequel/extensions/pg_inet.rb +10 -11
  52. data/lib/sequel/extensions/pg_interval.rb +10 -11
  53. data/lib/sequel/extensions/pg_json.rb +10 -10
  54. data/lib/sequel/extensions/pg_json_ops.rb +0 -52
  55. data/lib/sequel/extensions/pg_multirange.rb +5 -10
  56. data/lib/sequel/extensions/pg_range.rb +6 -11
  57. data/lib/sequel/extensions/pg_row.rb +18 -13
  58. data/lib/sequel/model/associations.rb +7 -2
  59. data/lib/sequel/model/base.rb +6 -5
  60. data/lib/sequel/plugins/auto_validations.rb +53 -15
  61. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  62. data/lib/sequel/plugins/composition.rb +2 -2
  63. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  64. data/lib/sequel/plugins/dirty.rb +1 -1
  65. data/lib/sequel/plugins/finder.rb +3 -1
  66. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  67. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
  68. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  69. data/lib/sequel/plugins/sql_comments.rb +1 -1
  70. data/lib/sequel/plugins/validation_helpers.rb +20 -0
  71. data/lib/sequel/version.rb +2 -2
  72. metadata +12 -5
@@ -104,7 +104,7 @@ module Sequel
104
104
  # should reference the subquery alias (and qualified identifiers should not be needed
105
105
  # unless joining to another table):
106
106
  #
107
- # a = Executive.where(:id=>1).first # works
107
+ # a = Executive.where(id: 1).first # works
108
108
  # a = Executive.where{{employees[:id]=>1}}.first # works
109
109
  # a = Executive.where{{executives[:id]=>1}}.first # doesn't work
110
110
  #
@@ -115,7 +115,7 @@ module Sequel
115
115
  #
116
116
  # pks = Executive.where{num_staff < 10}.select_map(:id)
117
117
  # Executive.cti_tables.reverse_each do |table|
118
- # DB.from(table).where(:id=>pks).delete
118
+ # DB.from(table).where(id: pks).delete
119
119
  # end
120
120
  #
121
121
  # = Usage
@@ -39,8 +39,8 @@ module Sequel
39
39
  # also be implemented as:
40
40
  #
41
41
  # Album.composition :date,
42
- # :composer=>proc{Date.new(year, month, day) if year || month || day},
43
- # :decomposer=>(proc do
42
+ # composer: proc{Date.new(year, month, day) if year || month || day},
43
+ # decomposer: (proc do
44
44
  # if d = compositions[:date]
45
45
  # self.year = d.year
46
46
  # self.month = d.month
@@ -50,10 +50,10 @@ module Sequel
50
50
  # support concurrent eager loading. Taking this example from the
51
51
  # {Advanced Associations guide}[rdoc-ref:doc/advanced_associations.rdoc]
52
52
  #
53
- # Album.many_to_one :artist, :eager_loader=>(proc do |eo_opts|
53
+ # Album.many_to_one :artist, eager_loader: (proc do |eo_opts|
54
54
  # eo_opts[:rows].each{|album| album.associations[:artist] = nil}
55
55
  # id_map = eo_opts[:id_map]
56
- # Artist.where(:id=>id_map.keys).all do |artist|
56
+ # Artist.where(id: id_map.keys).all do |artist|
57
57
  # if albums = id_map[artist.id]
58
58
  # albums.each do |album|
59
59
  # album.associations[:artist] = artist
@@ -74,12 +74,12 @@ module Sequel
74
74
  # the code that loads the objects, since that will prevent concurrent loading.
75
75
  # So after the changes, the custom eager loader would look like this:
76
76
  #
77
- # Album.many_to_one :artist, :eager_loader=>(proc do |eo_opts|
77
+ # Album.many_to_one :artist, eager_loader: (proc do |eo_opts|
78
78
  # Sequel.synchronize_with(eo[:mutex]) do
79
79
  # eo_opts[:rows].each{|album| album.associations[:artist] = nil}
80
80
  # end
81
81
  # id_map = eo_opts[:id_map]
82
- # rows = Artist.where(:id=>id_map.keys).all
82
+ # rows = Artist.where(id: id_map.keys).all
83
83
  # Sequel.synchronize_with(eo[:mutex]) do
84
84
  # rows.each do |artist|
85
85
  # if albums = id_map[artist.id]
@@ -37,7 +37,7 @@ module Sequel
37
37
  #
38
38
  # It also saves the previously changed values after an update:
39
39
  #
40
- # artist.update(:name=>'Bar')
40
+ # artist.update(name: 'Bar')
41
41
  # artist.column_changes # => {}
42
42
  # artist.previous_changes # => {:name=>['Foo', 'Bar']}
43
43
  #
@@ -97,7 +97,9 @@ module Sequel
97
97
  # Artist.first_by_name(nil)
98
98
  # # WHERE (name IS NULL)
99
99
  #
100
- # See Dataset::PlaceholderLiteralizer for additional caveats.
100
+ # See Dataset::PlaceholderLiteralizer for additional caveats. Note that if the model's
101
+ # dataset does not support placeholder literalizers, you will not be able to use this
102
+ # method.
101
103
  def finder(meth=OPTS, opts=OPTS, &block)
102
104
  if block
103
105
  raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash)
@@ -33,7 +33,7 @@ module Sequel
33
33
  # objects. You just need to make sure that the primary key field is filled in for the
34
34
  # associated object:
35
35
  #
36
- # a.update(:albums_attributes => [{id: 1, name: 'T'}])
36
+ # a.update(albums_attributes: [{id: 1, name: 'T'}])
37
37
  #
38
38
  # Since the primary key field is filled in, the plugin will update the album with id 1 instead
39
39
  # of creating a new album.
@@ -42,14 +42,14 @@ module Sequel
42
42
  # entry to the hash, and also pass the :destroy option when calling +nested_attributes+:
43
43
  #
44
44
  # Artist.nested_attributes :albums, destroy: true
45
- # a.update(:albums_attributes => [{id: 1, _delete: true}])
45
+ # a.update(albums_attributes: [{id: 1, _delete: true}])
46
46
  #
47
47
  # This will delete the related associated object from the database. If you want to leave the
48
48
  # associated object in the database, but just remove it from the association, add a _remove
49
49
  # entry in the hash, and also pass the :remove option when calling +nested_attributes+:
50
50
  #
51
51
  # Artist.nested_attributes :albums, remove: true
52
- # a.update(:albums_attributes => [{id: 1, _remove: true}])
52
+ # a.update(albums_attributes: [{id: 1, _remove: true}])
53
53
  #
54
54
  # The above example was for a one_to_many association, but the plugin also works similarly
55
55
  # for other association types. For one_to_one and many_to_one associations, you need to
@@ -84,7 +84,7 @@ module Sequel
84
84
  # nested attributes options for that association. This is useful for per-call filtering
85
85
  # of the allowed fields:
86
86
  #
87
- # a.set_nested_attributes(:albums, params['artist'], :fields=>%w'name')
87
+ # a.set_nested_attributes(:albums, params['artist'], fields: %w'name')
88
88
  module NestedAttributes
89
89
  # Depend on the validate_associated plugin.
90
90
  def self.apply(model)
@@ -32,7 +32,7 @@ module Sequel
32
32
  #
33
33
  # Example:
34
34
  #
35
- # album = Album.new(:artist_id=>1) # Assume no such artist exists
35
+ # album = Album.new(artist_id: 1) # Assume no such artist exists
36
36
  # begin
37
37
  # album.save
38
38
  # rescue Sequel::ValidationFailed
@@ -0,0 +1,154 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The primary_key_lookup_check_values plugin typecasts given primary key
6
+ # values before performing a lookup by primary key. If the given primary
7
+ # key value cannot be typecasted correctly, the lookup returns nil
8
+ # without issuing a query. If the schema for the primary key column
9
+ # includes minimum and maximum values, this also checks the given value
10
+ # is not outside the range. If the given value is outside the allowed
11
+ # range, the lookup returns nil without issuing a query.
12
+ #
13
+ # This affects the following Model methods:
14
+ #
15
+ # * Model.[] (when called with non-Hash)
16
+ # * Model.with_pk
17
+ # * Model.with_pk!
18
+ #
19
+ # It also affects the following Model dataset methods:
20
+ #
21
+ # * Dataset#[] (when called with Integer)
22
+ # * Dataset#with_pk
23
+ # * dataset#with_pk!
24
+ #
25
+ # Note that this can break working code. The above methods accept
26
+ # any filter condition by default, not just primary key values. The
27
+ # plugin will handle Symbol, Sequel::SQL::Expression, and
28
+ # Sequel::LiteralString objects, but code such as the following will break:
29
+ #
30
+ # # Return first Album where primary key is one of the given values
31
+ # Album.dataset.with_pk([1, 2, 3])
32
+ #
33
+ # Usage:
34
+ #
35
+ # # Make all model subclasses support checking primary key values before
36
+ # # lookup # (called before loading subclasses)
37
+ # Sequel::Model.plugin :primary_key_lookup_check_values
38
+ #
39
+ # # Make the Album class support checking primary key values before lookup
40
+ # Album.plugin :primary_key_lookup_check_values
41
+ module PrimaryKeyLookupCheckValues
42
+ def self.configure(model)
43
+ model.instance_exec do
44
+ setup_primary_key_lookup_check_values if @dataset
45
+ end
46
+ end
47
+
48
+ module ClassMethods
49
+ Plugins.after_set_dataset(self, :setup_primary_key_lookup_check_values)
50
+
51
+ Plugins.inherited_instance_variables(self,
52
+ :@primary_key_type=>nil,
53
+ :@primary_key_value_range=>nil)
54
+
55
+ private
56
+
57
+ # Check the given primary key value. Typecast it to the appropriate
58
+ # database type if the database type is known. If it cannot be
59
+ # typecasted, or the typecasted value is outside the range of column
60
+ # values, return nil.
61
+ def _check_pk_lookup_value(pk)
62
+ return if nil == pk
63
+ case pk
64
+ when SQL::Expression, LiteralString, Symbol
65
+ return pk
66
+ end
67
+ return pk unless pk_type = @primary_key_type
68
+
69
+ if pk_type.is_a?(Array)
70
+ return unless pk.is_a?(Array)
71
+ return unless pk.size == pk_type.size
72
+ return if pk.any?(&:nil?)
73
+
74
+ pk_value_range = @primary_key_value_range
75
+ i = 0
76
+ pk.map do |v|
77
+ if type = pk_type[i]
78
+ v = _typecast_pk_lookup_value(v, type)
79
+ return if nil == v
80
+ if pk_value_range
81
+ min, max = pk_value_range[i]
82
+ return if min && v < min
83
+ return if max && v > max
84
+ end
85
+ end
86
+ i += 1
87
+ v
88
+ end
89
+ elsif pk.is_a?(Array)
90
+ return
91
+ elsif nil != (pk = _typecast_pk_lookup_value(pk, pk_type))
92
+ min, max = @primary_key_value_range
93
+ return if min && pk < min
94
+ return if max && pk > max
95
+ pk
96
+ end
97
+ end
98
+
99
+ # Typecast the value to the appropriate type,
100
+ # returning nil if it cannot be typecasted.
101
+ def _typecast_pk_lookup_value(value, type)
102
+ db.typecast_value(type, value)
103
+ rescue InvalidValue
104
+ nil
105
+ end
106
+
107
+ # Skip the primary key lookup if the typecasted and checked
108
+ # primary key value is nil.
109
+ def primary_key_lookup(pk)
110
+ unless nil == (pk = _check_pk_lookup_value(pk))
111
+ super
112
+ end
113
+ end
114
+
115
+ # Setup the primary key type and value range used for checking
116
+ # primary key values during lookup.
117
+ def setup_primary_key_lookup_check_values
118
+ if primary_key.is_a?(Array)
119
+ types = []
120
+ value_ranges = []
121
+ primary_key.each do |pk|
122
+ type, min, max = _type_min_max_values_for_column(pk)
123
+ types << type
124
+ value_ranges << ([min, max].freeze if min || max)
125
+ end
126
+ @primary_key_type = (types.freeze if types.any?)
127
+ @primary_key_value_range = (value_ranges.freeze if @primary_key_type && value_ranges.any?)
128
+ else
129
+ @primary_key_type, min, max = _type_min_max_values_for_column(primary_key)
130
+ @primary_key_value_range = ([min, max].freeze if @primary_key_type && (min || max))
131
+ end
132
+ end
133
+
134
+ # Return the type, min_value, and max_value schema entries
135
+ # for the column, if they exist.
136
+ def _type_min_max_values_for_column(column)
137
+ if schema = db_schema[column]
138
+ schema.values_at(:type, :min_value, :max_value)
139
+ end
140
+ end
141
+ end
142
+
143
+ module DatasetMethods
144
+ # Skip the primary key lookup if the typecasted and checked
145
+ # primary key value is nil.
146
+ def with_pk(pk)
147
+ unless nil == (pk = model.send(:_check_pk_lookup_value, pk))
148
+ super
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -12,7 +12,7 @@ module Sequel
12
12
  # # SELECT * FROM albums WHERE (id = 1) LIMIT 1
13
13
  # # -- model:Album,method_type:class,method:[]
14
14
  #
15
- # album.update(:name=>'A')
15
+ # album.update(name: 'A')
16
16
  # # UPDATE albums SET name = 'baz' WHERE (id = 1)
17
17
  # # -- model:Album,method_type:instance,method:update
18
18
  #
@@ -83,7 +83,9 @@ module Sequel
83
83
  :integer=>{:message=>lambda{"is not a number"}},
84
84
  :length_range=>{:message=>lambda{|range| "is too short or too long"}},
85
85
  :max_length=>{:message=>lambda{|max| "is longer than #{max} characters"}, :nil_message=>lambda{"is not present"}},
86
+ :max_value=>{:message=>lambda{|max| "is greater than maximum allowed value"}},
86
87
  :min_length=>{:message=>lambda{|min| "is shorter than #{min} characters"}},
88
+ :min_value=>{:message=>lambda{|min| "is less than minimum allowed value"}},
87
89
  :not_null=>{:message=>lambda{"is not present"}},
88
90
  :no_null_byte=>{:message=>lambda{"contains a null byte"}},
89
91
  :numeric=>{:message=>lambda{"is not a number"}},
@@ -141,11 +143,29 @@ module Sequel
141
143
  end
142
144
  end
143
145
 
146
+ # Check that the attribute values are not greater that the given maximum value.
147
+ # Does not perform validation if attribute value is nil.
148
+ # You should only call this if you have checked the attribute value has the expected type.
149
+ def validates_max_value(max, atts, opts=OPTS)
150
+ validatable_attributes_for_type(:max_value, atts, opts) do |a,v,m|
151
+ validation_error_message(m, max) if !v.nil? && v > max
152
+ end
153
+ end
154
+
144
155
  # Check that the attribute values are not shorter than the given min length.
145
156
  def validates_min_length(min, atts, opts=OPTS)
146
157
  validatable_attributes_for_type(:min_length, atts, opts){|a,v,m| validation_error_message(m, min) if v.nil? || v.length < min}
147
158
  end
148
159
 
160
+ # Check that the attribute values are not less that the given minimum value.
161
+ # Does not perform validation if attribute value is nil.
162
+ # You should only call this if you have checked the attribute value has the expected type.
163
+ def validates_min_value(min, atts, opts=OPTS)
164
+ validatable_attributes_for_type(:min_value, atts, opts) do |a,v,m|
165
+ validation_error_message(m, min) if !v.nil? && v < min
166
+ end
167
+ end
168
+
149
169
  # Check attribute value(s) are not NULL/nil.
150
170
  def validates_not_null(atts, opts=OPTS)
151
171
  validatable_attributes_for_type(:not_null, atts, opts){|a,v,m| validation_error_message(m) if v.nil?}
@@ -6,11 +6,11 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 60
9
+ MINOR = 62
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
13
- TINY = 1
13
+ TINY = 0
14
14
 
15
15
  # The version of Sequel you are using, as a string (e.g. "2.11.0")
16
16
  VERSION = [MAJOR, MINOR, TINY].join('.').freeze
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.60.1
4
+ version: 5.62.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-02 00:00:00.000000000 Z
11
+ date: 2022-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -192,6 +192,8 @@ extra_rdoc_files:
192
192
  - doc/release_notes/5.59.0.txt
193
193
  - doc/release_notes/5.6.0.txt
194
194
  - doc/release_notes/5.60.0.txt
195
+ - doc/release_notes/5.61.0.txt
196
+ - doc/release_notes/5.62.0.txt
195
197
  - doc/release_notes/5.7.0.txt
196
198
  - doc/release_notes/5.8.0.txt
197
199
  - doc/release_notes/5.9.0.txt
@@ -280,6 +282,8 @@ files:
280
282
  - doc/release_notes/5.59.0.txt
281
283
  - doc/release_notes/5.6.0.txt
282
284
  - doc/release_notes/5.60.0.txt
285
+ - doc/release_notes/5.61.0.txt
286
+ - doc/release_notes/5.62.0.txt
283
287
  - doc/release_notes/5.7.0.txt
284
288
  - doc/release_notes/5.8.0.txt
285
289
  - doc/release_notes/5.9.0.txt
@@ -418,8 +422,10 @@ files:
418
422
  - lib/sequel/extensions/pagination.rb
419
423
  - lib/sequel/extensions/pg_array.rb
420
424
  - lib/sequel/extensions/pg_array_ops.rb
425
+ - lib/sequel/extensions/pg_auto_parameterize.rb
421
426
  - lib/sequel/extensions/pg_enum.rb
422
427
  - lib/sequel/extensions/pg_extended_date_support.rb
428
+ - lib/sequel/extensions/pg_extended_integer_support.rb
423
429
  - lib/sequel/extensions/pg_hstore.rb
424
430
  - lib/sequel/extensions/pg_hstore_ops.rb
425
431
  - lib/sequel/extensions/pg_inet.rb
@@ -530,6 +536,7 @@ files:
530
536
  - lib/sequel/plugins/pg_row.rb
531
537
  - lib/sequel/plugins/prepared_statements.rb
532
538
  - lib/sequel/plugins/prepared_statements_safe.rb
539
+ - lib/sequel/plugins/primary_key_lookup_check_values.rb
533
540
  - lib/sequel/plugins/rcte_tree.rb
534
541
  - lib/sequel/plugins/require_valid_schema.rb
535
542
  - lib/sequel/plugins/serialization.rb
@@ -577,7 +584,7 @@ metadata:
577
584
  documentation_uri: https://sequel.jeremyevans.net/documentation.html
578
585
  mailing_list_uri: https://github.com/jeremyevans/sequel/discussions
579
586
  source_code_uri: https://github.com/jeremyevans/sequel
580
- post_install_message:
587
+ post_install_message:
581
588
  rdoc_options:
582
589
  - "--quiet"
583
590
  - "--line-numbers"
@@ -600,7 +607,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
600
607
  version: '0'
601
608
  requirements: []
602
609
  rubygems_version: 3.3.7
603
- signing_key:
610
+ signing_key:
604
611
  specification_version: 4
605
612
  summary: The Database Toolkit for Ruby
606
613
  test_files: []