sequel 5.17.0 → 5.18.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 +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
|