sequel 4.36.0 → 4.37.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/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
|