sequel 5.17.0 → 5.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +18 -0
- data/MIT-LICENSE +1 -1
- data/doc/release_notes/5.18.0.txt +69 -0
- data/doc/testing.rdoc +1 -0
- data/lib/sequel/adapters/jdbc.rb +25 -16
- data/lib/sequel/adapters/jdbc/oracle.rb +6 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +20 -20
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +5 -6
- data/lib/sequel/adapters/jdbc/sqlite.rb +4 -2
- data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -2
- data/lib/sequel/adapters/mysql.rb +12 -9
- data/lib/sequel/adapters/mysql2.rb +3 -0
- data/lib/sequel/adapters/postgres.rb +4 -1
- data/lib/sequel/adapters/shared/postgres.rb +36 -32
- data/lib/sequel/adapters/sqlite.rb +58 -50
- data/lib/sequel/extensions/connection_expiration.rb +4 -4
- data/lib/sequel/extensions/connection_validator.rb +5 -4
- data/lib/sequel/extensions/named_timezones.rb +34 -17
- data/lib/sequel/model/base.rb +13 -3
- data/lib/sequel/plugins/after_initialize.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +12 -1
- data/lib/sequel/plugins/throw_failures.rb +110 -0
- data/lib/sequel/sql.rb +5 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/spec_helper.rb +1 -0
- data/spec/core/database_spec.rb +2 -1
- data/spec/core/expression_filters_spec.rb +6 -2
- data/spec/extensions/after_initialize_spec.rb +4 -0
- data/spec/extensions/named_timezones_spec.rb +3 -1
- data/spec/extensions/spec_helper.rb +1 -0
- data/spec/extensions/throw_failures_spec.rb +74 -0
- data/spec/integration/dataset_test.rb +4 -0
- data/spec/integration/plugin_test.rb +1 -1
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/model/model_spec.rb +6 -3
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f181e0ec05e457e72b2517202bbd159a6a066c9dd946201de44576b64bca302
|
4
|
+
data.tar.gz: f554f6268798f72c667851c6d952f960564e70ebb1e2d3d1208db74bbcd98834
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 474c441a62bb901abaf12968ec62cdd239f9679ce4cb1743cd58e7ec6032cb1e26d128fd49bbe7c3edfce9a0fb63b33975aa6426ebb8ee79f1309a09967ffef5
|
7
|
+
data.tar.gz: 8717400fcb03235ad51ab208d35e9accca05018c6531da025bbcefb8f4b557345e1afbbfd65862f2c213ca4b05826db72c5ecc6b8abdda5920af83dcd8fe35d9
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
=== 5.18.0 (2019-03-01)
|
2
|
+
|
3
|
+
* Use singleton .call methods on plain objects instead of procs/methods for faster type conversion (jeremyevans)
|
4
|
+
|
5
|
+
* Add Sequel::SQL::Blob.call to avoid indirection when converting values from the database (jeremyevans)
|
6
|
+
|
7
|
+
* Use while instead of each for inner loops in sqlite and jdbc adapters for better performance (jeremyevans)
|
8
|
+
|
9
|
+
* Make after_initialize plugin not make the argument to Model.call optional (jeremyevans)
|
10
|
+
|
11
|
+
* Allow Dataset#paged_each to be called without a block in the postgres and mysql2 adapters (jeremyevans)
|
12
|
+
|
13
|
+
* Remove flow-control exceptions in connection_expiration and connection_validator extensions (jeremyevans)
|
14
|
+
|
15
|
+
* Add throw_failures plugin for throwing ValidationFailed and HookFailed exceptions instead of raising them, up to 10x performance increase on JRuby (jeremyevans)
|
16
|
+
|
17
|
+
* Support tzinfo 2 in addition to tzinfo 1 in the named_timezones extension (jeremyevans) (#1596)
|
18
|
+
|
1
19
|
=== 5.17.0 (2019-02-01)
|
2
20
|
|
3
21
|
* Support skip_auto_validations instance method in auto_validations plugin (oldgreen, jeremyevans) (#1592)
|
data/MIT-LICENSE
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
Copyright (c) 2007-2008 Sharon Rosner
|
2
|
-
Copyright (c) 2008-
|
2
|
+
Copyright (c) 2008-2019 Jeremy Evans
|
3
3
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
of this software and associated documentation files (the "Software"), to
|
@@ -0,0 +1,69 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A throw_failures plugin has been added for throwing ValidationFailed
|
4
|
+
and HookFailed exceptions instead of raising them. This can improve
|
5
|
+
performance by up to 10x on JRuby and 10-15% on CRuby. However,
|
6
|
+
you would need to modify your exception handling from:
|
7
|
+
|
8
|
+
begin
|
9
|
+
# model.save
|
10
|
+
rescue Sequel::ValidationFailed => e
|
11
|
+
# handle failure
|
12
|
+
end
|
13
|
+
|
14
|
+
to:
|
15
|
+
|
16
|
+
e = catch(Sequel::ValidationFailed) do
|
17
|
+
# model.save
|
18
|
+
end
|
19
|
+
if e.is_a?(Sequel::ValidationFailed)
|
20
|
+
# handle failure
|
21
|
+
end
|
22
|
+
|
23
|
+
The throw_failures plugin will still work if you are not catching
|
24
|
+
the exception, falling back to the default behavior of raising
|
25
|
+
the exception.
|
26
|
+
|
27
|
+
* SQL::Blob.call has been added, so that SQL::Blob can be used
|
28
|
+
directly as a callable to create a new instance, resulting in
|
29
|
+
better performance in cases where a callable is needed.
|
30
|
+
|
31
|
+
= Other Improvements
|
32
|
+
|
33
|
+
* Type conversion is many adapters is now faster by switching from
|
34
|
+
Proc/Method instances to using singleton call methods on plain
|
35
|
+
objects. This can improve performance of row fetching by up to
|
36
|
+
10% in some cases.
|
37
|
+
|
38
|
+
* Row fetching is slightly faster in the jdbc and sqlite adapters,
|
39
|
+
by switching from each to while.
|
40
|
+
|
41
|
+
* tzinfo 2 is now supported when using the named_timezones extension.
|
42
|
+
tzinfo 1 remains supported.
|
43
|
+
|
44
|
+
* The optimized Dataset#paged_each methods in the postgres and mysql2
|
45
|
+
adapters now support being called without a block, returning an
|
46
|
+
Enumerator in that case, to mirror the behavior of the default
|
47
|
+
Dataset#paged_each method.
|
48
|
+
|
49
|
+
* Sequel no longer uses flow-control exceptions in the
|
50
|
+
connection_expiration and connection_validator extensions,
|
51
|
+
significantly improving performance on JRuby.
|
52
|
+
|
53
|
+
* The after_initialize plugin no longer makes the argument to
|
54
|
+
Model.call optional.
|
55
|
+
|
56
|
+
= Backwards Compatibility
|
57
|
+
|
58
|
+
* Some internal by not private constants and methods previously used
|
59
|
+
for type conversion in adapters have been removed:
|
60
|
+
|
61
|
+
* JDBC::Oracle.OracleDecimal
|
62
|
+
* JDBC::Oracle.OracleClob
|
63
|
+
* JDBC::Postgres.RubyPGArray
|
64
|
+
* JDBC::Postgres.RubyPGHstore
|
65
|
+
* JDBC::SqlAnywhere.SqlAnywhereBoolean
|
66
|
+
* JDBC::SQLServer.MSSQLRubyTime
|
67
|
+
* MySQL::TYPE_TRANSLATOR
|
68
|
+
* Postgres::TYPE_TRANSLATOR
|
69
|
+
|
data/doc/testing.rdoc
CHANGED
@@ -161,6 +161,7 @@ SEQUEL_FREEZE_DATABASE :: Freeze the database before running the integration spe
|
|
161
161
|
SEQUEL_IDENTIFIER_MANGLING :: Use the identifier_mangling extension when running the specs
|
162
162
|
SEQUEL_INTEGER64 :: Use the integer64 extension when running the adapter or integration specs
|
163
163
|
SEQUEL_MODEL_PREPARED_STATEMENTS :: Use the prepared_statements plugin when running the specs
|
164
|
+
SEQUEL_MODEL_THROW_FAILURES :: Use the throw_failures plugin when running the specs
|
164
165
|
SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running the specs
|
165
166
|
SEQUEL_NO_CHECK_SQLS :: Don't check for specific SQL syntax when running the specs
|
166
167
|
SEQUEL_CHECK_PENDING :: Try running all specs (note, can cause lockups for some adapters), and raise errors for skipped specs that don't fail
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -57,45 +57,53 @@ module Sequel
|
|
57
57
|
end
|
58
58
|
|
59
59
|
class TypeConvertor
|
60
|
+
CONVERTORS = convertors = {}
|
60
61
|
%w'Boolean Float Double Int Long Short'.each do |meth|
|
61
|
-
|
62
|
+
x = convertors[meth.to_sym] = Object.new
|
63
|
+
class_eval("def x.call(r, i) v = r.get#{meth}(i); v unless r.wasNull end", __FILE__, __LINE__)
|
62
64
|
end
|
63
65
|
%w'Object Array String Time Date Timestamp BigDecimal Blob Bytes Clob'.each do |meth|
|
64
|
-
|
66
|
+
x = convertors[meth.to_sym] = Object.new
|
67
|
+
class_eval("def x.call(r, i) r.get#{meth}(i) end", __FILE__, __LINE__)
|
65
68
|
end
|
66
|
-
|
69
|
+
x = convertors[:RubyTime] = Object.new
|
70
|
+
def x.call(r, i)
|
67
71
|
if v = r.getTime(i)
|
68
72
|
Sequel.string_to_time("#{v.to_string}.#{sprintf('%03i', v.getTime.divmod(1000).last)}")
|
69
73
|
end
|
70
74
|
end
|
71
|
-
|
75
|
+
x = convertors[:RubyDate] = Object.new
|
76
|
+
def x.call(r, i)
|
72
77
|
if v = r.getDate(i)
|
73
78
|
Date.civil(v.getYear + 1900, v.getMonth + 1, v.getDate)
|
74
79
|
end
|
75
80
|
end
|
76
|
-
|
81
|
+
x = convertors[:RubyTimestamp] = Object.new
|
82
|
+
def x.call(r, i)
|
77
83
|
if v = r.getTimestamp(i)
|
78
84
|
Sequel.database_to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
|
79
85
|
end
|
80
86
|
end
|
81
|
-
|
87
|
+
x = convertors[:RubyBigDecimal] = Object.new
|
88
|
+
def x.call(r, i)
|
82
89
|
if v = r.getBigDecimal(i)
|
83
90
|
::Kernel::BigDecimal(v.to_string)
|
84
91
|
end
|
85
92
|
end
|
86
|
-
|
93
|
+
x = convertors[:RubyBlob] = Object.new
|
94
|
+
def x.call(r, i)
|
87
95
|
if v = r.getBytes(i)
|
88
96
|
Sequel::SQL::Blob.new(String.from_java_bytes(v))
|
89
97
|
end
|
90
98
|
end
|
91
|
-
|
99
|
+
x = convertors[:RubyClob] = Object.new
|
100
|
+
def x.call(r, i)
|
92
101
|
if v = r.getClob(i)
|
93
102
|
v.getSubString(1, v.length)
|
94
103
|
end
|
95
104
|
end
|
96
105
|
|
97
|
-
|
98
|
-
MAP = Hash.new(o.method(:Object))
|
106
|
+
MAP = Hash.new(convertors[:Object])
|
99
107
|
types = Java::JavaSQL::Types
|
100
108
|
|
101
109
|
{
|
@@ -113,7 +121,7 @@ module Sequel
|
|
113
121
|
:TINYINT => :Short,
|
114
122
|
:VARCHAR => :String,
|
115
123
|
}.each do |type, meth|
|
116
|
-
MAP[types.const_get(type)] =
|
124
|
+
MAP[types.const_get(type)] = convertors[meth]
|
117
125
|
end
|
118
126
|
BASIC_MAP = MAP.dup
|
119
127
|
|
@@ -130,13 +138,11 @@ module Sequel
|
|
130
138
|
:TIMESTAMP => :Timestamp,
|
131
139
|
:VARBINARY => :Blob,
|
132
140
|
}.each do |type, meth|
|
133
|
-
BASIC_MAP[types.const_get(type)] =
|
134
|
-
MAP[types.const_get(type)] =
|
141
|
+
BASIC_MAP[types.const_get(type)] = convertors[meth]
|
142
|
+
MAP[types.const_get(type)] = convertors[:"Ruby#{meth}"]
|
135
143
|
end
|
136
|
-
|
137
144
|
MAP.freeze
|
138
145
|
BASIC_MAP.freeze
|
139
|
-
freeze
|
140
146
|
end
|
141
147
|
|
142
148
|
class Database < Sequel::Database
|
@@ -785,11 +791,14 @@ module Sequel
|
|
785
791
|
i += 1
|
786
792
|
cols << [output_identifier(meta.getColumnLabel(i)), i, convert ? type_convertor(map, meta, meta.getColumnType(i), i) : basic_type_convertor(map, meta, meta.getColumnType(i), i)]
|
787
793
|
end
|
794
|
+
max = i
|
788
795
|
self.columns = cols.map{|c| c[0]}
|
789
796
|
|
790
797
|
while result.next
|
791
798
|
row = {}
|
792
|
-
|
799
|
+
i = -1
|
800
|
+
while (i += 1) < max
|
801
|
+
n, j, pr = cols[i]
|
793
802
|
row[n] = pr.call(result, j)
|
794
803
|
end
|
795
804
|
yield row
|
@@ -16,8 +16,8 @@ module Sequel
|
|
16
16
|
|
17
17
|
module Oracle
|
18
18
|
JAVA_BIG_DECIMAL_CONSTRUCTOR = java.math.BigDecimal.java_class.constructor(Java::long).method(:new_instance)
|
19
|
-
|
20
|
-
def
|
19
|
+
ORACLE_DECIMAL = Object.new
|
20
|
+
def ORACLE_DECIMAL.call(r, i)
|
21
21
|
if v = r.getBigDecimal(i)
|
22
22
|
i = v.long_value
|
23
23
|
if v == JAVA_BIG_DECIMAL_CONSTRUCTOR.call(i)
|
@@ -28,7 +28,8 @@ module Sequel
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
ORACLE_CLOB = Object.new
|
32
|
+
def ORACLE_CLOB.call(r, i)
|
32
33
|
return unless clob = r.getClob(i)
|
33
34
|
str = clob.getSubString(1, clob.length)
|
34
35
|
clob.freeTemporary if clob.isTemporary
|
@@ -110,8 +111,8 @@ module Sequel
|
|
110
111
|
|
111
112
|
def setup_type_convertor_map
|
112
113
|
super
|
113
|
-
@type_convertor_map[:OracleDecimal] =
|
114
|
-
@type_convertor_map[:OracleClob] =
|
114
|
+
@type_convertor_map[:OracleDecimal] = ORACLE_DECIMAL
|
115
|
+
@type_convertor_map[:OracleClob] = ORACLE_CLOB
|
115
116
|
end
|
116
117
|
end
|
117
118
|
|
@@ -14,24 +14,6 @@ module Sequel
|
|
14
14
|
end
|
15
15
|
|
16
16
|
module Postgres
|
17
|
-
# Return PostgreSQL array types as ruby Arrays instead of
|
18
|
-
# JDBC PostgreSQL driver-specific array type. Only used if the
|
19
|
-
# database does not have a conversion proc for the type.
|
20
|
-
def self.RubyPGArray(r, i)
|
21
|
-
if v = r.getArray(i)
|
22
|
-
v.array.to_ary
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Return PostgreSQL hstore types as ruby Hashes instead of
|
27
|
-
# Java HashMaps. Only used if the database does not have a
|
28
|
-
# conversion proc for the type.
|
29
|
-
def self.RubyPGHstore(r, i)
|
30
|
-
if v = r.getObject(i)
|
31
|
-
v.to_hash
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
17
|
module DatabaseMethods
|
36
18
|
include Sequel::Postgres::DatabaseMethods
|
37
19
|
|
@@ -213,9 +195,27 @@ module Sequel
|
|
213
195
|
|
214
196
|
STRING_TYPE = Java::JavaSQL::Types::VARCHAR
|
215
197
|
ARRAY_TYPE = Java::JavaSQL::Types::ARRAY
|
216
|
-
ARRAY_METHOD = Postgres.method(:RubyPGArray)
|
217
198
|
PG_SPECIFIC_TYPES = [ARRAY_TYPE, Java::JavaSQL::Types::OTHER, Java::JavaSQL::Types::STRUCT, Java::JavaSQL::Types::TIME_WITH_TIMEZONE, Java::JavaSQL::Types::TIME].freeze
|
218
|
-
|
199
|
+
|
200
|
+
# Return PostgreSQL array types as ruby Arrays instead of
|
201
|
+
# JDBC PostgreSQL driver-specific array type. Only used if the
|
202
|
+
# database does not have a conversion proc for the type.
|
203
|
+
ARRAY_METHOD = Object.new
|
204
|
+
def ARRAY_METHOD.call(r, i)
|
205
|
+
if v = r.getArray(i)
|
206
|
+
v.array.to_ary
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Return PostgreSQL hstore types as ruby Hashes instead of
|
211
|
+
# Java HashMaps. Only used if the database does not have a
|
212
|
+
# conversion proc for the type.
|
213
|
+
HSTORE_METHOD = Object.new
|
214
|
+
def HSTORE_METHOD.call(r, i)
|
215
|
+
if v = r.getObject(i)
|
216
|
+
v.to_hash
|
217
|
+
end
|
218
|
+
end
|
219
219
|
|
220
220
|
def type_convertor(map, meta, type, i)
|
221
221
|
case type
|
@@ -30,11 +30,6 @@ module Sequel
|
|
30
30
|
end
|
31
31
|
|
32
32
|
module SqlAnywhere
|
33
|
-
def self.SqlAnywhereBoolean(r, i)
|
34
|
-
v = r.getShort(i)
|
35
|
-
v != 0 unless r.wasNull
|
36
|
-
end
|
37
|
-
|
38
33
|
module DatabaseMethods
|
39
34
|
include Sequel::SqlAnywhere::DatabaseMethods
|
40
35
|
include Sequel::JDBC::Transactions
|
@@ -58,7 +53,11 @@ module Sequel
|
|
58
53
|
private
|
59
54
|
|
60
55
|
SMALLINT_TYPE = Java::JavaSQL::Types::SMALLINT
|
61
|
-
BOOLEAN_METHOD =
|
56
|
+
BOOLEAN_METHOD = Object.new
|
57
|
+
def BOOLEAN_METHOD.call(r, i)
|
58
|
+
v = r.getShort(i)
|
59
|
+
v != 0 unless r.wasNull
|
60
|
+
end
|
62
61
|
|
63
62
|
def type_convertor(map, meta, type, i)
|
64
63
|
if convert_smallint_to_bool && type == SMALLINT_TYPE
|
@@ -78,12 +78,14 @@ module Sequel
|
|
78
78
|
super
|
79
79
|
@type_convertor_map[Java::JavaSQL::Types::INTEGER] = @type_convertor_map[Java::JavaSQL::Types::BIGINT]
|
80
80
|
@basic_type_convertor_map[Java::JavaSQL::Types::INTEGER] = @basic_type_convertor_map[Java::JavaSQL::Types::BIGINT]
|
81
|
-
@type_convertor_map[Java::JavaSQL::Types::DATE] =
|
81
|
+
x = @type_convertor_map[Java::JavaSQL::Types::DATE] = Object.new
|
82
|
+
def x.call(r, i)
|
82
83
|
if v = r.getString(i)
|
83
84
|
Sequel.string_to_date(v)
|
84
85
|
end
|
85
86
|
end
|
86
|
-
@type_convertor_map[Java::JavaSQL::Types::BLOB] =
|
87
|
+
x = @type_convertor_map[Java::JavaSQL::Types::BLOB] = Object.new
|
88
|
+
def x.call(r, i)
|
87
89
|
if v = r.getBytes(i)
|
88
90
|
Sequel::SQL::Blob.new(String.from_java_bytes(v))
|
89
91
|
elsif !r.wasNull
|
@@ -15,7 +15,8 @@ module Sequel
|
|
15
15
|
end
|
16
16
|
|
17
17
|
module SQLServer
|
18
|
-
|
18
|
+
MSSQL_RUBY_TIME = Object.new
|
19
|
+
def MSSQL_RUBY_TIME.call(r, i)
|
19
20
|
# MSSQL-Server TIME should be fetched as string to keep the precision intact, see:
|
20
21
|
# https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql#a-namebackwardcompatibilityfordownlevelclientsa-backward-compatibility-for-down-level-clients
|
21
22
|
if v = r.getString(i)
|
@@ -29,7 +30,7 @@ module Sequel
|
|
29
30
|
def setup_type_convertor_map
|
30
31
|
super
|
31
32
|
map = @type_convertor_map
|
32
|
-
map[Java::JavaSQL::Types::TIME] =
|
33
|
+
map[Java::JavaSQL::Types::TIME] = MSSQL_RUBY_TIME
|
33
34
|
|
34
35
|
# Work around constant lazy loading in some drivers
|
35
36
|
begin
|
@@ -8,19 +8,22 @@ require_relative 'utils/mysql_prepared_statements'
|
|
8
8
|
|
9
9
|
module Sequel
|
10
10
|
module MySQL
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
boolean = Object.new
|
12
|
+
def boolean.call(s) s.to_i != 0 end
|
13
|
+
TYPE_TRANSLATOR_BOOLEAN = boolean.freeze
|
14
|
+
integer = Object.new
|
15
|
+
def integer.call(s) s.to_i end
|
16
|
+
TYPE_TRANSLATOR_INTEGER = integer.freeze
|
17
|
+
float = Object.new
|
18
|
+
def float.call(s) s.to_f end
|
16
19
|
|
17
20
|
# Hash with integer keys and callable values for converting MySQL types.
|
18
21
|
MYSQL_TYPES = {}
|
19
22
|
{
|
20
23
|
[0, 246] => ::Kernel.method(:BigDecimal),
|
21
|
-
[2, 3, 8, 9, 13, 247, 248] =>
|
22
|
-
[4, 5] =>
|
23
|
-
[249, 250, 251, 252] => ::Sequel::SQL::Blob
|
24
|
+
[2, 3, 8, 9, 13, 247, 248] => integer,
|
25
|
+
[4, 5] => float,
|
26
|
+
[249, 250, 251, 252] => ::Sequel::SQL::Blob
|
24
27
|
}.each do |k,v|
|
25
28
|
k.each{|n| MYSQL_TYPES[n] = v}
|
26
29
|
end
|
@@ -131,7 +134,7 @@ module Sequel
|
|
131
134
|
# Modify the type translator used for the tinyint type based
|
132
135
|
# on the value given.
|
133
136
|
def convert_tinyint_to_bool=(v)
|
134
|
-
@conversion_procs[1] =
|
137
|
+
@conversion_procs[1] = v ? TYPE_TRANSLATOR_BOOLEAN : TYPE_TRANSLATOR_INTEGER
|
135
138
|
@convert_tinyint_to_bool = v
|
136
139
|
end
|
137
140
|
|
@@ -508,7 +508,7 @@ module Sequel
|
|
508
508
|
@use_iso_date_format = typecast_value_boolean(@opts.fetch(:use_iso_date_format, true))
|
509
509
|
initialize_postgres_adapter
|
510
510
|
add_conversion_proc(17, method(:unescape_bytea)) if USES_PG
|
511
|
-
add_conversion_proc(1082,
|
511
|
+
add_conversion_proc(1082, TYPE_TRANSLATOR_DATE) if @use_iso_date_format
|
512
512
|
self.convert_infinite_timestamps = @opts[:convert_infinite_timestamps]
|
513
513
|
end
|
514
514
|
|
@@ -612,6 +612,9 @@ module Sequel
|
|
612
612
|
|
613
613
|
# Use a cursor for paging.
|
614
614
|
def paged_each(opts=OPTS, &block)
|
615
|
+
unless block_given?
|
616
|
+
return enum_for(:paged_each, opts)
|
617
|
+
end
|
615
618
|
use_cursor(opts).each(&block)
|
616
619
|
end
|
617
620
|
|
@@ -23,43 +23,47 @@ module Sequel
|
|
23
23
|
PLUS_INFINITY = 1.0/0.0
|
24
24
|
MINUS_INFINITY = -1.0/0.0
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
26
|
+
boolean = Object.new
|
27
|
+
def boolean.call(s) s == 't' end
|
28
|
+
integer = Object.new
|
29
|
+
def integer.call(s) s.to_i end
|
30
|
+
float = Object.new
|
31
|
+
def float.call(s)
|
32
|
+
case s
|
33
|
+
when 'NaN'
|
34
|
+
NAN
|
35
|
+
when 'Infinity'
|
36
|
+
PLUS_INFINITY
|
37
|
+
when '-Infinity'
|
38
|
+
MINUS_INFINITY
|
39
|
+
else
|
40
|
+
s.to_f
|
40
41
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
42
|
+
end
|
43
|
+
date = Object.new
|
44
|
+
def date.call(s) ::Date.new(*s.split('-').map(&:to_i)) end
|
45
|
+
TYPE_TRANSLATOR_DATE = date.freeze
|
46
|
+
bytea = Object.new
|
47
|
+
def bytea.call(str)
|
48
|
+
str = if str =~ /\A\\x/
|
49
|
+
# PostgreSQL 9.0+ bytea hex format
|
50
|
+
str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
|
51
|
+
else
|
52
|
+
# Historical PostgreSQL bytea escape format
|
53
|
+
str.gsub(/\\(\\|'|[0-3][0-7][0-7])/) {|s|
|
54
|
+
if s.size == 2 then s[1,1] else s[1,3].oct.chr end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
::Sequel::SQL::Blob.new(str)
|
58
|
+
end
|
55
59
|
|
56
60
|
CONVERSION_PROCS = {}
|
57
61
|
|
58
62
|
{
|
59
|
-
[16] =>
|
60
|
-
[17] =>
|
61
|
-
[20, 21, 23, 26] =>
|
62
|
-
[700, 701] =>
|
63
|
+
[16] => boolean,
|
64
|
+
[17] => bytea,
|
65
|
+
[20, 21, 23, 26] => integer,
|
66
|
+
[700, 701] => float,
|
63
67
|
[1700] => ::Kernel.method(:BigDecimal),
|
64
68
|
[1083, 1266] => ::Sequel.method(:string_to_time),
|
65
69
|
[1082] => ::Sequel.method(:string_to_date),
|
@@ -7,67 +7,72 @@ module Sequel
|
|
7
7
|
module SQLite
|
8
8
|
FALSE_VALUES = (%w'0 false f no n' + [0]).freeze
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
blob = Object.new
|
11
|
+
def blob.call(s)
|
12
|
+
Sequel::SQL::Blob.new(s.to_s)
|
13
|
+
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
boolean = Object.new
|
16
|
+
def boolean.call(s)
|
17
|
+
s = s.downcase if s.is_a?(String)
|
18
|
+
!FALSE_VALUES.include?(s)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
date = Object.new
|
22
|
+
def date.call(s)
|
23
|
+
case s
|
24
|
+
when String
|
25
|
+
Sequel.string_to_date(s)
|
26
|
+
when Integer
|
27
|
+
Date.jd(s)
|
28
|
+
when Float
|
29
|
+
Date.jd(s.to_i)
|
30
|
+
else
|
31
|
+
raise Sequel::Error, "unhandled type when converting to date: #{s.inspect} (#{s.class.inspect})"
|
31
32
|
end
|
33
|
+
end
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
integer = Object.new
|
36
|
+
def integer.call(s)
|
37
|
+
s.to_i
|
38
|
+
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
float = Object.new
|
41
|
+
def float.call(s)
|
42
|
+
s.to_f
|
43
|
+
end
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
numeric = Object.new
|
46
|
+
def numeric.call(s)
|
47
|
+
s = s.to_s unless s.is_a?(String)
|
48
|
+
BigDecimal(s) rescue s
|
49
|
+
end
|
45
50
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
time = Object.new
|
52
|
+
def time.call(s)
|
53
|
+
case s
|
54
|
+
when String
|
55
|
+
Sequel.string_to_time(s)
|
56
|
+
when Integer
|
57
|
+
Sequel::SQLTime.create(s/3600, (s % 3600)/60, s % 60)
|
58
|
+
when Float
|
59
|
+
s, f = s.divmod(1)
|
60
|
+
Sequel::SQLTime.create(s/3600, (s % 3600)/60, s % 60, (f*1000000).round)
|
61
|
+
else
|
62
|
+
raise Sequel::Error, "unhandled type when converting to date: #{s.inspect} (#{s.class.inspect})"
|
58
63
|
end
|
59
|
-
end
|
64
|
+
end
|
60
65
|
|
61
66
|
# Hash with string keys and callable values for converting SQLite types.
|
62
67
|
SQLITE_TYPES = {}
|
63
68
|
{
|
64
|
-
%w'date' =>
|
65
|
-
%w'time' =>
|
66
|
-
%w'bit bool boolean' =>
|
67
|
-
%w'integer smallint mediumint int bigint' =>
|
68
|
-
%w'numeric decimal money' =>
|
69
|
-
%w'float double real dec fixed' + ['double precision'] =>
|
70
|
-
%w'blob' =>
|
69
|
+
%w'date' => date,
|
70
|
+
%w'time' => time,
|
71
|
+
%w'bit bool boolean' => boolean,
|
72
|
+
%w'integer smallint mediumint int bigint' => integer,
|
73
|
+
%w'numeric decimal money' => numeric,
|
74
|
+
%w'float double real dec fixed' + ['double precision'] => float,
|
75
|
+
%w'blob' => blob
|
71
76
|
}.each do |k,v|
|
72
77
|
k.each{|n| SQLITE_TYPES[n] = v}
|
73
78
|
end
|
@@ -317,10 +322,13 @@ module Sequel
|
|
317
322
|
cps = db.conversion_procs
|
318
323
|
type_procs = result.types.map{|t| cps[base_type_name(t)]}
|
319
324
|
cols = result.columns.map{|c| i+=1; [output_identifier(c), i, type_procs[i]]}
|
325
|
+
max = i+1
|
320
326
|
self.columns = cols.map(&:first)
|
321
327
|
result.each do |values|
|
322
328
|
row = {}
|
323
|
-
|
329
|
+
i = -1
|
330
|
+
while (i += 1) < max
|
331
|
+
name, id, type_proc = cols[i]
|
324
332
|
v = values[id]
|
325
333
|
if type_proc && v
|
326
334
|
v = type_proc.call(v)
|
@@ -32,6 +32,7 @@
|
|
32
32
|
module Sequel
|
33
33
|
module ConnectionExpiration
|
34
34
|
class Retry < Error; end
|
35
|
+
Sequel::Deprecation.deprecate_constant(self, :Retry)
|
35
36
|
|
36
37
|
# The number of seconds that need to pass since
|
37
38
|
# connection creation before expiring a connection.
|
@@ -72,7 +73,8 @@ module Sequel
|
|
72
73
|
# If it is expired, disconnect the connection, and retry with a new
|
73
74
|
# connection.
|
74
75
|
def acquire(*a)
|
75
|
-
|
76
|
+
conn = nil
|
77
|
+
1.times do
|
76
78
|
if (conn = super) &&
|
77
79
|
(cet = sync{@connection_expiration_timestamps[conn]}) &&
|
78
80
|
Sequel.elapsed_seconds_since(cet[0]) > cet[1]
|
@@ -84,10 +86,8 @@ module Sequel
|
|
84
86
|
end
|
85
87
|
|
86
88
|
disconnect_connection(conn)
|
87
|
-
|
89
|
+
redo
|
88
90
|
end
|
89
|
-
rescue Retry
|
90
|
-
retry
|
91
91
|
end
|
92
92
|
|
93
93
|
conn
|
@@ -51,6 +51,7 @@
|
|
51
51
|
module Sequel
|
52
52
|
module ConnectionValidator
|
53
53
|
class Retry < Error; end
|
54
|
+
Sequel::Deprecation.deprecate_constant(self, :Retry)
|
54
55
|
|
55
56
|
# The number of seconds that need to pass since
|
56
57
|
# connection checkin before attempting to validate
|
@@ -94,7 +95,9 @@ module Sequel
|
|
94
95
|
# test the connection for validity. If it is not valid,
|
95
96
|
# disconnect the connection, and retry with a new connection.
|
96
97
|
def acquire(*a)
|
97
|
-
|
98
|
+
conn = nil
|
99
|
+
|
100
|
+
1.times do
|
98
101
|
if (conn = super) &&
|
99
102
|
(timer = sync{@connection_timestamps.delete(conn)}) &&
|
100
103
|
Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout &&
|
@@ -107,10 +110,8 @@ module Sequel
|
|
107
110
|
end
|
108
111
|
|
109
112
|
disconnect_connection(conn)
|
110
|
-
|
113
|
+
redo
|
111
114
|
end
|
112
|
-
rescue Retry
|
113
|
-
retry
|
114
115
|
end
|
115
116
|
|
116
117
|
conn
|
@@ -63,24 +63,41 @@ module Sequel
|
|
63
63
|
|
64
64
|
private
|
65
65
|
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
66
|
+
# Handle both TZInfo 1 and TZInfo 2
|
67
|
+
if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
|
68
|
+
# :nodoc:
|
69
|
+
def convert_input_datetime_other(v, input_timezone)
|
70
|
+
local_offset = Rational(input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset, 86400)
|
71
|
+
(v - local_offset).new_offset(local_offset)
|
72
|
+
end
|
74
73
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
74
|
+
def convert_output_datetime_other(v, output_timezone)
|
75
|
+
v = output_timezone.utc_to_local(v.new_offset(0))
|
76
|
+
|
77
|
+
# Force DateTime output instead of TZInfo::DateTimeWithOffset
|
78
|
+
DateTime.jd(v.jd, v.hour, v.minute, v.second + v.sec_fraction, v.offset, v.start)
|
79
|
+
end
|
80
|
+
# :nodoc:
|
81
|
+
else
|
82
|
+
# Assume the given DateTime has a correct time but a wrong timezone. It is
|
83
|
+
# currently in UTC timezone, but it should be converted to the input_timezone.
|
84
|
+
# Keep the time the same but convert the timezone to the input_timezone.
|
85
|
+
# Expects the input_timezone to be a TZInfo::Timezone instance.
|
86
|
+
def convert_input_datetime_other(v, input_timezone)
|
87
|
+
local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset_rational
|
88
|
+
(v - local_offset).new_offset(local_offset)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Convert the given DateTime to use the given output_timezone.
|
92
|
+
# Expects the output_timezone to be a TZInfo::Timezone instance.
|
93
|
+
def convert_output_datetime_other(v, output_timezone)
|
94
|
+
# TZInfo 1 converts times, but expects the given DateTime to have an offset
|
95
|
+
# of 0 and always leaves the timezone offset as 0
|
96
|
+
v = output_timezone.utc_to_local(v.new_offset(0))
|
97
|
+
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset_rational
|
98
|
+
# Convert timezone offset from UTC to the offset for the output_timezone
|
99
|
+
(v - local_offset).new_offset(local_offset)
|
100
|
+
end
|
84
101
|
end
|
85
102
|
|
86
103
|
# Returns TZInfo::Timezone instance if given a String.
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1459,7 +1459,7 @@ module Sequel
|
|
1459
1459
|
raise Sequel::Error, "can't save frozen object" if frozen?
|
1460
1460
|
set_server(opts[:server]) if opts[:server]
|
1461
1461
|
unless _save_valid?(opts)
|
1462
|
-
raise(
|
1462
|
+
raise(validation_failed_error) if raise_on_failure?(opts)
|
1463
1463
|
return
|
1464
1464
|
end
|
1465
1465
|
checked_save_failure(opts){checked_transaction(opts){_save(opts)}}
|
@@ -1916,6 +1916,11 @@ module Sequel
|
|
1916
1916
|
Errors
|
1917
1917
|
end
|
1918
1918
|
|
1919
|
+
# A HookFailed exception for the given message tied to the current instance.
|
1920
|
+
def hook_failed_error(msg)
|
1921
|
+
HookFailed.new(msg, self)
|
1922
|
+
end
|
1923
|
+
|
1919
1924
|
# Clone constructor -- freeze internal data structures if the original's
|
1920
1925
|
# are frozen.
|
1921
1926
|
def initialize_clone(other)
|
@@ -1965,9 +1970,9 @@ module Sequel
|
|
1965
1970
|
"a hook failed"
|
1966
1971
|
end
|
1967
1972
|
|
1968
|
-
raise
|
1973
|
+
raise hook_failed_error(msg)
|
1969
1974
|
end
|
1970
|
-
|
1975
|
+
|
1971
1976
|
# Get the ruby class or classes related to the given column's type.
|
1972
1977
|
def schema_type_class(column)
|
1973
1978
|
if (sch = db_schema[column]) && (type = sch[:type])
|
@@ -2060,6 +2065,11 @@ module Sequel
|
|
2060
2065
|
def use_transaction?(opts = OPTS)
|
2061
2066
|
opts.fetch(:transaction, use_transactions)
|
2062
2067
|
end
|
2068
|
+
|
2069
|
+
# An ValidationFailed exception instance to raise for this instance.
|
2070
|
+
def validation_failed_error
|
2071
|
+
ValidationFailed.new(self)
|
2072
|
+
end
|
2063
2073
|
end
|
2064
2074
|
|
2065
2075
|
# DatasetMethods contains methods that all model datasets have.
|
@@ -70,10 +70,21 @@ module Sequel
|
|
70
70
|
#
|
71
71
|
# Then you can do:
|
72
72
|
#
|
73
|
-
# artist.update_fields(params['artist'], %w'name
|
73
|
+
# artist.update_fields(params['artist'], %w'name albums_attributes')
|
74
|
+
#
|
75
|
+
# Note that Rails 5+ does not use a Hash for submitted parameters, and therefore
|
76
|
+
# the above will not work. With Rails 5+, you have to use:
|
77
|
+
#
|
78
|
+
# artist.update_fields(params.to_unsafe_h['artist'], %w'name albums_attributes')
|
74
79
|
#
|
75
80
|
# To save changes to the artist, create the first album and associate it to the artist,
|
76
81
|
# and update the other existing associated album.
|
82
|
+
#
|
83
|
+
# You can pass options for individual nested attributes, which will override the default
|
84
|
+
# nested attributes options for that association. This is useful for per-call filtering
|
85
|
+
# of the allowed fields:
|
86
|
+
#
|
87
|
+
# a.set_nested_attributes(:albums, params['artist'], :fields=>%w'name')
|
77
88
|
module NestedAttributes
|
78
89
|
# Depend on the validate_associated plugin.
|
79
90
|
def self.apply(model)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The throw_failures plugin throws HookFailed and ValidationFailed exceptions instead
|
6
|
+
# of raising them. If there is no matching catch block, the UncaughtThrowError will be rescued
|
7
|
+
# and the HookFailed or ValidationFailed exception will be raised normally.
|
8
|
+
#
|
9
|
+
# If you are setting up the catch blocks to handle these failures, in the failure case this
|
10
|
+
# plugin is about 10-15% faster on CRuby and 10x faster on JRuby. If you are not
|
11
|
+
# setting up the catch blocks, in the failure case this plugin is about 30% slower on CRuby
|
12
|
+
# and 2x slower on JRuby. So this plugin should only be used if you are setting up catch
|
13
|
+
# blocks manually.
|
14
|
+
#
|
15
|
+
# This plugin will setup catch blocks automatically for internally rescued HookFailed
|
16
|
+
# exceptions when the model is configured to not raise exceptions on failure (by default,
|
17
|
+
# the exceptions are internally rescued in that case.
|
18
|
+
#
|
19
|
+
# To set up the catch blocks, use the class of the exception:
|
20
|
+
#
|
21
|
+
# ret = catch(Sequel::ValidationFailed) do
|
22
|
+
# model_instance.save
|
23
|
+
# end
|
24
|
+
# if ret.is_a?(Sequel::ValidationFailed)
|
25
|
+
# # handle failure
|
26
|
+
# else
|
27
|
+
# # handle success
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Usage:
|
31
|
+
#
|
32
|
+
# # Make all model subclass instances throw HookFailed and ValidationFailed exceptions
|
33
|
+
# # (called before loading subclasses)
|
34
|
+
# Sequel::Model.plugin :throw_failures
|
35
|
+
#
|
36
|
+
# # Make the Album class throw HookFailed and ValidationFailed exceptions
|
37
|
+
# Album.plugin :throw_failures
|
38
|
+
module ThrowFailures
|
39
|
+
module InstanceMethods
|
40
|
+
# Catch any thrown HookFailed exceptions.
|
41
|
+
def valid?(opts = OPTS)
|
42
|
+
catch_hook_failures{super} || false
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Catch any HookFailed exceptions thrown inside the block, and return
|
48
|
+
# nil if there were any.
|
49
|
+
def catch_hook_failures
|
50
|
+
called = ret = nil
|
51
|
+
caught = catch(HookFailed) do
|
52
|
+
ret = yield
|
53
|
+
called = true
|
54
|
+
end
|
55
|
+
ret if called
|
56
|
+
end
|
57
|
+
|
58
|
+
# Catch any thrown HookFailed exceptions if not raising on failure.
|
59
|
+
def checked_save_failure(opts)
|
60
|
+
if raise_on_failure?(opts)
|
61
|
+
super
|
62
|
+
else
|
63
|
+
catch_hook_failures{super}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if RUBY_VERSION >= '2.2' && (!defined?(JRUBY_VERSION) || JRUBY_VERSION > '9.1')
|
68
|
+
# Throw HookFailed with the generated error. If the throw is not
|
69
|
+
# caught, just return the originally generated error.
|
70
|
+
def hook_failed_error(msg)
|
71
|
+
e = super
|
72
|
+
throw HookFailed, e
|
73
|
+
rescue UncaughtThrowError
|
74
|
+
e
|
75
|
+
end
|
76
|
+
|
77
|
+
# Throw ValidationFailed with the generated error. If the throw is not
|
78
|
+
# caught, just return the originally generated error.
|
79
|
+
def validation_failed_error
|
80
|
+
e = super
|
81
|
+
throw ValidationFailed, e
|
82
|
+
rescue UncaughtThrowError
|
83
|
+
e
|
84
|
+
end
|
85
|
+
else
|
86
|
+
# UncaughtThrowError was added in Ruby 2.2. Older Ruby versions
|
87
|
+
# used ArgumentError with "uncaught throw" at the start of the message
|
88
|
+
|
89
|
+
# :nocov:
|
90
|
+
def hook_failed_error(msg)
|
91
|
+
e = super
|
92
|
+
throw HookFailed, e
|
93
|
+
rescue ArgumentError => e2
|
94
|
+
raise e2 unless e2.message.start_with?('uncaught throw')
|
95
|
+
e
|
96
|
+
end
|
97
|
+
|
98
|
+
def validation_failed_error
|
99
|
+
e = super
|
100
|
+
throw ValidationFailed, e
|
101
|
+
rescue ArgumentError => e2
|
102
|
+
raise e2 unless e2.message.start_with?('uncaught throw')
|
103
|
+
e
|
104
|
+
end
|
105
|
+
# :nocov:
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/sequel/sql.rb
CHANGED
@@ -1020,6 +1020,11 @@ module Sequel
|
|
1020
1020
|
include SQL::AliasMethods
|
1021
1021
|
include SQL::CastMethods
|
1022
1022
|
|
1023
|
+
class << self
|
1024
|
+
# Alias new to call for usage in conversion procs
|
1025
|
+
alias call new
|
1026
|
+
end
|
1027
|
+
|
1023
1028
|
# Return a LiteralString with the same content if no args are given, otherwise
|
1024
1029
|
# return a SQL::PlaceholderLiteralString with the current string and the given args.
|
1025
1030
|
def lit(*args)
|
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
8
8
|
# release, generally around once a month.
|
9
|
-
MINOR =
|
9
|
+
MINOR = 18
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
@@ -20,6 +20,7 @@ Sequel::Database.extension :duplicate_column_handler if ENV['SEQUEL_DUPLICATE_CO
|
|
20
20
|
Sequel::Database.extension :columns_introspection if ENV['SEQUEL_COLUMNS_INTROSPECTION']
|
21
21
|
Sequel::Model.cache_associations = false if ENV['SEQUEL_NO_CACHE_ASSOCIATIONS']
|
22
22
|
Sequel::Model.plugin :prepared_statements if ENV['SEQUEL_MODEL_PREPARED_STATEMENTS']
|
23
|
+
Sequel::Model.plugin :throw_failures if ENV['SEQUEL_MODEL_THROW_FAILURES']
|
23
24
|
Sequel::Model.cache_anonymous_models = false
|
24
25
|
|
25
26
|
require_relative '../guards_helper'
|
data/spec/core/database_spec.rb
CHANGED
@@ -2681,10 +2681,11 @@ describe "Database extensions" do
|
|
2681
2681
|
x = []
|
2682
2682
|
Sequel::Database.register_extension(:a, Module.new{define_singleton_method(:extended){|_| x << :a}})
|
2683
2683
|
Sequel::Database.register_extension(:b, Module.new{define_singleton_method(:extended){|_| x << :b}})
|
2684
|
+
m = Mutex.new
|
2684
2685
|
c = Class.new(Sequel::Database) do
|
2685
2686
|
def dataset_class_default; Sequel::Dataset end
|
2686
2687
|
define_method(:connect) do |_|
|
2687
|
-
x << :c
|
2688
|
+
m.synchronize{x << :c}
|
2688
2689
|
:connect
|
2689
2690
|
end
|
2690
2691
|
end
|
@@ -1280,8 +1280,12 @@ describe "Sequel::SQL::Wrapper" do
|
|
1280
1280
|
end
|
1281
1281
|
end
|
1282
1282
|
|
1283
|
-
describe "Sequel::SQL::Blob
|
1284
|
-
it "should
|
1283
|
+
describe "Sequel::SQL::Blob" do
|
1284
|
+
it ".call should be an alias for .new" do
|
1285
|
+
Sequel::SQL::Blob.call('a').must_equal Sequel::SQL::Blob.new('a')
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
it "#to_sequel_blob should return self" do
|
1285
1289
|
c = Sequel::SQL::Blob.new('a')
|
1286
1290
|
c.to_sequel_blob.must_be_same_as(c)
|
1287
1291
|
end
|
@@ -21,4 +21,8 @@ describe "Sequel::Plugins::AfterInitialize" do
|
|
21
21
|
it "should have after_initialize hook be called for objects loaded from the database" do
|
22
22
|
@c.call(:id=>1, :name=>'foo').values.must_equal(:id=>3, :name=>'foofoo')
|
23
23
|
end
|
24
|
+
|
25
|
+
it "should not allow .call to be called without arguments" do
|
26
|
+
proc{@c.call}.must_raise ArgumentError
|
27
|
+
end
|
24
28
|
end
|
@@ -73,11 +73,13 @@ describe "Sequel named_timezones extension" do
|
|
73
73
|
|
74
74
|
it "should assume datetimes coming out of the database that don't have an offset as coming from database_timezone" do
|
75
75
|
dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30')
|
76
|
+
dt.must_be_instance_of DateTime
|
76
77
|
dt.must_equal @dt
|
77
78
|
dt.offset.must_equal(-7/24.0)
|
78
79
|
|
79
80
|
dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30')
|
80
|
-
dt.
|
81
|
+
dt.must_be_instance_of DateTime
|
82
|
+
dt.must_equal(@dt + 1/6.0)
|
81
83
|
dt.offset.must_equal(-7/24.0)
|
82
84
|
end
|
83
85
|
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe "throw_failures plugin" do
|
4
|
+
before do
|
5
|
+
@c = Class.new(Sequel::Model(:items)) do
|
6
|
+
plugin :throw_failures
|
7
|
+
columns :x
|
8
|
+
set_primary_key :x
|
9
|
+
unrestrict_primary_key
|
10
|
+
def before_create
|
11
|
+
super
|
12
|
+
cancel_action 'bc' if x == 2
|
13
|
+
end
|
14
|
+
def before_destroy
|
15
|
+
super
|
16
|
+
cancel_action 'bd' if x == 2
|
17
|
+
end
|
18
|
+
def validate
|
19
|
+
super
|
20
|
+
errors.add(:x, "3") if x == 3
|
21
|
+
end
|
22
|
+
end
|
23
|
+
DB.reset
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should work normally if no exceptions are thrown/raised" do
|
27
|
+
o = @c.create(:x=>1)
|
28
|
+
o.must_be_kind_of @c
|
29
|
+
o.valid?.must_equal true
|
30
|
+
o.destroy.must_equal o
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should work normally when not rescuing exceptions internally when calling save" do
|
34
|
+
@c.new.set(:x => 2).save(:raise_on_failure=>false).must_be_nil
|
35
|
+
@c.raise_on_save_failure = false
|
36
|
+
@c.create(:x => 2).must_be_nil
|
37
|
+
@c.load(:x => 2).destroy(:raise_on_failure=>false).must_be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should work normally when not rescuing exceptions internally when calling valid?" do
|
41
|
+
@c.send(:define_method, :before_validation){cancel_action "bv"}
|
42
|
+
@c.new(:x => 2).valid?.must_equal false
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should raise exceptions if no catch blocks have been setup and set to raise on failure" do
|
46
|
+
begin
|
47
|
+
@c.create(:x => 2)
|
48
|
+
rescue Sequel::HookFailed => e
|
49
|
+
e.backtrace.wont_be_empty
|
50
|
+
1
|
51
|
+
end.must_equal 1
|
52
|
+
|
53
|
+
begin
|
54
|
+
@c.create(:x => 3)
|
55
|
+
rescue Sequel::ValidationFailed => e
|
56
|
+
e.backtrace.wont_be_empty
|
57
|
+
1
|
58
|
+
end.must_equal 1
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should allow catching exceptions instead of rescuing them" do
|
62
|
+
e = catch(Sequel::HookFailed){@c.create(:x => 2)}
|
63
|
+
e.must_be_kind_of Sequel::HookFailed
|
64
|
+
e.backtrace.must_be_nil
|
65
|
+
|
66
|
+
e = catch(Sequel::ValidationFailed){@c.create(:x => 3)}
|
67
|
+
e.must_be_kind_of Sequel::ValidationFailed
|
68
|
+
e.backtrace.must_be_nil
|
69
|
+
|
70
|
+
e = catch(Sequel::HookFailed){@c.load(:x => 2).destroy}
|
71
|
+
e.must_be_kind_of Sequel::HookFailed
|
72
|
+
e.backtrace.must_be_nil
|
73
|
+
end
|
74
|
+
end
|
@@ -149,6 +149,10 @@ describe "Simple Dataset operations" do
|
|
149
149
|
rows.must_equal((1..100).map{|i| {:id=>i, :number=>i*10}}.reverse)
|
150
150
|
end
|
151
151
|
|
152
|
+
rows = []
|
153
|
+
@ds.order(:number).limit(50, 25).paged_each(:rows_per_fetch=>3).each{|row| rows << row}
|
154
|
+
rows.must_equal((26..75).map{|i| {:id=>i, :number=>i*10}})
|
155
|
+
|
152
156
|
rows = []
|
153
157
|
@ds.order(:number).limit(50, 25).paged_each(:rows_per_fetch=>3){|row| rows << row}
|
154
158
|
rows.must_equal((26..75).map{|i| {:id=>i, :number=>i*10}})
|
@@ -140,7 +140,7 @@ describe "Class Table Inheritance Plugin" do
|
|
140
140
|
end
|
141
141
|
|
142
142
|
it "should update rows in all tables" do
|
143
|
-
Executive.
|
143
|
+
Executive[:id=>@i4].update(:name=>'Ex2', :num_managers=>8, :num_staff=>9)
|
144
144
|
@db[:employees][:id=>@i4].must_equal(:id=>@i4, :name=>'Ex2', :kind=>'Executive')
|
145
145
|
@db[:managers][:id=>@i4].must_equal(:id=>@i4, :num_staff=>9)
|
146
146
|
@db[:executives][:id=>@i4].must_equal(:id=>@i4, :num_managers=>8)
|
@@ -20,6 +20,7 @@ Sequel.split_symbols = true if ENV['SEQUEL_SPLIT_SYMBOLS']
|
|
20
20
|
Sequel::Database.extension :columns_introspection if ENV['SEQUEL_COLUMNS_INTROSPECTION']
|
21
21
|
Sequel::Model.cache_associations = false if ENV['SEQUEL_NO_CACHE_ASSOCIATIONS']
|
22
22
|
Sequel::Model.plugin :prepared_statements if ENV['SEQUEL_MODEL_PREPARED_STATEMENTS']
|
23
|
+
Sequel::Model.plugin :throw_failures if ENV['SEQUEL_MODEL_THROW_FAILURES']
|
23
24
|
Sequel::Model.use_transactions = false
|
24
25
|
Sequel::Model.cache_anonymous_models = false
|
25
26
|
|
data/spec/model/model_spec.rb
CHANGED
@@ -588,9 +588,12 @@ describe Sequel::Model, ".fetch" do
|
|
588
588
|
end
|
589
589
|
|
590
590
|
it "should return true for .empty? and not raise an error on empty selection" do
|
591
|
-
|
592
|
-
|
593
|
-
|
591
|
+
@c.dataset = @c.dataset.with_extend do
|
592
|
+
def fetch_rows(sql)
|
593
|
+
yield({:count => 0})
|
594
|
+
end
|
595
|
+
end
|
596
|
+
@c.fetch("SELECT * FROM items WHERE FALSE").empty?
|
594
597
|
end
|
595
598
|
end
|
596
599
|
|
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.
|
4
|
+
version: 5.18.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: 2019-
|
11
|
+
date: 2019-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -201,6 +201,7 @@ extra_rdoc_files:
|
|
201
201
|
- doc/release_notes/5.15.0.txt
|
202
202
|
- doc/release_notes/5.16.0.txt
|
203
203
|
- doc/release_notes/5.17.0.txt
|
204
|
+
- doc/release_notes/5.18.0.txt
|
204
205
|
files:
|
205
206
|
- CHANGELOG
|
206
207
|
- MIT-LICENSE
|
@@ -288,6 +289,7 @@ files:
|
|
288
289
|
- doc/release_notes/5.15.0.txt
|
289
290
|
- doc/release_notes/5.16.0.txt
|
290
291
|
- doc/release_notes/5.17.0.txt
|
292
|
+
- doc/release_notes/5.18.0.txt
|
291
293
|
- doc/release_notes/5.2.0.txt
|
292
294
|
- doc/release_notes/5.3.0.txt
|
293
295
|
- doc/release_notes/5.4.0.txt
|
@@ -534,6 +536,7 @@ files:
|
|
534
536
|
- lib/sequel/plugins/subset_conditions.rb
|
535
537
|
- lib/sequel/plugins/table_select.rb
|
536
538
|
- lib/sequel/plugins/tactical_eager_loading.rb
|
539
|
+
- lib/sequel/plugins/throw_failures.rb
|
537
540
|
- lib/sequel/plugins/timestamps.rb
|
538
541
|
- lib/sequel/plugins/touch.rb
|
539
542
|
- lib/sequel/plugins/tree.rb
|
@@ -709,6 +712,7 @@ files:
|
|
709
712
|
- spec/extensions/table_select_spec.rb
|
710
713
|
- spec/extensions/tactical_eager_loading_spec.rb
|
711
714
|
- spec/extensions/thread_local_timezones_spec.rb
|
715
|
+
- spec/extensions/throw_failures_spec.rb
|
712
716
|
- spec/extensions/timestamps_spec.rb
|
713
717
|
- spec/extensions/to_dot_spec.rb
|
714
718
|
- spec/extensions/touch_spec.rb
|