fluent-plugin-mysql-select-insert 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a43d623e098edb37e7977ad86d9dfdfdc7748c129f0dc6127f773d755d19f8e0
4
- data.tar.gz: 89c6745c98fc33f0961b476cfa668129a4a07c4bf6e5fe2d21540e64950774fb
3
+ metadata.gz: 0f67d87a1bfad110f3c66632eacc9768b2203457209447ff081c07561c5e6356
4
+ data.tar.gz: 3634f61c95855e08e2e1742b6f1b154a916c15825ec3ea45eb4da1de324a7e09
5
5
  SHA512:
6
- metadata.gz: d0dab5f92c67d336c6ec60505d4755e21ec941775a2cac4d8cbfbd7aeb3e068652ad9bbdf52393ebee3b5a106758b01a5eeccc62ecb189155b4ea609f9360e92
7
- data.tar.gz: 995963147d81ef09a96111e9b112d9177b127ebd3459a97b71c187ddd71392670db9974e6cb2126afafadfeb4755a14339811da8ff125bfaba323b4f66321260
6
+ metadata.gz: 66341a37c23a9ce9f05948d30b17ef90ebfcfc44f30a23d13d177b3e859b34a82f8af632766986995f45f2a6f8397c64277038e68ded1b8f903ca4832b9a9237
7
+ data.tar.gz: 2e5695b27761643f506b048e485468c5956a46b6dbd51b258e5b72fd20c1f20ad1b41b070ae314370c64746c5ce43f505f26c2b9dfbe9e7fcc69685f0696508d
@@ -1,8 +1,10 @@
1
- sudo: false
2
1
  language: ruby
3
2
  cache: bundler
4
3
  rvm:
5
- - 2.5.0
4
+ - 2.5.6
5
+ - 2.6.4
6
+
7
+ services:
8
+ - mysql
6
9
  before_install:
7
- - gem install bundler -v 1.16.1
8
10
  - mysql -e 'CREATE DATABASE IF NOT EXISTS fluent_plugin_mysql_select_insert;'
data/LICENSE CHANGED
@@ -187,7 +187,7 @@
187
187
  same "printed page" as the copyright notice for easier
188
188
  identification within third-party archives.
189
189
 
190
- Copyright [yyyy] [name of copyright owner]
190
+ Copyright 2018-2019 Takeshi Arabiki (abicky)
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
193
193
  you may not use this file except in compliance with the License.
data/README.md CHANGED
@@ -39,6 +39,7 @@ select_query | SELECT query without WHERE clause to insert records | (required)
39
39
  condition_column | Column used in WHERE clause to compare with values of 'condition_key' in events | (required)
40
40
  condition_key | Key whose value is used in WHERE clause to compare with 'condition_column' | (required)
41
41
  extra_condition | Extra condition used in WHERE clause. You can specify placeholders like 'col1 = ? AND col2 = ?, ${tag[0]}, ${tag[1]}'. The metadata is extracted in the second or following arguments. | (empty array)
42
+ inserted_columns | Columns to be inserted. You should insert all columns by the select query if you don't specify the value. | nil
42
43
  ignore | Add 'IGNORE' modifier to INSERT statement | false
43
44
 
44
45
  ## Example Configuration
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "fluent-plugin-mysql-select-insert"
6
- spec.version = "0.1.0"
6
+ spec.version = "0.1.1"
7
7
  spec.authors = ["abicky"]
8
8
  spec.email = ["takeshi.arabiki@gmail.com"]
9
9
 
@@ -20,9 +20,9 @@ Gem::Specification.new do |spec|
20
20
  spec.test_files = test_files
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.16"
23
+ spec.add_development_dependency "bundler", ">= 1.16", "< 3"
24
24
  spec.add_development_dependency "rake", "~> 12.0"
25
25
  spec.add_development_dependency "test-unit", "~> 3.0"
26
- spec.add_runtime_dependency "fluentd", [">= 0.14.10", "< 2"]
26
+ spec.add_runtime_dependency "fluentd", [">= 1.2.0", "< 2"]
27
27
  spec.add_runtime_dependency "mysql2-cs-bind"
28
28
  end
@@ -42,6 +42,8 @@ module Fluent
42
42
  desc: "Key whose value is used in WHERE clause to compare with 'condition_column'"
43
43
  config_param :extra_condition, :array, value_type: :string, default: [],
44
44
  desc: "Extra condition used in WHERE clause. You can specify placeholders like 'col1 = ? AND col2 = ?, ${tag[0]}, ${tag[1]}'. The metadata is extracted in the second or following arguments."
45
+ config_param :inserted_columns, :array, value_type: :string, default: nil,
46
+ desc: "Columns to be inserted. You should insert all columns by the select query if you don't specify the value."
45
47
 
46
48
  config_param :ignore, :bool, default: false,
47
49
  desc: "Add 'IGNORE' modifier to INSERT statement"
@@ -56,6 +58,9 @@ module Fluent
56
58
  if select_query =~ /\bwhere\b/i
57
59
  fail Fluent::ConfigError, "You can't specify WHERE clause in 'select_query'"
58
60
  end
61
+ if select_query !~ /\A\s*select\b/i
62
+ fail Fluent::ConfigError, "'select_query' should begin with 'SELECT'"
63
+ end
59
64
  end
60
65
 
61
66
  def write(chunk)
@@ -64,19 +69,29 @@ module Fluent
64
69
  chunk.msgpack_each do |time, record|
65
70
  condition_values << "'#{client.escape(record[condition_key])}'"
66
71
  end
72
+
67
73
  sql = <<~SQL
68
- INSERT #{"IGNORE" if ignore} INTO `#{table}` #{select_query}
74
+ INSERT #{"IGNORE" if ignore} INTO `#{table}` #{"(#{inserted_columns.join(",")})" if inserted_columns}
75
+ #{select_query}
69
76
  WHERE #{condition_column} IN (#{condition_values.join(",")})
70
77
  SQL
71
-
72
78
  sql << " AND (#{extra_condition[0]})" unless extra_condition.empty?
73
- if extra_condition.size < 2
74
- client.query(sql)
75
- else
76
- require "mysql2-cs-bind"
77
- bound_params = extra_condition[1..-1].map { |c| extract_placeholders(c, chunk.metadata) }
78
- client.xquery(sql, bound_params)
79
+
80
+ bound_params = extra_condition[1..-1]&.map { |c| extract_placeholders(c, chunk.metadata) }
81
+ begin
82
+ if bound_params.nil? || bound_params.empty?
83
+ client.query(sql)
84
+ else
85
+ require "mysql2-cs-bind"
86
+ client.xquery(sql, bound_params)
87
+ end
88
+ rescue Mysql2::Error => e
89
+ if e.message.start_with?("Column count doesn't match value count")
90
+ raise Fluent::UnrecoverableError, "#{e.class}: #{e}"
91
+ end
92
+ raise
79
93
  end
94
+
80
95
  client.close
81
96
  end
82
97
 
@@ -4,66 +4,81 @@ require "fluent/plugin/out_mysql_select_insert"
4
4
  class MysqlSelectInsertOutputTest < Test::Unit::TestCase
5
5
  class << self
6
6
  def startup
7
- new_client.query(<<~SQL)
7
+ client = new_client
8
+ client.query(<<~SQL)
8
9
  CREATE TABLE IF NOT EXISTS `devices` (
9
10
  `id` int(10) unsigned NOT NULL,
10
11
  `app_id` int(10) unsigned NOT NULL,
11
12
  `uuid` char(36) NOT NULL,
12
13
  PRIMARY KEY (`id`),
13
14
  UNIQUE KEY (`app_id`, `uuid`)
14
- );
15
+ )
16
+ SQL
17
+ client.query(<<~SQL)
15
18
  CREATE TABLE IF NOT EXISTS `users` (
16
19
  `id` int(10) unsigned NOT NULL,
17
20
  `device_id` int(10) unsigned NOT NULL,
18
21
  PRIMARY KEY (`id`)
19
- );
22
+ )
23
+ SQL
24
+ client.query(<<~SQL)
20
25
  CREATE TABLE IF NOT EXISTS `accessed_users` (
21
26
  `user_id` int(10) unsigned NOT NULL,
22
27
  PRIMARY KEY (`user_id`)
23
- );
28
+ )
24
29
  SQL
25
30
  end
26
31
 
27
32
  def shutdown
28
- new_client.query(<<~SQL)
29
- DROP TABLE IF EXISTS `devices`;
30
- DROP TABLE IF EXISTS `users`;
31
- DROP TABLE IF EXISTS `accessed_users`;
32
- SQL
33
+ client = new_client
34
+ client.query("DROP TABLE IF EXISTS `devices`")
35
+ client.query("DROP TABLE IF EXISTS `users`")
36
+ client.query("DROP TABLE IF EXISTS `accessed_users`")
33
37
  end
34
38
 
35
39
  def new_client
36
40
  Mysql2::Client.new(
37
41
  username: "root",
38
42
  database: "fluent_plugin_mysql_select_insert",
39
- flags: Mysql2::Client::MULTI_STATEMENTS,
40
43
  )
41
44
  end
42
45
  end
43
46
 
47
+ def suppress_errors
48
+ prev_value = Thread.report_on_exception
49
+ # Supporess errors from flush treads
50
+ Thread.report_on_exception = false
51
+ # Suppress errors like "unexpected error while after_shutdown"
52
+ capture_stdout { yield }
53
+ ensure
54
+ Thread.report_on_exception = prev_value
55
+ end
56
+
44
57
  setup do
45
58
  Fluent::Test.setup
46
59
 
47
- self.class.new_client.query(<<~SQL)
60
+ client = self.class.new_client
61
+ client.query(<<~SQL)
48
62
  INSERT INTO `devices` (`id`, `app_id`, `uuid`) VALUES
49
63
  (1, 1, '03449258-29ce-403c-900a-a2c6ea1d09a2'),
50
64
  (2, 1, '11aebc82-b661-44ab-951d-d1618058699a'),
51
65
  (3, 2, '2c9a7bb7-aec2-441c-ade0-edf31fdacb43'),
52
- (4, 2, '3eae0b4b-bdd1-4c82-b04a-5335535f5b7b');
66
+ (4, 2, '3eae0b4b-bdd1-4c82-b04a-5335535f5b7b')
67
+ SQL
68
+ client.query(<<~SQL)
53
69
  INSERT INTO `users` (`id`, `device_id`) VALUES
54
70
  (1001, 1),
55
71
  (1002, 2),
56
72
  (1003, 3),
57
- (1004, 4);
73
+ (1004, 4)
58
74
  SQL
59
75
  end
60
76
 
61
77
  teardown do
62
- self.class.new_client.query(<<~SQL)
63
- TRUNCATE TABLE `devices`;
64
- TRUNCATE TABLE `users`;
65
- TRUNCATE TABLE `accessed_users`;
66
- SQL
78
+ client = self.class.new_client
79
+ client.query("TRUNCATE TABLE `devices`")
80
+ client.query("TRUNCATE TABLE `users`")
81
+ client.query("TRUNCATE TABLE `accessed_users`")
67
82
  end
68
83
 
69
84
  CONFIG = <<~CONF
@@ -196,22 +211,22 @@ class MysqlSelectInsertOutputTest < Test::Unit::TestCase
196
211
  condition_column devices.uuid
197
212
  CONF
198
213
 
199
- test "write with ignore false" do
200
- # XXX: "Mysql2::Error: Table 'fluent_plugin_mysql_select_insert.accessed_users' doesn't exist" occurs without sleep
201
- sleep 1
214
+ setup do
202
215
  self.class.new_client.query("INSERT INTO `accessed_users` VALUES (1001)")
216
+ end
203
217
 
218
+ test "write with ignore false" do
204
219
  d = create_driver("#{base_config}\n ignore false")
205
220
  assert_raise Mysql2::Error do
206
- d.run(default_tag: "tag") do
207
- d.feed(event_time, { "uuid" => "03449258-29ce-403c-900a-a2c6ea1d09a2", "app_id" => 1 })
221
+ suppress_errors do
222
+ d.run(default_tag: "tag") do
223
+ d.feed(event_time, { "uuid" => "03449258-29ce-403c-900a-a2c6ea1d09a2", "app_id" => 1 })
224
+ end
208
225
  end
209
226
  end
210
227
  end
211
228
 
212
229
  test "write with ignore true" do
213
- self.class.new_client.query("INSERT INTO `accessed_users` VALUES (1001)")
214
-
215
230
  d = create_driver("#{base_config}\n ignore true")
216
231
  assert_nothing_raised do
217
232
  d.run(default_tag: "tag") do
@@ -221,6 +236,36 @@ class MysqlSelectInsertOutputTest < Test::Unit::TestCase
221
236
  end
222
237
  end
223
238
 
239
+ sub_test_case "'inserted_columns' is specified" do
240
+ setup do
241
+ self.class.new_client.query("ALTER TABLE `accessed_users` ADD `extra_column` VARCHAR(63) DEFAULT 'default'")
242
+ end
243
+
244
+ teardown do
245
+ self.class.new_client.query("ALTER TABLE `accessed_users` DROP `extra_column`")
246
+ end
247
+
248
+ test "write with inserted_columns" do
249
+ d = create_driver("#{CONFIG}\n inserted_columns 'user_id'")
250
+ d.run(default_tag: "tag") do
251
+ d.feed(event_time, { "uuid" => "03449258-29ce-403c-900a-a2c6ea1d09a2", "app_id" => 1 })
252
+ end
253
+ result = self.class.new_client.query('SELECT * FROM `accessed_users`')
254
+ assert_equal [{ "user_id" => 1001, "extra_column" => "default" }], result.to_a
255
+ end
256
+
257
+ test "write with invalid inserted_columns" do
258
+ d = create_driver("#{CONFIG}\n inserted_columns 'user_id, extra_column'")
259
+
260
+ d.run(default_tag: "tag") do
261
+ d.feed(event_time, { "uuid" => "03449258-29ce-403c-900a-a2c6ea1d09a2", "app_id" => 1 })
262
+ end
263
+ assert do
264
+ d.logs.any? { |l| l.include?("got unrecoverable error") && l.include?("Column count doesn't match") }
265
+ end
266
+ end
267
+ end
268
+
224
269
  sub_test_case "WHERE clause is specified in 'select_query'" do
225
270
  test "configure" do
226
271
  assert_raise Fluent::ConfigError do
@@ -248,6 +293,33 @@ class MysqlSelectInsertOutputTest < Test::Unit::TestCase
248
293
  end
249
294
  end
250
295
 
296
+ sub_test_case "'select_query' doesn't begin with 'SELECT'" do
297
+ test "configure" do
298
+ assert_raise Fluent::ConfigError do
299
+ create_driver(<<~CONF)
300
+ database fluent_plugin_mysql_select_insert
301
+ username root
302
+
303
+ table accessed_users
304
+
305
+ select_query "(id) SELECT
306
+ users.id
307
+ FROM
308
+ users
309
+ INNER JOIN
310
+ devices
311
+ ON
312
+ devices.id = users.device_id
313
+ WHERE
314
+ app_id = 1"
315
+
316
+ condition_key uuid
317
+ condition_column devices.uuid
318
+ CONF
319
+ end
320
+ end
321
+ end
322
+
251
323
  private
252
324
 
253
325
  def create_driver(conf = CONFIG)
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-mysql-select-insert
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - abicky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-27 00:00:00.000000000 Z
11
+ date: 2019-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.16'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
20
23
  type: :development
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: '1.16'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: rake
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -58,7 +64,7 @@ dependencies:
58
64
  requirements:
59
65
  - - ">="
60
66
  - !ruby/object:Gem::Version
61
- version: 0.14.10
67
+ version: 1.2.0
62
68
  - - "<"
63
69
  - !ruby/object:Gem::Version
64
70
  version: '2'
@@ -68,7 +74,7 @@ dependencies:
68
74
  requirements:
69
75
  - - ">="
70
76
  - !ruby/object:Gem::Version
71
- version: 0.14.10
77
+ version: 1.2.0
72
78
  - - "<"
73
79
  - !ruby/object:Gem::Version
74
80
  version: '2'
@@ -121,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
127
  - !ruby/object:Gem::Version
122
128
  version: '0'
123
129
  requirements: []
124
- rubyforge_project:
125
- rubygems_version: 2.7.3
130
+ rubygems_version: 3.0.3
126
131
  signing_key:
127
132
  specification_version: 4
128
133
  summary: Fluentd output plugin to insert records by SELECT query.