sequel 5.60.1 → 5.62.0

Sign up to get free protection for your applications and to get access to all the features.
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: []