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

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: 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.