sequel 5.1.0 → 5.2.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
  SHA1:
3
- metadata.gz: d380edb41288b7e5fd091c8ffd8215b9a8c0a8b2
4
- data.tar.gz: e4c34f33035f7e2354f53e264683a60337c66d28
3
+ metadata.gz: ef9b7568e8d79bc20f28c50164f3f51da9882fa6
4
+ data.tar.gz: 24f2cda374fdc6e9b2e552d69cdad19542a9a06f
5
5
  SHA512:
6
- metadata.gz: a18ccd0f5974af2610e2d274963b83fdba2aab99559c3e1cf2bceba99d890d6d4c0dda13c0c6b2f9fba913f41ba39983d65df52e39651e745fa86a1097c3c003
7
- data.tar.gz: ac9efe8f2e33539d322b94b6e9e76cadf64091a2af6958bdbdd8e67bea691849382c5aae0032b29498ab4d3b9c4ddf599737be8170d6f435fd03028fbcfc1019
6
+ metadata.gz: 142a793eec4984fb6e83637758cb2211981199caa498b8bcee98cc87c0e4facf16b2c6829df0fc5cd8a783c99a728eefb84b53fb36d49d312815f712c6cdb667
7
+ data.tar.gz: '083f6448c19ec9d55567954c316b6a14934f1743e599ccf459b276485064f0af749a7492f42b728ee70df38bf61589775cdd77157b7fee3fcfdf5518d9719f3f'
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ === 5.2.0 (2017-10-27)
2
+
3
+ * Fix type conversion for smallint unsigned and integer unsigned types on jdbc/mysql (jeremyevans) (#1443)
4
+
5
+ * Add pg_extended_date_support extension, for handling infinite and BC dates/timestamps (jeremyevans)
6
+
7
+ * Do not ignore existing @dataset instance variable when subclassing Sequel::Model (bjmllr) (#1435)
8
+
1
9
  === 5.1.0 (2017-10-01)
2
10
 
3
11
  * Make jdbc/h2 and jdbc/hsqldb adapters respect :foreign_key_constraint_name option when adding new foreign key column (jeremyevans)
@@ -1165,7 +1165,7 @@ Called before adding an object to the association:
1165
1165
 
1166
1166
  class Artist
1167
1167
  # Don't allow adding an album to an artist if it has no tracks
1168
- one_to_many :albums, before_add: lambda{|ar, al| artist.cancel_action if al.num_tracks == 0}
1168
+ one_to_many :albums, before_add: lambda{|ar, al| ar.cancel_action if al.num_tracks == 0}
1169
1169
  end
1170
1170
 
1171
1171
  ==== :after_add [+one_to_many+, +many_to_many+]
@@ -1189,7 +1189,7 @@ Called before removing an object from the association using <tt>remove_<i>associ
1189
1189
 
1190
1190
  class Artist
1191
1191
  # Don't allow removing a self-titled album
1192
- one_to_many :albums, before_remove: lambda{|ar, al| artist.cancel_action if al.name == ar.name}
1192
+ one_to_many :albums, before_remove: lambda{|ar, al| ar.cancel_action if al.name == ar.name}
1193
1193
  end
1194
1194
 
1195
1195
  This is not called when using <tt>remove_all_<i>association</i></tt>.
@@ -0,0 +1,33 @@
1
+ = New Features
2
+
3
+ * A pg_extended_date_support extension has been added. This
4
+ extension adds support for infinite and BC dates/timestamps on
5
+ PostgreSQL.
6
+
7
+ The postgres adapter already had a convert_infinite_timestamps
8
+ setting, but it wasn't supported in the jdbc/postgresql adapter
9
+ and it didn't handle BC dates/timestamps. Setting a non-default
10
+ convert_infinite_timestamps setting in the postgres adapter will
11
+ now automatically load the extension for backwards compatibility.
12
+
13
+ The pg_extended_date_support extension by default just fixes the
14
+ handling of BC dates/timestamps. To get it to handle infinite
15
+ timestamps, you need to choose the appropriate setting for your
16
+ application:
17
+
18
+ DB.extension :pg_extended_date_support
19
+ DB.convert_infinite_timestamps = :string # or :float or :nil
20
+
21
+ This extension also enables the handling of timezone offsets
22
+ with seconds, which is not natively supported by ruby's Time
23
+ class in ruby <2.5.
24
+
25
+ = Improvements
26
+
27
+ * The jdbc/mysql adapter now handles smallint unsigned and
28
+ integer unsigned column types where the value for the column
29
+ is outside of the range of a Java short or integer.
30
+
31
+ * Sequel::Model.inherited no longer modifies an existing @dataset
32
+ instance variable if one has already been set. This fixes a
33
+ regression that was introduced in Sequel 5.0.0.
@@ -152,7 +152,7 @@ plain strings to literal strings.
152
152
  ==== SQL Fragment passed to Dataset#lock_style and Model#lock!
153
153
 
154
154
  The Sequel::Dataset#lock_style and Sequel::Model#lock! methods also treat
155
- an input string as SQL code. This method should not be called with user input.
155
+ an input string as SQL code. These methods should not be called with user input.
156
156
 
157
157
  DB[:table].lock_style(params[:id]) # SQL injection!
158
158
  Album.first.lock!(params[:id]) # SQL injection!
@@ -184,8 +184,8 @@ injection:
184
184
  # SQL injection when using auto_literal_strings extension
185
185
 
186
186
  If you are using the auto_literal_strings extension, you need to be very careful,
187
- as the following methods will treat a plain string given as the first arguments
188
- as a literal strings:
187
+ as the following methods will treat a plain string given as the first argument
188
+ as a literal string:
189
189
 
190
190
  * Sequel::Dataset#where
191
191
  * Sequel::Dataset#having
@@ -204,7 +204,7 @@ Even stuff that looks like it may be safe isn't:
204
204
 
205
205
  The Model.find[rdoc-ref:Sequel::Model::ClassMethods#find] and
206
206
  Model.find_or_create[rdoc-ref:Sequel::Model::ClassMethods#find_or_create]
207
- class methods also will treat string arguments as literal strings if the
207
+ class methods will also treat string arguments as literal strings if the
208
208
  auto_literal_strings extension is used:
209
209
 
210
210
  Album.find(params[:id])
@@ -642,7 +642,7 @@ module Sequel
642
642
  end
643
643
 
644
644
  # Called before loading subadapter-specific code, necessary so that subadapter initialization code
645
- # that runs queries works correctly. This cannot be overriding in subadapters,
645
+ # that runs queries works correctly. This cannot be overridden in subadapters.
646
646
  def setup_type_convertor_map_early
647
647
  @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>method(:timestamp_convert))
648
648
  @basic_type_convertor_map = TypeConvertor::BASIC_MAP.dup
@@ -73,6 +73,16 @@ module Sequel
73
73
  mysql_connection_setting_sqls.each{|sql| statement(conn){|s| log_connection_yield(sql, conn){s.execute(sql)}}}
74
74
  super
75
75
  end
76
+
77
+ # Handle unsigned integer values
78
+ def setup_type_convertor_map
79
+ super
80
+ TypeConvertor::BASIC_MAP.dup
81
+ @type_convertor_map[Java::JavaSQL::Types::SMALLINT] = @type_convertor_map[Java::JavaSQL::Types::INTEGER]
82
+ @type_convertor_map[Java::JavaSQL::Types::INTEGER] = @type_convertor_map[Java::JavaSQL::Types::BIGINT]
83
+ @basic_type_convertor_map[Java::JavaSQL::Types::SMALLINT] = @basic_type_convertor_map[Java::JavaSQL::Types::INTEGER]
84
+ @basic_type_convertor_map[Java::JavaSQL::Types::INTEGER] = @basic_type_convertor_map[Java::JavaSQL::Types::BIGINT]
85
+ end
76
86
  end
77
87
  end
78
88
  end
@@ -149,18 +149,9 @@ module Sequel
149
149
  class Database < Sequel::Database
150
150
  include Sequel::Postgres::DatabaseMethods
151
151
 
152
- INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
153
- INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
154
-
155
152
  set_adapter_scheme :postgresql
156
153
  set_adapter_scheme :postgres
157
154
 
158
- # Whether infinite timestamps/dates should be converted on retrieval. By default, no
159
- # conversion is done, so an error is raised if you attempt to retrieve an infinite
160
- # timestamp/date. You can set this to :nil to convert to nil, :string to leave
161
- # as a string, or :float to convert to an infinite float.
162
- attr_reader :convert_infinite_timestamps
163
-
164
155
  # Convert given argument so that it can be used directly by pg. Currently, pg doesn't
165
156
  # handle fractional seconds in Time/DateTime or blobs with "\0". Only public for use by
166
157
  # the adapter, shouldn't be used by external code.
@@ -232,36 +223,19 @@ module Sequel
232
223
  conn
233
224
  end
234
225
 
235
- # Set whether to allow infinite timestamps/dates. Make sure the
236
- # conversion proc for date reflects that setting.
237
- def convert_infinite_timestamps=(v)
238
- @convert_infinite_timestamps = case v
239
- when Symbol
240
- v
241
- when 'nil'
242
- :nil
243
- when 'string'
244
- :string
245
- when 'float'
246
- :float
247
- when String
248
- typecast_value_boolean(v)
249
- else
250
- false
251
- end
226
+ # Always false, support was moved to pg_extended_date_support extension.
227
+ # Needs to stay defined here so that sequel_pg works.
228
+ def convert_infinite_timestamps
229
+ false
230
+ end
252
231
 
253
- pr = old_pr = @use_iso_date_format ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
254
- if v
255
- pr = lambda do |val|
256
- case val
257
- when *INFINITE_TIMESTAMP_STRINGS
258
- infinite_timestamp_value(val)
259
- else
260
- old_pr.call(val)
261
- end
262
- end
232
+ # Enable pg_extended_date_support extension if symbol or string is given.
233
+ def convert_infinite_timestamps=(v)
234
+ case v
235
+ when Symbol, String, true
236
+ extension(:pg_extended_date_support)
237
+ self.convert_infinite_timestamps = v
263
238
  end
264
- add_conversion_proc(1082, pr)
265
239
  end
266
240
 
267
241
  def disconnect_connection(conn)
@@ -312,6 +286,9 @@ module Sequel
312
286
  # a version of PostgreSQL before 9.0, you will probably want to
313
287
  # use a string if you are using any options at all, as the syntax
314
288
  # Sequel uses for options is only compatible with PostgreSQL 9.0+.
289
+ # This should be the full COPY statement passed to PostgreSQL, not
290
+ # just the SELECT query. If a string is given, the :format and
291
+ # :options options are ignored.
315
292
  # Dataset :: Uses a query instead of a table name when copying.
316
293
  # other :: Uses a table name (usually a symbol) when copying.
317
294
  #
@@ -462,21 +439,6 @@ module Sequel
462
439
  end
463
440
  end
464
441
 
465
- # If convert_infinite_timestamps is true and the value is infinite, return an appropriate
466
- # value based on the convert_infinite_timestamps setting.
467
- def to_application_timestamp(value)
468
- if convert_infinite_timestamps
469
- case value
470
- when *INFINITE_TIMESTAMP_STRINGS
471
- infinite_timestamp_value(value)
472
- else
473
- super
474
- end
475
- else
476
- super
477
- end
478
- end
479
-
480
442
  private
481
443
 
482
444
  # Execute the given SQL string or prepared statement on the connection object.
@@ -583,54 +545,10 @@ module Sequel
583
545
  end
584
546
  end
585
547
 
586
- # Return an appropriate value for the given infinite timestamp string.
587
- def infinite_timestamp_value(value)
588
- case convert_infinite_timestamps
589
- when :nil
590
- nil
591
- when :string
592
- value
593
- else
594
- value == 'infinity' ? PLUS_INFINITY : MINUS_INFINITY
595
- end
596
- end
597
-
598
548
  # Don't log, since logging is done by the underlying connection.
599
549
  def log_connection_execute(conn, sql)
600
550
  conn.execute(sql)
601
551
  end
602
-
603
- # If the value is an infinite value (either an infinite float or a string returned by
604
- # by PostgreSQL for an infinite date), return it without converting it if
605
- # convert_infinite_timestamps is set.
606
- def typecast_value_date(value)
607
- if convert_infinite_timestamps
608
- case value
609
- when *INFINITE_DATETIME_VALUES
610
- value
611
- else
612
- super
613
- end
614
- else
615
- super
616
- end
617
- end
618
-
619
- # If the value is an infinite value (either an infinite float or a string returned by
620
- # by PostgreSQL for an infinite timestamp), return it without converting it if
621
- # convert_infinite_timestamps is set.
622
- def typecast_value_datetime(value)
623
- if convert_infinite_timestamps
624
- case value
625
- when *INFINITE_DATETIME_VALUES
626
- value
627
- else
628
- super
629
- end
630
- else
631
- super
632
- end
633
- end
634
552
  end
635
553
 
636
554
  class Dataset < Sequel::Dataset
@@ -0,0 +1,215 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The pg_extended_date_support extension allows support
4
+ # for BC dates/timestamps by default, and infinite
5
+ # dates/timestamps if configured. Without this extension,
6
+ # BC and infinite dates/timestamps will be handled incorrectly
7
+ # or raise an error. This behavior isn't the default because
8
+ # it can hurt performance, and few users need support for BC
9
+ # and infinite dates/timestamps.
10
+ #
11
+ # To load the extension into the database:
12
+ #
13
+ # DB.extension :pg_extended_date_support
14
+ #
15
+ # To enable support for infinite dates/timestamps:
16
+ #
17
+ # DB.convert_infinite_timestamps = 'string' # or 'nil' or 'float'
18
+ #
19
+ # Related module: Sequel::Postgres::ExtendedDateSupport
20
+
21
+ #
22
+ module Sequel
23
+ module Postgres
24
+ module ExtendedDateSupport
25
+ DATE_YEAR_1 = Date.new(1)
26
+ DATETIME_YEAR_1 = DateTime.new(1)
27
+ TIME_YEAR_1 = Time.at(-62135596800).utc
28
+ INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
29
+ INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
30
+
31
+ # Add dataset methods and update the conversion proces for dates and timestamps.
32
+ def self.extended(db)
33
+ db.extend_datasets(DatasetMethods)
34
+ procs = db.conversion_procs
35
+ procs[1082] = ::Sequel.method(:string_to_date)
36
+ procs[1184] = procs[1114] = db.method(:to_application_timestamp)
37
+ end
38
+
39
+ # Whether infinite timestamps/dates should be converted on retrieval. By default, no
40
+ # conversion is done, so an error is raised if you attempt to retrieve an infinite
41
+ # timestamp/date. You can set this to :nil to convert to nil, :string to leave
42
+ # as a string, or :float to convert to an infinite float.
43
+ attr_reader :convert_infinite_timestamps
44
+
45
+ # Set whether to allow infinite timestamps/dates. Make sure the
46
+ # conversion proc for date reflects that setting.
47
+ def convert_infinite_timestamps=(v)
48
+ @convert_infinite_timestamps = case v
49
+ when Symbol
50
+ v
51
+ when 'nil'
52
+ :nil
53
+ when 'string'
54
+ :string
55
+ when 'float'
56
+ :float
57
+ when String, true
58
+ typecast_value_boolean(v)
59
+ else
60
+ false
61
+ end
62
+
63
+ pr = old_pr = Sequel.method(:string_to_date)
64
+ if @convert_infinite_timestamps
65
+ pr = lambda do |val|
66
+ case val
67
+ when *INFINITE_TIMESTAMP_STRINGS
68
+ infinite_timestamp_value(val)
69
+ else
70
+ old_pr.call(val)
71
+ end
72
+ end
73
+ end
74
+ add_conversion_proc(1082, pr)
75
+ end
76
+
77
+ # Handle BC dates in timestamps by moving the BC from after the time to
78
+ # after the date, to appease ruby's date parser.
79
+ # If convert_infinite_timestamps is true and the value is infinite, return an appropriate
80
+ # value based on the convert_infinite_timestamps setting.
81
+ def to_application_timestamp(value)
82
+ if value.is_a?(String) && (m = value.match(/(?:(?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/)) && (m[1] || m[2])
83
+ if m[2]
84
+ value = value.sub(' BC', '').sub(' ', ' BC ')
85
+ end
86
+ if m[1]
87
+ dt = DateTime.parse(value)
88
+ dt = dt.to_time unless Sequel.datetime_class == DateTime
89
+ Sequel.convert_output_timestamp(dt, Sequel.application_timezone)
90
+ else
91
+ super(value)
92
+ end
93
+ elsif convert_infinite_timestamps
94
+ case value
95
+ when *INFINITE_TIMESTAMP_STRINGS
96
+ infinite_timestamp_value(value)
97
+ else
98
+ super
99
+ end
100
+ else
101
+ super
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ # Return an appropriate value for the given infinite timestamp string.
108
+ def infinite_timestamp_value(value)
109
+ case convert_infinite_timestamps
110
+ when :nil
111
+ nil
112
+ when :string
113
+ value
114
+ else
115
+ value == 'infinity' ? PLUS_INFINITY : MINUS_INFINITY
116
+ end
117
+ end
118
+
119
+ # If the value is an infinite value (either an infinite float or a string returned by
120
+ # by PostgreSQL for an infinite date), return it without converting it if
121
+ # convert_infinite_timestamps is set.
122
+ def typecast_value_date(value)
123
+ if convert_infinite_timestamps
124
+ case value
125
+ when *INFINITE_DATETIME_VALUES
126
+ value
127
+ else
128
+ super
129
+ end
130
+ else
131
+ super
132
+ end
133
+ end
134
+
135
+ # If the value is an infinite value (either an infinite float or a string returned by
136
+ # by PostgreSQL for an infinite timestamp), return it without converting it if
137
+ # convert_infinite_timestamps is set.
138
+ def typecast_value_datetime(value)
139
+ if convert_infinite_timestamps
140
+ case value
141
+ when *INFINITE_DATETIME_VALUES
142
+ value
143
+ else
144
+ super
145
+ end
146
+ else
147
+ super
148
+ end
149
+ end
150
+
151
+ module DatasetMethods
152
+ private
153
+
154
+ # Handle BC Date objects.
155
+ def literal_date(date)
156
+ if date < DATE_YEAR_1
157
+ date <<= ((date.year) * 24 - 12)
158
+ date.strftime("'%Y-%m-%d BC'")
159
+ else
160
+ super
161
+ end
162
+ end
163
+
164
+ # Handle BC DateTime objects.
165
+ def literal_datetime(date)
166
+ if date < DATETIME_YEAR_1
167
+ date <<= ((date.year) * 24 - 12)
168
+ date = db.from_application_timestamp(date)
169
+ minutes = (date.is_a?(DateTime) ? date.offset * 1440 : date.utc_offset/60).to_i
170
+ date.strftime("'%Y-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*minutes.divmod(60))} BC'")
171
+ else
172
+ super
173
+ end
174
+ end
175
+
176
+ if RUBY_ENGINE == 'jruby'
177
+ # :nocov:
178
+
179
+ # Work around JRuby bug #4822 in Time#to_datetime for times before date of calendar reform
180
+ def literal_time(time)
181
+ if time < TIME_YEAR_1
182
+ literal_datetime(DateTime.parse(super))
183
+ else
184
+ super
185
+ end
186
+ end
187
+
188
+ ExtendedDateSupport::CONVERT_TYPES = [Java::JavaSQL::Types::DATE, Java::JavaSQL::Types::TIMESTAMP]
189
+
190
+ # Use non-JDBC parsing as JDBC parsing doesn't work for BC dates/timestamps.
191
+ def type_convertor(map, meta, type, i)
192
+ case type
193
+ when *CONVERT_TYPES
194
+ db.oid_convertor_proc(meta.getField(i).getOID)
195
+ else
196
+ super
197
+ end
198
+ end
199
+ # :nocov:
200
+ else
201
+ # Handle BC Time objects.
202
+ def literal_time(time)
203
+ if time < TIME_YEAR_1
204
+ literal_datetime(time.to_datetime)
205
+ else
206
+ super
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ Database.register_extension(:pg_extended_date_support, Postgres::ExtendedDateSupport)
215
+ end
@@ -484,7 +484,7 @@ module Sequel
484
484
  subclass.instance_variable_set(iv, sup_class_value)
485
485
  end
486
486
 
487
- unless ivs.include?("@dataset")
487
+ unless ivs.include?(:@dataset)
488
488
  if @dataset && self != Model
489
489
  subclass.set_dataset(@dataset.clone, :inherited=>true)
490
490
  elsif (n = subclass.name) && !n.to_s.empty?
@@ -164,7 +164,6 @@ module Sequel
164
164
  ary << offset
165
165
  end
166
166
  convert_input_timestamp(ary, input_timezone)
167
- convert_input_timestamp(ary, input_timezone)
168
167
  when Time
169
168
  if datetime_class == DateTime
170
169
  v.to_datetime
@@ -5,7 +5,7 @@ module Sequel
5
5
  MAJOR = 5
6
6
  # The minor version of Sequel. Bumped for every non-patch level
7
7
  # release, generally around once a month.
8
- MINOR = 1
8
+ MINOR = 2
9
9
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
10
10
  # releases that fix regressions from previous versions.
11
11
  TINY = 0
@@ -142,6 +142,18 @@ describe "A MySQL dataset" do
142
142
  DB.drop_table?(:items)
143
143
  end
144
144
 
145
+ it "should handle large unsigned smallint/integer values" do
146
+ DB.alter_table(:items){set_column_type :value, 'smallint unsigned'}
147
+ @d.insert(:value=>(1 << 15) + 1)
148
+ @d.get(:value).must_equal((1 << 15) + 1)
149
+ DB.alter_table(:items){set_column_type :value, 'integer unsigned'}
150
+ @d.update(:value=>(1 << 31) + 1)
151
+ @d.get(:value).must_equal((1 << 31) + 1)
152
+ DB.alter_table(:items){set_column_type :value, 'bigint unsigned'}
153
+ @d.update(:value=>(1 << 63) + 1)
154
+ @d.get(:value).must_equal((1 << 63) + 1)
155
+ end
156
+
145
157
  it "should support ORDER clause in UPDATE statements" do
146
158
  @d.order(:name).update_sql(:value => 1).must_equal 'UPDATE `items` SET `value` = 1 ORDER BY `name`'
147
159
  end
@@ -727,12 +727,13 @@ describe "A PostgreSQL dataset with a timestamp field" do
727
727
  DateTime :time
728
728
  end
729
729
  @d = @db[:test3]
730
+ @db.extension :pg_extended_date_support
730
731
  end
731
732
  before do
732
733
  @d.delete
733
734
  end
734
735
  after do
735
- @db.convert_infinite_timestamps = false if @db.adapter_scheme == :postgres
736
+ @db.convert_infinite_timestamps = false
736
737
  end
737
738
  after(:all) do
738
739
  @db.drop_table?(:test3)
@@ -756,74 +757,126 @@ describe "A PostgreSQL dataset with a timestamp field" do
756
757
  (t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).must_equal t.strftime('%N').to_i/1000
757
758
  end
758
759
 
759
- if DB.adapter_scheme == :postgres
760
- it "should handle infinite timestamps if convert_infinite_timestamps is set" do
761
- @d.insert(:time=>Sequel.cast('infinity', DateTime))
762
- @db.convert_infinite_timestamps = :nil
763
- @db[:test3].get(:time).must_be_nil
764
- @db.convert_infinite_timestamps = :string
765
- @db[:test3].get(:time).must_equal 'infinity'
766
- @db.convert_infinite_timestamps = :float
767
- @db[:test3].get(:time).must_equal 1.0/0.0
768
- @db.convert_infinite_timestamps = 'nil'
769
- @db[:test3].get(:time).must_be_nil
770
- @db.convert_infinite_timestamps = 'string'
771
- @db[:test3].get(:time).must_equal 'infinity'
772
- @db.convert_infinite_timestamps = 'float'
773
- @db[:test3].get(:time).must_equal 1.0/0.0
774
- @db.convert_infinite_timestamps = 't'
775
- @db[:test3].get(:time).must_equal 1.0/0.0
776
- @db.convert_infinite_timestamps = 'f'
777
- proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
778
- @db.convert_infinite_timestamps = nil
779
- proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
780
- @db.convert_infinite_timestamps = false
781
- proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
782
-
783
- @d.update(:time=>Sequel.cast('-infinity', DateTime))
784
- @db.convert_infinite_timestamps = :nil
785
- @db[:test3].get(:time).must_be_nil
786
- @db.convert_infinite_timestamps = :string
787
- @db[:test3].get(:time).must_equal '-infinity'
788
- @db.convert_infinite_timestamps = :float
789
- @db[:test3].get(:time).must_equal(-1.0/0.0)
790
- end
791
-
792
- it "should handle conversions from infinite strings/floats in models" do
793
- c = Class.new(Sequel::Model(:test3))
794
- @db.convert_infinite_timestamps = :float
795
- c.new(:time=>'infinity').time.must_equal 'infinity'
796
- c.new(:time=>'-infinity').time.must_equal '-infinity'
797
- c.new(:time=>1.0/0.0).time.must_equal 1.0/0.0
798
- c.new(:time=>-1.0/0.0).time.must_equal(-1.0/0.0)
799
- end
800
-
801
- it "should handle infinite dates if convert_infinite_timestamps is set" do
802
- @d.insert(:date=>Sequel.cast('infinity', Date))
803
- @db.convert_infinite_timestamps = :nil
804
- @db[:test3].get(:date).must_be_nil
805
- @db.convert_infinite_timestamps = :string
806
- @db[:test3].get(:date).must_equal 'infinity'
807
- @db.convert_infinite_timestamps = :float
808
- @db[:test3].get(:date).must_equal 1.0/0.0
809
-
810
- @d.update(:date=>Sequel.cast('-infinity', :timestamp))
811
- @db.convert_infinite_timestamps = :nil
812
- @db[:test3].get(:date).must_be_nil
813
- @db.convert_infinite_timestamps = :string
814
- @db[:test3].get(:date).must_equal '-infinity'
815
- @db.convert_infinite_timestamps = :float
816
- @db[:test3].get(:date).must_equal(-1.0/0.0)
817
- end
818
-
819
- it "should handle conversions from infinite strings/floats in models" do
820
- c = Class.new(Sequel::Model(:test3))
821
- @db.convert_infinite_timestamps = :float
822
- c.new(:date=>'infinity').date.must_equal 'infinity'
823
- c.new(:date=>'-infinity').date.must_equal '-infinity'
824
- c.new(:date=>1.0/0.0).date.must_equal 1.0/0.0
825
- c.new(:date=>-1.0/0.0).date.must_equal(-1.0/0.0)
826
- end
760
+ it "should handle BC times and dates" do
761
+ d = Date.new(-1234, 2, 3)
762
+ @db.get(Sequel.cast(d, Date)).must_equal d
763
+ begin
764
+ Sequel.default_timezone = :utc
765
+ t = Time.at(-100000000000).utc + 0.5
766
+ @db.get(Sequel.cast(t, Time)).must_equal t
767
+ @db.get(Sequel.cast(t, :timestamptz)).must_equal t
768
+ Sequel.datetime_class = DateTime
769
+ dt = DateTime.new(-1234, 2, 3, 10, 20, Rational(30, 20))
770
+ @db.get(Sequel.cast(dt, DateTime)).must_equal dt
771
+ @db.get(Sequel.cast(dt, :timestamptz)).must_equal dt
772
+ ensure
773
+ Sequel.datetime_class = Time
774
+ Sequel.default_timezone = nil
775
+ end
776
+ end
777
+
778
+ it "should handle infinite timestamps if convert_infinite_timestamps is set" do
779
+ @d.insert(:time=>Sequel.cast('infinity', DateTime))
780
+ @db.convert_infinite_timestamps = :nil
781
+ @db[:test3].get(:time).must_be_nil
782
+ @db.convert_infinite_timestamps = :string
783
+ @db[:test3].get(:time).must_equal 'infinity'
784
+ @db.convert_infinite_timestamps = :float
785
+ @db[:test3].get(:time).must_equal 1.0/0.0
786
+ @db.convert_infinite_timestamps = 'nil'
787
+ @db[:test3].get(:time).must_be_nil
788
+ @db.convert_infinite_timestamps = 'string'
789
+ @db[:test3].get(:time).must_equal 'infinity'
790
+ @db.convert_infinite_timestamps = 'float'
791
+ @db[:test3].get(:time).must_equal 1.0/0.0
792
+ @db.convert_infinite_timestamps = 't'
793
+ @db[:test3].get(:time).must_equal 1.0/0.0
794
+ @db.convert_infinite_timestamps = true
795
+ @db[:test3].get(:time).must_equal 1.0/0.0
796
+ @db.convert_infinite_timestamps = 'f'
797
+ proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
798
+ @db.convert_infinite_timestamps = nil
799
+ proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
800
+ @db.convert_infinite_timestamps = false
801
+ proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
802
+
803
+ @d.update(:time=>Sequel.cast('-infinity', DateTime))
804
+ @db.convert_infinite_timestamps = :nil
805
+ @db[:test3].get(:time).must_be_nil
806
+ @db.convert_infinite_timestamps = :string
807
+ @db[:test3].get(:time).must_equal '-infinity'
808
+ @db.convert_infinite_timestamps = :float
809
+ @db[:test3].get(:time).must_equal(-1.0/0.0)
810
+ end
811
+
812
+ it "should handle infinite dates if convert_infinite_timestamps is set" do
813
+ @d.insert(:time=>Sequel.cast('infinity', DateTime))
814
+ @db.convert_infinite_timestamps = :nil
815
+ @db[:test3].get(Sequel.cast(:time, Date)).must_be_nil
816
+ @db.convert_infinite_timestamps = :string
817
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal 'infinity'
818
+ @db.convert_infinite_timestamps = :float
819
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal 1.0/0.0
820
+ @db.convert_infinite_timestamps = 'nil'
821
+ @db[:test3].get(Sequel.cast(:time, Date)).must_be_nil
822
+ @db.convert_infinite_timestamps = 'string'
823
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal 'infinity'
824
+ @db.convert_infinite_timestamps = 'float'
825
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal 1.0/0.0
826
+ @db.convert_infinite_timestamps = 't'
827
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal 1.0/0.0
828
+ @db.convert_infinite_timestamps = true
829
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal 1.0/0.0
830
+ @db.convert_infinite_timestamps = 'f'
831
+ proc{@db[:test3].get(Sequel.cast(:time, Date))}.must_raise ArgumentError, Sequel::InvalidValue
832
+ @db.convert_infinite_timestamps = nil
833
+ proc{@db[:test3].get(Sequel.cast(:time, Date))}.must_raise ArgumentError, Sequel::InvalidValue
834
+ @db.convert_infinite_timestamps = false
835
+ proc{@db[:test3].get(Sequel.cast(:time, Date))}.must_raise ArgumentError, Sequel::InvalidValue
836
+
837
+ @d.update(:time=>Sequel.cast('-infinity', DateTime))
838
+ @db.convert_infinite_timestamps = :nil
839
+ @db[:test3].get(Sequel.cast(:time, Date)).must_be_nil
840
+ @db.convert_infinite_timestamps = :string
841
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal '-infinity'
842
+ @db.convert_infinite_timestamps = :float
843
+ @db[:test3].get(Sequel.cast(:time, Date)).must_equal(-1.0/0.0)
844
+ end
845
+
846
+ it "should handle conversions from infinite strings/floats in models" do
847
+ c = Class.new(Sequel::Model(:test3))
848
+ @db.convert_infinite_timestamps = :float
849
+ c.new(:time=>'infinity').time.must_equal 'infinity'
850
+ c.new(:time=>'-infinity').time.must_equal '-infinity'
851
+ c.new(:time=>1.0/0.0).time.must_equal 1.0/0.0
852
+ c.new(:time=>-1.0/0.0).time.must_equal(-1.0/0.0)
853
+ end
854
+
855
+ it "should handle infinite dates if convert_infinite_timestamps is set" do
856
+ @d.insert(:date=>Sequel.cast('infinity', Date))
857
+ @db.convert_infinite_timestamps = :nil
858
+ @db[:test3].get(:date).must_be_nil
859
+ @db.convert_infinite_timestamps = :string
860
+ @db[:test3].get(:date).must_equal 'infinity'
861
+ @db.convert_infinite_timestamps = :float
862
+ @db[:test3].get(:date).must_equal 1.0/0.0
863
+
864
+ @d.update(:date=>Sequel.cast('-infinity', :timestamp))
865
+ @db.convert_infinite_timestamps = :nil
866
+ @db[:test3].get(:date).must_be_nil
867
+ @db.convert_infinite_timestamps = :string
868
+ @db[:test3].get(:date).must_equal '-infinity'
869
+ @db.convert_infinite_timestamps = :float
870
+ @db[:test3].get(:date).must_equal(-1.0/0.0)
871
+ end
872
+
873
+ it "should handle conversions from infinite strings/floats in models" do
874
+ c = Class.new(Sequel::Model(:test3))
875
+ @db.convert_infinite_timestamps = :float
876
+ c.new(:date=>'infinity').date.must_equal 'infinity'
877
+ c.new(:date=>'-infinity').date.must_equal '-infinity'
878
+ c.new(:date=>1.0/0.0).date.must_equal 1.0/0.0
879
+ c.new(:date=>-1.0/0.0).date.must_equal(-1.0/0.0)
827
880
  end
828
881
 
829
882
  it "explain and analyze should not raise errors" do
@@ -0,0 +1,109 @@
1
+ require_relative "spec_helper"
2
+
3
+ describe "pg_extended_date_support extension" do
4
+ before do
5
+ @db = Sequel.mock(:host=>'postgres', :fetch=>{:v=>1}).extension(:pg_extended_date_support)
6
+ @db.extend_datasets{def quote_identifiers?; false end}
7
+ end
8
+ after do
9
+ Sequel.datetime_class = Time
10
+ Sequel.default_timezone = nil
11
+ end
12
+
13
+ it "should convert infinite timestamps and dates as configured" do
14
+ cp = @db.conversion_procs
15
+ d = lambda{|v| cp[1082].call(v)}
16
+ t = lambda{|v| cp[1114].call(v)}
17
+ pi = 'infinity'
18
+ ni = '-infinity'
19
+ today = Date.today
20
+ now = Time.now
21
+
22
+ d.(today.to_s).must_equal today
23
+ t.(now.strftime("%Y-%m-%d %H:%M:%S.%N")).must_equal now
24
+ proc{@db.typecast_value(:date, pi)}.must_raise Sequel::InvalidValue
25
+ proc{@db.typecast_value(:datetime, pi)}.must_raise Sequel::InvalidValue
26
+
27
+ [:nil, 'nil'].each do |v|
28
+ @db.convert_infinite_timestamps = v
29
+ d.(pi).must_be_nil
30
+ t.(pi).must_be_nil
31
+ d.(ni).must_be_nil
32
+ t.(ni).must_be_nil
33
+ @db.typecast_value(:date, pi).must_equal pi
34
+ @db.typecast_value(:datetime, pi).must_equal pi
35
+ @db.typecast_value(:date, ni).must_equal ni
36
+ @db.typecast_value(:datetime, ni).must_equal ni
37
+ end
38
+
39
+ d.(today.to_s).must_equal today
40
+ t.(now.strftime("%Y-%m-%d %H:%M:%S.%N")).must_equal now
41
+ @db.typecast_value(:date, today.to_s).must_equal today
42
+ @db.typecast_value(:datetime, now.strftime("%Y-%m-%d %H:%M:%S.%N")).must_equal now
43
+
44
+ [:string, 'string'].each do |v|
45
+ @db.convert_infinite_timestamps = v
46
+ d.(pi).must_equal pi
47
+ t.(pi).must_equal pi
48
+ d.(ni).must_equal ni
49
+ t.(ni).must_equal ni
50
+ end
51
+
52
+ [:float, 'float', 't', true].each do |v|
53
+ @db.convert_infinite_timestamps = v
54
+ d.(pi).must_equal 1.0/0.0
55
+ t.(pi).must_equal 1.0/0.0
56
+ d.(ni).must_equal -1.0/0.0
57
+ t.(ni).must_equal -1.0/0.0
58
+ end
59
+
60
+ ['f', false].each do |v|
61
+ @db.convert_infinite_timestamps = v
62
+ proc{d.(pi)}.must_raise ArgumentError, Sequel::InvalidValue
63
+ proc{t.(pi)}.must_raise ArgumentError, Sequel::InvalidValue
64
+ proc{d.(ni)}.must_raise ArgumentError, Sequel::InvalidValue
65
+ proc{t.(ni)}.must_raise ArgumentError, Sequel::InvalidValue
66
+ end
67
+ end
68
+
69
+ it "should handle parsing BC dates" do
70
+ @db.conversion_procs[1082].call("1092-10-20 BC").must_equal Date.new(-1091, 10, 20)
71
+ end
72
+
73
+ it "should handle parsing BC timestamps as Time values" do
74
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20-00:00 BC").must_equal Time.at(-100000000000).utc
75
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20-00:00:00 BC").must_equal Time.at(-100000000000).utc
76
+ Sequel.default_timezone = :utc
77
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20 BC").must_equal Time.at(-100000000000).utc
78
+ Sequel.default_timezone = nil
79
+ end
80
+
81
+ it "should handle parsing BC timestamps as DateTime values" do
82
+ Sequel.datetime_class = DateTime
83
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20-00:00 BC").must_equal DateTime.new(-1199, 2, 15, 14, 13, 20)
84
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20-00:00:00 BC").must_equal DateTime.new(-1199, 2, 15, 14, 13, 20)
85
+ Sequel.default_timezone = :utc
86
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20 BC").must_equal DateTime.new(-1199, 2, 15, 14, 13, 20)
87
+ end
88
+
89
+ it "should handle parsing AD timestamps with offset seconds" do
90
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20-00:00:00").must_equal DateTime.new(1200, 2, 15, 14, 13, 20).to_time
91
+ Sequel.datetime_class = DateTime
92
+ @db.conversion_procs[1114].call("1200-02-15 14:13:20-00:00:00").must_equal DateTime.new(1200, 2, 15, 14, 13, 20)
93
+ end
94
+
95
+ it "should format BC dates" do
96
+ @db.literal(Date.new(-1091, 10, 20)).must_equal "'1092-10-20 BC'"
97
+ @db.literal(Date.new(1092, 10, 20)).must_equal "'1092-10-20'"
98
+ end
99
+
100
+ it "should format BC datetimes" do
101
+ @db.literal(DateTime.new(-1199, 2, 15, 14, 13, 20)).must_equal "'1200-02-15 14:13:20.000000000+0000 BC'"
102
+ @db.literal(DateTime.new(1200, 2, 15, 14, 13, 20)).must_equal "'1200-02-15 14:13:20.000000+0000'"
103
+ end
104
+
105
+ it "should format BC times" do
106
+ @db.literal(Time.at(-100000000000).utc).must_equal "'1200-02-15 14:13:20.000000000+0000 BC'"
107
+ @db.literal(Time.at(100000000000).utc).must_equal "'5138-11-16 09:46:40.000000+0000'"
108
+ end
109
+ end
@@ -381,6 +381,25 @@ describe "A model inheriting from a model" do
381
381
  end
382
382
  end
383
383
 
384
+ describe "A model inheriting from a custom base that sets @dataset" do
385
+ before do
386
+ ::Feline = Class.new(Sequel::Model)
387
+ def Feline.inherited(subclass)
388
+ subclass.instance_variable_set(:@dataset, nil)
389
+ superclass.inherited(subclass)
390
+ end
391
+ class ::Leopard < Feline; end
392
+ end
393
+ after do
394
+ Object.send(:remove_const, :Leopard)
395
+ Object.send(:remove_const, :Feline)
396
+ end
397
+
398
+ it "should not infer the dataset of the subclass" do
399
+ proc{Leopard.dataset}.must_raise(Sequel::Error)
400
+ end
401
+ end
402
+
384
403
  describe "Model.primary_key" do
385
404
  before do
386
405
  @c = Class.new(Sequel::Model)
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.1.0
4
+ version: 5.2.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: 2017-10-01 00:00:00.000000000 Z
11
+ date: 2017-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -252,6 +252,7 @@ extra_rdoc_files:
252
252
  - doc/release_notes/4.49.0.txt
253
253
  - doc/release_notes/5.0.0.txt
254
254
  - doc/release_notes/5.1.0.txt
255
+ - doc/release_notes/5.2.0.txt
255
256
  files:
256
257
  - CHANGELOG
257
258
  - MIT-LICENSE
@@ -398,6 +399,7 @@ files:
398
399
  - doc/release_notes/4.9.0.txt
399
400
  - doc/release_notes/5.0.0.txt
400
401
  - doc/release_notes/5.1.0.txt
402
+ - doc/release_notes/5.2.0.txt
401
403
  - doc/schema_modification.rdoc
402
404
  - doc/security.rdoc
403
405
  - doc/sharding.rdoc
@@ -521,6 +523,7 @@ files:
521
523
  - lib/sequel/extensions/pg_array.rb
522
524
  - lib/sequel/extensions/pg_array_ops.rb
523
525
  - lib/sequel/extensions/pg_enum.rb
526
+ - lib/sequel/extensions/pg_extended_date_support.rb
524
527
  - lib/sequel/extensions/pg_hstore.rb
525
528
  - lib/sequel/extensions/pg_hstore_ops.rb
526
529
  - lib/sequel/extensions/pg_inet.rb
@@ -739,6 +742,7 @@ files:
739
742
  - spec/extensions/pg_array_ops_spec.rb
740
743
  - spec/extensions/pg_array_spec.rb
741
744
  - spec/extensions/pg_enum_spec.rb
745
+ - spec/extensions/pg_extended_date_support_spec.rb
742
746
  - spec/extensions/pg_hstore_ops_spec.rb
743
747
  - spec/extensions/pg_hstore_spec.rb
744
748
  - spec/extensions/pg_inet_ops_spec.rb