Skip to content

Commit 94821e5

Browse files
authored
driver loading: provide loaded driver to Sequel::connect (#34)
`Sequel::connect` will first attempt to auto-detect the driver based on the jdbc connection string, and will [fall-back][] to the driver given as opts[:driver] when auto-detection fails. By memoizing the loaded driver from our previous `Sequel::JDBC::load_driver` and providing it to `Sequel::connect`, we provide one more chance for loading to work. This specifically seems to be the only path forward for Sybase drivers, whose jdbc connection strings begin with `jdbc:sybase:Tds:` but use the driver with path `com.sybase.jdbc4.jdbc.SybDriver`. [fall-back]: https://github.com/jeremyevans/sequel/blob/5.19.0/lib/sequel/adapters/jdbc.rb#L211-L213 Fixes: #23 Fixes: #30
1 parent b7d2ee7 commit 94821e5

File tree

7 files changed

+36
-21
lines changed

7 files changed

+36
-21
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 5.0.4
2+
- Fixed issue where JDBC Drivers that don't correctly register with Java's DriverManager fail to load (such as Sybase) [#34](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/34)
3+
14
## 5.0.3
25
- Fixed issue where a lost connection to the database can cause errors when using prepared statements with the scheduler [#25](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/25)
36

lib/logstash/filters/jdbc/basic_database.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def verify_connection(connection_string, driver_class, driver_library, user, pas
9595
end
9696
begin
9797
db = nil
98-
::Sequel::JDBC.load_driver(driver_class)
98+
@options_hash[:driver] = ::Sequel::JDBC.load_driver(driver_class)
9999
@connection_string = connection_string
100100
if user
101101
@options_hash[:user] = user

lib/logstash/plugin_mixins/jdbc/jdbc.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,14 @@ def jdbc_connect
110110
:user => @jdbc_user,
111111
:password => @jdbc_password.nil? ? nil : @jdbc_password.value,
112112
:pool_timeout => @jdbc_pool_timeout,
113+
:driver => @driver_impl, # Sequel uses this as a fallback, if given URI doesn't auto-load the driver correctly
113114
:keep_reference => false
114115
}.merge(@sequel_opts)
115116
retry_attempts = @connection_retry_attempts
116117
loop do
117118
retry_attempts -= 1
118119
begin
119-
return Sequel.connect(@jdbc_connection_string, opts=opts)
120+
return Sequel.connect(@jdbc_connection_string, opts)
120121
rescue Sequel::PoolTimeout => e
121122
if retry_attempts <= 0
122123
@logger.error("Failed to connect to database. #{@jdbc_pool_timeout} second timeout exceeded. Tried #{@connection_retry_attempts} times.")
@@ -147,7 +148,7 @@ def load_driver
147148

148149
load_driver_jars
149150
begin
150-
Sequel::JDBC.load_driver(@jdbc_driver_class)
151+
@driver_impl = Sequel::JDBC.load_driver(@jdbc_driver_class)
151152
rescue Sequel::AdapterNotFound => e # Sequel::AdapterNotFound, "#{@jdbc_driver_class} not loaded"
152153
# fix this !!!
153154
message = if jdbc_driver_library_set?

lib/logstash/plugin_mixins/jdbc_streaming.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def prepare_jdbc_connection
8484
@sequel_opts_symbols[:user] = @jdbc_user unless @jdbc_user.nil? || @jdbc_user.empty?
8585
@sequel_opts_symbols[:password] = @jdbc_password.value unless @jdbc_password.nil?
8686

87-
Sequel::JDBC.load_driver(@jdbc_driver_class)
87+
@sequel_opts_symbols[:driver] = Sequel::JDBC.load_driver(@jdbc_driver_class)
8888
@database = Sequel.connect(@jdbc_connection_string, @sequel_opts_symbols)
8989
if @jdbc_validate_connection
9090
@database.extension(:connection_validator)

logstash-integration-jdbc.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |s|
22
s.name = 'logstash-integration-jdbc'
3-
s.version = '5.0.3'
3+
s.version = '5.0.4'
44
s.licenses = ['Apache License (2.0)']
55
s.summary = "Integration with JDBC - input and filter plugins"
66
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"

spec/filters/jdbc/read_only_database_spec.rb

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,37 @@ module LogStash module Filters module Jdbc
1010
let(:driver_class) { "org.apache.derby.jdbc.EmbeddedDriver" }
1111
let(:driver_library) { nil }
1212
subject(:read_only_db) { described_class.create(connection_string, driver_class, driver_library) }
13+
let(:stub_driver_class) { double(driver_class).as_null_object }
1314

1415
describe "basic operations" do
1516
describe "initializing" do
17+
before(:each) do
18+
allow(Sequel::JDBC).to receive(:load_driver).and_return(stub_driver_class)
19+
end
20+
1621
it "tests the connection with defaults" do
1722
expect(Sequel::JDBC).to receive(:load_driver).once.with(driver_class)
18-
expect(Sequel).to receive(:connect).once.with(connection_string, {:test => true})
23+
expect(Sequel).to receive(:connect).once.with(connection_string, {:test => true, :driver => stub_driver_class})
1924
expect(read_only_db.empty_record_set).to eq([])
2025
end
2126

22-
it "tests the connection with fully specified arguments" do
23-
connection_str = "a connection string"
24-
user = "a user"
25-
password = Util::Password.new("secret")
26-
expect(Sequel::JDBC).to receive(:load_driver).once.with("a driver class")
27-
expect(Sequel).to receive(:connect).once.with(connection_str, {:user => user, :password => password.value, :test => true}).and_return(db)
28-
described_class.create(connection_str, "a driver class", nil, user, password)
27+
context 'with fully-specified arguments' do
28+
let(:connection_string) { "a connection string" }
29+
let(:user) { "a user" }
30+
let(:password) { Util::Password.new("secret") }
31+
let(:driver_class) { "a driver class" }
32+
33+
it "tests the connection" do
34+
expect(Sequel::JDBC).to receive(:load_driver).once.with(driver_class)
35+
expect(Sequel).to receive(:connect).once.with(connection_string, {:user => user, :password => password.value, :test => true, :driver => stub_driver_class}).and_return(db)
36+
described_class.create(connection_string, driver_class, nil, user, password)
37+
end
2938
end
3039

3140
it "connects with defaults" do
3241
expect(Sequel::JDBC).to receive(:load_driver).once.with(driver_class)
33-
expect(Sequel).to receive(:connect).once.with(connection_string, {:test => true}).and_return(db)
34-
expect(Sequel).to receive(:connect).once.with(connection_string, {}).and_return(db)
42+
expect(Sequel).to receive(:connect).once.with(connection_string, {:test => true, :driver => stub_driver_class}).and_return(db)
43+
expect(Sequel).to receive(:connect).once.with(connection_string, {:driver => stub_driver_class}).and_return(db)
3544
expect(read_only_db.connected?).to be_falsey
3645
read_only_db.connect("a caller specific error message")
3746
expect(read_only_db.connected?).to be_truthy

spec/filters/jdbc/read_write_database_spec.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,22 @@ module LogStash module Filters module Jdbc
1616
describe "basic operations" do
1717
context "connecting to a db" do
1818
it "connects with defaults" do
19-
expect(::Sequel::JDBC).to receive(:load_driver).once.with("org.apache.derby.jdbc.EmbeddedDriver")
19+
stub_driver_class = double('org.apache.derby.jdbc.EmbeddedDriver').as_null_object
20+
expect(::Sequel::JDBC).to receive(:load_driver).once.with("org.apache.derby.jdbc.EmbeddedDriver").and_return(stub_driver_class)
2021
# two calls to connect because ReadWriteDatabase does verify_connection and connect
21-
expect(::Sequel).to receive(:connect).once.with(connection_string_regex, {:test => true}).and_return(db)
22-
expect(::Sequel).to receive(:connect).once.with(connection_string_regex, {}).and_return(db)
22+
expect(::Sequel).to receive(:connect).once.with(connection_string_regex, {:driver => stub_driver_class, :test => true}).and_return(db)
23+
expect(::Sequel).to receive(:connect).once.with(connection_string_regex, {:driver => stub_driver_class}).and_return(db)
2324
expect(read_write_db.empty_record_set).to eq([])
2425
end
2526

2627
it "connects with fully specified arguments" do
2728
connection_str = "a connection string"
2829
user = "a user"
2930
password = Util::Password.new("secret")
30-
expect(::Sequel::JDBC).to receive(:load_driver).once.with("a driver class")
31-
expect(::Sequel).to receive(:connect).once.with(connection_str, {:user => user, :password => password.value, :test => true}).and_return(db)
32-
expect(::Sequel).to receive(:connect).once.with(connection_str, {:user => user, :password => password.value}).and_return(db)
31+
stub_driver_class = double('com.example.Driver')
32+
expect(::Sequel::JDBC).to receive(:load_driver).once.with("a driver class").and_return(stub_driver_class)
33+
expect(::Sequel).to receive(:connect).once.with(connection_str, {:driver => stub_driver_class, :user => user, :password => password.value, :test => true}).and_return(db)
34+
expect(::Sequel).to receive(:connect).once.with(connection_str, {:driver => stub_driver_class, :user => user, :password => password.value}).and_return(db)
3335
described_class.create(connection_str, "a driver class", nil, user, password)
3436
end
3537
end

0 commit comments

Comments
 (0)