sequel 5.67.0 → 5.68.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff5af191af88ca381359503cfd9601ec8f18a9d782b68a3de4a8c9dac080eddc
4
- data.tar.gz: 786d20f7eac9325748c259130951643856e34051e64b02aa17493f6d0283eb52
3
+ metadata.gz: e682467c455e044847fffb313b6d8a669e0dc89ca8ea330468841de642b84227
4
+ data.tar.gz: cd0687fd059a6b6d38cb31279d5cdacccd4a0a309f1155171f12551d43eacb0b
5
5
  SHA512:
6
- metadata.gz: db251488ea0739af5ecf6db332a1cff729cffbf31dfabfc7235f3c27f807366bc9feddf147797e4dd5980348fda6a640cc5ceef7e6255cddcd317acb7172be90
7
- data.tar.gz: 45b0e24847ccb365f1d7d06d35c85f05718802375877ab3d0315deb952f72171e26b5676861c25cf1e840f831d216f99eaddcbb717fc752d75077f19ae13980c
6
+ metadata.gz: 3c16f027251aa67e04cab0973a57da21682e083c7362f37bdbf84937266ca6878e200fb7c44a0583b8ac6fab62d1fd4b55744929760fc63a9d1f850452db5df6
7
+ data.tar.gz: 60eee01297c47ee9c18ae76b6974b6eb3e492e58c7f408ee9c32476f1ae0ae5848ffc47cb80889f7070b02088e7e66509b64e460a1ef61c90ffb5a59da6a3c09
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ === 5.68.0 (2023-05-01)
2
+
3
+ * Add validation_helpers_generic_type_messages plugin for more useful type validation failure messages (jeremyevans) (#2028)
4
+
5
+ * Make constraint_validations plugin not validate missing columns that have a default value (jeremyevans) (#2023)
6
+
7
+ * Skip normal type name parsing for enum/array/composite/range/multirange types on PostgreSQL (jeremyevans) (#2019)
8
+
9
+ * Fix corner case where pg_extended_date_support did not work correctly when using the jdbc/postgresql adapter (jeremyevans)
10
+
11
+ * Include :min_value and :max_value schema entries for date/timestamp/timestamptz columns on PostgreSQL 9.6+ (jeremyevans)
12
+
1
13
  === 5.67.0 (2023-04-01)
2
14
 
3
15
  * Fix dumping of string column sizes in the schema dumper on MSSQL (jeremyevans) (#2013)
@@ -670,8 +670,10 @@ polymorphic associations in Sequel about as easy as it is in ActiveRecord. Howe
670
670
  here's how they can be done using Sequel's custom associations (the sequel_polymorphic
671
671
  external plugin is just a generic version of this code):
672
672
 
673
+ Sequel.extension :inflector # for attachable_type.constantize
674
+
673
675
  class Asset < Sequel::Model
674
- many_to_one :attachable, reciprocal: :assets,
676
+ many_to_one :attachable, reciprocal: :assets, reciprocal_type: :one_to_many,
675
677
  setter: (lambda do |attachable|
676
678
  self[:attachable_id] = (attachable.pk if attachable)
677
679
  self[:attachable_type] = (attachable.class.name if attachable)
@@ -0,0 +1,61 @@
1
+ = New Features
2
+
3
+ * On PostgreSQL 9.6+, date, timestamp, and timestamptz columns now
4
+ have min_value and max_value column schema entries, allowing the
5
+ auto_validations plugin to automatically enforce minimum and
6
+ maximum values for these column types, raising a validation error
7
+ before saving, instead of database error when the query is sent
8
+ to the database.
9
+
10
+ * A validation_helpers_generic_type_messages plugin has been added,
11
+ which improves the default type validation error messages in
12
+ validation_helpers. This change was not made directly to
13
+ validation_helpers for backwards compatibility reasons, but will
14
+ probably become the default behavior in Sequel 6. Some examples
15
+ of the improvements:
16
+
17
+ # :blob type
18
+ # validation_helpers default: "value is not a valid sequel::sql::blob"
19
+ # with this plugin: "value is not a blob"
20
+
21
+ # :boolean type
22
+ # validation_helpers default: "value is not a valid trueclass or falseclass"
23
+ # with this plugin: "value is not true or false"
24
+
25
+ # :datetime type
26
+ # validation_helpers default: "value is not a valid time or datetime"
27
+ # with this plugin: "value is not a valid timestamp"
28
+
29
+ # custom/database-specific types
30
+ # validation_helpers default: "value is not a valid sequel::class_name"
31
+ # with this plugin: "value is not the expected type"
32
+
33
+ = Improvements
34
+
35
+ * The constraint_validations plugin no longer raises validation
36
+ errors for missing columns that have a default value. If a column
37
+ is missing but has a default value, we can assume the default
38
+ value is valid. Additionally, the constraint validations are now
39
+ based on the underlying column value and not any deserialized
40
+ value, so that the validation matches the constraint.
41
+
42
+ * On PostgreSQL, normal type name parsing is skipped for enum,
43
+ array, composite, range, and multirange types, ensuring that
44
+ such types will not be treated incorrectly based on their
45
+ type name.
46
+
47
+ * The pg_extended_date_support extension now works correctly with
48
+ the jdbc/postgresql adapter if there is already an entry in the
49
+ oid_convertor_map for the timestamp and timestamptz types.
50
+
51
+ = Backwards Compatibility
52
+
53
+ * Database#schema_column_type is no longer called for all columns
54
+ on PostgreSQL when parsing schema. Enum, array, composite, range,
55
+ and multirange types each have their own separate type parsing
56
+ method. So if you were overriding Database#schema_column_type to
57
+ implement custom column schema parsing, you may need to adjust
58
+ your code.
59
+
60
+ * The Sequel::Postgres::ExtendedDateSupport::DATE_YEAR_1 constant
61
+ has been removed.
@@ -1007,11 +1007,15 @@ module Sequel
1007
1007
  SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1008
1008
  SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
1009
1009
  SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
1010
- SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
1010
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key),
1011
+ Sequel[:pg_type][:typtype],
1012
+ (~Sequel[Sequel[:elementtype][:oid]=>nil]).as(:is_array),
1013
+ ]}.
1011
1014
  from(:pg_class).
1012
1015
  join(:pg_attribute, :attrelid=>:oid).
1013
1016
  join(:pg_type, :oid=>:atttypid).
1014
1017
  left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
1018
+ left_outer_join(Sequel[:pg_type].as(:elementtype), :typarray=>Sequel[:pg_type][:oid]).
1015
1019
  left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
1016
1020
  left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
1017
1021
  where{{pg_attribute[:attisdropped]=>false}}.
@@ -1538,11 +1542,12 @@ module Sequel
1538
1542
  end
1539
1543
 
1540
1544
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
1541
- # a rename table operation, so speciying a new schema in new_name will not have an effect.
1545
+ # a rename table operation, so specifying a new schema in new_name will not have an effect.
1542
1546
  def rename_table_sql(name, new_name)
1543
1547
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
1544
1548
  end
1545
1549
 
1550
+ # Handle interval and citext types.
1546
1551
  def schema_column_type(db_type)
1547
1552
  case db_type
1548
1553
  when /\Ainterval\z/io
@@ -1554,6 +1559,43 @@ module Sequel
1554
1559
  end
1555
1560
  end
1556
1561
 
1562
+ # The schema :type entry to use for array types.
1563
+ def schema_array_type(db_type)
1564
+ :array
1565
+ end
1566
+
1567
+ # The schema :type entry to use for row/composite types.
1568
+ def schema_composite_type(db_type)
1569
+ :composite
1570
+ end
1571
+
1572
+ # The schema :type entry to use for enum types.
1573
+ def schema_enum_type(db_type)
1574
+ :enum
1575
+ end
1576
+
1577
+ # The schema :type entry to use for range types.
1578
+ def schema_range_type(db_type)
1579
+ :range
1580
+ end
1581
+
1582
+ # The schema :type entry to use for multirange types.
1583
+ def schema_multirange_type(db_type)
1584
+ :multirange
1585
+ end
1586
+
1587
+ MIN_DATE = Date.new(-4713, 11, 24)
1588
+ MAX_DATE = Date.new(5874897, 12, 31)
1589
+ MIN_TIMESTAMP = Time.utc(-4713, 11, 24).freeze
1590
+ MAX_TIMESTAMP = (Time.utc(294277) - Rational(1, 1000000)).freeze
1591
+ TYPTYPE_METHOD_MAP = {
1592
+ 'c' => :schema_composite_type,
1593
+ 'e' => :schema_enum_type,
1594
+ 'r' => :schema_range_type,
1595
+ 'm' => :schema_multirange_type,
1596
+ }
1597
+ TYPTYPE_METHOD_MAP.default = :schema_column_type
1598
+ TYPTYPE_METHOD_MAP.freeze
1557
1599
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
1558
1600
  def schema_parse_table(table_name, opts)
1559
1601
  m = output_identifier_meth(opts[:dataset])
@@ -1569,11 +1611,33 @@ module Sequel
1569
1611
  row.delete(:base_oid)
1570
1612
  row.delete(:db_base_type)
1571
1613
  end
1572
- row[:type] = schema_column_type(row[:db_type])
1614
+
1615
+ db_type = row[:db_type]
1616
+ row[:type] = if row.delete(:is_array)
1617
+ schema_array_type(db_type)
1618
+ else
1619
+ send(TYPTYPE_METHOD_MAP[row.delete(:typtype)], db_type)
1620
+ end
1573
1621
  identity = row.delete(:attidentity)
1574
1622
  if row[:primary_key]
1575
1623
  row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
1576
1624
  end
1625
+
1626
+ # :nocov:
1627
+ if server_version >= 90600
1628
+ # :nocov:
1629
+ case row[:oid]
1630
+ when 1082
1631
+ row[:min_value] = MIN_DATE
1632
+ row[:max_value] = MAX_DATE
1633
+ when 1184, 1114
1634
+ if Sequel.datetime_class == Time
1635
+ row[:min_value] = MIN_TIMESTAMP
1636
+ row[:max_value] = MAX_TIMESTAMP
1637
+ end
1638
+ end
1639
+ end
1640
+
1577
1641
  [m.call(row.delete(:name)), row]
1578
1642
  end
1579
1643
  end
@@ -900,7 +900,7 @@ module Sequel
900
900
  #
901
901
  # Any other object given is just converted to a string, with "_" converted to " " and upcased.
902
902
  def on_delete_clause(action)
903
- action.to_s.gsub("_", " ").upcase
903
+ action.to_s.tr("_", " ").upcase
904
904
  end
905
905
 
906
906
  # Alias of #on_delete_clause, since the two usually behave the same.
@@ -257,7 +257,7 @@ module Sequel
257
257
  end
258
258
 
259
259
  # Make the column type detection handle registered array types.
260
- def schema_column_type(db_type)
260
+ def schema_array_type(db_type)
261
261
  if (db_type =~ /\A([^(]+)(?:\([^(]+\))?\[\]\z/io) && (type = pg_array_schema_type($1))
262
262
  type
263
263
  else
@@ -166,8 +166,7 @@ module Sequel
166
166
  def schema_post_process(_)
167
167
  super.each do |_, s|
168
168
  oid = s[:oid]
169
- if values = Sequel.synchronize{@enum_labels[oid]}
170
- s[:type] = :enum
169
+ if s[:type] == :enum && (values = Sequel.synchronize{@enum_labels[oid]})
171
170
  s[:enum_values] = values
172
171
  end
173
172
  end
@@ -22,7 +22,6 @@
22
22
  module Sequel
23
23
  module Postgres
24
24
  module ExtendedDateSupport
25
- DATE_YEAR_1 = Date.new(1)
26
25
  DATETIME_YEAR_1 = DateTime.new(1)
27
26
  TIME_YEAR_1 = Time.at(-62135596800).utc
28
27
  INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
@@ -38,6 +37,15 @@ module Sequel
38
37
  procs = db.conversion_procs
39
38
  procs[1082] = ::Sequel.method(:string_to_date)
40
39
  procs[1184] = procs[1114] = db.method(:to_application_timestamp)
40
+ if ocps = db.instance_variable_get(:@oid_convertor_map)
41
+ # Clear the oid convertor map entries for timestamps if they
42
+ # exist, so it will regenerate new ones that use this extension.
43
+ # This is only taken when using the jdbc adapter.
44
+ Sequel.synchronize do
45
+ ocps.delete(1184)
46
+ ocps.delete(1114)
47
+ end
48
+ end
41
49
  end
42
50
 
43
51
  # Handle BC dates and times in bound variables. This is necessary for Date values
@@ -181,7 +189,7 @@ module Sequel
181
189
 
182
190
  # Handle BC Date objects.
183
191
  def literal_date(date)
184
- if date < DATE_YEAR_1
192
+ if date.year < 1
185
193
  date <<= ((date.year) * 24 - 12)
186
194
  date.strftime("'%Y-%m-%d BC'")
187
195
  else
@@ -221,7 +221,7 @@ module Sequel
221
221
  private
222
222
 
223
223
  # Recognize the registered database multirange types.
224
- def schema_column_type(db_type)
224
+ def schema_multirange_type(db_type)
225
225
  @pg_multirange_schema_types[db_type] || super
226
226
  end
227
227
 
@@ -234,7 +234,7 @@ module Sequel
234
234
  private
235
235
 
236
236
  # Recognize the registered database range types.
237
- def schema_column_type(db_type)
237
+ def schema_range_type(db_type)
238
238
  @pg_range_schema_types[db_type] || super
239
239
  end
240
240
 
@@ -538,12 +538,8 @@ module Sequel
538
538
  private
539
539
 
540
540
  # Make the column type detection handle registered row types.
541
- def schema_column_type(db_type)
542
- if type = @row_schema_types[db_type]
543
- type
544
- else
545
- super
546
- end
541
+ def schema_composite_type(db_type)
542
+ @row_schema_types[db_type] || super
547
543
  end
548
544
  end
549
545
  end
@@ -125,14 +125,15 @@ module Sequel
125
125
  ds = @dataset.with_quote_identifiers(false)
126
126
  table_name = ds.literal(ds.first_source_table)
127
127
  reflections = {}
128
- @constraint_validations = (Sequel.synchronize{hash[table_name]} || []).map{|r| constraint_validation_array(r, reflections)}
128
+ allow_missing_columns = db_schema.select{|col, sch| sch[:allow_null] == false && nil != sch[:default]}.map(&:first)
129
+ @constraint_validations = (Sequel.synchronize{hash[table_name]} || []).map{|r| constraint_validation_array(r, reflections, allow_missing_columns)}
129
130
  @constraint_validation_reflections = reflections
130
131
  end
131
132
  end
132
133
 
133
134
  # Given a specific database constraint validation metadata row hash, transform
134
135
  # it in an validation method call array suitable for splatting to send.
135
- def constraint_validation_array(r, reflections)
136
+ def constraint_validation_array(r, reflections, allow_missing_columns=EMPTY_ARRAY)
136
137
  opts = {}
137
138
  opts[:message] = r[:message] if r[:message]
138
139
  opts[:allow_nil] = true if db.typecast_value(:boolean, r[:allow_nil])
@@ -191,11 +192,13 @@ module Sequel
191
192
  reflection_opts[:argument] = arg
192
193
  end
193
194
 
194
- a << column
195
- unless opts.empty?
196
- a << opts
195
+ opts[:from] = :values
196
+ if column.is_a?(Symbol) && allow_missing_columns.include?(column)
197
+ opts[:allow_missing] = true
197
198
  end
198
199
 
200
+ a << column << opts
201
+
199
202
  if column.is_a?(Array) && column.length == 1
200
203
  column = column.first
201
204
  end
@@ -28,7 +28,9 @@ module Sequel
28
28
  #
29
29
  # This plugin only works on the postgres adapter when using the pg 0.16+ driver,
30
30
  # PostgreSQL 9.3+ server, and PostgreSQL 9.3+ client library (libpq). In other cases
31
- # it will be a no-op.
31
+ # it will be a no-op. Additionally, the plugin only handles models that select
32
+ # from tables. It does not handle models that select from subqueries, such as
33
+ # subclasses of models using the class_table_inheritance plugin.
32
34
  #
33
35
  # Example:
34
36
  #
@@ -75,6 +75,10 @@ module Sequel
75
75
  # "#{Array(attribute).join(I18n.t('errors.joiner'))} #{error_msg}"
76
76
  # end
77
77
  # end
78
+ #
79
+ # It is recommended that users of this plugin that use validates_schema_types also use
80
+ # the validation_helpers_generic_type_messages plugin for more useful type validation
81
+ # failure messages.
78
82
  module ValidationHelpers
79
83
  DEFAULT_OPTIONS = {
80
84
  :exact_length=>{:message=>lambda{|exact| "is not #{exact} characters"}},
@@ -211,7 +215,7 @@ module Sequel
211
215
  klass = klass.to_s.constantize if klass.is_a?(String) || klass.is_a?(Symbol)
212
216
  validatable_attributes_for_type(:type, atts, opts) do |a,v,m|
213
217
  if klass.is_a?(Array) ? !klass.any?{|kls| v.is_a?(kls)} : !v.is_a?(klass)
214
- validation_error_message(m, klass)
218
+ validates_type_error_message(m, klass)
215
219
  end
216
220
  end
217
221
  end
@@ -338,6 +342,9 @@ module Sequel
338
342
  def validation_error_message(message, *args)
339
343
  message.is_a?(Proc) ? message.call(*args) : message
340
344
  end
345
+
346
+ # The validation error message for type validations, for the given class.
347
+ alias validates_type_error_message validation_error_message
341
348
  end
342
349
  end
343
350
  end
@@ -0,0 +1,73 @@
1
+ # frozen-string-literal: true
2
+
3
+ require_relative 'validation_helpers'
4
+
5
+ module Sequel
6
+ module Plugins
7
+ # The validation_helpers_generic_type_messages plugin overrides the default
8
+ # type validation failure messages in the validation_helpers plugin to be
9
+ # more generic and understandable by the average user, instead of always
10
+ # be based on the names of the allowed classes for the type. For example:
11
+ #
12
+ # # :blob type
13
+ # # validation_helpers default: "value is not a valid sequel::sql::blob"
14
+ # # with this plugin: "value is not a blob"
15
+ #
16
+ # # :boolean type
17
+ # # validation_helpers default: "value is not a valid trueclass or falseclass"
18
+ # # with this plugin: "value is not true or false"
19
+ #
20
+ # # :datetime type
21
+ # # validation_helpers default: "value is not a valid time or datetime"
22
+ # # with this plugin: "value is not a valid timestamp"
23
+ #
24
+ # # custom/database-specific types
25
+ # # validation_helpers default: "value is not a valid sequel::class_name"
26
+ # # with this plugin: "value is not the expected type"
27
+ #
28
+ # It is expected that this plugin will become the default behavior of
29
+ # validation_helpers in Sequel 6.
30
+ #
31
+ # To enable this the use of generic type messages for all models, load this
32
+ # plugin into Sequel::Model.
33
+ #
34
+ # Sequel::Model.plugin :validation_helpers_generic_type_messages
35
+ module ValidationHelpersGenericTypeMessages
36
+ OVERRIDE_PROC = ValidationHelpers::DEFAULT_OPTIONS[:type][:message]
37
+ private_constant :OVERRIDE_PROC
38
+
39
+ TYPE_ERROR_STRINGS = {
40
+ String => 'is not a string'.freeze,
41
+ Integer => 'is not an integer'.freeze,
42
+ Date => 'is not a valid date'.freeze,
43
+ [Time, DateTime].freeze => 'is not a valid timestamp'.freeze,
44
+ Sequel::SQLTime => 'is not a valid time'.freeze,
45
+ [TrueClass, FalseClass].freeze => 'is not true or false'.freeze,
46
+ Float => 'is not a number'.freeze,
47
+ BigDecimal => 'is not a number'.freeze,
48
+ Sequel::SQL::Blob => 'is not a blob'.freeze,
49
+ }
50
+ TYPE_ERROR_STRINGS.default = "is not the expected type".freeze
51
+ TYPE_ERROR_STRINGS.freeze
52
+ private_constant :TYPE_ERROR_STRINGS
53
+
54
+ def self.apply(mod)
55
+ mod.plugin :validation_helpers
56
+ end
57
+
58
+ module InstanceMethods
59
+ private
60
+
61
+ # Use a generic error message for type validations.
62
+ def validates_type_error_message(m, klass)
63
+ # SEQUEL6: Make this the default behavior in validation_helpers
64
+ if OVERRIDE_PROC.equal?(m)
65
+ TYPE_ERROR_STRINGS[klass]
66
+ else
67
+ super
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -6,7 +6,7 @@ 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 = 67
9
+ MINOR = 68
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
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.67.0
4
+ version: 5.68.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-01 00:00:00.000000000 Z
11
+ date: 2023-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -199,6 +199,7 @@ extra_rdoc_files:
199
199
  - doc/release_notes/5.65.0.txt
200
200
  - doc/release_notes/5.66.0.txt
201
201
  - doc/release_notes/5.67.0.txt
202
+ - doc/release_notes/5.68.0.txt
202
203
  - doc/release_notes/5.7.0.txt
203
204
  - doc/release_notes/5.8.0.txt
204
205
  - doc/release_notes/5.9.0.txt
@@ -294,6 +295,7 @@ files:
294
295
  - doc/release_notes/5.65.0.txt
295
296
  - doc/release_notes/5.66.0.txt
296
297
  - doc/release_notes/5.67.0.txt
298
+ - doc/release_notes/5.68.0.txt
297
299
  - doc/release_notes/5.7.0.txt
298
300
  - doc/release_notes/5.8.0.txt
299
301
  - doc/release_notes/5.9.0.txt
@@ -583,6 +585,7 @@ files:
583
585
  - lib/sequel/plugins/validation_class_methods.rb
584
586
  - lib/sequel/plugins/validation_contexts.rb
585
587
  - lib/sequel/plugins/validation_helpers.rb
588
+ - lib/sequel/plugins/validation_helpers_generic_type_messages.rb
586
589
  - lib/sequel/plugins/whitelist_security.rb
587
590
  - lib/sequel/plugins/xml_serializer.rb
588
591
  - lib/sequel/sql.rb