logstash-integration-jdbc 5.0.0.alpha1
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 +7 -0
- data/CHANGELOG.md +8 -0
- data/CONTRIBUTORS +22 -0
- data/Gemfile +11 -0
- data/LICENSE +13 -0
- data/NOTICE.TXT +5 -0
- data/README.md +105 -0
- data/docs/filter-jdbc_static.asciidoc +606 -0
- data/docs/filter-jdbc_streaming.asciidoc +317 -0
- data/docs/index.asciidoc +32 -0
- data/docs/input-jdbc.asciidoc +573 -0
- data/lib/logstash/filters/jdbc/basic_database.rb +125 -0
- data/lib/logstash/filters/jdbc/column.rb +39 -0
- data/lib/logstash/filters/jdbc/db_object.rb +101 -0
- data/lib/logstash/filters/jdbc/loader.rb +119 -0
- data/lib/logstash/filters/jdbc/loader_schedule.rb +64 -0
- data/lib/logstash/filters/jdbc/lookup.rb +253 -0
- data/lib/logstash/filters/jdbc/lookup_processor.rb +100 -0
- data/lib/logstash/filters/jdbc/lookup_result.rb +40 -0
- data/lib/logstash/filters/jdbc/read_only_database.rb +57 -0
- data/lib/logstash/filters/jdbc/read_write_database.rb +108 -0
- data/lib/logstash/filters/jdbc/repeating_load_runner.rb +13 -0
- data/lib/logstash/filters/jdbc/single_load_runner.rb +46 -0
- data/lib/logstash/filters/jdbc/validatable.rb +46 -0
- data/lib/logstash/filters/jdbc_static.rb +240 -0
- data/lib/logstash/filters/jdbc_streaming.rb +196 -0
- data/lib/logstash/inputs/jdbc.rb +341 -0
- data/lib/logstash/inputs/tzinfo_jruby_patch.rb +57 -0
- data/lib/logstash/plugin_mixins/jdbc/checked_count_logger.rb +43 -0
- data/lib/logstash/plugin_mixins/jdbc/jdbc.rb +298 -0
- data/lib/logstash/plugin_mixins/jdbc/statement_handler.rb +129 -0
- data/lib/logstash/plugin_mixins/jdbc/value_tracking.rb +140 -0
- data/lib/logstash/plugin_mixins/jdbc_streaming/cache_payload.rb +28 -0
- data/lib/logstash/plugin_mixins/jdbc_streaming/parameter_handler.rb +64 -0
- data/lib/logstash/plugin_mixins/jdbc_streaming/statement_handler.rb +143 -0
- data/lib/logstash/plugin_mixins/jdbc_streaming.rb +100 -0
- data/lib/logstash/plugin_mixins/statement_handler.rb +0 -0
- data/lib/logstash-integration-jdbc_jars.rb +5 -0
- data/logstash-integration-jdbc.gemspec +44 -0
- data/spec/filters/env_helper.rb +10 -0
- data/spec/filters/integration/jdbc_static_spec.rb +154 -0
- data/spec/filters/integration/jdbcstreaming_spec.rb +173 -0
- data/spec/filters/jdbc/column_spec.rb +70 -0
- data/spec/filters/jdbc/db_object_spec.rb +81 -0
- data/spec/filters/jdbc/loader_spec.rb +77 -0
- data/spec/filters/jdbc/lookup_processor_spec.rb +132 -0
- data/spec/filters/jdbc/lookup_spec.rb +253 -0
- data/spec/filters/jdbc/read_only_database_spec.rb +67 -0
- data/spec/filters/jdbc/read_write_database_spec.rb +90 -0
- data/spec/filters/jdbc/repeating_load_runner_spec.rb +24 -0
- data/spec/filters/jdbc/single_load_runner_spec.rb +16 -0
- data/spec/filters/jdbc_static_file_local_spec.rb +83 -0
- data/spec/filters/jdbc_static_spec.rb +162 -0
- data/spec/filters/jdbc_streaming_spec.rb +350 -0
- data/spec/filters/remote_server_helper.rb +24 -0
- data/spec/filters/shared_helpers.rb +34 -0
- data/spec/helpers/WHY-THIS-JAR.txt +4 -0
- data/spec/helpers/derbyrun.jar +0 -0
- data/spec/inputs/integration/integ_spec.rb +78 -0
- data/spec/inputs/jdbc_spec.rb +1431 -0
- data/vendor/jar-dependencies/org/apache/derby/derby/10.14.1.0/derby-10.14.1.0.jar +0 -0
- data/vendor/jar-dependencies/org/apache/derby/derbyclient/10.14.1.0/derbyclient-10.14.1.0.jar +0 -0
- metadata +319 -0
@@ -0,0 +1,1431 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/inputs/jdbc"
|
4
|
+
require "jdbc/derby"
|
5
|
+
require "sequel"
|
6
|
+
require "sequel/adapters/jdbc"
|
7
|
+
require "timecop"
|
8
|
+
require "stud/temporary"
|
9
|
+
require "time"
|
10
|
+
require "date"
|
11
|
+
|
12
|
+
# We do not need to set TZ env var anymore because we can have 'Sequel.application_timezone' set to utc by default now.
|
13
|
+
|
14
|
+
describe LogStash::Inputs::Jdbc do
|
15
|
+
let(:mixin_settings) do
|
16
|
+
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
|
17
|
+
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"}
|
18
|
+
end
|
19
|
+
let(:settings) { {} }
|
20
|
+
let(:plugin) { LogStash::Inputs::Jdbc.new(mixin_settings.merge(settings)) }
|
21
|
+
let(:queue) { Queue.new }
|
22
|
+
let (:db) do
|
23
|
+
Sequel.connect(mixin_settings['jdbc_connection_string'], :user=> nil, :password=> nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
before :each do
|
27
|
+
if !RSpec.current_example.metadata[:no_connection]
|
28
|
+
# before body
|
29
|
+
Jdbc::Derby.load_driver
|
30
|
+
db.create_table :test_table do
|
31
|
+
DateTime :created_at
|
32
|
+
Integer :num
|
33
|
+
String :string
|
34
|
+
DateTime :custom_time
|
35
|
+
end
|
36
|
+
db << "CREATE TABLE types_table (num INTEGER, string VARCHAR(255), started_at DATE, custom_time TIMESTAMP, ranking DECIMAL(16,6))"
|
37
|
+
db << "CREATE TABLE test1_table (num INTEGER, string VARCHAR(255), custom_time TIMESTAMP, created_at TIMESTAMP)"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
after :each do
|
42
|
+
if !RSpec.current_example.metadata[:no_connection]
|
43
|
+
db.drop_table(:test_table)
|
44
|
+
db.drop_table(:types_table)
|
45
|
+
db.drop_table(:test1_table)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when registering and tearing down" do
|
50
|
+
let(:settings) { {"statement" => "SELECT 1 as col1 FROM test_table"} }
|
51
|
+
|
52
|
+
it "should register without raising exception" do
|
53
|
+
expect { plugin.register }.to_not raise_error
|
54
|
+
plugin.stop
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should register with password set" do
|
58
|
+
mixin_settings['jdbc_password'] = 'pass'
|
59
|
+
expect { plugin.register }.to_not raise_error
|
60
|
+
plugin.stop
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should stop without raising exception" do
|
64
|
+
plugin.register
|
65
|
+
expect { plugin.stop }.to_not raise_error
|
66
|
+
end
|
67
|
+
|
68
|
+
it_behaves_like "an interruptible input plugin" do
|
69
|
+
let(:settings) do
|
70
|
+
{
|
71
|
+
"statement" => "SELECT 1 FROM test_table",
|
72
|
+
"schedule" => "* * * * * UTC"
|
73
|
+
}
|
74
|
+
end
|
75
|
+
let(:config) { mixin_settings.merge(settings) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when both jdbc_password and jdbc_password_filepath arguments are passed" do
|
80
|
+
let(:statement) { "SELECT * from test_table" }
|
81
|
+
let(:jdbc_password) { "secret" }
|
82
|
+
let(:jdbc_password_file_path) { Stud::Temporary.pathname }
|
83
|
+
let(:settings) { { "jdbc_password_filepath" => jdbc_password_file_path,
|
84
|
+
"jdbc_password" => jdbc_password,
|
85
|
+
"statement" => statement } }
|
86
|
+
|
87
|
+
it "should fail to register" do
|
88
|
+
expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when jdbc_password is passed in from a file" do
|
93
|
+
let(:statement) { "SELECT * from test_table" }
|
94
|
+
let(:jdbc_password) { "secret" }
|
95
|
+
let(:jdbc_password_file_path) { Stud::Temporary.pathname }
|
96
|
+
let(:settings) { { "jdbc_password_filepath" => jdbc_password_file_path,
|
97
|
+
"statement" => statement } }
|
98
|
+
|
99
|
+
before do
|
100
|
+
File.write(jdbc_password_file_path, jdbc_password)
|
101
|
+
plugin.register
|
102
|
+
end
|
103
|
+
|
104
|
+
after do
|
105
|
+
plugin.stop
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should read in jdbc_password from file" do
|
109
|
+
expect(plugin.jdbc_password.value).to eq(jdbc_password)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
context "when neither statement and statement_filepath arguments are passed" do
|
115
|
+
it "should fail to register" do
|
116
|
+
expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when both statement and statement_filepath arguments are passed" do
|
121
|
+
let(:statement) { "SELECT * from test_table" }
|
122
|
+
let(:statement_file_path) { Stud::Temporary.pathname }
|
123
|
+
let(:settings) { { "statement_filepath" => statement_file_path, "statement" => statement } }
|
124
|
+
|
125
|
+
it "should fail to register" do
|
126
|
+
expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "when statement is passed in from a file" do
|
131
|
+
let(:statement) { "SELECT * from test_table" }
|
132
|
+
let(:statement_file_path) { Stud::Temporary.pathname }
|
133
|
+
let(:settings) { { "statement_filepath" => statement_file_path } }
|
134
|
+
|
135
|
+
before do
|
136
|
+
File.write(statement_file_path, statement)
|
137
|
+
plugin.register
|
138
|
+
end
|
139
|
+
|
140
|
+
after do
|
141
|
+
plugin.stop
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should read in statement from file" do
|
145
|
+
expect(plugin.statement).to eq(statement)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "when passing parameters" do
|
150
|
+
let(:settings) do
|
151
|
+
{
|
152
|
+
"statement" => "SELECT :num_param as num_param FROM SYSIBM.SYSDUMMY1",
|
153
|
+
"parameters" => { "num_param" => 10}
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
before do
|
158
|
+
plugin.register
|
159
|
+
end
|
160
|
+
|
161
|
+
after do
|
162
|
+
plugin.stop
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should retrieve params correctly from Event" do
|
166
|
+
plugin.run(queue)
|
167
|
+
expect(queue.pop.get('num_param')).to eq(settings['parameters']['num_param'])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "when scheduling" do
|
172
|
+
let(:settings) { {"statement" => "SELECT 1 as num_param FROM SYSIBM.SYSDUMMY1", "schedule" => "* * * * * UTC"} }
|
173
|
+
|
174
|
+
before do
|
175
|
+
plugin.register
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should properly schedule" do
|
179
|
+
Timecop.travel(Time.new(2000))
|
180
|
+
Timecop.scale(60)
|
181
|
+
runner = Thread.new do
|
182
|
+
plugin.run(queue)
|
183
|
+
end
|
184
|
+
sleep 3
|
185
|
+
plugin.stop
|
186
|
+
runner.kill
|
187
|
+
runner.join
|
188
|
+
expect(queue.size).to eq(2)
|
189
|
+
Timecop.return
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
context "when scheduling and previous runs are to be preserved" do
|
195
|
+
let(:settings) do
|
196
|
+
{
|
197
|
+
"statement" => "SELECT 1 as num_param FROM SYSIBM.SYSDUMMY1",
|
198
|
+
"schedule" => "* * * * * UTC",
|
199
|
+
"last_run_metadata_path" => Stud::Temporary.pathname
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
let(:last_run_time) { Time.at(1).utc }
|
204
|
+
|
205
|
+
before do
|
206
|
+
plugin.register
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should flush previous run metadata per query execution" do
|
210
|
+
Timecop.travel(Time.new(2000))
|
211
|
+
Timecop.scale(60)
|
212
|
+
runner = Thread.new do
|
213
|
+
plugin.run(queue)
|
214
|
+
end
|
215
|
+
sleep 1
|
216
|
+
for i in 0..1
|
217
|
+
sleep 1
|
218
|
+
updated_last_run = YAML.load(File.read(settings["last_run_metadata_path"]))
|
219
|
+
expect(updated_last_run).to be > last_run_time
|
220
|
+
last_run_time = updated_last_run
|
221
|
+
end
|
222
|
+
|
223
|
+
plugin.stop
|
224
|
+
runner.join
|
225
|
+
Timecop.return
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
context "when iterating result-set via paging" do
|
231
|
+
|
232
|
+
let(:settings) do
|
233
|
+
{
|
234
|
+
"statement" => "SELECT * from test_table",
|
235
|
+
"jdbc_paging_enabled" => true,
|
236
|
+
"jdbc_page_size" => 20
|
237
|
+
}
|
238
|
+
end
|
239
|
+
|
240
|
+
let(:num_rows) { 1000 }
|
241
|
+
|
242
|
+
before do
|
243
|
+
plugin.register
|
244
|
+
end
|
245
|
+
|
246
|
+
after do
|
247
|
+
plugin.stop
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should fetch all rows" do
|
251
|
+
num_rows.times do
|
252
|
+
db[:test_table].insert(:num => 1, :custom_time => Time.now.utc, :created_at => Time.now.utc)
|
253
|
+
end
|
254
|
+
|
255
|
+
plugin.run(queue)
|
256
|
+
|
257
|
+
expect(queue.size).to eq(num_rows)
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
context "when fetching time data" do
|
263
|
+
|
264
|
+
let(:settings) do
|
265
|
+
{
|
266
|
+
"statement" => "SELECT * from test_table",
|
267
|
+
}
|
268
|
+
end
|
269
|
+
|
270
|
+
let(:num_rows) { 10 }
|
271
|
+
|
272
|
+
before do
|
273
|
+
num_rows.times do
|
274
|
+
db[:test_table].insert(:num => 1, :custom_time => Time.now.utc, :created_at => Time.now.utc)
|
275
|
+
end
|
276
|
+
|
277
|
+
plugin.register
|
278
|
+
end
|
279
|
+
|
280
|
+
after do
|
281
|
+
plugin.stop
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should convert it to LogStash::Timestamp " do
|
285
|
+
plugin.run(queue)
|
286
|
+
event = queue.pop
|
287
|
+
expect(event.get("custom_time")).to be_a(LogStash::Timestamp)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe "when jdbc_default_timezone is set" do
|
292
|
+
let(:mixin_settings) do
|
293
|
+
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
|
294
|
+
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true",
|
295
|
+
"jdbc_default_timezone" => "America/Chicago"
|
296
|
+
}
|
297
|
+
end
|
298
|
+
|
299
|
+
let(:hours) { [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] }
|
300
|
+
|
301
|
+
context "when fetching time data and the tracking column is set and tracking column type defaults to 'numeric'" do
|
302
|
+
let(:settings) do
|
303
|
+
{
|
304
|
+
"statement" => "SELECT * from test_table WHERE num > :sql_last_value",
|
305
|
+
"last_run_metadata_path" => Stud::Temporary.pathname,
|
306
|
+
"tracking_column" => "num",
|
307
|
+
"use_column_value" => true
|
308
|
+
}
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should convert the time to reflect the timezone " do
|
312
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(42))
|
313
|
+
|
314
|
+
db[:test_table].insert(:num => 42, :custom_time => "2015-01-01 10:10:10", :created_at => Time.now.utc)
|
315
|
+
db[:test_table].insert(:num => 43, :custom_time => "2015-01-01 11:11:11", :created_at => Time.now.utc)
|
316
|
+
|
317
|
+
plugin.register
|
318
|
+
plugin.run(queue)
|
319
|
+
plugin.stop
|
320
|
+
expect(queue.size).to eq(1)
|
321
|
+
event = queue.pop
|
322
|
+
expect(event.get("num")).to eq(43)
|
323
|
+
expect(event.get("custom_time").time).to eq(Time.iso8601("2015-01-01T17:11:11.000Z"))
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
context "when fetching time data and the tracking column is NOT set, sql_last_value is time of run" do
|
328
|
+
|
329
|
+
let(:settings) do
|
330
|
+
{
|
331
|
+
"statement" => "SELECT * from test_table WHERE custom_time > :sql_last_value",
|
332
|
+
"last_run_metadata_path" => Stud::Temporary.pathname
|
333
|
+
}
|
334
|
+
end
|
335
|
+
|
336
|
+
before do
|
337
|
+
last_run_value = DateTime.iso8601("2000-01-01T12:00:00.000Z")
|
338
|
+
File.write(settings["last_run_metadata_path"], last_run_value)
|
339
|
+
Timecop.travel(DateTime.iso8601("2015-01-01T15:50:01.000Z")) do
|
340
|
+
# simulate earlier records written
|
341
|
+
hours.each do |i|
|
342
|
+
db[:test_table].insert(:num => i, :custom_time => "2015-01-01 #{i}:00:00", :created_at => Time.now.utc)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should convert the time to reflect the timezone " do
|
348
|
+
Timecop.travel(DateTime.iso8601("2015-01-02T02:10:00.000Z")) do
|
349
|
+
# simulate the first plugin run after the custom time of the last record
|
350
|
+
plugin.register
|
351
|
+
plugin.run(queue)
|
352
|
+
expected = hours.map{|hour| Time.iso8601("2015-01-01T06:00:00.000Z") + (hour * 3600) }# because Sequel converts the column values to Time instances.
|
353
|
+
actual = queue.size.times.map { queue.pop.get("custom_time").time }
|
354
|
+
expect(actual).to eq(expected)
|
355
|
+
plugin.stop
|
356
|
+
end
|
357
|
+
Timecop.travel(DateTime.iso8601("2015-01-02T02:20:00.000Z")) do
|
358
|
+
# simulate a run 10 minutes later
|
359
|
+
plugin.register
|
360
|
+
plugin.run(queue)
|
361
|
+
expect(queue.size).to eq(0) # no new records
|
362
|
+
plugin.stop
|
363
|
+
# now add records
|
364
|
+
db[:test_table].insert(:num => 11, :custom_time => "2015-01-01 20:20:20", :created_at => Time.now.utc)
|
365
|
+
db[:test_table].insert(:num => 12, :custom_time => "2015-01-01 21:21:21", :created_at => Time.now.utc)
|
366
|
+
end
|
367
|
+
Timecop.travel(DateTime.iso8601("2015-01-02T03:30:00.000Z")) do
|
368
|
+
# simulate another run later than the custom time of the last record
|
369
|
+
plugin.register
|
370
|
+
plugin.run(queue)
|
371
|
+
expect(queue.size).to eq(2)
|
372
|
+
plugin.stop
|
373
|
+
end
|
374
|
+
event = queue.pop
|
375
|
+
expect(event.get("num")).to eq(11)
|
376
|
+
expect(event.get("custom_time").time).to eq(Time.iso8601("2015-01-02T02:20:20.000Z"))
|
377
|
+
event = queue.pop
|
378
|
+
expect(event.get("num")).to eq(12)
|
379
|
+
expect(event.get("custom_time").time).to eq(Time.iso8601("2015-01-02T03:21:21.000Z"))
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context "when fetching time data and the tracking column is set, sql_last_value is sourced from a column, sub-second precision is maintained" do
|
384
|
+
let(:settings) do
|
385
|
+
{
|
386
|
+
"statement" => "SELECT * from test1_table WHERE custom_time > :sql_last_value ORDER BY custom_time",
|
387
|
+
"use_column_value" => true,
|
388
|
+
"tracking_column" => "custom_time",
|
389
|
+
"tracking_column_type" => "timestamp",
|
390
|
+
"last_run_metadata_path" => Stud::Temporary.pathname
|
391
|
+
}
|
392
|
+
end
|
393
|
+
|
394
|
+
let(:msecs) { [111, 122, 233, 244, 355, 366, 477, 488, 599, 611, 722] }
|
395
|
+
|
396
|
+
it "should convert the time to reflect the timezone " do
|
397
|
+
# Sequel only does the *correct* timezone calc on a DateTime instance
|
398
|
+
last_run_value = DateTime.iso8601("2000-01-01T00:00:00.987Z")
|
399
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_value))
|
400
|
+
hours.each_with_index do |i, j|
|
401
|
+
time_value = Time.utc(2015, 1, 1, i, 0, 0, msecs[j] * 1000)
|
402
|
+
db[:test1_table].insert(:num => i, :custom_time => time_value, :created_at => Time.now.utc)
|
403
|
+
end
|
404
|
+
|
405
|
+
plugin.register
|
406
|
+
|
407
|
+
plugin.run(queue)
|
408
|
+
expected = hours.map.with_index {|hour, i| Time.iso8601("2015-01-01T06:00:00.000Z") + (hour * 3600 + (msecs[i] / 1000.0)) }
|
409
|
+
actual = queue.size.times.map { queue.pop.get("custom_time").time }
|
410
|
+
expect(actual).to eq(expected)
|
411
|
+
plugin.stop
|
412
|
+
raw_last_run_value = File.read(settings["last_run_metadata_path"])
|
413
|
+
last_run_value = YAML.load(raw_last_run_value)
|
414
|
+
expect(last_run_value).to be_a(DateTime)
|
415
|
+
expect(last_run_value.strftime("%F %T.%N %Z")).to eq("2015-01-02 02:00:00.722000000 +00:00")
|
416
|
+
|
417
|
+
plugin.run(queue)
|
418
|
+
plugin.stop
|
419
|
+
db[:test1_table].insert(:num => 11, :custom_time => "2015-01-01 11:00:00.099", :created_at => Time.now.utc)
|
420
|
+
db[:test1_table].insert(:num => 12, :custom_time => "2015-01-01 21:00:00.811", :created_at => Time.now.utc)
|
421
|
+
expect(queue.size).to eq(0)
|
422
|
+
plugin.run(queue)
|
423
|
+
expect(queue.size).to eq(1)
|
424
|
+
event = queue.pop
|
425
|
+
plugin.stop
|
426
|
+
expect(event.get("num")).to eq(12)
|
427
|
+
expect(event.get("custom_time").time).to eq(Time.iso8601("2015-01-02T03:00:00.811Z"))
|
428
|
+
last_run_value = YAML.load(File.read(settings["last_run_metadata_path"]))
|
429
|
+
expect(last_run_value).to be_a(DateTime)
|
430
|
+
# verify that sub-seconds are recorded to the file
|
431
|
+
expect(last_run_value.strftime("%F %T.%N %Z")).to eq("2015-01-02 03:00:00.811000000 +00:00")
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
context "when fetching time data without jdbc_default_timezone set" do
|
437
|
+
let(:mixin_settings) do
|
438
|
+
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
|
439
|
+
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"
|
440
|
+
}
|
441
|
+
end
|
442
|
+
|
443
|
+
let(:settings) do
|
444
|
+
{
|
445
|
+
"statement" => "SELECT * from test_table",
|
446
|
+
}
|
447
|
+
end
|
448
|
+
|
449
|
+
let(:num_rows) { 1 }
|
450
|
+
|
451
|
+
before do
|
452
|
+
num_rows.times do
|
453
|
+
db.run "INSERT INTO test_table (created_at, num, custom_time) VALUES (TIMESTAMP('2015-01-01 12:00:00'), 1, TIMESTAMP('2015-01-01 12:00:00'))"
|
454
|
+
end
|
455
|
+
|
456
|
+
plugin.register
|
457
|
+
end
|
458
|
+
|
459
|
+
after do
|
460
|
+
plugin.stop
|
461
|
+
end
|
462
|
+
|
463
|
+
it "should not convert the time to reflect the timezone " do
|
464
|
+
plugin.run(queue)
|
465
|
+
event = queue.pop
|
466
|
+
# With no timezone set, no change should occur
|
467
|
+
expect(event.get("custom_time").time).to eq(Time.iso8601("2015-01-01T12:00:00Z"))
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
context "when iteratively running plugin#run" do
|
472
|
+
let(:settings) do
|
473
|
+
{"statement" => "SELECT num, created_at FROM test_table WHERE created_at > :sql_last_value"}
|
474
|
+
end
|
475
|
+
|
476
|
+
let(:nums) { [10, 20, 30, 40, 50] }
|
477
|
+
|
478
|
+
before do
|
479
|
+
plugin.register
|
480
|
+
end
|
481
|
+
|
482
|
+
after do
|
483
|
+
plugin.stop
|
484
|
+
end
|
485
|
+
|
486
|
+
it "should successfully iterate table with respect to field values" do
|
487
|
+
test_table = db[:test_table]
|
488
|
+
|
489
|
+
plugin.run(queue)
|
490
|
+
test_table.insert(:num => nums[0], :created_at => Time.now.utc)
|
491
|
+
test_table.insert(:num => nums[1], :created_at => Time.now.utc)
|
492
|
+
plugin.run(queue)
|
493
|
+
test_table.insert(:num => nums[2], :created_at => Time.now.utc)
|
494
|
+
test_table.insert(:num => nums[3], :created_at => Time.now.utc)
|
495
|
+
test_table.insert(:num => nums[4], :created_at => Time.now.utc)
|
496
|
+
plugin.run(queue)
|
497
|
+
|
498
|
+
actual_sum = 0
|
499
|
+
until queue.empty? do
|
500
|
+
actual_sum += queue.pop.get('num')
|
501
|
+
end
|
502
|
+
|
503
|
+
expect(actual_sum).to eq(nums.inject{|sum,x| sum + x })
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
context "when iteratively running plugin#run with tracking_column" do
|
508
|
+
let(:mixin_settings) do
|
509
|
+
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
|
510
|
+
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"
|
511
|
+
}
|
512
|
+
end
|
513
|
+
|
514
|
+
let(:settings) do
|
515
|
+
{ "statement" => "SELECT num, created_at FROM test_table WHERE num > :sql_last_value",
|
516
|
+
"use_column_value" => true,
|
517
|
+
"tracking_column" => "num",
|
518
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
519
|
+
end
|
520
|
+
|
521
|
+
let(:nums) { [10, 20, 30, 40, 50] }
|
522
|
+
|
523
|
+
before do
|
524
|
+
plugin.register
|
525
|
+
end
|
526
|
+
|
527
|
+
after do
|
528
|
+
plugin.stop
|
529
|
+
end
|
530
|
+
|
531
|
+
it "should successfully update sql_last_value" do
|
532
|
+
test_table = db[:test_table]
|
533
|
+
|
534
|
+
plugin.run(queue)
|
535
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(0)
|
536
|
+
test_table.insert(:num => nums[0], :created_at => Time.now.utc)
|
537
|
+
test_table.insert(:num => nums[1], :created_at => Time.now.utc)
|
538
|
+
plugin.run(queue)
|
539
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(20)
|
540
|
+
test_table.insert(:num => nums[2], :created_at => Time.now.utc)
|
541
|
+
test_table.insert(:num => nums[3], :created_at => Time.now.utc)
|
542
|
+
test_table.insert(:num => nums[4], :created_at => Time.now.utc)
|
543
|
+
plugin.run(queue)
|
544
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(50)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
context "when iteratively running plugin#run with timestamp tracking column with column value" do
|
549
|
+
let(:mixin_settings) do
|
550
|
+
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
|
551
|
+
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"
|
552
|
+
}
|
553
|
+
end
|
554
|
+
|
555
|
+
let(:settings) do
|
556
|
+
{ "statement" => "SELECT num, created_at, custom_time FROM test_table WHERE custom_time > :sql_last_value",
|
557
|
+
"use_column_value" => true,
|
558
|
+
"tracking_column" => "custom_time",
|
559
|
+
"tracking_column_type" => "timestamp",
|
560
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
561
|
+
end
|
562
|
+
|
563
|
+
let(:nums) { [10, 20, 30, 40, 50] }
|
564
|
+
let(:times) {["2015-05-06 13:14:15","2015-05-07 13:14:15","2015-05-08 13:14:15","2015-05-09 13:14:15","2015-05-10 13:14:15"]}
|
565
|
+
|
566
|
+
before do
|
567
|
+
plugin.register
|
568
|
+
end
|
569
|
+
|
570
|
+
after do
|
571
|
+
plugin.stop
|
572
|
+
end
|
573
|
+
|
574
|
+
it "should successfully update sql_last_value" do
|
575
|
+
test_table = db[:test_table]
|
576
|
+
|
577
|
+
plugin.run(queue)
|
578
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(Time.parse("1970-01-01 00:00:00.000000000 +0000"))
|
579
|
+
test_table.insert(:num => nums[0], :created_at => Time.now.utc, :custom_time => times[0])
|
580
|
+
test_table.insert(:num => nums[1], :created_at => Time.now.utc, :custom_time => times[1])
|
581
|
+
plugin.run(queue)
|
582
|
+
expect(plugin.instance_variable_get("@value_tracker").value.class).to eq(Time.parse(times[0]).class)
|
583
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(Time.parse(times[1]))
|
584
|
+
test_table.insert(:num => nums[2], :created_at => Time.now.utc, :custom_time => times[2])
|
585
|
+
test_table.insert(:num => nums[3], :created_at => Time.now.utc, :custom_time => times[3])
|
586
|
+
test_table.insert(:num => nums[4], :created_at => Time.now.utc, :custom_time => times[4])
|
587
|
+
plugin.run(queue)
|
588
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(Time.parse(times[4]))
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
context "when iteratively running plugin#run with tracking_column and stored metadata" do
|
593
|
+
let(:mixin_settings) do
|
594
|
+
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
|
595
|
+
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"
|
596
|
+
}
|
597
|
+
end
|
598
|
+
|
599
|
+
let(:settings) do
|
600
|
+
{ "statement" => "SELECT num, created_at FROM test_table WHERE num > :sql_last_value",
|
601
|
+
"use_column_value" => true,
|
602
|
+
"tracking_column" => "num",
|
603
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
604
|
+
end
|
605
|
+
|
606
|
+
let(:nums) { [10, 20, 30, 40, 50] }
|
607
|
+
let(:last_run_value) { 20 }
|
608
|
+
|
609
|
+
before do
|
610
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_value))
|
611
|
+
plugin.register
|
612
|
+
end
|
613
|
+
|
614
|
+
after do
|
615
|
+
plugin.stop
|
616
|
+
end
|
617
|
+
|
618
|
+
it "should successfully update sql_last_value and only add appropriate events" do
|
619
|
+
test_table = db[:test_table]
|
620
|
+
|
621
|
+
plugin.run(queue)
|
622
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(20)
|
623
|
+
expect(queue.length).to eq(0) # Shouldn't grab anything here.
|
624
|
+
test_table.insert(:num => nums[0], :created_at => Time.now.utc)
|
625
|
+
test_table.insert(:num => nums[1], :created_at => Time.now.utc)
|
626
|
+
plugin.run(queue)
|
627
|
+
expect(queue.length).to eq(0) # Shouldn't grab anything here either.
|
628
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(20)
|
629
|
+
test_table.insert(:num => nums[2], :created_at => Time.now.utc)
|
630
|
+
test_table.insert(:num => nums[3], :created_at => Time.now.utc)
|
631
|
+
test_table.insert(:num => nums[4], :created_at => Time.now.utc)
|
632
|
+
plugin.run(queue)
|
633
|
+
expect(queue.length).to eq(3) # Only values greater than 20 should be grabbed.
|
634
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(50)
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
context "when iteratively running plugin#run with BAD tracking_column and stored metadata" do
|
639
|
+
let(:mixin_settings) do
|
640
|
+
{ "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
|
641
|
+
"jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"
|
642
|
+
}
|
643
|
+
end
|
644
|
+
|
645
|
+
let(:settings) do
|
646
|
+
{ "statement" => "SELECT num, created_at FROM test_table WHERE num > :sql_last_value",
|
647
|
+
"use_column_value" => true,
|
648
|
+
"tracking_column" => "not_num",
|
649
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
650
|
+
end
|
651
|
+
|
652
|
+
let(:nums) { [10, 20, 30, 40, 50] }
|
653
|
+
let(:last_run_value) { 20 }
|
654
|
+
|
655
|
+
before do
|
656
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_value))
|
657
|
+
plugin.register
|
658
|
+
end
|
659
|
+
|
660
|
+
after do
|
661
|
+
plugin.stop
|
662
|
+
end
|
663
|
+
|
664
|
+
it "should send a warning and not update sql_last_value" do
|
665
|
+
test_table = db[:test_table]
|
666
|
+
|
667
|
+
plugin.run(queue)
|
668
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(20)
|
669
|
+
expect(queue.length).to eq(0) # Shouldn't grab anything here.
|
670
|
+
test_table.insert(:num => nums[0], :created_at => Time.now.utc)
|
671
|
+
test_table.insert(:num => nums[1], :created_at => Time.now.utc)
|
672
|
+
plugin.run(queue)
|
673
|
+
expect(queue.length).to eq(0) # Shouldn't grab anything here either.
|
674
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(20)
|
675
|
+
test_table.insert(:num => nums[2], :created_at => Time.now.utc)
|
676
|
+
test_table.insert(:num => nums[3], :created_at => Time.now.utc)
|
677
|
+
test_table.insert(:num => nums[4], :created_at => Time.now.utc)
|
678
|
+
plugin.run(queue)
|
679
|
+
expect(queue.length).to eq(3) # Only values greater than 20 should be grabbed.
|
680
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(20)
|
681
|
+
expect(plugin.instance_variable_get("@tracking_column_warning_sent")).to eq(true)
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
685
|
+
context "when previous runs are to be respected upon successful query execution (by time)" do
|
686
|
+
|
687
|
+
let(:settings) do
|
688
|
+
{ "statement" => "SELECT 1 as num_param FROM SYSIBM.SYSDUMMY1",
|
689
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
690
|
+
end
|
691
|
+
|
692
|
+
let(:last_run_time) { Time.now.utc }
|
693
|
+
|
694
|
+
before do
|
695
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_time))
|
696
|
+
plugin.register
|
697
|
+
end
|
698
|
+
|
699
|
+
after do
|
700
|
+
plugin.stop
|
701
|
+
end
|
702
|
+
|
703
|
+
it "should respect last run metadata" do
|
704
|
+
plugin.run(queue)
|
705
|
+
|
706
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to be > last_run_time
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
context "when previous runs are to be respected upon successful query execution (by time string)" do
|
711
|
+
|
712
|
+
let(:settings) do
|
713
|
+
{ "statement" => "SELECT custom_time FROM test_table WHERE custom_time > :sql_last_value",
|
714
|
+
"use_column_value" => true,
|
715
|
+
"tracking_column" => "custom_time",
|
716
|
+
"tracking_column_type" => "timestamp",
|
717
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
718
|
+
end
|
719
|
+
|
720
|
+
let(:last_run_time) { '2010-03-19T14:48:40.483Z' }
|
721
|
+
|
722
|
+
before do
|
723
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_time))
|
724
|
+
test_table = db[:test_table]
|
725
|
+
test_table.insert(:num => 0, :custom_time => Time.now.utc)
|
726
|
+
plugin.register
|
727
|
+
end
|
728
|
+
|
729
|
+
after do
|
730
|
+
plugin.stop
|
731
|
+
end
|
732
|
+
|
733
|
+
it "should respect last run metadata" do
|
734
|
+
plugin.run(queue)
|
735
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to be > DateTime.parse(last_run_time).to_time
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
context "when previous runs are to be respected upon successful query execution (by date/time string)" do
|
740
|
+
|
741
|
+
let(:settings) do
|
742
|
+
{ "statement" => "SELECT custom_time FROM test_table WHERE custom_time > :sql_last_value",
|
743
|
+
"use_column_value" => true,
|
744
|
+
"tracking_column" => "custom_time",
|
745
|
+
"tracking_column_type" => "timestamp",
|
746
|
+
"jdbc_default_timezone" => "UTC", #this triggers the last_run_time to be treated as date/time
|
747
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
748
|
+
end
|
749
|
+
|
750
|
+
let(:last_run_time) { '2010-03-19T14:48:40.483Z' }
|
751
|
+
|
752
|
+
before do
|
753
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_time))
|
754
|
+
test_table = db[:test_table]
|
755
|
+
test_table.insert(:num => 0, :custom_time => Time.now.utc)
|
756
|
+
plugin.register
|
757
|
+
end
|
758
|
+
|
759
|
+
after do
|
760
|
+
plugin.stop
|
761
|
+
end
|
762
|
+
|
763
|
+
it "should respect last run metadata" do
|
764
|
+
plugin.run(queue)
|
765
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to be > DateTime.parse(last_run_time)
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
context "when previous runs are to be respected upon successful query execution (by column)" do
|
770
|
+
|
771
|
+
let(:settings) do
|
772
|
+
{ "statement" => "SELECT 1 as num_param FROM SYSIBM.SYSDUMMY1",
|
773
|
+
"use_column_value" => true,
|
774
|
+
"tracking_column" => "num_param",
|
775
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
776
|
+
end
|
777
|
+
|
778
|
+
let(:last_run_value) { 1 }
|
779
|
+
|
780
|
+
before do
|
781
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_value))
|
782
|
+
plugin.register
|
783
|
+
end
|
784
|
+
|
785
|
+
after do
|
786
|
+
plugin.stop
|
787
|
+
end
|
788
|
+
|
789
|
+
it "metadata should equal last_run_value" do
|
790
|
+
plugin.run(queue)
|
791
|
+
|
792
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(last_run_value)
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
context "when previous runs are to be respected upon query failure (by time)" do
|
797
|
+
let(:settings) do
|
798
|
+
{ "statement" => "SELECT col from non_existent_table",
|
799
|
+
"last_run_metadata_path" => Stud::Temporary.pathname }
|
800
|
+
end
|
801
|
+
|
802
|
+
let(:last_run_time) { Time.now.utc }
|
803
|
+
|
804
|
+
before do
|
805
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_time))
|
806
|
+
plugin.register
|
807
|
+
end
|
808
|
+
|
809
|
+
after do
|
810
|
+
plugin.stop
|
811
|
+
end
|
812
|
+
|
813
|
+
it "should not respect last run metadata" do
|
814
|
+
plugin.run(queue)
|
815
|
+
|
816
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(last_run_time)
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
context "when previous runs are to be respected upon query failure (by column)" do
|
821
|
+
let(:settings) do
|
822
|
+
{ "statement" => "SELECT col from non_existent_table",
|
823
|
+
"use_column_value" => true,
|
824
|
+
"tracking_column" => "num_param",
|
825
|
+
"last_run_metadata_path" => Stud::Temporary.pathname
|
826
|
+
}
|
827
|
+
end
|
828
|
+
|
829
|
+
let(:last_run_value) { 1 }
|
830
|
+
|
831
|
+
before do
|
832
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_value))
|
833
|
+
plugin.register
|
834
|
+
end
|
835
|
+
|
836
|
+
after do
|
837
|
+
plugin.stop
|
838
|
+
end
|
839
|
+
|
840
|
+
it "metadata should still reflect last value" do
|
841
|
+
plugin.run(queue)
|
842
|
+
|
843
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(last_run_value)
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
context "when doing a clean run (by time)" do
|
848
|
+
|
849
|
+
let(:settings) do
|
850
|
+
{
|
851
|
+
"statement" => "SELECT * FROM test_table",
|
852
|
+
"last_run_metadata_path" => Stud::Temporary.pathname,
|
853
|
+
"clean_run" => true
|
854
|
+
}
|
855
|
+
end
|
856
|
+
|
857
|
+
let(:last_run_time) { Time.at(1).utc }
|
858
|
+
|
859
|
+
before do
|
860
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_time))
|
861
|
+
plugin.register
|
862
|
+
end
|
863
|
+
|
864
|
+
after do
|
865
|
+
plugin.stop
|
866
|
+
end
|
867
|
+
|
868
|
+
it "should ignore last run metadata if :clean_run set to true" do
|
869
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(Time.at(0).utc)
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
context "when doing a clean run (by value)" do
|
874
|
+
|
875
|
+
let(:settings) do
|
876
|
+
{
|
877
|
+
"statement" => "SELECT * FROM test_table",
|
878
|
+
"last_run_metadata_path" => Stud::Temporary.pathname,
|
879
|
+
"use_column_value" => true,
|
880
|
+
"tracking_column" => "num_param",
|
881
|
+
"clean_run" => true
|
882
|
+
}
|
883
|
+
end
|
884
|
+
|
885
|
+
let(:last_run_value) { 1000 }
|
886
|
+
|
887
|
+
before do
|
888
|
+
File.write(settings["last_run_metadata_path"], YAML.dump(last_run_value))
|
889
|
+
plugin.register
|
890
|
+
end
|
891
|
+
|
892
|
+
after do
|
893
|
+
plugin.stop
|
894
|
+
end
|
895
|
+
|
896
|
+
it "should ignore last run metadata if :clean_run set to true" do
|
897
|
+
expect(plugin.instance_variable_get("@value_tracker").value).to eq(0)
|
898
|
+
end
|
899
|
+
end
|
900
|
+
|
901
|
+
|
902
|
+
context "when state is not to be persisted" do
|
903
|
+
let(:settings) do
|
904
|
+
{
|
905
|
+
"statement" => "SELECT * FROM test_table",
|
906
|
+
"last_run_metadata_path" => Stud::Temporary.pathname,
|
907
|
+
"record_last_run" => false
|
908
|
+
}
|
909
|
+
end
|
910
|
+
|
911
|
+
before do
|
912
|
+
plugin.register
|
913
|
+
end
|
914
|
+
|
915
|
+
after do
|
916
|
+
plugin.stop
|
917
|
+
end
|
918
|
+
|
919
|
+
it "should not save state if :record_last_run is false" do
|
920
|
+
expect(File).not_to exist(settings["last_run_metadata_path"])
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
context "when setting fetch size" do
|
925
|
+
|
926
|
+
let(:settings) do
|
927
|
+
{
|
928
|
+
"statement" => "SELECT * from test_table",
|
929
|
+
"jdbc_fetch_size" => 1
|
930
|
+
}
|
931
|
+
end
|
932
|
+
|
933
|
+
let(:num_rows) { 10 }
|
934
|
+
|
935
|
+
before do
|
936
|
+
num_rows.times do
|
937
|
+
db[:test_table].insert(:num => 1, :custom_time => Time.now.utc, :created_at => Time.now.utc)
|
938
|
+
end
|
939
|
+
|
940
|
+
plugin.register
|
941
|
+
end
|
942
|
+
|
943
|
+
after do
|
944
|
+
plugin.stop
|
945
|
+
end
|
946
|
+
|
947
|
+
it "should fetch all rows" do
|
948
|
+
plugin.run(queue)
|
949
|
+
expect(queue.size).to eq(num_rows)
|
950
|
+
end
|
951
|
+
end
|
952
|
+
|
953
|
+
context "when driver is not found" do
|
954
|
+
let(:settings) { { "statement" => "SELECT * FROM test_table" } }
|
955
|
+
|
956
|
+
before do
|
957
|
+
mixin_settings['jdbc_driver_class'] = "org.not.ExistsDriver"
|
958
|
+
end
|
959
|
+
|
960
|
+
it "should fail" do
|
961
|
+
expect do
|
962
|
+
plugin.register
|
963
|
+
plugin.run(queue) # load when first run
|
964
|
+
end.to raise_error(LogStash::PluginLoadingError)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
context "when timing out on connection" do
|
969
|
+
let(:settings) do
|
970
|
+
{
|
971
|
+
"statement" => "SELECT * FROM test_table",
|
972
|
+
"jdbc_pool_timeout" => 0,
|
973
|
+
"jdbc_connection_string" => 'mock://localhost:1527/db',
|
974
|
+
"sequel_opts" => {
|
975
|
+
"max_connections" => 1
|
976
|
+
}
|
977
|
+
}
|
978
|
+
end
|
979
|
+
|
980
|
+
it "should raise PoolTimeout error" do
|
981
|
+
plugin.register
|
982
|
+
plugin.run(queue)
|
983
|
+
db = plugin.instance_variable_get(:@database)
|
984
|
+
expect(db.pool.instance_variable_get(:@timeout)).to eq(0)
|
985
|
+
expect(db.pool.instance_variable_get(:@max_size)).to eq(1)
|
986
|
+
|
987
|
+
q, q1 = Queue.new, Queue.new
|
988
|
+
t = Thread.new{db.pool.hold{|c| q1.push nil; q.pop}}
|
989
|
+
q1.pop
|
990
|
+
expect{db.pool.hold {|c|}}.to raise_error(Sequel::PoolTimeout)
|
991
|
+
q.push nil
|
992
|
+
t.join
|
993
|
+
end
|
994
|
+
|
995
|
+
it "should log error message" do
|
996
|
+
allow(Sequel).to receive(:connect).and_raise(Sequel::PoolTimeout)
|
997
|
+
expect(plugin.logger).to receive(:error).with("Failed to connect to database. 0 second timeout exceeded. Tried 1 times.")
|
998
|
+
expect do
|
999
|
+
plugin.register
|
1000
|
+
plugin.run(queue)
|
1001
|
+
end.to raise_error(Sequel::PoolTimeout)
|
1002
|
+
end
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
context "when using logging" do
|
1006
|
+
|
1007
|
+
let(:settings) do
|
1008
|
+
{
|
1009
|
+
"statement" => "SELECT * from test_table", "sql_log_level" => "debug"
|
1010
|
+
}
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
let(:num_rows) { 5 }
|
1014
|
+
|
1015
|
+
before do
|
1016
|
+
allow(plugin.logger).to receive(:debug?)
|
1017
|
+
num_rows.times do
|
1018
|
+
db[:test_table].insert(:num => 1)
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
plugin.register
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
after do
|
1025
|
+
plugin.stop
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
it "should report the statements to logging" do
|
1029
|
+
expect(plugin.logger).to receive(:debug).once
|
1030
|
+
plugin.run(queue)
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
describe "config option lowercase_column_names behaviour" do
|
1035
|
+
let(:settings) { { "statement" => "SELECT * FROM ttt" } }
|
1036
|
+
let(:events) { [] }
|
1037
|
+
|
1038
|
+
before do
|
1039
|
+
db.create_table(:ttt) do
|
1040
|
+
Integer(:num)
|
1041
|
+
String(:somestring)
|
1042
|
+
end
|
1043
|
+
db[:ttt].insert(:num => 42, :somestring => "This is a string")
|
1044
|
+
plugin.register
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
after do
|
1048
|
+
plugin.stop
|
1049
|
+
db.drop_table(:ttt)
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
context "when lowercase_column_names is on (default)" do
|
1053
|
+
it "the field names are lower case" do
|
1054
|
+
plugin.run(events)
|
1055
|
+
expect(events.first.to_hash.keys.sort).to eq(
|
1056
|
+
["@timestamp", "@version","num", "somestring"])
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
context "when lowercase_column_names is off" do
|
1061
|
+
let(:settings) { { "statement" => "SELECT * FROM ttt", "lowercase_column_names" => false } }
|
1062
|
+
it "the field names are UPPER case (natural for Derby DB)" do
|
1063
|
+
plugin.run(events)
|
1064
|
+
expect(events.first.to_hash.keys.sort).to eq(
|
1065
|
+
["@timestamp", "@version","NUM", "SOMESTRING"])
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
context "when specifying connection_retry_attempts" do
|
1071
|
+
let(:settings) { {"statement" => "SELECT 1 as col1 FROM test_table"} }
|
1072
|
+
|
1073
|
+
it "should try to connect connection_retry_attempts times" do
|
1074
|
+
mixin_settings['connection_retry_attempts'] = 2
|
1075
|
+
mixin_settings['jdbc_pool_timeout'] = 0
|
1076
|
+
allow(Sequel).to receive(:connect).and_raise(Sequel::PoolTimeout)
|
1077
|
+
expect(plugin.logger).to receive(:error).with("Failed to connect to database. 0 second timeout exceeded. Trying again.")
|
1078
|
+
expect(plugin.logger).to receive(:error).with("Failed to connect to database. 0 second timeout exceeded. Tried 2 times.")
|
1079
|
+
expect do
|
1080
|
+
plugin.register
|
1081
|
+
plugin.run(queue)
|
1082
|
+
end.to raise_error(Sequel::PoolTimeout)
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
it "should not fail when passed a non-positive value" do
|
1086
|
+
mixin_settings['connection_retry_attempts'] = -2
|
1087
|
+
expect { plugin.register }.to_not raise_error
|
1088
|
+
plugin.stop
|
1089
|
+
end
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
context "when encoding of some columns need to be changed" do
|
1093
|
+
|
1094
|
+
let(:settings) {{ "statement" => "SELECT * from test_table" }}
|
1095
|
+
let(:events) { [] }
|
1096
|
+
let(:row) do
|
1097
|
+
{
|
1098
|
+
"column0" => "foo",
|
1099
|
+
"column1" => "bar".force_encoding(Encoding::ISO_8859_1),
|
1100
|
+
"column2" => 3
|
1101
|
+
}
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
before(:each) do
|
1105
|
+
dataset = double("Dataset")
|
1106
|
+
allow(dataset).to receive(:each).and_yield(row)
|
1107
|
+
allow(plugin).to receive(:jdbc_connect).and_wrap_original do |m, *args|
|
1108
|
+
_db = m.call(*args)
|
1109
|
+
allow(_db).to receive(:[]).and_return(dataset)
|
1110
|
+
_db
|
1111
|
+
end
|
1112
|
+
# allow_any_instance_of(Sequel::JDBC::Derby::Dataset).to receive(:each).and_yield(row)
|
1113
|
+
plugin.register
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
after(:each) do
|
1117
|
+
plugin.stop
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
it "should not convert any column by default" do
|
1121
|
+
encoded_row = {
|
1122
|
+
"column0" => "foo",
|
1123
|
+
"column1" => "bar".force_encoding(Encoding::ISO_8859_1),
|
1124
|
+
"column2" => 3
|
1125
|
+
}
|
1126
|
+
event = LogStash::Event.new(row)
|
1127
|
+
expect(LogStash::Event).to receive(:new) do |row|
|
1128
|
+
row.each do |k, v|
|
1129
|
+
next unless v.is_a?(String)
|
1130
|
+
expect(row[k].encoding).to eq(encoded_row[k].encoding)
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
event
|
1134
|
+
end
|
1135
|
+
plugin.run(events)
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
context "when all string columns should be encoded" do
|
1139
|
+
|
1140
|
+
let(:settings) do
|
1141
|
+
{
|
1142
|
+
"statement" => "SELECT * from test_table",
|
1143
|
+
"charset" => "ISO-8859-1"
|
1144
|
+
}
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
let(:row) do
|
1148
|
+
{
|
1149
|
+
"column0" => "foo".force_encoding(Encoding::ISO_8859_1),
|
1150
|
+
"column1" => "bar".force_encoding(Encoding::ISO_8859_1),
|
1151
|
+
"column2" => 3
|
1152
|
+
}
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
it "should transform all column string to UTF-8, default encoding" do
|
1156
|
+
encoded_row = {
|
1157
|
+
"column0" => "foo",
|
1158
|
+
"column1" => "bar",
|
1159
|
+
"column2" => 3
|
1160
|
+
}
|
1161
|
+
event = LogStash::Event.new(row)
|
1162
|
+
expect(LogStash::Event).to receive(:new) do |row|
|
1163
|
+
row.each do |k, v|
|
1164
|
+
next unless v.is_a?(String)
|
1165
|
+
expect(row[k].encoding).to eq(encoded_row[k].encoding)
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
event
|
1169
|
+
end
|
1170
|
+
plugin.run(events)
|
1171
|
+
end
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
context "when only an specific column should be converted" do
|
1175
|
+
|
1176
|
+
let(:settings) do
|
1177
|
+
{
|
1178
|
+
"statement" => "SELECT * from test_table",
|
1179
|
+
"columns_charset" => { "column1" => "ISO-8859-1" }
|
1180
|
+
}
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
let(:row) do
|
1184
|
+
{
|
1185
|
+
"column0" => "foo",
|
1186
|
+
"column1" => "bar".force_encoding(Encoding::ISO_8859_1),
|
1187
|
+
"column2" => 3,
|
1188
|
+
"column3" => "berlin".force_encoding(Encoding::ASCII_8BIT)
|
1189
|
+
}
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
it "should only convert the selected column" do
|
1193
|
+
encoded_row = {
|
1194
|
+
"column0" => "foo",
|
1195
|
+
"column1" => "bar",
|
1196
|
+
"column2" => 3,
|
1197
|
+
"column3" => "berlin".force_encoding(Encoding::ASCII_8BIT)
|
1198
|
+
}
|
1199
|
+
event = LogStash::Event.new(row)
|
1200
|
+
expect(LogStash::Event).to receive(:new) do |row|
|
1201
|
+
row.each do |k, v|
|
1202
|
+
next unless v.is_a?(String)
|
1203
|
+
expect(row[k].encoding).to eq(encoded_row[k].encoding)
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
event
|
1207
|
+
end
|
1208
|
+
plugin.run(events)
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
context "when fetching Various Typed data" do
|
1214
|
+
|
1215
|
+
let(:settings) do
|
1216
|
+
{
|
1217
|
+
"statement" => "SELECT * from types_table"
|
1218
|
+
}
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
before do
|
1222
|
+
db << "INSERT INTO types_table (num, string, started_at, custom_time, ranking) VALUES (1, 'A test', '1999-12-31', '1999-12-31 23:59:59', 95.67)"
|
1223
|
+
|
1224
|
+
plugin.register
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
after do
|
1228
|
+
plugin.stop
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
it "should convert all columns to valid Event acceptable data types" do
|
1232
|
+
plugin.run(queue)
|
1233
|
+
event = queue.pop
|
1234
|
+
expect(event.get("num")).to eq(1)
|
1235
|
+
expect(event.get("string")).to eq("A test")
|
1236
|
+
expect(event.get("started_at")).to be_a(LogStash::Timestamp)
|
1237
|
+
expect(event.get("started_at").to_s).to eq("1999-12-31T00:00:00.000Z")
|
1238
|
+
expect(event.get("custom_time")).to be_a(LogStash::Timestamp)
|
1239
|
+
expect(event.get("custom_time").to_s).to eq("1999-12-31T23:59:59.000Z")
|
1240
|
+
expect(event.get("ranking").to_f).to eq(95.67)
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
context "when debug logging and a count query raises a count related error" do
|
1245
|
+
let(:settings) do
|
1246
|
+
{ "statement" => "SELECT * from types_table" }
|
1247
|
+
end
|
1248
|
+
let(:logger) { double("logger", :debug? => true) }
|
1249
|
+
let(:statement_logger) { LogStash::PluginMixins::Jdbc::CheckedCountLogger.new(logger) }
|
1250
|
+
let(:value_tracker) { double("value tracker", :set_value => nil, :write => nil) }
|
1251
|
+
let(:msg) { 'Java::JavaSql::SQLSyntaxErrorException: Dynamic SQL Error; SQL error code = -104; Token unknown - line 1, column 105; LIMIT [SQLState:42000, ISC error code:335544634]' }
|
1252
|
+
let(:error_args) do
|
1253
|
+
{"exception" => msg}
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
before do
|
1257
|
+
db << "INSERT INTO types_table (num, string, started_at, custom_time, ranking) VALUES (1, 'A test', '1999-12-31', '1999-12-31 23:59:59', 95.67)"
|
1258
|
+
plugin.register
|
1259
|
+
plugin.set_statement_logger(statement_logger)
|
1260
|
+
plugin.set_value_tracker(value_tracker)
|
1261
|
+
allow(value_tracker).to receive(:value).and_return("bar")
|
1262
|
+
allow(statement_logger).to receive(:execute_count).once.and_raise(StandardError.new(msg))
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
after do
|
1266
|
+
plugin.stop
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
context "if the count query raises an error" do
|
1270
|
+
it "should log a debug line without a count key as its unknown whether a count works at this stage" do
|
1271
|
+
expect(logger).to receive(:warn).once.with("Attempting a count query raised an error, the generated count statement is most likely incorrect but check networking, authentication or your statement syntax", error_args)
|
1272
|
+
expect(logger).to receive(:warn).once.with("Ongoing count statement generation is being prevented")
|
1273
|
+
expect(logger).to receive(:debug).once.with("Executing JDBC query", :statement => settings["statement"], :parameters => {:sql_last_value=>"bar"})
|
1274
|
+
plugin.run(queue)
|
1275
|
+
queue.pop
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
it "should create an event normally" do
|
1279
|
+
allow(logger).to receive(:warn)
|
1280
|
+
allow(logger).to receive(:debug)
|
1281
|
+
plugin.run(queue)
|
1282
|
+
event = queue.pop
|
1283
|
+
expect(event.get("num")).to eq(1)
|
1284
|
+
expect(event.get("string")).to eq("A test")
|
1285
|
+
expect(event.get("started_at")).to be_a(LogStash::Timestamp)
|
1286
|
+
expect(event.get("started_at").to_s).to eq("1999-12-31T00:00:00.000Z")
|
1287
|
+
expect(event.get("custom_time")).to be_a(LogStash::Timestamp)
|
1288
|
+
expect(event.get("custom_time").to_s).to eq("1999-12-31T23:59:59.000Z")
|
1289
|
+
expect(event.get("ranking").to_f).to eq(95.67)
|
1290
|
+
end
|
1291
|
+
end
|
1292
|
+
end
|
1293
|
+
|
1294
|
+
context "when using prepared statements" do
|
1295
|
+
let(:last_run_value) { 250 }
|
1296
|
+
let(:expected_queue_size) { 100 }
|
1297
|
+
let(:num_rows) { 1000 }
|
1298
|
+
|
1299
|
+
context "check validation" do
|
1300
|
+
context "with an empty name setting" do
|
1301
|
+
let(:settings) do
|
1302
|
+
{
|
1303
|
+
"statement" => "SELECT * FROM test_table ORDER BY num FETCH NEXT ? ROWS ONLY",
|
1304
|
+
"prepared_statement_bind_values" => [100],
|
1305
|
+
"use_prepared_statements" => true,
|
1306
|
+
}
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
it "should fail to register" do
|
1310
|
+
expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
|
1311
|
+
end
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
context "with an mismatched placeholder vs bind values" do
|
1315
|
+
let(:settings) do
|
1316
|
+
{
|
1317
|
+
"statement" => "SELECT * FROM test_table ORDER BY num FETCH NEXT ? ROWS ONLY",
|
1318
|
+
"prepared_statement_bind_values" => [],
|
1319
|
+
"use_prepared_statements" => true,
|
1320
|
+
}
|
1321
|
+
end
|
1322
|
+
|
1323
|
+
it "should fail to register" do
|
1324
|
+
expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
|
1325
|
+
end
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
context "with jdbc paging enabled" do
|
1329
|
+
let(:settings) do
|
1330
|
+
{
|
1331
|
+
"statement" => "SELECT * FROM test_table ORDER BY num FETCH NEXT 100 ROWS ONLY",
|
1332
|
+
"prepared_statement_bind_values" => [],
|
1333
|
+
"prepared_statement_name" => "pstmt_test_without",
|
1334
|
+
"use_prepared_statements" => true,
|
1335
|
+
"jdbc_paging_enabled" => true,
|
1336
|
+
"jdbc_page_size" => 20,
|
1337
|
+
"jdbc_fetch_size" => 10
|
1338
|
+
}
|
1339
|
+
end
|
1340
|
+
|
1341
|
+
it "should fail to register" do
|
1342
|
+
expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
|
1343
|
+
end
|
1344
|
+
end
|
1345
|
+
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
context "and no validation failures" do
|
1349
|
+
before do
|
1350
|
+
::File.write(settings["last_run_metadata_path"], YAML.dump(last_run_value))
|
1351
|
+
num_rows.times do |n|
|
1352
|
+
db[:test_table].insert(:num => n.succ, :string => SecureRandom.hex(8), :custom_time => Time.now.utc, :created_at => Time.now.utc)
|
1353
|
+
end
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
after do
|
1357
|
+
plugin.stop
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
context "with jdbc paging enabled" do
|
1361
|
+
let(:settings) do
|
1362
|
+
{
|
1363
|
+
"statement" => "SELECT * FROM test_table ORDER BY num FETCH NEXT 100 ROWS ONLY",
|
1364
|
+
"prepared_statement_bind_values" => [],
|
1365
|
+
"prepared_statement_name" => "pstmt_test_without",
|
1366
|
+
"use_prepared_statements" => true,
|
1367
|
+
"tracking_column_type" => "numeric",
|
1368
|
+
"tracking_column" => "num",
|
1369
|
+
"use_column_value" => true,
|
1370
|
+
"last_run_metadata_path" => Stud::Temporary.pathname,
|
1371
|
+
"jdbc_paging_enabled" => true,
|
1372
|
+
"jdbc_page_size" => 20,
|
1373
|
+
"jdbc_fetch_size" => 10
|
1374
|
+
}
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
it "should fail to register" do
|
1378
|
+
expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
|
1379
|
+
end
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
context "without placeholder and bind parameters" do
|
1383
|
+
let(:settings) do
|
1384
|
+
{
|
1385
|
+
"statement" => "SELECT * FROM test_table ORDER BY num FETCH NEXT 100 ROWS ONLY",
|
1386
|
+
"prepared_statement_bind_values" => [],
|
1387
|
+
"prepared_statement_name" => "pstmt_test_without",
|
1388
|
+
"use_prepared_statements" => true,
|
1389
|
+
"tracking_column_type" => "numeric",
|
1390
|
+
"tracking_column" => "num",
|
1391
|
+
"use_column_value" => true,
|
1392
|
+
"last_run_metadata_path" => Stud::Temporary.pathname
|
1393
|
+
}
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
it "should fetch some rows" do
|
1397
|
+
plugin.register
|
1398
|
+
plugin.run(queue)
|
1399
|
+
|
1400
|
+
expect(queue.size).to eq(expected_queue_size)
|
1401
|
+
expect(YAML.load(File.read(settings["last_run_metadata_path"]))).to eq(expected_queue_size)
|
1402
|
+
end
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
|
1406
|
+
context "with bind parameters" do
|
1407
|
+
let(:settings) do
|
1408
|
+
{
|
1409
|
+
# Sadly, postgres does but derby doesn't - It is not allowed for both operands of '+' to be ? parameters.: PREPARE pstmt_test: SELECT * FROM test_table WHERE (num > ?) AND (num <= ? + ?) ORDER BY num
|
1410
|
+
"statement" => "SELECT * FROM test_table WHERE (num > ?) ORDER BY num FETCH NEXT ? ROWS ONLY",
|
1411
|
+
"prepared_statement_bind_values" => [":sql_last_value", expected_queue_size],
|
1412
|
+
"prepared_statement_name" => "pstmt_test_with",
|
1413
|
+
"use_prepared_statements" => true,
|
1414
|
+
"tracking_column_type" => "numeric",
|
1415
|
+
"tracking_column" => "num",
|
1416
|
+
"use_column_value" => true,
|
1417
|
+
"last_run_metadata_path" => Stud::Temporary.pathname
|
1418
|
+
}
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
it "should fetch some rows" do
|
1422
|
+
plugin.register
|
1423
|
+
plugin.run(queue)
|
1424
|
+
|
1425
|
+
expect(queue.size).to eq(expected_queue_size)
|
1426
|
+
expect(YAML.load(File.read(settings["last_run_metadata_path"]))).to eq(last_run_value + expected_queue_size)
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
end
|
1430
|
+
end
|
1431
|
+
end
|