Skip to content

Commit f7a4022

Browse files
authored
Merge pull request rails#47880 from adrianna-chang-shopify/ac-trilogy-adapter
Introduce adapter for Trilogy, a MySQL-compatible DB client
2 parents 112c3cd + 5ed3f60 commit f7a4022

File tree

68 files changed

+1735
-161
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1735
-161
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ platforms :ruby, :windows do
150150
group :db do
151151
gem "pg", "~> 1.3"
152152
gem "mysql2", "~> 0.5"
153+
gem "trilogy", "~> 2.4"
153154
end
154155
end
155156

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ GEM
507507
timeout (0.3.2)
508508
tomlrb (2.0.3)
509509
trailblazer-option (0.1.2)
510+
trilogy (2.4.0)
510511
turbo-rails (1.3.2)
511512
actionpack (>= 6.0.0)
512513
activejob (>= 6.0.0)
@@ -618,6 +619,7 @@ DEPENDENCIES
618619
sucker_punch
619620
tailwindcss-rails
620621
terser (>= 1.1.4)
622+
trilogy (~> 2.4)
621623
turbo-rails
622624
tzinfo-data
623625
w3c_validators (~> 1.3.6)

activerecord/CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
* Introduce adapter for Trilogy database client
2+
3+
Trilogy is a MySQL-compatible database client. Rails applications can use Trilogy
4+
by configuring their `config/database.yml`:
5+
6+
```yaml
7+
development:
8+
adapter: trilogy
9+
database: blog_development
10+
pool: 5
11+
```
12+
13+
Or by using the `DATABASE_URL` environment variable:
14+
15+
```ruby
16+
ENV['DATABASE_URL'] # => "trilogy://localhost/blog_development?pool=5"
17+
```
18+
19+
*Adrianna Chang*
20+
121
* `after_commit` callbacks defined on models now execute in the correct order.
222

323
```ruby

activerecord/RUNNING_UNIT_TESTS.rdoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ example:
2121
Simply executing <tt>bundle exec rake test</tt> is equivalent to the following:
2222

2323
$ bundle exec rake test:mysql2
24+
$ bundle exec rake test:trilogy
2425
$ bundle exec rake test:postgresql
2526
$ bundle exec rake test:sqlite3
2627

activerecord/Rakefile

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@ def run_without_aborting(*tasks)
1818
abort "Errors running #{errors.join(', ')}" if errors.any?
1919
end
2020

21-
desc "Run mysql2, sqlite, and postgresql tests by default"
21+
desc "Run mysql2, trilogy, sqlite, and postgresql tests by default"
2222
task default: :test
2323

2424
task :package
2525

26-
desc "Run mysql2, sqlite, and postgresql tests"
26+
desc "Run mysql2, trilogy, sqlite, and postgresql tests"
2727
task :test do
2828
tasks = defined?(JRUBY_VERSION) ?
2929
%w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) :
30-
%w(test_mysql2 test_sqlite3 test_postgresql)
30+
%w(test_mysql2 test_trilogy test_sqlite3 test_postgresql)
3131
run_without_aborting(*tasks)
3232
end
3333

3434
namespace :test do
3535
task :isolated do
3636
tasks = defined?(JRUBY_VERSION) ?
3737
%w(isolated_test_jdbcmysql isolated_test_jdbcsqlite3 isolated_test_jdbcpostgresql) :
38-
%w(isolated_test_mysql2 isolated_test_sqlite3 isolated_test_postgresql)
38+
%w(isolated_test_mysql2 isolated_test_trilogy isolated_test_sqlite3 isolated_test_postgresql)
3939
run_without_aborting(*tasks)
4040
end
4141

@@ -56,18 +56,19 @@ namespace :db do
5656
task drop: ["db:mysql:drop", "db:postgresql:drop"]
5757
end
5858

59-
%w( mysql2 postgresql sqlite3 sqlite3_mem oracle jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter|
59+
%w( mysql2 trilogy postgresql sqlite3 sqlite3_mem oracle jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter|
6060
namespace :test do
6161
Rake::TestTask.new(adapter => "#{adapter}:env") do |t|
6262
adapter_short = adapter[/^[a-z0-9]+/]
6363
t.libs << "test"
6464
files = (FileList["test/cases/**/*_test.rb"].reject {
6565
|x| x.include?("/adapters/") || x.include?("/encryption/performance")
6666
} + FileList["test/cases/adapters/#{adapter_short}/**/*_test.rb"])
67-
files = files + FileList["test/cases/adapters/abstract_mysql_adapter/**/*_test.rb"] if adapter == "mysql2"
67+
files = files + FileList["test/cases/adapters/abstract_mysql_adapter/**/*_test.rb"] if ["mysql2", "trilogy"].include?(adapter)
6868

6969
t.test_files = files
7070

71+
t.test_files = files
7172
t.warning = true
7273
t.verbose = true
7374
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)

activerecord/bin/test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module Minitest
1515
opts.separator ""
1616
opts.separator "Active Record options:"
1717
opts.on("-a", "--adapter [ADAPTER]",
18-
"Run tests using a specific adapter (sqlite3, sqlite3_mem, mysql2, postgresql)") do |adapter|
18+
"Run tests using a specific adapter (sqlite3, sqlite3_mem, mysql2, trilogy, postgresql)") do |adapter|
1919
ENV["ARCONN"] = adapter.strip
2020
end
2121

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveRecord
4+
module ConnectionAdapters
5+
module Trilogy
6+
module Errors
7+
# ServerShutdown will be raised when the database server was shutdown.
8+
class ServerShutdown < ActiveRecord::ConnectionFailed
9+
end
10+
11+
# ServerLost will be raised when the database connection was lost.
12+
class ServerLost < ActiveRecord::ConnectionFailed
13+
end
14+
15+
# ServerGone will be raised when the database connection is gone.
16+
class ServerGone < ActiveRecord::ConnectionFailed
17+
end
18+
19+
# BrokenPipe will be raised when a system process connection fails.
20+
class BrokenPipe < ActiveRecord::ConnectionFailed
21+
end
22+
23+
# SocketError will be raised when Ruby encounters a network error.
24+
class SocketError < ActiveRecord::ConnectionFailed
25+
end
26+
27+
# ConnectionResetByPeer will be raised when a network connection is closed
28+
# outside the sytstem process.
29+
class ConnectionResetByPeer < ActiveRecord::ConnectionFailed
30+
end
31+
32+
# ClosedConnection will be raised when the Trilogy encounters a closed
33+
# connection.
34+
class ClosedConnection < ActiveRecord::ConnectionFailed
35+
end
36+
37+
# InvalidSequenceId will be raised when Trilogy ecounters an invalid sequence
38+
# id.
39+
class InvalidSequenceId < ActiveRecord::ConnectionFailed
40+
end
41+
42+
# UnexpectedPacket will be raised when Trilogy ecounters an unexpected
43+
# response packet.
44+
class UnexpectedPacket < ActiveRecord::ConnectionFailed
45+
end
46+
end
47+
end
48+
end
49+
end
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveRecord
4+
module ConnectionAdapters
5+
module Trilogy
6+
class LostConnectionExceptionTranslator
7+
attr_reader :exception, :message, :error_number
8+
9+
def initialize(exception, message, error_number)
10+
@exception = exception
11+
@message = message
12+
@error_number = error_number
13+
end
14+
15+
def translate
16+
translate_database_exception || translate_ruby_exception || translate_trilogy_exception
17+
end
18+
19+
private
20+
ER_SERVER_SHUTDOWN = 1053
21+
CR_SERVER_LOST = 2013
22+
CR_SERVER_LOST_EXTENDED = 2055
23+
CR_SERVER_GONE_ERROR = 2006
24+
25+
def translate_database_exception
26+
case error_number
27+
when ER_SERVER_SHUTDOWN
28+
Errors::ServerShutdown.new(message)
29+
when CR_SERVER_LOST, CR_SERVER_LOST_EXTENDED
30+
Errors::ServerLost.new(message)
31+
when CR_SERVER_GONE_ERROR
32+
Errors::ServerGone.new(message)
33+
end
34+
end
35+
36+
def translate_ruby_exception
37+
case exception
38+
when Errno::EPIPE
39+
Errors::BrokenPipe.new(message)
40+
when SocketError, IOError
41+
Errors::SocketError.new(message)
42+
when ::Trilogy::ConnectionError
43+
if message.include?("Connection reset by peer")
44+
Errors::ConnectionResetByPeer.new(message)
45+
end
46+
end
47+
end
48+
49+
def translate_trilogy_exception
50+
return unless exception.is_a?(::Trilogy::Error)
51+
52+
case message
53+
when /TRILOGY_CLOSED_CONNECTION/
54+
Errors::ClosedConnection.new(message)
55+
when /TRILOGY_INVALID_SEQUENCE_ID/
56+
Errors::InvalidSequenceId.new(message)
57+
when /TRILOGY_UNEXPECTED_PACKET/
58+
Errors::UnexpectedPacket.new(message)
59+
end
60+
end
61+
end
62+
end
63+
end
64+
end

0 commit comments

Comments
 (0)