mysql_framework 2.0.1 → 2.1.4
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87c4479177595f0b81fd81122c23473b63416ecf3a6427620c3cef4ba25d3e58
|
4
|
+
data.tar.gz: c668c3276dfe1120f7be7f8d30238bd1b8123c5559221ec175a1d3d844cc8e80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc44ac34b35b2c615ce87b8a1a627edc5d9f97c50f14ba3da1cae08e4089b0dcea405c495e48efbba95c852f2418334b3e1c32d029ae3fc4ec09f5d4028cf68d
|
7
|
+
data.tar.gz: 2eba7935da06a55d525b96126655f6a30415a62cf31225f184a0a305571f4e2f83a292a7214c446b63c01cbdd005b652bf30a2796bcb2e24f8644e8a8182830c
|
@@ -4,6 +4,7 @@ module MysqlFramework
|
|
4
4
|
class Connector
|
5
5
|
def initialize(options = {})
|
6
6
|
@options = default_options.merge(options)
|
7
|
+
@mutex = Mutex.new
|
7
8
|
|
8
9
|
Mysql2::Client.default_query_options.merge!(symbolize_keys: true, cast_booleans: true)
|
9
10
|
end
|
@@ -25,7 +26,7 @@ module MysqlFramework
|
|
25
26
|
|
26
27
|
until @connection_pool.empty?
|
27
28
|
conn = @connection_pool.pop(true)
|
28
|
-
conn
|
29
|
+
conn&.close
|
29
30
|
end
|
30
31
|
|
31
32
|
@connection_pool = nil
|
@@ -38,32 +39,37 @@ module MysqlFramework
|
|
38
39
|
|
39
40
|
# This method is called to fetch a client from the connection pool.
|
40
41
|
def check_out
|
41
|
-
|
42
|
+
@mutex.synchronize do
|
43
|
+
begin
|
44
|
+
return new_client unless connection_pool_enabled?
|
42
45
|
|
43
|
-
|
46
|
+
client = @connection_pool.pop(true)
|
44
47
|
|
45
|
-
|
48
|
+
client.ping if @options[:reconnect]
|
46
49
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
client
|
51
|
+
rescue ThreadError
|
52
|
+
if @created_connections < max_pool_size
|
53
|
+
client = new_client
|
54
|
+
@created_connections += 1
|
55
|
+
return client
|
56
|
+
end
|
54
57
|
|
55
|
-
|
58
|
+
MysqlFramework.logger.error { "[#{self.class}] - Database connection pool depleted." }
|
56
59
|
|
57
|
-
|
60
|
+
raise 'Database connection pool depleted.'
|
61
|
+
end
|
62
|
+
end
|
58
63
|
end
|
59
64
|
|
60
65
|
# This method is called to check a client back in to the connection when no longer needed.
|
61
66
|
def check_in(client)
|
62
|
-
|
67
|
+
@mutex.synchronize do
|
68
|
+
return client&.close unless connection_pool_enabled?
|
63
69
|
|
64
|
-
|
65
|
-
|
66
|
-
|
70
|
+
client = new_client if client&.closed?
|
71
|
+
@connection_pool.push(client)
|
72
|
+
end
|
67
73
|
end
|
68
74
|
|
69
75
|
# This method is called to use a client from the connection pool.
|
@@ -75,10 +81,20 @@ module MysqlFramework
|
|
75
81
|
end
|
76
82
|
|
77
83
|
# This method is called to execute a prepared statement
|
84
|
+
#
|
85
|
+
# @note Ensure we free any result and close each statement, otherwise we
|
86
|
+
# can run into a 'Commands out of sync' error if multiple threads are
|
87
|
+
# running different queries at the same time.
|
78
88
|
def execute(query, provided_client = nil)
|
79
89
|
with_client(provided_client) do |client|
|
80
|
-
|
81
|
-
|
90
|
+
begin
|
91
|
+
statement = client.prepare(query.sql)
|
92
|
+
result = statement.execute(*query.params)
|
93
|
+
result&.to_a
|
94
|
+
ensure
|
95
|
+
result&.free
|
96
|
+
statement&.close
|
97
|
+
end
|
82
98
|
end
|
83
99
|
end
|
84
100
|
|
@@ -50,7 +50,12 @@ module MysqlFramework
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def invalid_nil_value?(value)
|
53
|
+
return false if skip_nil_validation?
|
53
54
|
nil_comparison? == false && value.nil?
|
54
55
|
end
|
56
|
+
|
57
|
+
def skip_nil_validation?
|
58
|
+
ENV.fetch('MYSQL_FRAMEWORK_SKIP_NIL_VALUE_VALIDATION', 'false').downcase == 'true'
|
59
|
+
end
|
55
60
|
end
|
56
61
|
end
|
@@ -118,6 +118,12 @@ describe MysqlFramework::Connector do
|
|
118
118
|
end
|
119
119
|
|
120
120
|
describe '#check_out' do
|
121
|
+
it 'calls synchronize on the mutex' do
|
122
|
+
expect(subject.instance_variable_get(:@mutex)).to receive(:synchronize)
|
123
|
+
|
124
|
+
subject.check_out
|
125
|
+
end
|
126
|
+
|
121
127
|
context 'when connection pooling is enabled' do
|
122
128
|
context 'when there are available connections' do
|
123
129
|
before do
|
@@ -203,6 +209,12 @@ describe MysqlFramework::Connector do
|
|
203
209
|
end
|
204
210
|
|
205
211
|
describe '#check_in' do
|
212
|
+
it 'calls synchronize on the mutex' do
|
213
|
+
expect(subject.instance_variable_get(:@mutex)).to receive(:synchronize)
|
214
|
+
|
215
|
+
subject.check_out
|
216
|
+
end
|
217
|
+
|
206
218
|
context 'when connection pooling is enabled' do
|
207
219
|
it 'returns the provided client to the connection pool' do
|
208
220
|
expect(subject.connections).to receive(:push).with(client)
|
@@ -232,6 +244,24 @@ describe MysqlFramework::Connector do
|
|
232
244
|
subject.check_in(client)
|
233
245
|
end
|
234
246
|
end
|
247
|
+
|
248
|
+
context 'when client is nil' do
|
249
|
+
let(:client) { nil }
|
250
|
+
|
251
|
+
context 'when connection pooling is enabled' do
|
252
|
+
it 'does not raise an error' do
|
253
|
+
expect { subject.check_in(client) }.not_to raise_error
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'when connection pooling is disabled' do
|
258
|
+
let(:connection_pooling_enabled) { 'false' }
|
259
|
+
|
260
|
+
it 'does not raise an error' do
|
261
|
+
expect { subject.check_in(client) }.not_to raise_error
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
235
265
|
end
|
236
266
|
|
237
267
|
describe '#with_client' do
|
@@ -272,6 +302,62 @@ describe MysqlFramework::Connector do
|
|
272
302
|
expect(results.length).to eq(1)
|
273
303
|
expect(results[0][:id]).to eq(guid)
|
274
304
|
end
|
305
|
+
|
306
|
+
context 'when cleaning up resources' do
|
307
|
+
let(:mock_client) { double('client') }
|
308
|
+
let(:mock_statement) { double('statement') }
|
309
|
+
let(:mock_result) { double('result') }
|
310
|
+
let(:select_query) { MysqlFramework::SqlQuery.new.select('*').from('demo') }
|
311
|
+
|
312
|
+
before do
|
313
|
+
allow(mock_result).to receive(:to_a)
|
314
|
+
allow(mock_result).to receive(:free)
|
315
|
+
|
316
|
+
allow(mock_statement).to receive(:close)
|
317
|
+
allow(mock_statement).to receive(:execute).and_return(mock_result)
|
318
|
+
|
319
|
+
allow(mock_client).to receive(:prepare).and_return(mock_statement)
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'frees the result' do
|
323
|
+
expect(mock_result).to receive(:free)
|
324
|
+
|
325
|
+
subject.execute(select_query, mock_client)
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'closes the statement' do
|
329
|
+
expect(mock_statement).to receive(:close)
|
330
|
+
|
331
|
+
subject.execute(select_query, mock_client)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'does not raise a commands out of sync error' do
|
336
|
+
threads = []
|
337
|
+
threads << Thread.new do
|
338
|
+
350.times do
|
339
|
+
update_query = MysqlFramework::SqlQuery.new.update('gems')
|
340
|
+
.set(updated_at: Time.now)
|
341
|
+
expect { subject.execute(update_query) }.not_to raise_error
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
threads << Thread.new do
|
346
|
+
350.times do
|
347
|
+
select_query = MysqlFramework::SqlQuery.new.select('*').from('demo')
|
348
|
+
expect { subject.execute(select_query) }.not_to raise_error
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
threads << Thread.new do
|
353
|
+
350.times do
|
354
|
+
select_query = MysqlFramework::SqlQuery.new.select('*').from('test')
|
355
|
+
expect { subject.execute(select_query) }.not_to raise_error
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
threads.each(&:join)
|
360
|
+
end
|
275
361
|
end
|
276
362
|
|
277
363
|
describe '#query' do
|
@@ -3,6 +3,12 @@
|
|
3
3
|
describe MysqlFramework::SqlCondition do
|
4
4
|
subject { described_class.new(column: 'version', comparison: '=', value: '1.0.0') }
|
5
5
|
|
6
|
+
before :each do
|
7
|
+
allow_any_instance_of(MysqlFramework::SqlCondition).to receive(:skip_nil_validation?).and_return(skip_nil_validation)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:skip_nil_validation) { false }
|
11
|
+
|
6
12
|
describe '#to_s' do
|
7
13
|
it 'returns the condition as a string for a prepared statement' do
|
8
14
|
expect(subject.to_s).to eq('version = ?')
|
@@ -16,6 +22,14 @@ describe MysqlFramework::SqlCondition do
|
|
16
22
|
it 'does raises an ArgumentError' do
|
17
23
|
expect { subject }.to raise_error(ArgumentError, "Comparison of = requires value to be not nil")
|
18
24
|
end
|
25
|
+
|
26
|
+
context 'when skip_nil_validation? is true' do
|
27
|
+
let(:skip_nil_validation) { true }
|
28
|
+
|
29
|
+
it 'does not raise an ArgumentError' do
|
30
|
+
expect(subject.value).to be_nil
|
31
|
+
end
|
32
|
+
end
|
19
33
|
end
|
20
34
|
end
|
21
35
|
|
@@ -33,6 +47,14 @@ describe MysqlFramework::SqlCondition do
|
|
33
47
|
it 'raises an ArgumentError if value is set' do
|
34
48
|
expect { subject }.to raise_error(ArgumentError, 'Cannot set value when comparison is IS NULL')
|
35
49
|
end
|
50
|
+
|
51
|
+
context 'when skip_nil_validation? is true' do
|
52
|
+
let(:skip_nil_validation) { true }
|
53
|
+
|
54
|
+
it 'raises an ArgumentError if value is set' do
|
55
|
+
expect { subject }.to raise_error(ArgumentError, 'Cannot set value when comparison is IS NULL')
|
56
|
+
end
|
57
|
+
end
|
36
58
|
end
|
37
59
|
end
|
38
60
|
|
@@ -67,6 +89,14 @@ describe MysqlFramework::SqlCondition do
|
|
67
89
|
it 'raises an ArgumentError if value is set' do
|
68
90
|
expect { subject }.to raise_error(ArgumentError, 'Cannot set value when comparison is IS NOT NULL')
|
69
91
|
end
|
92
|
+
|
93
|
+
context 'when skip_nil_validation? is true' do
|
94
|
+
let(:skip_nil_validation) { true }
|
95
|
+
|
96
|
+
it 'raises an ArgumentError if value is set' do
|
97
|
+
expect { subject }.to raise_error(ArgumentError, 'Cannot set value when comparison is IS NOT NULL')
|
98
|
+
end
|
99
|
+
end
|
70
100
|
end
|
71
101
|
end
|
72
102
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sage
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -137,8 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
requirements: []
|
140
|
-
|
141
|
-
rubygems_version: 2.7.7
|
140
|
+
rubygems_version: 3.0.8
|
142
141
|
signing_key:
|
143
142
|
specification_version: 4
|
144
143
|
summary: A lightweight framework to provide managers for working with MySQL.
|