sequel 4.36.0 → 4.37.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/Rakefile +1 -1
- data/doc/prepared_statements.rdoc +2 -2
- data/doc/release_notes/4.35.0.txt +1 -1
- data/doc/release_notes/4.37.0.txt +50 -0
- data/doc/schema_modification.rdoc +6 -0
- data/lib/sequel/adapters/jdbc.rb +1 -1
- data/lib/sequel/adapters/mock.rb +3 -2
- data/lib/sequel/adapters/mysql.rb +3 -1
- data/lib/sequel/adapters/mysql2.rb +105 -14
- data/lib/sequel/adapters/postgres.rb +9 -2
- data/lib/sequel/adapters/shared/oracle.rb +40 -0
- data/lib/sequel/adapters/shared/sqlite.rb +17 -1
- data/lib/sequel/adapters/swift.rb +5 -3
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +82 -0
- data/lib/sequel/adapters/{shared → utils}/mysql_prepared_statements.rb +0 -70
- data/lib/sequel/ast_transformer.rb +7 -1
- data/lib/sequel/connection_pool.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +2 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +2 -2
- data/lib/sequel/connection_pool/single.rb +2 -2
- data/lib/sequel/connection_pool/threaded.rb +7 -5
- data/lib/sequel/database/schema_generator.rb +4 -0
- data/lib/sequel/extensions/migration.rb +3 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +18 -0
- data/spec/core/connection_pool_spec.rb +44 -2
- data/spec/core/dataset_spec.rb +4 -0
- data/spec/core/mock_adapter_spec.rb +5 -4
- data/spec/extensions/migration_spec.rb +3 -0
- data/spec/extensions/pg_json_spec.rb +1 -1
- data/spec/integration/schema_test.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e32a73f763f4abf6ea2b3e2833cd0d0a85356842
|
4
|
+
data.tar.gz: 9856a7e6a4c175f16c745339609b8b4d3d6b4d61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5286ffd2c900e56b4d07598dd6ecb4fcdf7bfd5694a56537ba4ca29a55f1801055e42d6ccd2b49c2aa655065f63380d8f068ef55602bb81c768947540788afa4
|
7
|
+
data.tar.gz: 842a025e1ecdcbe2990dbfd5f40ebcf2d9db9408d73e62e73ef4c36e0a56c27affa76a60d780845abb34d23e3108d6783fb3f206f7a13f304daad868d62b2667
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
=== 4.37.0 (2016-08-01)
|
2
|
+
|
3
|
+
* Add support for regular expression matching on Oracle 10g+ using REGEXP_LIKE (johndcaldwell) (#1221)
|
4
|
+
|
5
|
+
* Recognize an additional disconnect error in the postgres adapter (jeremyevans)
|
6
|
+
|
7
|
+
* Make connection pool remove connections for disconnect errors not raised as DatabaseDisconnectError (jeremyevans)
|
8
|
+
|
9
|
+
* Support mysql2 0.4+ native prepared statements and bound variables (jeremyevans)
|
10
|
+
|
11
|
+
* Add Database#values for VALUES support on SQLite 3.8.3+ (jeremyevans)
|
12
|
+
|
13
|
+
* Support create_view :columns option on SQLite 3.9.0+ (jeremyevans)
|
14
|
+
|
15
|
+
* Make migration reverser handle alter_table add_constraint using a hash as the first argument (soupmatt) (#1215)
|
16
|
+
|
17
|
+
* Make ASTTransformer handle Sequel.extract (jeremyevans) (#1213)
|
18
|
+
|
1
19
|
=== 4.36.0 (2016-07-01)
|
2
20
|
|
3
21
|
* Deprecate use of Bignum class as generic type, since the behavior will change in ruby 2.4 (jeremyevans)
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ VERS = lambda do
|
|
6
6
|
require File.expand_path("../lib/sequel/version", __FILE__)
|
7
7
|
Sequel.version
|
8
8
|
end
|
9
|
-
CLEAN.include ["**/.*.sw?", "sequel-*.gem", ".config", "rdoc", "coverage", "www/public/*.html", "www/public/rdoc*", '**/*.rbc']
|
9
|
+
CLEAN.include ["**/.*.sw?", "sequel-*.gem", ".config", "rdoc", "coverage", "www/public/*.html", "www/public/rdoc*", '**/*.rbc', "spec/bin-sequel-*"]
|
10
10
|
|
11
11
|
# Gem Packaging and Release
|
12
12
|
|
@@ -7,8 +7,8 @@ the following adapters:
|
|
7
7
|
|
8
8
|
* ibmdb (prepared statements only)
|
9
9
|
* jdbc
|
10
|
-
* mysql (prepared statements
|
11
|
-
* mysql2 (prepared statements
|
10
|
+
* mysql (server prepared statements using literalized connection variables)
|
11
|
+
* mysql2 (full support on 0.4+, otherwise server prepared statements using literalized connection variables)
|
12
12
|
* oracle (requires type specifiers for nil/NULL values)
|
13
13
|
* postgres (when using the pg driver)
|
14
14
|
* sqlite
|
@@ -56,7 +56,7 @@
|
|
56
56
|
Example.first.lock!('FOR NO KEY UPDATE')
|
57
57
|
#=> SELECT * FROM examples WHERE id = 1 FOR NO KEY UPDATE LIMIT 1
|
58
58
|
|
59
|
-
* Sequel::
|
59
|
+
* Sequel::Dataset#skip_locked has been added, which skips locked rows
|
60
60
|
when returning query results. This is useful whenever you are
|
61
61
|
implementing a queue or similar data structure. Currently, this is
|
62
62
|
supported on PostgreSQL 9.5+, Oracle, and Microsoft SQL Server.
|
@@ -0,0 +1,50 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Database#values has been added on SQLite#3.8.3+, operating similarly
|
4
|
+
to the support on PostgreSQL:
|
5
|
+
|
6
|
+
DB.values([[1, 2], [3, 4]]).select_map([:column1, :column2])
|
7
|
+
# => [[1, 2], [3, 4]]
|
8
|
+
|
9
|
+
* Regular expressions in dataset filters are now supported on Oracle
|
10
|
+
10g+:
|
11
|
+
|
12
|
+
DB[:t].where(:c=>/re/)
|
13
|
+
# SELECT * FROM "T" WHERE REGEXP_LIKE("C",'re')
|
14
|
+
|
15
|
+
= Other Improvements
|
16
|
+
|
17
|
+
* Sequel now supports the use of native prepared statements and bound
|
18
|
+
variables in the mysql2 adapter, when mysql2 0.4+ is used.
|
19
|
+
Previously, the mysql2 adapter supported database prepared
|
20
|
+
statements, but variables were always literalized. That is still
|
21
|
+
supported when mysql2 <0.4 is used.
|
22
|
+
|
23
|
+
* The connection pool now removes connections if it detects a
|
24
|
+
disconnect error that is not raised as a
|
25
|
+
Sequel::DatabaseDisconnectError. Such exceptions are reraised
|
26
|
+
without converted them to Sequel::DatabaseDisconnectError, but the
|
27
|
+
related connection is now removed from the pool.
|
28
|
+
|
29
|
+
* The reversible migration support now handles add_constraint with an
|
30
|
+
options hash as the first argument.
|
31
|
+
|
32
|
+
* ASTTransformer now handles Sequel.extract, allowing Dataset#qualify
|
33
|
+
and other uses of ASTTransformer to work with such values.
|
34
|
+
|
35
|
+
* The create_view :columns option is now suppported on SQLite 3.9.0+.
|
36
|
+
|
37
|
+
* An additional disconnect error is now recognized in the postgres
|
38
|
+
adapter.
|
39
|
+
|
40
|
+
* A frozen string literal issue has been fixed when multiple different
|
41
|
+
database connection approaches have failed in the jdbc adapter.
|
42
|
+
|
43
|
+
= Backwards Compatibility
|
44
|
+
|
45
|
+
* External database adapters need to make sure that
|
46
|
+
Database#database_error_classes returns a valid result if called
|
47
|
+
during Database#initialize. If you have an external adapter where
|
48
|
+
one of the error classes depends on an argument given when
|
49
|
+
connecting (such as the connection string), you may have to make
|
50
|
+
some changes.
|
@@ -519,6 +519,12 @@ This modifies the default value of a column:
|
|
519
519
|
set_column_default :copies_sold, 0
|
520
520
|
end
|
521
521
|
|
522
|
+
To remove a default value for a column, use +nil+ as the value:
|
523
|
+
|
524
|
+
alter_table(:albums) do
|
525
|
+
set_column_default :copies_sold, nil
|
526
|
+
end
|
527
|
+
|
522
528
|
=== +set_column_type+
|
523
529
|
|
524
530
|
This modifies a column's type. Most databases will attempt to convert existing values in
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -224,7 +224,7 @@ module Sequel
|
|
224
224
|
c
|
225
225
|
rescue JavaSQL::SQLException, NativeException, StandardError => e2
|
226
226
|
unless e2.message == e.message
|
227
|
-
e2.message
|
227
|
+
e2.message = "#{e2.message}\n#{e.class.name}: #{e.message}"
|
228
228
|
end
|
229
229
|
raise e2
|
230
230
|
end
|
data/lib/sequel/adapters/mock.rb
CHANGED
@@ -53,7 +53,7 @@ module Sequel
|
|
53
53
|
SHARED_ADAPTER_SETUP = {
|
54
54
|
'postgres' => lambda do |db|
|
55
55
|
db.instance_eval do
|
56
|
-
@server_version =
|
56
|
+
@server_version = 90500
|
57
57
|
initialize_postgres_adapter
|
58
58
|
end
|
59
59
|
db.extend(Module.new do
|
@@ -68,6 +68,7 @@ module Sequel
|
|
68
68
|
end,
|
69
69
|
'oracle' => lambda do |db|
|
70
70
|
db.instance_eval do
|
71
|
+
@server_version = 11000000
|
71
72
|
@primary_key_sequences = {}
|
72
73
|
end
|
73
74
|
end,
|
@@ -83,7 +84,7 @@ module Sequel
|
|
83
84
|
end,
|
84
85
|
'sqlite' => lambda do |db|
|
85
86
|
db.instance_eval do
|
86
|
-
@sqlite_version =
|
87
|
+
@sqlite_version = 30903
|
87
88
|
end
|
88
89
|
end
|
89
90
|
}
|
@@ -7,7 +7,7 @@ rescue LoadError
|
|
7
7
|
end
|
8
8
|
raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
|
9
9
|
|
10
|
-
Sequel.require %w'
|
10
|
+
Sequel.require %w'utils/mysql_mysql2 utils/mysql_prepared_statements', 'adapters'
|
11
11
|
|
12
12
|
module Sequel
|
13
13
|
# Module for holding all MySQL-related classes and modules for Sequel.
|
@@ -41,6 +41,7 @@ module Sequel
|
|
41
41
|
# Database class for MySQL databases used with Sequel.
|
42
42
|
class Database < Sequel::Database
|
43
43
|
include Sequel::MySQL::DatabaseMethods
|
44
|
+
include Sequel::MySQL::MysqlMysql2::DatabaseMethods
|
44
45
|
include Sequel::MySQL::PreparedStatements::DatabaseMethods
|
45
46
|
|
46
47
|
# Regular expression used for getting accurate number of rows
|
@@ -287,6 +288,7 @@ module Sequel
|
|
287
288
|
# Dataset class for MySQL datasets accessed via the native driver.
|
288
289
|
class Dataset < Sequel::Dataset
|
289
290
|
include Sequel::MySQL::DatasetMethods
|
291
|
+
include Sequel::MySQL::MysqlMysql2::DatasetMethods
|
290
292
|
include Sequel::MySQL::PreparedStatements::DatasetMethods
|
291
293
|
|
292
294
|
Database::DatasetClass = self
|
@@ -1,15 +1,23 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
3
|
require 'mysql2'
|
4
|
-
Sequel.require %w'
|
4
|
+
Sequel.require %w'utils/mysql_mysql2', 'adapters'
|
5
5
|
|
6
6
|
module Sequel
|
7
7
|
# Module for holding all Mysql2-related classes and modules for Sequel.
|
8
8
|
module Mysql2
|
9
|
+
NativePreparedStatements = if ::Mysql2::VERSION >= '0.4'
|
10
|
+
true
|
11
|
+
else
|
12
|
+
Sequel.require %w'utils/mysql_prepared_statements', 'adapters'
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
9
16
|
# Database class for MySQL databases used with Sequel.
|
10
17
|
class Database < Sequel::Database
|
11
18
|
include Sequel::MySQL::DatabaseMethods
|
12
|
-
include Sequel::MySQL::
|
19
|
+
include Sequel::MySQL::MysqlMysql2::DatabaseMethods
|
20
|
+
include Sequel::MySQL::PreparedStatements::DatabaseMethods unless NativePreparedStatements
|
13
21
|
|
14
22
|
set_adapter_scheme :mysql2
|
15
23
|
|
@@ -37,6 +45,10 @@ module Sequel
|
|
37
45
|
opts[:encoding] ||= opts[:charset]
|
38
46
|
conn = ::Mysql2::Client.new(opts)
|
39
47
|
conn.query_options.merge!(:symbolize_keys=>true, :cache_rows=>false)
|
48
|
+
|
49
|
+
if NativePreparedStatements
|
50
|
+
@default_query_options ||= conn.query_options.dup
|
51
|
+
end
|
40
52
|
|
41
53
|
sqls = mysql_connection_setting_sqls
|
42
54
|
|
@@ -71,13 +83,60 @@ module Sequel
|
|
71
83
|
|
72
84
|
private
|
73
85
|
|
86
|
+
if NativePreparedStatements
|
87
|
+
# Use a native mysql2 prepared statement to implement prepared statements.
|
88
|
+
def execute_prepared_statement(ps_name, opts, &block)
|
89
|
+
args = opts[:arguments]
|
90
|
+
ps = prepared_statement(ps_name)
|
91
|
+
sql = ps.prepared_sql
|
92
|
+
|
93
|
+
synchronize(opts[:server]) do |conn|
|
94
|
+
stmt, ps_sql = conn.prepared_statements[ps_name]
|
95
|
+
unless ps_sql == sql
|
96
|
+
stmt.close if stmt
|
97
|
+
stmt = log_connection_yield(conn, "Preparing #{ps_name}: #{sql}"){conn.prepare(sql)}
|
98
|
+
conn.prepared_statements[ps_name] = [stmt, sql]
|
99
|
+
end
|
100
|
+
|
101
|
+
if ps.log_sql
|
102
|
+
opts = Hash[opts]
|
103
|
+
opts = opts[:log_sql] = " (#{sql})"
|
104
|
+
end
|
105
|
+
|
106
|
+
_execute(conn, stmt, opts, &block)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
74
111
|
# Execute the given SQL on the given connection. If the :type
|
75
112
|
# option is :select, yield the result of the query, otherwise
|
76
113
|
# yield the connection if a block is given.
|
77
114
|
def _execute(conn, sql, opts)
|
78
115
|
begin
|
79
116
|
stream = opts[:stream]
|
80
|
-
|
117
|
+
if NativePreparedStatements
|
118
|
+
if args = opts[:arguments]
|
119
|
+
args = args.map{|arg| bound_variable_value(arg)}
|
120
|
+
end
|
121
|
+
|
122
|
+
case sql
|
123
|
+
when ::Mysql2::Statement
|
124
|
+
stmt = sql
|
125
|
+
when Dataset
|
126
|
+
sql = sql.sql
|
127
|
+
close_stmt = true
|
128
|
+
stmt = conn.prepare(sql)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn, args) do
|
133
|
+
if stmt
|
134
|
+
conn.query_options.merge!(:cache_rows=>true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream, :cast_booleans=>convert_tinyint_to_bool)
|
135
|
+
stmt.execute(*args)
|
136
|
+
else
|
137
|
+
conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream)
|
138
|
+
end
|
139
|
+
end
|
81
140
|
if opts[:type] == :select
|
82
141
|
if r
|
83
142
|
if stream
|
@@ -98,6 +157,11 @@ module Sequel
|
|
98
157
|
end
|
99
158
|
rescue ::Mysql2::Error => e
|
100
159
|
raise_error(e)
|
160
|
+
ensure
|
161
|
+
if stmt
|
162
|
+
conn.query_options.replace(@default_query_options)
|
163
|
+
stmt.close if close_stmt
|
164
|
+
end
|
101
165
|
end
|
102
166
|
end
|
103
167
|
|
@@ -106,6 +170,22 @@ module Sequel
|
|
106
170
|
self.convert_tinyint_to_bool = Sequel::MySQL.convert_tinyint_to_bool
|
107
171
|
end
|
108
172
|
|
173
|
+
if NativePreparedStatements
|
174
|
+
# Handle bound variable arguments that Mysql2 does not handle natively.
|
175
|
+
def bound_variable_value(arg)
|
176
|
+
case arg
|
177
|
+
when true
|
178
|
+
1
|
179
|
+
when false
|
180
|
+
0
|
181
|
+
when DateTime, Time
|
182
|
+
literal(arg)[1...-1]
|
183
|
+
else
|
184
|
+
arg
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
109
189
|
# MySQL connections use the query method to execute SQL without a result
|
110
190
|
def connection_execute_method
|
111
191
|
:query
|
@@ -146,11 +226,31 @@ module Sequel
|
|
146
226
|
# Dataset class for MySQL datasets accessed via the native driver.
|
147
227
|
class Dataset < Sequel::Dataset
|
148
228
|
include Sequel::MySQL::DatasetMethods
|
149
|
-
include Sequel::MySQL::
|
229
|
+
include Sequel::MySQL::MysqlMysql2::DatasetMethods
|
230
|
+
include Sequel::MySQL::PreparedStatements::DatasetMethods unless NativePreparedStatements
|
150
231
|
STREAMING_SUPPORTED = ::Mysql2::VERSION >= '0.3.12'
|
151
232
|
|
152
233
|
Database::DatasetClass = self
|
153
234
|
|
235
|
+
if NativePreparedStatements
|
236
|
+
PreparedStatementMethods = prepared_statements_module(
|
237
|
+
"sql = self; opts = Hash[opts]; opts[:arguments] = bind_arguments",
|
238
|
+
Sequel::Dataset::UnnumberedArgumentMapper,
|
239
|
+
%w"execute execute_dui execute_insert")
|
240
|
+
|
241
|
+
# Create a named prepared statement that is stored in the
|
242
|
+
# database (and connection) for reuse.
|
243
|
+
def prepare(type, name=nil, *values)
|
244
|
+
ps = to_prepared_statement(type, values)
|
245
|
+
ps.extend(PreparedStatementMethods)
|
246
|
+
if name
|
247
|
+
ps.prepared_statement_name = name
|
248
|
+
db.set_prepared_statement(name, ps)
|
249
|
+
end
|
250
|
+
ps
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
154
254
|
# Yield all rows matching this dataset.
|
155
255
|
def fetch_rows(sql)
|
156
256
|
execute(sql) do |r|
|
@@ -199,16 +299,7 @@ module Sequel
|
|
199
299
|
|
200
300
|
# Handle correct quoting of strings using ::Mysql2::Client#escape.
|
201
301
|
def literal_string_append(sql, v)
|
202
|
-
s =
|
203
|
-
db.synchronize(@opts[:server]) do |c|
|
204
|
-
begin
|
205
|
-
c.escape(v)
|
206
|
-
rescue ::Mysql2::Error => e
|
207
|
-
db.send(:raise_error, e)
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
302
|
+
s = db.synchronize(@opts[:server]){|c| c.escape(v)}
|
212
303
|
sql << APOS << s << APOS
|
213
304
|
end
|
214
305
|
end
|
@@ -125,6 +125,7 @@ module Sequel
|
|
125
125
|
'could not receive data from server',
|
126
126
|
'no connection to the server',
|
127
127
|
'connection not open',
|
128
|
+
'connection is closed',
|
128
129
|
'terminating connection due to administrator command',
|
129
130
|
'PQconsumeInput() '
|
130
131
|
]
|
@@ -560,6 +561,12 @@ module Sequel
|
|
560
561
|
[PGError]
|
561
562
|
end
|
562
563
|
|
564
|
+
def disconnect_error?(exception, opts)
|
565
|
+
super ||
|
566
|
+
Adapter::DISCONNECT_ERROR_CLASSES.any?{|klass| exception.is_a?(klass)} ||
|
567
|
+
exception.message =~ Adapter::DISCONNECT_ERROR_RE
|
568
|
+
end
|
569
|
+
|
563
570
|
def database_exception_sqlstate(exception, opts)
|
564
571
|
if exception.respond_to?(:result) && (result = exception.result)
|
565
572
|
result.error_field(::PGresult::PG_DIAG_SQLSTATE)
|
@@ -841,12 +848,12 @@ module Sequel
|
|
841
848
|
|
842
849
|
# Use the driver's escape_bytea
|
843
850
|
def literal_blob_append(sql, v)
|
844
|
-
sql << APOS << db.synchronize(@opts[:server]){|c| c.
|
851
|
+
sql << APOS << db.synchronize(@opts[:server]){|c| c.escape_bytea(v)} << APOS
|
845
852
|
end
|
846
853
|
|
847
854
|
# Use the driver's escape_string
|
848
855
|
def literal_string_append(sql, v)
|
849
|
-
sql << APOS << db.synchronize(@opts[:server]){|c| c.
|
856
|
+
sql << APOS << db.synchronize(@opts[:server]){|c| c.escape_string(v)} << APOS
|
850
857
|
end
|
851
858
|
|
852
859
|
# For each row in the result set, yield a hash with column name symbol
|
@@ -90,6 +90,23 @@ module Sequel
|
|
90
90
|
count > 0
|
91
91
|
end
|
92
92
|
|
93
|
+
# The version of the Oracle server, used for determining capability.
|
94
|
+
def server_version(server=nil)
|
95
|
+
return @server_version if @server_version
|
96
|
+
@server_version = synchronize(server) do |conn|
|
97
|
+
(conn.server_version rescue nil) if conn.respond_to?(:server_version)
|
98
|
+
end
|
99
|
+
unless @server_version
|
100
|
+
@server_version = if m = /(\d+)\.(\d+)\.?(\d+)?\.?(\d+)?/.match(fetch("select version from PRODUCT_COMPONENT_VERSION where lower(product) like 'oracle%'").single_value)
|
101
|
+
(m[1].to_i*1000000) + (m[2].to_i*10000) + (m[3].to_i*100) + m[4].to_i
|
102
|
+
else
|
103
|
+
0
|
104
|
+
end
|
105
|
+
end
|
106
|
+
@server_version
|
107
|
+
end
|
108
|
+
|
109
|
+
|
93
110
|
# Oracle supports deferrable constraints.
|
94
111
|
def supports_deferrable_constraints?
|
95
112
|
true
|
@@ -305,6 +322,19 @@ module Sequel
|
|
305
322
|
s2 = complex_expression_arg_pairs(x, &BITAND_PROC)
|
306
323
|
Sequel.lit(["(", " - ", ")"], s1, s2)
|
307
324
|
end
|
325
|
+
when :~, :'!~', :'~*', :'!~*'
|
326
|
+
raise InvalidOperation, "Pattern matching via regular expressions is not supported in this Oracle version" unless supports_regexp?
|
327
|
+
if op == :'!~' || op == :'!~*'
|
328
|
+
sql << 'NOT '
|
329
|
+
end
|
330
|
+
sql << 'REGEXP_LIKE('
|
331
|
+
literal_append(sql, args.at(0))
|
332
|
+
sql << ','
|
333
|
+
literal_append(sql, args.at(1))
|
334
|
+
if op == :'~*' || op == :'!~*'
|
335
|
+
sql << ", 'i'"
|
336
|
+
end
|
337
|
+
sql << ')'
|
308
338
|
when :%, :<<, :>>, :'B~'
|
309
339
|
complex_expression_emulate_append(sql, op, args)
|
310
340
|
else
|
@@ -452,6 +482,16 @@ module Sequel
|
|
452
482
|
true
|
453
483
|
end
|
454
484
|
|
485
|
+
# The version of the database server
|
486
|
+
def server_version
|
487
|
+
db.server_version(@opts[:server])
|
488
|
+
end
|
489
|
+
|
490
|
+
# Oracle supports pattern matching via regular expressions
|
491
|
+
def supports_regexp?
|
492
|
+
server_version >= 10010002
|
493
|
+
end
|
494
|
+
|
455
495
|
private
|
456
496
|
|
457
497
|
# Allow preparing prepared statements, since determining the prepared sql to use for
|
@@ -204,6 +204,14 @@ module Sequel
|
|
204
204
|
pragma_set(:temp_store, value)
|
205
205
|
end
|
206
206
|
|
207
|
+
# Creates a dataset that uses the VALUES clause:
|
208
|
+
#
|
209
|
+
# DB.values([[1, 2], [3, 4]])
|
210
|
+
# VALUES ((1, 2), (3, 4))
|
211
|
+
def values(v)
|
212
|
+
@default_dataset.clone(:values=>v)
|
213
|
+
end
|
214
|
+
|
207
215
|
# Array of symbols specifying the view names in the current database.
|
208
216
|
#
|
209
217
|
# Options:
|
@@ -328,7 +336,7 @@ module Sequel
|
|
328
336
|
|
329
337
|
# SQLite support creating temporary views.
|
330
338
|
def create_view_prefix_sql(name, options)
|
331
|
-
"CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}"
|
339
|
+
create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
|
332
340
|
end
|
333
341
|
|
334
342
|
DATABASE_ERROR_REGEXPS = {
|
@@ -517,9 +525,11 @@ module Sequel
|
|
517
525
|
DATETIME_OPEN = "datetime(".freeze
|
518
526
|
ONLY_OFFSET = " LIMIT -1 OFFSET ".freeze
|
519
527
|
OR = " OR ".freeze
|
528
|
+
SELECT_VALUES = "VALUES ".freeze
|
520
529
|
|
521
530
|
Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
|
522
531
|
Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 30803', %w'with insert conflict into columns values'], ["else", %w'insert conflict into columns values']])
|
532
|
+
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'with values compounds'], ['else', %w'with select distinct columns from join where group having compounds order limit lock']])
|
523
533
|
Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
|
524
534
|
|
525
535
|
def cast_sql_append(sql, expr, type)
|
@@ -747,6 +757,12 @@ module Sequel
|
|
747
757
|
literal_append(sql, @opts[:offset])
|
748
758
|
end
|
749
759
|
|
760
|
+
# Support VALUES clause instead of the SELECT clause to return rows.
|
761
|
+
def select_values_sql(sql)
|
762
|
+
sql << SELECT_VALUES
|
763
|
+
expression_list_append(sql, opts[:values])
|
764
|
+
end
|
765
|
+
|
750
766
|
# SQLite supports quoted function names.
|
751
767
|
def supports_quoted_function_names?
|
752
768
|
true
|
@@ -12,19 +12,16 @@ module Sequel
|
|
12
12
|
# Contains procs keyed on sub adapter type that extend the
|
13
13
|
# given database object so it supports the correct database type.
|
14
14
|
DATABASE_SETUP = {:postgres=>proc do |db|
|
15
|
-
Sequel.require 'adapters/swift/postgres'
|
16
15
|
db.extend(Sequel::Swift::Postgres::DatabaseMethods)
|
17
16
|
db.extend_datasets Sequel::Postgres::DatasetMethods
|
18
17
|
db.swift_class = ::Swift::DB::Postgres
|
19
18
|
end,
|
20
19
|
:mysql=>proc do |db|
|
21
|
-
Sequel.require 'adapters/swift/mysql'
|
22
20
|
db.extend(Sequel::Swift::MySQL::DatabaseMethods)
|
23
21
|
db.dataset_class = Sequel::Swift::MySQL::Dataset
|
24
22
|
db.swift_class = ::Swift::DB::Mysql
|
25
23
|
end,
|
26
24
|
:sqlite=>proc do |db|
|
27
|
-
Sequel.require 'adapters/swift/sqlite'
|
28
25
|
db.extend(Sequel::Swift::SQLite::DatabaseMethods)
|
29
26
|
db.dataset_class = Sequel::Swift::SQLite::Dataset
|
30
27
|
db.swift_class = ::Swift::DB::Sqlite3
|
@@ -38,6 +35,11 @@ module Sequel
|
|
38
35
|
# The Swift adapter class being used by this database. Connections
|
39
36
|
# in this database's connection pool will be instances of this class.
|
40
37
|
attr_accessor :swift_class
|
38
|
+
|
39
|
+
def initialize(opts=OPTS)
|
40
|
+
Sequel.require "adapters/swift/#{opts[:db_type]}" if %w'postgres mysql sqlite'.include?(opts[:db_type].to_s)
|
41
|
+
super
|
42
|
+
end
|
41
43
|
|
42
44
|
# Create an instance of swift_class for the given options.
|
43
45
|
def connect(server)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
|
4
|
+
|
5
|
+
module Sequel
|
6
|
+
module MySQL
|
7
|
+
# This module is used by the mysql and mysql2 adapters to support
|
8
|
+
# prepared statements and stored procedures.
|
9
|
+
module MysqlMysql2
|
10
|
+
module DatabaseMethods
|
11
|
+
disconnect_errors = <<-END.split("\n").map(&:strip)
|
12
|
+
Commands out of sync; you can't run this command now
|
13
|
+
Can't connect to local MySQL server through socket
|
14
|
+
MySQL server has gone away
|
15
|
+
Lost connection to MySQL server during query
|
16
|
+
This connection is still waiting for a result, try again once you have the result
|
17
|
+
closed MySQL connection
|
18
|
+
END
|
19
|
+
# Error messages for mysql and mysql2 that indicate the current connection should be disconnected
|
20
|
+
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A#{Regexp.union(disconnect_errors)}/o
|
21
|
+
|
22
|
+
# Support stored procedures on MySQL
|
23
|
+
def call_sproc(name, opts=OPTS, &block)
|
24
|
+
args = opts[:args] || []
|
25
|
+
execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Executes the given SQL using an available connection, yielding the
|
29
|
+
# connection if the block is given.
|
30
|
+
def execute(sql, opts=OPTS, &block)
|
31
|
+
if opts[:sproc]
|
32
|
+
call_sproc(sql, opts, &block)
|
33
|
+
elsif sql.is_a?(Symbol)
|
34
|
+
execute_prepared_statement(sql, opts, &block)
|
35
|
+
else
|
36
|
+
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def add_prepared_statements_cache(conn)
|
43
|
+
class << conn
|
44
|
+
attr_accessor :prepared_statements
|
45
|
+
end
|
46
|
+
conn.prepared_statements = {}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Stupid MySQL doesn't use SQLState error codes correctly, mapping
|
50
|
+
# all constraint violations to 23000 even though it recognizes
|
51
|
+
# different types.
|
52
|
+
def database_specific_error_class(exception, opts)
|
53
|
+
case exception.errno
|
54
|
+
when 1048
|
55
|
+
NotNullConstraintViolation
|
56
|
+
when 1062
|
57
|
+
UniqueConstraintViolation
|
58
|
+
when 1451, 1452
|
59
|
+
ForeignKeyConstraintViolation
|
60
|
+
else
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module DatasetMethods
|
67
|
+
include Sequel::Dataset::StoredProcedures
|
68
|
+
|
69
|
+
StoredProcedureMethods = Sequel::Dataset.send(:prepared_statements_module,
|
70
|
+
"sql = @sproc_name; opts = Hash[opts]; opts[:args] = @sproc_args; opts[:sproc] = true",
|
71
|
+
Sequel::Dataset::StoredProcedureMethods, %w'execute execute_dui')
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Extend the dataset with the MySQL stored procedure methods.
|
76
|
+
def prepare_extend_sproc(ds)
|
77
|
+
ds.extend(StoredProcedureMethods)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -1,67 +1,11 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
|
4
|
-
|
5
3
|
module Sequel
|
6
4
|
module MySQL
|
7
|
-
# This module is used by the mysql and mysql2 adapters to support
|
8
|
-
# prepared statements and stored procedures.
|
9
5
|
module PreparedStatements
|
10
6
|
module DatabaseMethods
|
11
|
-
disconnect_errors = <<-END.split("\n").map(&:strip)
|
12
|
-
Commands out of sync; you can't run this command now
|
13
|
-
Can't connect to local MySQL server through socket
|
14
|
-
MySQL server has gone away
|
15
|
-
Lost connection to MySQL server during query
|
16
|
-
This connection is still waiting for a result, try again once you have the result
|
17
|
-
closed MySQL connection
|
18
|
-
END
|
19
|
-
# Error messages for mysql and mysql2 that indicate the current connection should be disconnected
|
20
|
-
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A#{Regexp.union(disconnect_errors)}/o
|
21
|
-
|
22
|
-
# Support stored procedures on MySQL
|
23
|
-
def call_sproc(name, opts=OPTS, &block)
|
24
|
-
args = opts[:args] || []
|
25
|
-
execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Executes the given SQL using an available connection, yielding the
|
29
|
-
# connection if the block is given.
|
30
|
-
def execute(sql, opts=OPTS, &block)
|
31
|
-
if opts[:sproc]
|
32
|
-
call_sproc(sql, opts, &block)
|
33
|
-
elsif sql.is_a?(Symbol)
|
34
|
-
execute_prepared_statement(sql, opts, &block)
|
35
|
-
else
|
36
|
-
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
7
|
private
|
41
8
|
|
42
|
-
def add_prepared_statements_cache(conn)
|
43
|
-
class << conn
|
44
|
-
attr_accessor :prepared_statements
|
45
|
-
end
|
46
|
-
conn.prepared_statements = {}
|
47
|
-
end
|
48
|
-
|
49
|
-
# Stupid MySQL doesn't use SQLState error codes correctly, mapping
|
50
|
-
# all constraint violations to 23000 even though it recognizes
|
51
|
-
# different types.
|
52
|
-
def database_specific_error_class(exception, opts)
|
53
|
-
case exception.errno
|
54
|
-
when 1048
|
55
|
-
NotNullConstraintViolation
|
56
|
-
when 1062
|
57
|
-
UniqueConstraintViolation
|
58
|
-
when 1451, 1452
|
59
|
-
ForeignKeyConstraintViolation
|
60
|
-
else
|
61
|
-
super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
9
|
# Executes a prepared statement on an available connection. If the
|
66
10
|
# prepared statement already exists for the connection and has the same
|
67
11
|
# SQL, reuse it, otherwise, prepare the new statement. Because of the
|
@@ -86,8 +30,6 @@ module Sequel
|
|
86
30
|
end
|
87
31
|
|
88
32
|
module DatasetMethods
|
89
|
-
include Sequel::Dataset::StoredProcedures
|
90
|
-
|
91
33
|
# Methods to add to MySQL prepared statement calls without using a
|
92
34
|
# real database prepared statement and bound variables.
|
93
35
|
module CallableStatementMethods
|
@@ -112,10 +54,6 @@ module Sequel
|
|
112
54
|
end
|
113
55
|
end
|
114
56
|
|
115
|
-
StoredProcedureMethods = Sequel::Dataset.send(:prepared_statements_module,
|
116
|
-
"sql = @sproc_name; opts = Hash[opts]; opts[:args] = @sproc_args; opts[:sproc] = true",
|
117
|
-
Sequel::Dataset::StoredProcedureMethods, %w'execute execute_dui')
|
118
|
-
|
119
57
|
# MySQL is different in that it supports prepared statements but not bound
|
120
58
|
# variables outside of prepared statements. The default implementation
|
121
59
|
# breaks the use of subselects in prepared statements, so extend the
|
@@ -138,14 +76,6 @@ module Sequel
|
|
138
76
|
end
|
139
77
|
ps
|
140
78
|
end
|
141
|
-
|
142
|
-
private
|
143
|
-
|
144
|
-
# Extend the dataset with the MySQL stored procedure methods.
|
145
|
-
def prepare_extend_sproc(ds)
|
146
|
-
ds.extend(StoredProcedureMethods)
|
147
|
-
end
|
148
|
-
|
149
79
|
end
|
150
80
|
end
|
151
81
|
end
|
@@ -26,8 +26,14 @@ module Sequel
|
|
26
26
|
h = {}
|
27
27
|
o.each{|k, val| h[v(k)] = v(val)}
|
28
28
|
h
|
29
|
+
when SQL::NumericExpression
|
30
|
+
if o.op == :extract
|
31
|
+
o.class.new(o.op, o.args[0], v(o.args[1]))
|
32
|
+
else
|
33
|
+
o.class.new(o.op, *v(o.args))
|
34
|
+
end
|
29
35
|
when SQL::ComplexExpression
|
30
|
-
|
36
|
+
o.class.new(o.op, *v(o.args))
|
31
37
|
when SQL::Identifier
|
32
38
|
SQL::Identifier.new(v(o.value))
|
33
39
|
when SQL::QualifiedIdentifier
|
@@ -84,6 +84,7 @@ class Sequel::ConnectionPool
|
|
84
84
|
def initialize(db, opts=OPTS)
|
85
85
|
@db = db
|
86
86
|
@after_connect = opts[:after_connect]
|
87
|
+
@error_classes = db.send(:database_error_classes).dup.freeze
|
87
88
|
end
|
88
89
|
|
89
90
|
# Alias for +size+, not aliased directly for ease of subclass implementation
|
@@ -102,6 +103,11 @@ class Sequel::ConnectionPool
|
|
102
103
|
def disconnect_connection(conn)
|
103
104
|
db.disconnect_connection(conn)
|
104
105
|
end
|
106
|
+
|
107
|
+
# Whether the given exception is a disconnect exception.
|
108
|
+
def disconnect_error?(exception)
|
109
|
+
exception.is_a?(Sequel::DatabaseDisconnectError) || db.send(:disconnect_error?, exception, OPTS)
|
110
|
+
end
|
105
111
|
|
106
112
|
# Return a new connection by calling the connection proc with the given server name,
|
107
113
|
# and checking for connection errors.
|
@@ -50,8 +50,8 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
|
|
50
50
|
begin
|
51
51
|
server = pick_server(server)
|
52
52
|
yield(@conns[server] ||= make_new(server))
|
53
|
-
rescue Sequel::DatabaseDisconnectError
|
54
|
-
disconnect_server(server)
|
53
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
54
|
+
disconnect_server(server) if disconnect_error?(e)
|
55
55
|
raise
|
56
56
|
end
|
57
57
|
end
|
@@ -126,8 +126,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
126
126
|
begin
|
127
127
|
conn = acquire(t, server)
|
128
128
|
yield conn
|
129
|
-
rescue Sequel::DatabaseDisconnectError
|
130
|
-
sync{@connections_to_remove << conn} if conn
|
129
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
130
|
+
sync{@connections_to_remove << conn} if conn && disconnect_error?(e)
|
131
131
|
raise
|
132
132
|
ensure
|
133
133
|
sync{release(t, conn, server)} if conn
|
@@ -20,8 +20,8 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
|
|
20
20
|
def hold(server=nil)
|
21
21
|
begin
|
22
22
|
yield(@conn ||= make_new(DEFAULT_SERVER))
|
23
|
-
rescue Sequel::DatabaseDisconnectError
|
24
|
-
disconnect
|
23
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
24
|
+
disconnect if disconnect_error?(e)
|
25
25
|
raise
|
26
26
|
end
|
27
27
|
end
|
@@ -105,11 +105,13 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
105
105
|
begin
|
106
106
|
conn = acquire(t)
|
107
107
|
yield conn
|
108
|
-
rescue Sequel::DatabaseDisconnectError
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
108
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
109
|
+
if disconnect_error?(e)
|
110
|
+
oconn = conn
|
111
|
+
conn = nil
|
112
|
+
disconnect_connection(oconn) if oconn
|
113
|
+
@allocated.delete(t)
|
114
|
+
end
|
113
115
|
raise
|
114
116
|
ensure
|
115
117
|
sync{release(t)} if conn
|
@@ -494,6 +494,10 @@ module Sequel
|
|
494
494
|
#
|
495
495
|
# set_column_default(:artist_name, 'a') # ALTER COLUMN artist_name SET DEFAULT 'a'
|
496
496
|
#
|
497
|
+
# To remove an existing default value, use +nil+ as the value:
|
498
|
+
#
|
499
|
+
# set_column_default(:artist_name, nil) # ALTER COLUMN artist_name SET DEFAULT NULL
|
500
|
+
#
|
497
501
|
# On MySQL, make sure to use a symbol for the name of the column, as otherwise you
|
498
502
|
# can lose the type and NULL/NOT NULL setting for the column.
|
499
503
|
def set_column_default(name, default)
|
@@ -245,7 +245,9 @@ module Sequel
|
|
245
245
|
end
|
246
246
|
|
247
247
|
def add_constraint(*args)
|
248
|
-
|
248
|
+
name = args.first
|
249
|
+
name = name.is_a?(Hash) ? name[:name] : name
|
250
|
+
@actions << [:drop_constraint, name]
|
249
251
|
end
|
250
252
|
|
251
253
|
def add_foreign_key(key, table, *args)
|
data/lib/sequel/version.rb
CHANGED
@@ -5,7 +5,7 @@ module Sequel
|
|
5
5
|
MAJOR = 4
|
6
6
|
# The minor version of Sequel. Bumped for every non-patch level
|
7
7
|
# release, generally around once a month.
|
8
|
-
MINOR =
|
8
|
+
MINOR = 37
|
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
|
@@ -190,6 +190,24 @@ describe "SQLite temporary views" do
|
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
193
|
+
describe "SQLite VALUES support" do
|
194
|
+
before do
|
195
|
+
@db = DB
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should create a dataset using the VALUES clause via #values" do
|
199
|
+
@db.values([[1, 2], [3, 4]]).map([:column1, :column2]).must_equal [[1, 2], [3, 4]]
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should support VALUES with unions" do
|
203
|
+
@db.values([[1]]).union(@db.values([[3]])).map(&:values).map(&:first).must_equal [1, 3]
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should support VALUES in CTEs" do
|
207
|
+
@db[:a].cross_join(:b).with(:a, @db.values([[1, 2]]), :args=>[:c1, :c2]).with(:b, @db.values([[3, 4]]), :args=>[:c3, :c4]).map([:c1, :c2, :c3, :c4]).must_equal [[1, 2, 3, 4]]
|
208
|
+
end
|
209
|
+
end if DB.sqlite_version >= 30803
|
210
|
+
|
193
211
|
describe "SQLite type conversion" do
|
194
212
|
before do
|
195
213
|
@db = DB
|
@@ -125,13 +125,13 @@ end
|
|
125
125
|
|
126
126
|
describe "A connection pool handling connection errors" do
|
127
127
|
it "#hold should raise a Sequel::DatabaseConnectionError if an exception is raised by the connection_proc" do
|
128
|
-
cpool = Sequel::ConnectionPool.get_pool(
|
128
|
+
cpool = Sequel::ConnectionPool.get_pool(mock_db.call{raise Interrupt}, CONNECTION_POOL_DEFAULTS)
|
129
129
|
proc{cpool.hold{:block_return}}.must_raise(Sequel::DatabaseConnectionError)
|
130
130
|
cpool.created_count.must_equal 0
|
131
131
|
end
|
132
132
|
|
133
133
|
it "#hold should raise a Sequel::DatabaseConnectionError if nil is returned by the connection_proc" do
|
134
|
-
cpool = Sequel::ConnectionPool.get_pool(
|
134
|
+
cpool = Sequel::ConnectionPool.get_pool(mock_db.call{nil}, CONNECTION_POOL_DEFAULTS)
|
135
135
|
proc{cpool.hold{:block_return}}.must_raise(Sequel::DatabaseConnectionError)
|
136
136
|
cpool.created_count.must_equal 0
|
137
137
|
end
|
@@ -905,6 +905,48 @@ describe "A single threaded pool with multiple servers" do
|
|
905
905
|
end
|
906
906
|
|
907
907
|
AllConnectionPoolClassesSpecs = shared_description do
|
908
|
+
it "should have pool correctly handle disconnect errors not raised as DatabaseDisconnectError" do
|
909
|
+
db = mock_db.call{Object.new}
|
910
|
+
def db.dec; @dec ||= Class.new(StandardError) end
|
911
|
+
def db.database_error_classes; super + [dec] end
|
912
|
+
def db.disconnect_error?(e, opts); e.message =~ /foo/ end
|
913
|
+
cp = @class.new(db, {})
|
914
|
+
|
915
|
+
conn = nil
|
916
|
+
cp.hold do |c|
|
917
|
+
conn = c
|
918
|
+
end
|
919
|
+
|
920
|
+
proc do
|
921
|
+
cp.hold do |c|
|
922
|
+
c.must_equal conn
|
923
|
+
raise db.dec, "bar"
|
924
|
+
end
|
925
|
+
end.must_raise db.dec
|
926
|
+
|
927
|
+
proc do
|
928
|
+
cp.hold do |c|
|
929
|
+
c.must_equal conn
|
930
|
+
raise StandardError
|
931
|
+
end
|
932
|
+
end.must_raise StandardError
|
933
|
+
|
934
|
+
cp.hold do |c|
|
935
|
+
c.must_equal conn
|
936
|
+
end
|
937
|
+
|
938
|
+
proc do
|
939
|
+
cp.hold do |c|
|
940
|
+
c.must_equal conn
|
941
|
+
raise db.dec, "foo"
|
942
|
+
end
|
943
|
+
end.must_raise db.dec
|
944
|
+
|
945
|
+
cp.hold do |c|
|
946
|
+
c.wont_equal conn
|
947
|
+
end
|
948
|
+
end
|
949
|
+
|
908
950
|
it "should have pool_type return a symbol" do
|
909
951
|
@class.new(mock_db.call{123}, {}).pool_type.must_be_kind_of(Symbol)
|
910
952
|
end
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -3888,6 +3888,10 @@ describe "Sequel::Dataset#qualify" do
|
|
3888
3888
|
@ds.select{sum(:a).order(:a)}.qualify.sql.must_equal 'SELECT sum(t.a ORDER BY t.a) FROM t'
|
3889
3889
|
end
|
3890
3890
|
|
3891
|
+
it "should handle Sequel.extract" do
|
3892
|
+
@ds.select(Sequel.extract(:year, :d)).qualify.sql.must_equal 'SELECT extract(year FROM t.d) FROM t'
|
3893
|
+
end
|
3894
|
+
|
3891
3895
|
it "should handle SQL::DelayedEvaluation" do
|
3892
3896
|
t = :a
|
3893
3897
|
ds = @ds.filter(Sequel.delay{t}).qualify
|
@@ -444,10 +444,11 @@ describe "Sequel Mock Adapter" do
|
|
444
444
|
end
|
445
445
|
|
446
446
|
it "should automatically set version for adapters nedding versions" do
|
447
|
-
Sequel.mock(:host=>'postgres').server_version.
|
448
|
-
Sequel.mock(:host=>'mssql').server_version.
|
449
|
-
Sequel.mock(:host=>'mysql').server_version.
|
450
|
-
Sequel.mock(:host=>'sqlite').sqlite_version.
|
447
|
+
Sequel.mock(:host=>'postgres').server_version.must_be :>=, 90400
|
448
|
+
Sequel.mock(:host=>'mssql').server_version.must_be :>=, 11000000
|
449
|
+
Sequel.mock(:host=>'mysql').server_version.must_be :>=, 50617
|
450
|
+
Sequel.mock(:host=>'sqlite').sqlite_version.must_be :>=, 30804
|
451
|
+
Sequel.mock(:host=>'oracle').server_version.must_be :>=, 11000000
|
451
452
|
end
|
452
453
|
|
453
454
|
it "should stub out the primary_key method for postgres" do
|
@@ -129,6 +129,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
129
129
|
alter_table(:b) do
|
130
130
|
add_column :d, String
|
131
131
|
add_constraint :blah, 'd IS NOT NULL'
|
132
|
+
add_constraint({:name=>:merp}, 'a > 1')
|
132
133
|
add_foreign_key :e, :b
|
133
134
|
add_foreign_key [:e], :b, :name=>'e_fk'
|
134
135
|
add_foreign_key [:e, :a], :b
|
@@ -154,6 +155,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
154
155
|
[:alter_table, [
|
155
156
|
[:add_column, :d, String],
|
156
157
|
[:add_constraint, :blah, "d IS NOT NULL"],
|
158
|
+
[:add_constraint, {:name=>:merp}, "a > 1"],
|
157
159
|
[:add_foreign_key, :e, :b],
|
158
160
|
[:add_foreign_key, [:e], :b, {:name=>"e_fk"}],
|
159
161
|
[:add_foreign_key, [:e, :a], :b],
|
@@ -182,6 +184,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
182
184
|
[:drop_foreign_key, [:e, :a]],
|
183
185
|
[:drop_foreign_key, [:e], {:name=>"e_fk"}],
|
184
186
|
[:drop_foreign_key, :e],
|
187
|
+
[:drop_constraint, :merp],
|
185
188
|
[:drop_constraint, :blah],
|
186
189
|
[:drop_column, :d]]
|
187
190
|
],
|
@@ -47,7 +47,7 @@ describe "pg_json extension" do
|
|
47
47
|
|
48
48
|
it "should raise an error when attempting to parse invalid json" do
|
49
49
|
proc{@m.parse_json('')}.must_raise(Sequel::InvalidValue)
|
50
|
-
proc{@m.parse_json('
|
50
|
+
proc{@m.parse_json('a')}.must_raise(Sequel::InvalidValue)
|
51
51
|
|
52
52
|
begin
|
53
53
|
Sequel.instance_eval do
|
@@ -354,7 +354,7 @@ describe "Database schema modifiers" do
|
|
354
354
|
@ds.select_order_map(:number).must_equal [1, 2, 2, 3, 4]
|
355
355
|
end if DB.supports_views_with_local_check_option?
|
356
356
|
|
357
|
-
cspecify "should create views with explicit columns correctly", :sqlite do
|
357
|
+
cspecify "should create views with explicit columns correctly", [proc{|db| db.sqlite_version < 30900}, :sqlite] do
|
358
358
|
@db.create_view(:items_view, @ds.where(:number=>1), :columns=>[:n])
|
359
359
|
@db[:items_view].map(:n).must_equal [1]
|
360
360
|
end
|
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: 4.
|
4
|
+
version: 4.37.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: 2016-
|
11
|
+
date: 2016-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -237,6 +237,7 @@ extra_rdoc_files:
|
|
237
237
|
- doc/release_notes/4.34.0.txt
|
238
238
|
- doc/release_notes/4.35.0.txt
|
239
239
|
- doc/release_notes/4.36.0.txt
|
240
|
+
- doc/release_notes/4.37.0.txt
|
240
241
|
files:
|
241
242
|
- CHANGELOG
|
242
243
|
- MIT-LICENSE
|
@@ -362,6 +363,7 @@ files:
|
|
362
363
|
- doc/release_notes/4.34.0.txt
|
363
364
|
- doc/release_notes/4.35.0.txt
|
364
365
|
- doc/release_notes/4.36.0.txt
|
366
|
+
- doc/release_notes/4.37.0.txt
|
365
367
|
- doc/release_notes/4.4.0.txt
|
366
368
|
- doc/release_notes/4.5.0.txt
|
367
369
|
- doc/release_notes/4.6.0.txt
|
@@ -424,7 +426,6 @@ files:
|
|
424
426
|
- lib/sequel/adapters/shared/informix.rb
|
425
427
|
- lib/sequel/adapters/shared/mssql.rb
|
426
428
|
- lib/sequel/adapters/shared/mysql.rb
|
427
|
-
- lib/sequel/adapters/shared/mysql_prepared_statements.rb
|
428
429
|
- lib/sequel/adapters/shared/oracle.rb
|
429
430
|
- lib/sequel/adapters/shared/postgres.rb
|
430
431
|
- lib/sequel/adapters/shared/progress.rb
|
@@ -439,6 +440,8 @@ files:
|
|
439
440
|
- lib/sequel/adapters/tinytds.rb
|
440
441
|
- lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb
|
441
442
|
- lib/sequel/adapters/utils/emulate_offset_with_row_number.rb
|
443
|
+
- lib/sequel/adapters/utils/mysql_mysql2.rb
|
444
|
+
- lib/sequel/adapters/utils/mysql_prepared_statements.rb
|
442
445
|
- lib/sequel/adapters/utils/pg_types.rb
|
443
446
|
- lib/sequel/adapters/utils/replace.rb
|
444
447
|
- lib/sequel/adapters/utils/split_alter_table.rb
|