sequel 5.1.0 → 5.2.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
  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