sequel 5.60.1 → 5.61.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd8695696d6b55edf6337870f3ff172aeb6fe2ef9ba4bbc23daae0af26908649
4
- data.tar.gz: 2483bbb3549adbcb9f179165119051b7bb234d6e714e4afa116c0ffddf379dc6
3
+ metadata.gz: f542d5f0e7cca642a8fccd52cb4099101f38dbd1617ff2437b8690766b5f2bdd
4
+ data.tar.gz: 79ff2215f1e38e523e7e64a5779bee695a939a03f046a5e2e9a5974a804a714c
5
5
  SHA512:
6
- metadata.gz: 0527cba1fcc06d8001cbd55473b3af5c23a9f5c004113fc24aa6f9e029c5ed2cf4f938bf184d79922164cdc3ea502d12860d46d64237bdbd306bf5ce50f59ffa
7
- data.tar.gz: e13c17553592773e70c0e9a6753438444a5245f3cd0664c6c5a6c0a08a271d1d92180b110c7b7e5981f7ca6dc2b18d90da3feeb05a14ae863fcfd99d5673650e
6
+ metadata.gz: 1e0bdc80d388f1bdbf39674624522f7086ca5b0904df2bb28d7b6f3c8f6057df38eca1c0854905a87d920286e578d1e0fe269deb33c3c3f1691d2113a9f23d11
7
+ data.tar.gz: 31ebb15cc504b82c5406c2f7eb6a02a99aa035e9486ecd52698403eff7bd79a99e606229dbbf392ac3fabd1b68c9a7ebc09c127dea3970e2a083c7d781159f33
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ === 5.61.0 (2022-10-01)
2
+
3
+ * Make Database#foreign_key_list on PostgreSQL return results for partitioned tables (jeremyevans)
4
+
5
+ * Add Database#check_string_typecast_bytesize for checking bytesize of strings before typecasting (jeremyevans)
6
+
7
+ * Treat negative hexidecimal strings similar to positive hexidecimal strings when typecasting to integer (jeremyevans)
8
+
9
+ * Remove is_json and is_not_json methods from the pg_json_ops extension, as the support was removed in PostgreSQL 15 beta 4 (jeremyevans)
10
+
11
+ * Fix handling of timestamps before the date of calendar reform when using pg_extended_date_support extension on Ruby 3.2 (jeremyevans)
12
+
1
13
  === 5.60.1 (2022-09-02)
2
14
 
3
15
  * Revert conversion of respond_to? to defined?, as it breaks with unused refinements on Ruby 2 (jeremyevans) (#1919)
@@ -0,0 +1,43 @@
1
+ = Improvements
2
+
3
+ * When typecasting strings to other types, Sequel::Database will now
4
+ by default not typecast strings that are much longer than expected
5
+ for the underlying type. Depending on the underlying type, there
6
+ is a limit of either 100 or 1000 bytes on the input string. This
7
+ avoids potential performance issues when trying to convert
8
+ arbitrary sized user input to specific types.
9
+
10
+ * The respond_to? to defined? change made in 5.60.0 was reverted in
11
+ 5.60.1 as it broke cases on Ruby < 3 where the object had an unused
12
+ refinement that added the method.
13
+
14
+ * When typecasting strings to integer, strings such as -0xa are now
15
+ treated as negative hexidecimal strings, similar to how 0xa is
16
+ treated as a positive hexidecimal string.
17
+
18
+ * Database#foreign_key_list now returns results for partitioned
19
+ tables on PostgreSQL 11+.
20
+
21
+ * Timestamps before the date of calendar reform are now handled
22
+ correctly by the pg_extended_date_support extension when using
23
+ Ruby 3.2 preview 2+.
24
+
25
+ = Backwards Compatibility
26
+
27
+ * The change to not typecast strings that are too long can break
28
+ backwards compatibility for applications that expect typecasting
29
+ for input beyond Sequel's limits. You can disable the string
30
+ bytesize checking by setting:
31
+
32
+ DB.check_string_typecast_bytesize = false
33
+
34
+ or by passing the check_string_typecast_bytesize: false option when
35
+ creating the Database instance.
36
+
37
+ * Code to workaround a bug in JRuby 9.2.0.0 has been removed from the
38
+ pg_extended_date_support extension. Users of the extension should
39
+ upgrade to a newer JRuby version.
40
+
41
+ * The is_json and is_not_json methods have been removed from the
42
+ pg_json_ops extension, as the underlying support was removed in
43
+ PostgreSQL 15 beta 4.
@@ -586,7 +586,7 @@ module Sequel
586
586
  join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
587
587
  order{[co[:conname], cpos]}.
588
588
  where{{
589
- cl[:relkind]=>'r',
589
+ cl[:relkind]=>%w'r p',
590
590
  co[:contype]=>'f',
591
591
  cl[:oid]=>oid,
592
592
  cpos=>rpos
@@ -91,6 +91,11 @@ module Sequel
91
91
  # The specific default size of string columns for this Sequel::Database, usually 255 by default.
92
92
  attr_accessor :default_string_column_size
93
93
 
94
+ # Whether to check the bytesize of strings before typecasting (to avoid typecasting strings that
95
+ # would be too long for the given type), true by default. Strings that are too long will raise
96
+ # a typecasting error.
97
+ attr_accessor :check_string_typecast_bytesize
98
+
94
99
  # Constructs a new instance of a database connection with the specified
95
100
  # options hash.
96
101
  #
@@ -98,6 +103,7 @@ module Sequel
98
103
  # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
99
104
  # but before any connections are created.
100
105
  # :cache_schema :: Whether schema should be cached for this Database instance
106
+ # :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting.
101
107
  # :default_string_column_size :: The default size of string columns, 255 by default.
102
108
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
103
109
  # or string with extensions separated by columns. These extensions are loaded after
@@ -107,7 +113,7 @@ module Sequel
107
113
  # :loggers :: An array of loggers to use.
108
114
  # :log_connection_info :: Whether connection information should be logged when logging queries.
109
115
  # :log_warn_duration :: The number of elapsed seconds after which queries should be logged at warn level.
110
- # :name :: A name to use for the Database object, displayed in PoolTimeout .
116
+ # :name :: A name to use for the Database object, displayed in PoolTimeout.
111
117
  # :preconnect :: Automatically create the maximum number of connections, so that they don't
112
118
  # need to be created as needed. This is useful when connecting takes a long time
113
119
  # and you want to avoid possible latency during runtime.
@@ -116,7 +122,7 @@ module Sequel
116
122
  # :preconnect_extensions :: Similar to the :extensions option, but loads the extensions before the
117
123
  # connections are made by the :preconnect option.
118
124
  # :quote_identifiers :: Whether to quote identifiers.
119
- # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol .
125
+ # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol.
120
126
  # :single_threaded :: Whether to use a single-threaded connection pool.
121
127
  # :sql_log_level :: Method to use to log SQL to a logger, :info by default.
122
128
  #
@@ -132,6 +138,7 @@ module Sequel
132
138
  @opts[:adapter_class] = self.class
133
139
  @opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, Sequel.single_threaded))
134
140
  @default_string_column_size = @opts[:default_string_column_size] || DEFAULT_STRING_COLUMN_SIZE
141
+ @check_string_typecast_bytesize = typecast_value_boolean(@opts.fetch(:check_string_typecast_bytesize, true))
135
142
 
136
143
  @schemas = {}
137
144
  @prepared_statements = {}
@@ -465,6 +472,21 @@ module Sequel
465
472
  # Don't rescue other exceptions, they will be raised normally.
466
473
  end
467
474
 
475
+ # Check the bytesize of a string before conversion. There is no point
476
+ # trying to typecast strings that would be way too long.
477
+ def typecast_check_string_length(string, max_size)
478
+ if @check_string_typecast_bytesize && string.bytesize > max_size
479
+ raise InvalidValue, "string too long to typecast (bytesize: #{string.bytesize}, max: #{max_size})"
480
+ end
481
+ string
482
+ end
483
+
484
+ # Check the bytesize of the string value, if value is a string.
485
+ def typecast_check_length(value, max_size)
486
+ typecast_check_string_length(value, max_size) if String === value
487
+ value
488
+ end
489
+
468
490
  # Typecast the value to an SQL::Blob
469
491
  def typecast_value_blob(value)
470
492
  value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
@@ -488,9 +510,9 @@ module Sequel
488
510
  when Date
489
511
  value
490
512
  when String
491
- Sequel.string_to_date(value)
513
+ Sequel.string_to_date(typecast_check_string_length(value, 100))
492
514
  when Hash
493
- Date.new(*[:year, :month, :day].map{|x| (value[x] || value[x.to_s]).to_i})
515
+ Date.new(*[:year, :month, :day].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
494
516
  else
495
517
  raise InvalidValue, "invalid value for Date: #{value.inspect}"
496
518
  end
@@ -498,7 +520,17 @@ module Sequel
498
520
 
499
521
  # Typecast the value to a DateTime or Time depending on Sequel.datetime_class
500
522
  def typecast_value_datetime(value)
501
- Sequel.typecast_to_application_timestamp(value)
523
+ case value
524
+ when String
525
+ Sequel.typecast_to_application_timestamp(typecast_check_string_length(value, 100))
526
+ when Hash
527
+ [:year, :month, :day, :hour, :minute, :second, :nanos, :offset].each do |x|
528
+ typecast_check_length(value[x] || value[x.to_s], 100)
529
+ end
530
+ Sequel.typecast_to_application_timestamp(value)
531
+ else
532
+ Sequel.typecast_to_application_timestamp(value)
533
+ end
502
534
  end
503
535
 
504
536
  if RUBY_VERSION >= '2.4'
@@ -531,18 +563,30 @@ module Sequel
531
563
  when Numeric
532
564
  BigDecimal(value.to_s)
533
565
  when String
534
- _typecast_value_string_to_decimal(value)
566
+ _typecast_value_string_to_decimal(typecast_check_string_length(value, 1000))
535
567
  else
536
568
  raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
537
569
  end
538
570
  end
539
571
 
540
572
  # Typecast the value to a Float
541
- alias typecast_value_float Float
573
+ def typecast_value_float(value)
574
+ Float(typecast_check_length(value, 1000))
575
+ end
542
576
 
543
577
  # Typecast the value to an Integer
544
578
  def typecast_value_integer(value)
545
- (value.is_a?(String) && value =~ /\A0+(\d)/) ? Integer(value, 10) : Integer(value)
579
+ case value
580
+ when String
581
+ typecast_check_string_length(value, 100)
582
+ if value =~ /\A-?0+(\d)/
583
+ Integer(value, 10)
584
+ else
585
+ Integer(value)
586
+ end
587
+ else
588
+ Integer(value)
589
+ end
546
590
  end
547
591
 
548
592
  # Typecast the value to a String
@@ -565,9 +609,9 @@ module Sequel
565
609
  SQLTime.create(value.hour, value.min, value.sec, value.nsec/1000.0)
566
610
  end
567
611
  when String
568
- Sequel.string_to_time(value)
612
+ Sequel.string_to_time(typecast_check_string_length(value, 100))
569
613
  when Hash
570
- SQLTime.create(*[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
614
+ SQLTime.create(*[:hour, :minute, :second].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
571
615
  else
572
616
  raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
573
617
  end
@@ -8,6 +8,9 @@
8
8
  # :decimal :: use 0.0 for unsupported strings
9
9
  # :string :: silently allow hash and array conversion to string
10
10
  #
11
+ # This also removes bytesize checks for string inputs for float, integer
12
+ # and decimal conversions.
13
+ #
11
14
  # To load the extension into the database:
12
15
  #
13
16
  # DB.extension :looser_typecasting
@@ -29,6 +29,8 @@ module Sequel
29
29
  INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
30
30
  PLUS_DATE_INFINITY = Date::Infinity.new
31
31
  MINUS_DATE_INFINITY = -PLUS_DATE_INFINITY
32
+ RATIONAL_60 = Rational(60)
33
+ TIME_CAN_PARSE_BC = RUBY_VERSION >= '2.5'
32
34
 
33
35
  # Add dataset methods and update the conversion proces for dates and timestamps.
34
36
  def self.extended(db)
@@ -86,27 +88,18 @@ module Sequel
86
88
  if value.is_a?(String) && (m = /((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/.match(value)) && (m[2] || m[3])
87
89
  if m[3]
88
90
  value = value.sub(' BC', '').sub(' ', ' BC ')
89
- conv = defined?(JRUBY_VERSION) && JRUBY_VERSION == '9.2.0.0'
90
91
  end
91
- if m[2] || conv
92
- dt = DateTime.parse(value)
93
- if conv
94
- # :nocov:
95
- if Sequel.datetime_class == DateTime
96
- dt >>= 12
97
- else
98
- dt >>= 24
99
- end
100
- # :nocov:
101
- end
102
- unless Sequel.datetime_class == DateTime
103
- dt = dt.to_time
104
- if conv && (timezone == nil || timezone == :local) && !m[1]
105
- # :nocov:
106
- dt = Sequel.send(:convert_input_timestamp, dt.strftime("%F %T.%6N"), :local)
107
- # :nocov:
108
- end
92
+ if m[2]
93
+ dt = if Sequel.datetime_class == DateTime
94
+ DateTime.parse(value)
95
+ elsif TIME_CAN_PARSE_BC
96
+ Time.parse(value)
97
+ # :nocov:
98
+ else
99
+ DateTime.parse(value).to_time
100
+ # :nocov:
109
101
  end
102
+
110
103
  Sequel.convert_output_timestamp(dt, Sequel.application_timezone)
111
104
  else
112
105
  super(value)
@@ -223,10 +216,7 @@ module Sequel
223
216
  # Work around JRuby bug #4822 in Time#to_datetime for times before date of calendar reform
224
217
  def literal_time(time)
225
218
  if time < TIME_YEAR_1
226
- dt = DateTime.parse(super)
227
- # Work around JRuby bug #5191
228
- dt >>= 12 if JRUBY_VERSION == '9.2.0.0'
229
- literal_datetime(dt)
219
+ literal_datetime(DateTime.parse(super))
230
220
  else
231
221
  super
232
222
  end
@@ -236,7 +226,8 @@ module Sequel
236
226
  # Handle BC Time objects.
237
227
  def literal_time(time)
238
228
  if time < TIME_YEAR_1
239
- literal_datetime(time.to_datetime)
229
+ time = db.from_application_timestamp(time)
230
+ time.strftime("'#{sprintf('%04i', time.year.abs+1)}-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*(time.utc_offset/RATIONAL_60).divmod(60))} BC'")
240
231
  else
241
232
  super
242
233
  end
@@ -111,7 +111,7 @@ module Sequel
111
111
  when IPAddr
112
112
  value
113
113
  when String
114
- IPAddr.new(value)
114
+ IPAddr.new(typecast_check_string_length(value, 100))
115
115
  else
116
116
  raise Sequel::InvalidValue, "invalid value for inet/cidr: #{value.inspect}"
117
117
  end
@@ -197,7 +197,7 @@ module Sequel
197
197
  when Numeric
198
198
  ActiveSupport::Duration.new(value, [[:seconds, value]])
199
199
  when String
200
- PARSER.call(value)
200
+ PARSER.call(typecast_check_string_length(value, 1000))
201
201
  else
202
202
  raise Sequel::InvalidValue, "invalid value for interval type: #{value.inspect}"
203
203
  end
@@ -123,15 +123,6 @@
123
123
  # c = Sequel.pg_jsonb_op(:c)
124
124
  # DB[:t].update(c['key1'] => 1.to_json, c['key2'] => "a".to_json)
125
125
  #
126
- # On PostgreSQL 15+, the <tt>IS [NOT] JSON</tt> operator is supported:
127
- #
128
- # j.is_json # j IS JSON
129
- # j.is_json(type: :object) # j IS JSON OBJECT
130
- # j.is_json(type: :object, unique: true) # j IS JSON OBJECT WITH UNIQUE
131
- # j.is_not_json # j IS NOT JSON
132
- # j.is_json(type: :array) # j IS NOT JSON ARRAY
133
- # j.is_json(unique: true) # j IS NOT JSON WITH UNIQUE
134
- #
135
126
  # If you are also using the pg_json extension, you should load it before
136
127
  # loading this extension. Doing so will allow you to use the #op method on
137
128
  # JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
@@ -160,18 +151,6 @@ module Sequel
160
151
  GET_PATH = ["(".freeze, " #> ".freeze, ")".freeze].freeze
161
152
  GET_PATH_TEXT = ["(".freeze, " #>> ".freeze, ")".freeze].freeze
162
153
 
163
- IS_JSON = ["(".freeze, " IS JSON".freeze, "".freeze, ")".freeze].freeze
164
- IS_NOT_JSON = ["(".freeze, " IS NOT JSON".freeze, "".freeze, ")".freeze].freeze
165
- EMPTY_STRING = Sequel::LiteralString.new('').freeze
166
- WITH_UNIQUE = Sequel::LiteralString.new(' WITH UNIQUE').freeze
167
- IS_JSON_MAP = {
168
- nil => EMPTY_STRING,
169
- :value => Sequel::LiteralString.new(' VALUE').freeze,
170
- :scalar => Sequel::LiteralString.new(' SCALAR').freeze,
171
- :object => Sequel::LiteralString.new(' OBJECT').freeze,
172
- :array => Sequel::LiteralString.new(' ARRAY').freeze
173
- }.freeze
174
-
175
154
  # Get JSON array element or object field as json. If an array is given,
176
155
  # gets the object at the specified path.
177
156
  #
@@ -254,30 +233,6 @@ module Sequel
254
233
  end
255
234
  end
256
235
 
257
- # Return whether the json object can be parsed as JSON.
258
- #
259
- # Options:
260
- # :type :: Check whether the json object can be parsed as a specific type
261
- # of JSON (:value, :scalar, :object, :array).
262
- # :unique :: Check JSON objects for unique keys.
263
- #
264
- # json_op.is_json # json IS JSON
265
- # json_op.is_json(type: :object) # json IS JSON OBJECT
266
- # json_op.is_json(unique: true) # json IS JSON WITH UNIQUE
267
- def is_json(opts=OPTS)
268
- _is_json(IS_JSON, opts)
269
- end
270
-
271
- # Return whether the json object cannot be parsed as JSON. The opposite
272
- # of #is_json. See #is_json for options.
273
- #
274
- # json_op.is_not_json # json IS NOT JSON
275
- # json_op.is_not_json(type: :object) # json IS NOT JSON OBJECT
276
- # json_op.is_not_json(unique: true) # json IS NOT JSON WITH UNIQUE
277
- def is_not_json(opts=OPTS)
278
- _is_json(IS_NOT_JSON, opts)
279
- end
280
-
281
236
  # Returns a set of keys AS text in the json object.
282
237
  #
283
238
  # json_op.keys # json_object_keys(json)
@@ -331,13 +286,6 @@ module Sequel
331
286
 
332
287
  private
333
288
 
334
- # Internals of IS [NOT] JSON support
335
- def _is_json(lit_array, opts)
336
- raise Error, "invalid is_json :type option: #{opts[:type].inspect}" unless type = IS_JSON_MAP[opts[:type]]
337
- unique = opts[:unique] ? WITH_UNIQUE : EMPTY_STRING
338
- Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(lit_array, [self, type, unique]))
339
- end
340
-
341
289
  # Return a placeholder literal with the given str and args, wrapped
342
290
  # in an JSONOp or JSONBOp, used by operators that return json or jsonb.
343
291
  def json_op(str, args)
@@ -282,7 +282,7 @@ module Sequel
282
282
  when Range
283
283
  PGRange.from_range(value, parser.db_type)
284
284
  when String
285
- parser.call(value)
285
+ parser.call(typecast_check_string_length(value, 100))
286
286
  else
287
287
  raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}"
288
288
  end
@@ -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 = 61
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.61.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: 2022-09-02 00:00:00.000000000 Z
11
+ date: 2022-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -192,6 +192,7 @@ 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
195
196
  - doc/release_notes/5.7.0.txt
196
197
  - doc/release_notes/5.8.0.txt
197
198
  - doc/release_notes/5.9.0.txt
@@ -280,6 +281,7 @@ files:
280
281
  - doc/release_notes/5.59.0.txt
281
282
  - doc/release_notes/5.6.0.txt
282
283
  - doc/release_notes/5.60.0.txt
284
+ - doc/release_notes/5.61.0.txt
283
285
  - doc/release_notes/5.7.0.txt
284
286
  - doc/release_notes/5.8.0.txt
285
287
  - doc/release_notes/5.9.0.txt