sequel-activerecord_connection 0.4.1 → 1.2.1
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.md +52 -0
- data/README.md +81 -46
- data/lib/sequel/extensions/activerecord_connection.rb +57 -27
- data/lib/sequel/extensions/activerecord_connection/jdbc.rb +8 -25
- data/lib/sequel/extensions/activerecord_connection/mysql2.rb +15 -23
- data/lib/sequel/extensions/activerecord_connection/oracle.rb +16 -0
- data/lib/sequel/extensions/activerecord_connection/postgres.rb +78 -14
- data/lib/sequel/extensions/activerecord_connection/sqlite.rb +8 -33
- data/lib/sequel/extensions/activerecord_connection/tinytds.rb +19 -0
- data/lib/sequel/extensions/activerecord_connection/utils.rb +22 -0
- data/sequel-activerecord_connection.gemspec +5 -2
- metadata +49 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72a4a1bf22a4a83664c7925b1d0b78ee5d20777eb12ffec4df9bd4b0c60a9930
|
4
|
+
data.tar.gz: 58c32e5d224d136cb797453bd8ac70f40d964160c4996377909349c8bb324af5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7cc94ff1690360f805eec685404d9017b9df0f327c32110e54694dd1da148f1141990a468b69787896ea5e9a4b13d84c41783d4dcee0fd8b1f020defd9288961
|
7
|
+
data.tar.gz: b67647d492d7d31478ca6db7ba4b107914d8ba692bd41bf4596cab5f06a16ee62b2f19ab199dfce9566234ac876b349c212199a23cbec2c15884108cf1e41f79
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,55 @@
|
|
1
|
+
## 1.2.1 (2020-01-10)
|
2
|
+
|
3
|
+
* Fix original mysql2 query options not being restored after nested `DB#synchronize` calls, e.g. when using Sequel transactions (@janko)
|
4
|
+
|
5
|
+
## 1.2.0 (2020-11-15)
|
6
|
+
|
7
|
+
* Attempt support for [activerecord-sqlserver-adapter](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter) (@janko)
|
8
|
+
|
9
|
+
* Attempt support for [oracle-enhanced](https://github.com/rsim/oracle-enhanced) Active Record adapter (@janko)
|
10
|
+
|
11
|
+
## 1.1.0 (2020-11-08)
|
12
|
+
|
13
|
+
* Drop support for Ruby 2.2 (@janko)
|
14
|
+
|
15
|
+
* Support transaction/savepoint hooks even when Active Record holds the transaction/savepoint (@janko)
|
16
|
+
|
17
|
+
* Don't test the connection on `Sequel.connect` by default (@janko)
|
18
|
+
|
19
|
+
## 1.0.1 (2020-10-28)
|
20
|
+
|
21
|
+
* Use Active Record connection lock in `Database#synchronize` (@janko)
|
22
|
+
|
23
|
+
## 1.0.0 (2020-10-25)
|
24
|
+
|
25
|
+
* Clear AR statement cache on `ActiveRecord::PreparedStatementCacheExpired` when Sequel holds the transaction (@janko)
|
26
|
+
|
27
|
+
* Pick up `ActiveRecord::Base.default_timezone` being changed on runtime (@janko)
|
28
|
+
|
29
|
+
* Support prepared statements and bound variables in all adapters (@janko)
|
30
|
+
|
31
|
+
* Correctly identify identity columns as primary keys in Postgres adapter (@janko)
|
32
|
+
|
33
|
+
* Avoid using deprecated `sqlite3` API in SQLite adapter (@janko)
|
34
|
+
|
35
|
+
* Allow using any external Active Record adapters (@janko)
|
36
|
+
|
37
|
+
* Avoid potential bugs when converting Active Record exceptions into Sequel exceptions (@janko)
|
38
|
+
|
39
|
+
* Don't use Active Record locks when executing queries with Sequel (@janko)
|
40
|
+
|
41
|
+
* Support `Database#valid_connection?` in Postgres adapter (@janko)
|
42
|
+
|
43
|
+
* Fully utilize Sequel's logic for detecting disconnects in Postgres adapter (@janko)
|
44
|
+
|
45
|
+
* Support `Database#{copy_table,copy_into,listen}` in Postgres adapter (@janko)
|
46
|
+
|
47
|
+
* Log all queries executed by Sequel (@janko)
|
48
|
+
|
49
|
+
* Log executed queries to Sequel logger(s) as well (@janko)
|
50
|
+
|
51
|
+
* Specially label queries executed by Sequel in Active Record logs (@janko)
|
52
|
+
|
1
53
|
## 0.4.1 (2020-09-28)
|
2
54
|
|
3
55
|
* Require Sequel version 5.16.0 or above (@janko)
|
data/README.md
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
#
|
1
|
+
# sequel-activerecord_connection
|
2
2
|
|
3
3
|
This is an extension for [Sequel] that allows it to reuse an existing
|
4
|
-
ActiveRecord connection for database interaction.
|
5
|
-
or higher, and supports the built-in `postgresql`, `mysql2` and `sqlite3`
|
6
|
-
adapters, as well as JDBC adapter for JRuby.
|
4
|
+
ActiveRecord connection for database interaction.
|
7
5
|
|
8
6
|
This can be useful if you're using a library that uses Sequel for database
|
9
|
-
interaction (e.g. [Rodauth]), but you want to avoid creating a
|
10
|
-
database connection. Or if you're transitioning from ActiveRecord to
|
11
|
-
and want the database connection to be shared.
|
7
|
+
interaction (e.g. [Rodauth] or [rom-sql]), but you want to avoid creating a
|
8
|
+
separate database connection. Or if you're transitioning from ActiveRecord to
|
9
|
+
Sequel, and want the database connection to be shared.
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
It works on ActiveRecord 4.2+ and fully supports PostgresSQL, MySQL and SQLite
|
12
|
+
adapters, both the native ones and JDBC (JRuby). There is attempted suppport
|
13
|
+
for [Oracle enhanced] and [SQL Server] Active Record adapters (`oracle` and
|
14
|
+
`tinytds` in Sequel). Other adapters might work too, but their integration
|
15
|
+
hasn't been tested.
|
16
16
|
|
17
17
|
## Installation
|
18
18
|
|
19
19
|
Add this line to your application's Gemfile:
|
20
20
|
|
21
|
-
```
|
22
|
-
gem "sequel-activerecord_connection", "~> 0
|
21
|
+
```rb
|
22
|
+
gem "sequel-activerecord_connection", "~> 1.0"
|
23
23
|
```
|
24
24
|
|
25
25
|
And then execute:
|
@@ -74,13 +74,17 @@ ActiveRecord adapters, just make sure to initialize the corresponding Sequel
|
|
74
74
|
adapter before loading the extension.
|
75
75
|
|
76
76
|
```rb
|
77
|
-
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
77
|
+
Sequel.postgres(extensions: :activerecord_connection) # for "postgresql" adapter
|
78
|
+
Sequel.mysql2(extensions: :activerecord_connection) # for "mysql2" adapter
|
79
|
+
Sequel.sqlite(extensions: :activerecord_connection) # for "sqlite3" adapter
|
80
|
+
```
|
81
|
+
|
82
|
+
If you're on JRuby, you should be using the JDBC adapters:
|
83
|
+
|
84
|
+
```rb
|
85
|
+
Sequel.connect("jdbc:postgresql://", extensions: :activerecord_connection) # for "jdbcpostgresql" adapter
|
86
|
+
Sequel.connect("jdbc:mysql://", extensions: :activerecord_connection) # for "jdbcmysql" adapter
|
87
|
+
Sequel.connect("jdbc:sqlite://", extensions: :activerecord_connection) # for "jdbcsqlite3" adapter
|
84
88
|
```
|
85
89
|
|
86
90
|
### Transactions
|
@@ -107,31 +111,71 @@ DB.transaction(isolation: :serializable) do
|
|
107
111
|
end
|
108
112
|
```
|
109
113
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
ActiveRecord commits or rolls back the transaction/savepoint.
|
114
|
+
When registering transaction hooks, they will be registered on Sequel
|
115
|
+
transactions when possible, in which case they will behave as described in the
|
116
|
+
[Sequel docs][sequel transaction hooks].
|
114
117
|
|
115
118
|
```rb
|
119
|
+
# Sequel: An after_commit transaction hook will always get executed if the outer
|
120
|
+
# transaction commits, even if it's added inside a savepoint that's rolled back.
|
116
121
|
DB.transaction do
|
117
|
-
|
122
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
123
|
+
DB.after_commit { puts "after commit" }
|
124
|
+
raise ActiveRecord::Rollback
|
125
|
+
end
|
118
126
|
end
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
127
|
+
#>> BEGIN
|
128
|
+
#>> SAVEPOINT active_record_1
|
129
|
+
#>> ROLLBACK TO SAVEPOINT active_record_1
|
130
|
+
#>> COMMIT
|
131
|
+
#>> after commit
|
132
|
+
|
133
|
+
# Sequel: An after_commit savepoint hook will get executed only after the outer
|
134
|
+
# transaction commits, given that all enclosing savepoints have been released.
|
135
|
+
DB.transaction(auto_savepoint: true) do
|
136
|
+
DB.transaction do
|
137
|
+
DB.after_commit(savepoint: true) { puts "after commit" }
|
138
|
+
raise Sequel::Rollback
|
123
139
|
end
|
124
140
|
end
|
141
|
+
#>> BEGIN
|
142
|
+
#>> SAVEPOINT active_record_1
|
143
|
+
#>> RELEASE SAVEPOINT active_record_1
|
144
|
+
#>> COMMIT
|
145
|
+
#>> after commit
|
146
|
+
```
|
147
|
+
|
148
|
+
In case of (a) adding a transaction hook while Active Record holds the
|
149
|
+
transaction, or (b) adding a savepoint hook when Active Record holds any
|
150
|
+
enclosing savepoint, Active Record transaction callbacks will be used instead
|
151
|
+
of Sequel hooks, which have slightly different behaviour in some circumstances.
|
125
152
|
|
153
|
+
```rb
|
154
|
+
# ActiveRecord: An after_commit transaction callback is not executed if any
|
155
|
+
# if the enclosing savepoints have been rolled back
|
126
156
|
ActiveRecord::Base.transaction do
|
127
|
-
DB.
|
157
|
+
DB.transaction(savepoint: true) do
|
158
|
+
DB.after_commit { puts "after commit" }
|
159
|
+
raise Sequel::Rollback
|
160
|
+
end
|
128
161
|
end
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
162
|
+
#>> BEGIN
|
163
|
+
#>> SAVEPOINT active_record_1
|
164
|
+
#>> ROLLBACK TO SAVEPOINT active_record_1
|
165
|
+
#>> COMMIT
|
166
|
+
|
167
|
+
# ActiveRecord: An after_commit transaction callback can be executed already
|
168
|
+
# after a savepoint is released, if the enclosing transaction is not joinable.
|
169
|
+
ActiveRecord::Base.transaction(joinable: false) do
|
170
|
+
DB.transaction do
|
171
|
+
DB.after_commit { puts "after commit" }
|
133
172
|
end
|
134
173
|
end
|
174
|
+
#>> BEGIN
|
175
|
+
#>> SAVEPOINT active_record_1
|
176
|
+
#>> RELEASE SAVEPOINT active_record_1
|
177
|
+
#>> after commit
|
178
|
+
#>> COMMIT
|
135
179
|
```
|
136
180
|
|
137
181
|
### Model
|
@@ -149,19 +193,6 @@ end
|
|
149
193
|
DB.activerecord_model = MyModel
|
150
194
|
```
|
151
195
|
|
152
|
-
### Timezone
|
153
|
-
|
154
|
-
Sequel's database timezone will be automatically set to ActiveRecord's default
|
155
|
-
timezone (`:utc` by default) when the extension is loaded.
|
156
|
-
|
157
|
-
If you happen to be changing ActiveRecord's default timezone after you've
|
158
|
-
loaded the extension, make sure to reflect that in your Sequel database object,
|
159
|
-
for example:
|
160
|
-
|
161
|
-
```rb
|
162
|
-
DB.timezone = :local
|
163
|
-
```
|
164
|
-
|
165
196
|
## Tests
|
166
197
|
|
167
198
|
You'll first want to run the rake tasks for setting up databases and users:
|
@@ -194,3 +225,7 @@ Everyone interacting in this project's codebases, issue trackers, chat rooms and
|
|
194
225
|
|
195
226
|
[Sequel]: https://github.com/jeremyevans/sequel
|
196
227
|
[Rodauth]: https://github.com/jeremyevans/rodauth
|
228
|
+
[rom-sql]: https://github.com/rom-rb/rom-sql
|
229
|
+
[sequel transaction hooks]: http://sequel.jeremyevans.net/rdoc/files/doc/transactions_rdoc.html#label-Transaction+Hooks
|
230
|
+
[Oracle enhanced]: https://github.com/rsim/oracle-enhanced
|
231
|
+
[SQL Server]: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "after_commit_everywhere"
|
4
|
+
|
3
5
|
module Sequel
|
4
6
|
module ActiveRecordConnection
|
5
7
|
Error = Class.new(Sequel::Error)
|
@@ -11,15 +13,17 @@ module Sequel
|
|
11
13
|
serializable: :serializable,
|
12
14
|
}
|
13
15
|
|
16
|
+
ACTIVERECORD_CALLBACKS = Object.new.extend(AfterCommitEverywhere)
|
17
|
+
|
14
18
|
def self.extended(db)
|
15
19
|
db.activerecord_model = ActiveRecord::Base
|
16
|
-
db.
|
20
|
+
db.opts[:test] = false unless db.opts.key?(:test)
|
17
21
|
|
18
22
|
begin
|
19
23
|
require "sequel/extensions/activerecord_connection/#{db.adapter_scheme}"
|
20
24
|
db.extend Sequel::ActiveRecordConnection.const_get(db.adapter_scheme.capitalize)
|
21
25
|
rescue LoadError
|
22
|
-
|
26
|
+
# assume the Sequel adapter already works with Active Record
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
@@ -30,17 +34,25 @@ module Sequel
|
|
30
34
|
raise Error, "creating a Sequel connection is not allowed"
|
31
35
|
end
|
32
36
|
|
33
|
-
# Avoid calling Sequel's connection pool, instead use
|
37
|
+
# Avoid calling Sequel's connection pool, instead use Active Record's.
|
34
38
|
def synchronize(*)
|
35
|
-
|
36
|
-
activerecord_connection.
|
37
|
-
yield activerecord_raw_connection
|
38
|
-
end
|
39
|
-
else
|
40
|
-
yield activerecord_raw_connection
|
39
|
+
activerecord_lock do
|
40
|
+
yield activerecord_connection.raw_connection
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
# Log executed queries into Active Record logger as well.
|
45
|
+
def log_connection_yield(sql, conn, args = nil)
|
46
|
+
sql += "; #{args.inspect}" if args
|
47
|
+
|
48
|
+
activerecord_log(sql) { super }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Match database timezone with Active Record.
|
52
|
+
def timezone
|
53
|
+
@timezone || ActiveRecord::Base.default_timezone
|
54
|
+
end
|
55
|
+
|
44
56
|
private
|
45
57
|
|
46
58
|
# Synchronizes transaction state with ActiveRecord. Sequel uses this
|
@@ -75,52 +87,70 @@ module Sequel
|
|
75
87
|
super
|
76
88
|
end
|
77
89
|
|
78
|
-
def begin_transaction(conn, opts =
|
90
|
+
def begin_transaction(conn, opts = OPTS)
|
79
91
|
isolation = TRANSACTION_ISOLATION_MAP.fetch(opts[:isolation]) if opts[:isolation]
|
80
92
|
joinable = !opts[:auto_savepoint]
|
81
93
|
|
82
94
|
activerecord_connection.begin_transaction(isolation: isolation, joinable: joinable)
|
83
95
|
end
|
84
96
|
|
85
|
-
def commit_transaction(conn, opts =
|
97
|
+
def commit_transaction(conn, opts = OPTS)
|
86
98
|
activerecord_connection.commit_transaction
|
87
99
|
end
|
88
100
|
|
89
|
-
def rollback_transaction(conn, opts =
|
101
|
+
def rollback_transaction(conn, opts = OPTS)
|
90
102
|
activerecord_connection.rollback_transaction
|
91
|
-
activerecord_connection.transaction_manager.send(:after_failure_actions, activerecord_connection.current_transaction, $!) if activerecord_connection.transaction_manager.respond_to?(:after_failure_actions)
|
92
103
|
end
|
93
104
|
|
105
|
+
# When Active Record holds the transaction, we cannot use Sequel hooks,
|
106
|
+
# because Sequel doesn't have knowledge of when the transaction is
|
107
|
+
# committed. So in this case we register an Active Record hook using the
|
108
|
+
# after_commit_everywhere gem.
|
94
109
|
def add_transaction_hook(conn, type, block)
|
95
110
|
if _trans(conn)[:activerecord]
|
96
|
-
|
111
|
+
ACTIVERECORD_CALLBACKS.public_send(type, &block)
|
112
|
+
else
|
113
|
+
super
|
97
114
|
end
|
98
|
-
|
99
|
-
super
|
100
115
|
end
|
101
116
|
|
117
|
+
# When Active Record holds the savepoint, we cannot use Sequel hooks,
|
118
|
+
# because Sequel doesn't have knowledge of when the savepoint is
|
119
|
+
# released. So in this case we register an Active Record hook using the
|
120
|
+
# after_commit_everywhere gem.
|
102
121
|
def add_savepoint_hook(conn, type, block)
|
103
122
|
if _trans(conn)[:savepoints].last[:activerecord]
|
104
|
-
|
123
|
+
ACTIVERECORD_CALLBACKS.public_send(type, &block)
|
124
|
+
else
|
125
|
+
super
|
105
126
|
end
|
106
|
-
|
107
|
-
super
|
108
127
|
end
|
109
128
|
|
110
|
-
|
111
|
-
|
129
|
+
# Active Record doesn't guarantee that a single connection can only be used
|
130
|
+
# by one thread at a time, so we need to use locking, which is what Active
|
131
|
+
# Record does internally as well.
|
132
|
+
def activerecord_lock
|
133
|
+
return yield if ActiveRecord.version < Gem::Version.new("5.1.0")
|
134
|
+
|
135
|
+
activerecord_connection.lock.synchronize do
|
136
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
137
|
+
yield
|
138
|
+
end
|
139
|
+
end
|
112
140
|
end
|
113
141
|
|
114
142
|
def activerecord_connection
|
115
143
|
activerecord_model.connection
|
116
144
|
end
|
117
145
|
|
118
|
-
def
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
146
|
+
def activerecord_log(sql, &block)
|
147
|
+
ActiveSupport::Notifications.instrument(
|
148
|
+
"sql.active_record",
|
149
|
+
sql: sql,
|
150
|
+
name: "Sequel",
|
151
|
+
connection: activerecord_connection,
|
152
|
+
&block
|
153
|
+
)
|
124
154
|
end
|
125
155
|
end
|
126
156
|
|
@@ -7,32 +7,15 @@ module Sequel
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
ensure
|
18
|
-
stmt.close if stmt
|
19
|
-
end
|
20
|
-
|
21
|
-
def execute(sql, opts=OPTS)
|
22
|
-
activerecord_connection.send(:log, sql) do
|
23
|
-
super
|
24
|
-
end
|
25
|
-
rescue ActiveRecord::StatementInvalid => exception
|
26
|
-
raise_error(exception.cause, classes: database_error_classes)
|
27
|
-
end
|
28
|
-
|
29
|
-
def execute_dui(sql, opts=OPTS)
|
30
|
-
activerecord_connection.send(:log, sql) do
|
31
|
-
super
|
10
|
+
def synchronize(*)
|
11
|
+
super do |conn|
|
12
|
+
if database_type == :oracle
|
13
|
+
yield conn.raw_connection
|
14
|
+
else
|
15
|
+
yield conn.connection
|
16
|
+
end
|
32
17
|
end
|
33
|
-
rescue ActiveRecord::StatementInvalid => exception
|
34
|
-
raise_error(exception.cause, classes: database_error_classes)
|
35
18
|
end
|
36
19
|
end
|
37
20
|
end
|
38
|
-
end
|
21
|
+
end
|
@@ -1,34 +1,26 @@
|
|
1
|
+
require_relative "utils"
|
2
|
+
|
1
3
|
module Sequel
|
2
4
|
module ActiveRecordConnection
|
3
5
|
module Mysql2
|
4
|
-
def
|
5
|
-
|
6
|
+
def synchronize(*)
|
7
|
+
super do |conn|
|
8
|
+
if conn.instance_variable_defined?(:@sequel_default_query_options)
|
9
|
+
return yield(conn)
|
10
|
+
end
|
6
11
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
cache_rows: false,
|
11
|
-
)
|
12
|
+
# required for prepared statements
|
13
|
+
conn.instance_variable_set(:@sequel_default_query_options, conn.query_options.dup)
|
14
|
+
Utils.add_prepared_statements_cache(conn)
|
12
15
|
|
13
|
-
|
16
|
+
conn.query_options.merge!(as: :hash, symbolize_keys: true, cache_rows: false)
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
result
|
18
|
+
begin
|
19
|
+
yield conn
|
20
|
+
ensure
|
21
|
+
conn.query_options.replace(conn.remove_instance_variable(:@sequel_default_query_options))
|
20
22
|
end
|
21
|
-
elsif block_given?
|
22
|
-
yield activerecord_raw_connection
|
23
|
-
end
|
24
|
-
rescue ActiveRecord::StatementInvalid => exception
|
25
|
-
if exception.cause.is_a?(::Mysql2::Error)
|
26
|
-
raise_error(exception.cause)
|
27
|
-
else
|
28
|
-
raise
|
29
23
|
end
|
30
|
-
ensure
|
31
|
-
activerecord_raw_connection.query_options.replace(original_query_options)
|
32
24
|
end
|
33
25
|
end
|
34
26
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative "utils"
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module ActiveRecordConnection
|
5
|
+
module Oracle
|
6
|
+
def synchronize(*)
|
7
|
+
super do |conn|
|
8
|
+
# required for prepared statements
|
9
|
+
Utils.add_prepared_statements_cache(conn.raw_oci_connection)
|
10
|
+
|
11
|
+
yield conn.raw_oci_connection
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,28 +1,92 @@
|
|
1
|
+
require_relative "utils"
|
2
|
+
|
1
3
|
module Sequel
|
2
4
|
module ActiveRecordConnection
|
3
5
|
module Postgres
|
4
|
-
def
|
5
|
-
|
6
|
+
def synchronize(*)
|
7
|
+
super do |conn|
|
8
|
+
conn.extend(ConnectionMethods)
|
9
|
+
conn.instance_variable_set(:@db, self)
|
10
|
+
|
11
|
+
Utils.add_prepared_statements_cache(conn)
|
6
12
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
result.cmd_tuples
|
13
|
+
Utils.set_value(conn, :type_map_for_results, PG::TypeMapAllStrings.new) do
|
14
|
+
yield conn
|
15
|
+
end
|
11
16
|
end
|
12
|
-
rescue ActiveRecord::PreparedStatementCacheExpired
|
13
|
-
raise # ActiveRecord's transaction manager needs to handle this exception
|
14
|
-
rescue ActiveRecord::StatementInvalid => exception
|
15
|
-
raise_error(exception.cause, classes: database_error_classes)
|
16
|
-
ensure
|
17
|
-
result.clear if result
|
18
17
|
end
|
19
18
|
|
20
|
-
|
19
|
+
# Reject unsupported Postgres-specific transaction options.
|
20
|
+
def transaction(opts = OPTS)
|
21
21
|
%i[deferrable read_only synchronous].each do |key|
|
22
|
-
fail Error, "#{key.inspect} transaction option is currently not supported" if
|
22
|
+
fail Error, "#{key.inspect} transaction option is currently not supported" if opts.key?(key)
|
23
23
|
end
|
24
24
|
|
25
25
|
super
|
26
|
+
rescue => e
|
27
|
+
activerecord_connection.clear_cache! if e.class.name == "ActiveRecord::PreparedStatementCacheExpired" && !in_transaction?
|
28
|
+
raise
|
29
|
+
end
|
30
|
+
|
31
|
+
# Copy-pasted from Sequel::Postgres::Adapter.
|
32
|
+
module ConnectionMethods
|
33
|
+
# The underlying exception classes to reraise as disconnect errors
|
34
|
+
# instead of regular database errors.
|
35
|
+
DISCONNECT_ERROR_CLASSES = Sequel::Postgres::Adapter::DISCONNECT_ERROR_CLASSES
|
36
|
+
|
37
|
+
# Since exception class based disconnect checking may not work,
|
38
|
+
# also trying parsing the exception message to look for disconnect
|
39
|
+
# errors.
|
40
|
+
DISCONNECT_ERROR_REGEX = Sequel::Postgres::Adapter::DISCONNECT_ERROR_RE
|
41
|
+
|
42
|
+
def async_exec_params(sql, args)
|
43
|
+
defined?(super) ? super : async_exec(sql, args)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Raise a Sequel::DatabaseDisconnectError if a one of the disconnect
|
47
|
+
# error classes is raised, or a PG::Error is raised and the connection
|
48
|
+
# status cannot be determined or it is not OK.
|
49
|
+
def check_disconnect_errors
|
50
|
+
begin
|
51
|
+
yield
|
52
|
+
rescue *DISCONNECT_ERROR_CLASSES => e
|
53
|
+
disconnect = true
|
54
|
+
raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
|
55
|
+
rescue PG::Error => e
|
56
|
+
disconnect = false
|
57
|
+
begin
|
58
|
+
s = status
|
59
|
+
rescue PG::Error
|
60
|
+
disconnect = true
|
61
|
+
end
|
62
|
+
status_ok = (s == PG::CONNECTION_OK)
|
63
|
+
disconnect ||= !status_ok
|
64
|
+
disconnect ||= e.message =~ DISCONNECT_ERROR_REGEX
|
65
|
+
disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
|
66
|
+
ensure
|
67
|
+
block if status_ok && !disconnect
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Execute the given SQL with this connection. If a block is given,
|
72
|
+
# yield the results, otherwise, return the number of changed rows.
|
73
|
+
def execute(sql, args = nil)
|
74
|
+
args = args.map { |v| @db.bound_variable_arg(v, self) } if args
|
75
|
+
result = check_disconnect_errors { execute_query(sql, args) }
|
76
|
+
|
77
|
+
block_given? ? yield(result) : result.cmd_tuples
|
78
|
+
ensure
|
79
|
+
result.clear if result
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Return the PG::Result containing the query results.
|
85
|
+
def execute_query(sql, args)
|
86
|
+
@db.log_connection_yield(sql, self, args) do
|
87
|
+
args ? async_exec_params(sql, args) : async_exec(sql)
|
88
|
+
end
|
89
|
+
end
|
26
90
|
end
|
27
91
|
end
|
28
92
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative "utils"
|
2
|
+
|
1
3
|
module Sequel
|
2
4
|
module ActiveRecordConnection
|
3
5
|
module Sqlite
|
@@ -7,43 +9,16 @@ module Sequel
|
|
7
9
|
end
|
8
10
|
end
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
private
|
12
|
+
def synchronize(*)
|
13
|
+
super do |conn|
|
14
|
+
conn.extended_result_codes = true if conn.respond_to?(:extended_result_codes=)
|
15
15
|
|
16
|
-
|
17
|
-
# try to replicate what ActiveRecord does around connection excecution.
|
18
|
-
def _execute(type, sql, opts, &block)
|
19
|
-
if activerecord_raw_connection.respond_to?(:extended_result_codes=)
|
20
|
-
activerecord_raw_connection.extended_result_codes = true
|
21
|
-
end
|
16
|
+
Utils.add_prepared_statements_cache(conn)
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
activerecord_connection.send(:log, sql) do
|
28
|
-
activesupport_interlock do
|
29
|
-
case type
|
30
|
-
when :select
|
31
|
-
activerecord_raw_connection.query(sql, &block)
|
32
|
-
when :insert
|
33
|
-
activerecord_raw_connection.execute(sql)
|
34
|
-
activerecord_raw_connection.last_insert_row_id
|
35
|
-
when :update
|
36
|
-
activerecord_raw_connection.execute_batch(sql)
|
37
|
-
activerecord_raw_connection.changes
|
38
|
-
end
|
18
|
+
Utils.set_value(conn, :results_as_hash, nil) do
|
19
|
+
yield conn
|
39
20
|
end
|
40
21
|
end
|
41
|
-
rescue ActiveRecord::StatementInvalid => exception
|
42
|
-
if exception.cause.is_a?(SQLite3::Exception)
|
43
|
-
raise_error(exception.cause)
|
44
|
-
else
|
45
|
-
raise exception
|
46
|
-
end
|
47
22
|
end
|
48
23
|
end
|
49
24
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "utils"
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module ActiveRecordConnection
|
5
|
+
module Tinytds
|
6
|
+
def synchronize(*)
|
7
|
+
super do |conn|
|
8
|
+
conn.query_options.merge!(cache_rows: false)
|
9
|
+
|
10
|
+
begin
|
11
|
+
yield conn
|
12
|
+
ensure
|
13
|
+
conn.query_options.merge!(cache_rows: true)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sequel
|
2
|
+
module ActiveRecordConnection
|
3
|
+
module Utils
|
4
|
+
def self.set_value(object, name, new_value)
|
5
|
+
original_value = object.send(name)
|
6
|
+
object.send(:"#{name}=", new_value)
|
7
|
+
yield
|
8
|
+
ensure
|
9
|
+
object.send(:"#{name}=", original_value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.add_prepared_statements_cache(conn)
|
13
|
+
return if conn.respond_to?(:prepared_statements)
|
14
|
+
|
15
|
+
class << conn
|
16
|
+
attr_accessor :prepared_statements
|
17
|
+
end
|
18
|
+
conn.prepared_statements = {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "sequel-activerecord_connection"
|
3
|
-
spec.version = "
|
3
|
+
spec.version = "1.2.1"
|
4
4
|
spec.authors = ["Janko Marohnić"]
|
5
5
|
spec.email = ["janko.marohnic@gmail.com"]
|
6
6
|
|
@@ -9,12 +9,15 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.homepage = "https://github.com/janko/sequel-activerecord_connection"
|
10
10
|
spec.license = "MIT"
|
11
11
|
|
12
|
-
spec.required_ruby_version =
|
12
|
+
spec.required_ruby_version = ">= 2.3"
|
13
13
|
|
14
14
|
spec.add_dependency "sequel", "~> 5.16"
|
15
15
|
spec.add_dependency "activerecord", ">= 4.2", "< 7"
|
16
|
+
spec.add_dependency "after_commit_everywhere", "~> 0.1.5"
|
16
17
|
|
18
|
+
spec.add_development_dependency "sequel", "~> 5.38"
|
17
19
|
spec.add_development_dependency "minitest"
|
20
|
+
spec.add_development_dependency "warning" if RUBY_VERSION >= "2.4"
|
18
21
|
|
19
22
|
spec.files = Dir["README.md", "LICENSE.txt", "CHANGELOG.md", "lib/**/*.rb", "*.gemspec"]
|
20
23
|
spec.require_paths = ["lib"]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel-activerecord_connection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -44,6 +44,34 @@ dependencies:
|
|
44
44
|
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: after_commit_everywhere
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.1.5
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.1.5
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: sequel
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.38'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '5.38'
|
47
75
|
- !ruby/object:Gem::Dependency
|
48
76
|
name: minitest
|
49
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,6 +86,20 @@ dependencies:
|
|
58
86
|
- - ">="
|
59
87
|
- !ruby/object:Gem::Version
|
60
88
|
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: warning
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
61
103
|
description: Allows Sequel to use ActiveRecord connection for database interaction.
|
62
104
|
email:
|
63
105
|
- janko.marohnic@gmail.com
|
@@ -71,8 +113,11 @@ files:
|
|
71
113
|
- lib/sequel/extensions/activerecord_connection.rb
|
72
114
|
- lib/sequel/extensions/activerecord_connection/jdbc.rb
|
73
115
|
- lib/sequel/extensions/activerecord_connection/mysql2.rb
|
116
|
+
- lib/sequel/extensions/activerecord_connection/oracle.rb
|
74
117
|
- lib/sequel/extensions/activerecord_connection/postgres.rb
|
75
118
|
- lib/sequel/extensions/activerecord_connection/sqlite.rb
|
119
|
+
- lib/sequel/extensions/activerecord_connection/tinytds.rb
|
120
|
+
- lib/sequel/extensions/activerecord_connection/utils.rb
|
76
121
|
- sequel-activerecord_connection.gemspec
|
77
122
|
homepage: https://github.com/janko/sequel-activerecord_connection
|
78
123
|
licenses:
|
@@ -86,14 +131,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
131
|
requirements:
|
87
132
|
- - ">="
|
88
133
|
- !ruby/object:Gem::Version
|
89
|
-
version: 2.
|
134
|
+
version: '2.3'
|
90
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
136
|
requirements:
|
92
137
|
- - ">="
|
93
138
|
- !ruby/object:Gem::Version
|
94
139
|
version: '0'
|
95
140
|
requirements: []
|
96
|
-
rubygems_version: 3.1.
|
141
|
+
rubygems_version: 3.1.4
|
97
142
|
signing_key:
|
98
143
|
specification_version: 4
|
99
144
|
summary: Allows Sequel to use ActiveRecord connection for database interaction.
|