flydata 0.7.9 → 0.7.10

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
  SHA1:
3
- metadata.gz: b6544ac842d41d5b8482f1d8996e8a853efda2b9
4
- data.tar.gz: 63e29a5dc775e88134129621791733b2a5ef301c
3
+ metadata.gz: 2dde125415d768ce91d31ba571250fcc82211a6c
4
+ data.tar.gz: 47de949761414acc3786397da03180e4f862cab8
5
5
  SHA512:
6
- metadata.gz: 714ba6763f79f026d4dce4cd19b0fc6d9b83114292b1bde90bc30426d8f4133c86e1e9e2a36d7e4200efd3bed12bc12cc472fa8e806f45b6bc5fe0a0e70e9065
7
- data.tar.gz: 1fe8b84fe9a71066df153046685ed8bc543f99fc6b32e9d8a36474e4b5e703a33c4647180ddc5a17213f6b15c97f2788ee04de97fec0da81464ca0d8c45a28c7
6
+ metadata.gz: 6f21358a59fb60e600a0a823a80574bd923b269093dc1d91618f9dc3e6ef7813ab385171666f62e8c86ad84a94e6db1d4c4dbad561acf9276b4ff4f94af12a32
7
+ data.tar.gz: 51daad9b9152816d384b367df2dbdffc3f3a0f3e9d07cf103759a8c2835d7b1bc71356961286f86da447a1dc0b2a218a9d9a669a6e4616f6da746ad8a4fb361d
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.9
1
+ 0.7.10
@@ -0,0 +1,24 @@
1
+ require 'flydata-core/errors'
2
+
3
+ module FlydataCore
4
+ module CompatibilityCheck
5
+ class Base
6
+ def initialize(option = {})
7
+ @option = option || {}
8
+ end
9
+
10
+ def do_check(option = @option, &block)
11
+ result = block.call create_query(option)
12
+ check_result(result, option)
13
+ end
14
+
15
+ # Override
16
+ #def create_query(option = @option)
17
+ #end
18
+
19
+ # Override
20
+ #def validate_result(result, option = @option)
21
+ #end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,131 @@
1
+ require 'flydata-core/compatibility_check/base'
2
+
3
+ module FlydataCore
4
+ module CompatibilityCheck
5
+ class DbCompatibilityChecker < Base
6
+ def do_check(option = @option, &block)
7
+ query = create_query(option)
8
+ result = if block
9
+ block.call query
10
+ else
11
+ exec_query(query)
12
+ end
13
+ check_result(result, option)
14
+ end
15
+
16
+ def exec_query(query)
17
+ begin
18
+ client = build_db_client(@option)
19
+ client.query(query)
20
+ ensure
21
+ client.close rescue nil if client
22
+ end
23
+ end
24
+
25
+ # Override
26
+ #def build_db_client(option)
27
+ #end
28
+
29
+ # Utility for building query
30
+ # input: Array ['table_1', 'table_2', 'table_3', ...]
31
+ # output: String "'table_1','table_2'"
32
+ def table_names_in_query(tables)
33
+ tables.collect{|tn| "'#{tn}'"}.join(',')
34
+ end
35
+
36
+ def word_for_table_schema
37
+ "schema"
38
+ end
39
+
40
+ def get_schema_and_tables(result)
41
+ table_schema = nil
42
+ tables = []
43
+ result.each do |r|
44
+ tbl = r['table_name']
45
+ tables << tbl unless tbl.to_s.empty?
46
+ table_schema = r['table_schema'] if table_schema.nil?
47
+ end
48
+ [table_schema, tables]
49
+ end
50
+ end
51
+
52
+ module TableExistenceCheck
53
+ # Override
54
+ # TABLE_EXISTENCE_CHECK_QUERY_TMPLT = SELECT...
55
+ # COMPATIBILITY_ERROR_CLASS = MysqlCompatibilityError
56
+
57
+ def create_query(option = @option)
58
+ self.class::TABLE_EXISTENCE_CHECK_QUERY_TMPLT % schema_and_table_in_query(option)
59
+ end
60
+
61
+ def check_result(result, option = @option)
62
+ table_schema, existing_tables = get_schema_and_tables(result)
63
+ missing_tables = option[:tables] - existing_tables
64
+
65
+ unless missing_tables.empty?
66
+ raise self.class::COMPATIBILITY_ERROR_CLASS,
67
+ "These tables are missing in \"#{table_schema}\" #{word_for_table_schema}. Create these tables on your database or remove them from the data entry: #{missing_tables.join(", ")}"
68
+ end
69
+ end
70
+ end
71
+
72
+ module PrimaryKeyCheck
73
+ # Override
74
+ # PK_CHECK_QUERY_TMPLT = SELECT...
75
+ # COMPATIBILITY_ERROR_CLASS = MysqlCompatibilityError
76
+
77
+ def create_query(option = @option)
78
+ pk_override = option[:pk_override] || {}
79
+ tables = option[:tables].select{|tbl| pk_override[tbl.to_s].nil? } # exclude tables having pk_override
80
+ return nil if tables.empty?
81
+ (self.class::PK_CHECK_QUERY_TMPLT) % schema_and_table_in_query(option.merge(tables: tables))
82
+ end
83
+
84
+ def check_result(result, option = @option)
85
+ table_schema, missing_pk_tables = get_schema_and_tables(result)
86
+ unless missing_pk_tables.empty?
87
+ raise self.class::COMPATIBILITY_ERROR_CLASS,
88
+ "Primary key is required for tables to sync. " +
89
+ "Add primary key or remove following tables in \"#{table_schema}\" #{word_for_table_schema} from data entry: #{missing_pk_tables.join(", ")}"
90
+ end
91
+ end
92
+ end
93
+
94
+ module PkOverrideCheck
95
+ # Override
96
+ # PK_OVERRIDE_QUERY_TMPLT = SELECT...
97
+ # COMPATIBILITY_ERROR_CLASS = MysqlCompatibilityError
98
+
99
+ def create_query(option = @option)
100
+ pk_override = option[:pk_override] || {}
101
+ return nil if pk_override.empty?
102
+ self.class::PK_OVERRIDE_QUERY_TMPLT % schema_and_table_in_query(option.merge(tables: pk_override.keys))
103
+ end
104
+
105
+ def check_result(result, option = @option)
106
+ tbl_cols = Hash.new{|h, k| h[k] = []} # key: table-name, value: array of column-name
107
+ table_schema = nil
108
+ result.each do |r|
109
+ tbl_cols[r['table_name']] << r['column_name']
110
+ table_schema = r['table_schema'] if table_schema.nil?
111
+ end
112
+
113
+ pk_override = option[:pk_override] || {}
114
+ missing_pk_override = {}
115
+ pk_override.each do |tbl, pk_cols|
116
+ missing_cols = pk_cols - tbl_cols[tbl]
117
+ missing_pk_override[tbl] = missing_cols unless missing_cols.empty?
118
+ end
119
+
120
+ unless missing_pk_override.empty?
121
+ msg = missing_pk_override.collect {|tbl, cols|
122
+ "#{tbl} (#{cols.join(',')})"
123
+ }.join(', ')
124
+ raise self.class::COMPATIBILITY_ERROR_CLASS,
125
+ "Columns set as primary key don't exist. " +
126
+ "Set correct column names to these tables in \"#{table_schema}\" #{word_for_table_schema}: #{msg}"
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -1,47 +1,32 @@
1
1
  require 'flydata-core/errors'
2
2
  require 'flydata-core/mysql/config'
3
+ require 'flydata-core/compatibility_check/db_compatibility_checker'
3
4
  require 'mysql2'
4
5
 
5
6
  module FlydataCore
6
7
  module Mysql
7
- class CompatibilityChecker
8
- def initialize(option = {})
9
- option ||= {}
10
- @option = option.merge FlydataCore::Mysql::Config.build_mysql_db_opts(option)
11
- end
8
+ class MysqlCompatibilityChecker < FlydataCore::CompatibilityCheck::DbCompatibilityChecker
9
+ COMPATIBILITY_ERROR_CLASS = MysqlCompatibilityError
12
10
 
13
- def do_check(option = @option, &block)
14
- result = block.call create_query(option)
15
- check_result(result, option)
11
+ def initialize(option = {})
12
+ super(option.merge FlydataCore::Mysql::Config.build_mysql_db_opts(option))
16
13
  end
17
14
 
18
15
  # Override
19
- #def create_query(option = @option)
20
- #end
21
-
22
- # Override
23
- #def validate_result(result, option = @option)
24
- #end
25
- end
16
+ def build_db_client(option)
17
+ Mysql2::Client.new(@option)
18
+ end
26
19
 
27
- class MysqlCompatibilityChecker < CompatibilityChecker
28
- def do_check(option = @option, &block)
29
- query = create_query(option)
30
- result = if block
31
- block.call query
32
- else
33
- exec_query(query)
34
- end
35
- check_result(result, option)
20
+ def schema_and_table_in_query(option = @option)
21
+ {
22
+ schema_name: "'#{option[:database]}'",
23
+ table_names: table_names_in_query(option[:tables]),
24
+ }
36
25
  end
37
26
 
38
- def exec_query(query)
39
- begin
40
- client = Mysql2::Client.new(@option)
41
- client.query(query)
42
- ensure
43
- client.close rescue nil if client
44
- end
27
+ # "table_schema" is equivalent to "database" on MySQL. No schema notion inside database.
28
+ def word_for_table_schema
29
+ "database"
45
30
  end
46
31
 
47
32
  # This command works only on RDS MySQL
@@ -221,29 +206,20 @@ module FlydataCore
221
206
  end
222
207
 
223
208
  class TableExistenceChecker < MysqlCompatibilityChecker
209
+ include FlydataCore::CompatibilityCheck::TableExistenceCheck
224
210
  TABLE_EXISTENCE_CHECK_QUERY_TMPLT = <<EOT
225
211
  SELECT
226
- t.table_name as table_name
212
+ t.table_schema,
213
+ t.table_name
227
214
  FROM
228
215
  information_schema.tables t
229
216
  WHERE
230
- t.table_schema='%{database}'
231
- AND t.table_name in (%{table_names});
217
+ t.table_schema = %{schema_name}
218
+ AND
219
+ t.table_name in (%{table_names})
220
+ UNION
221
+ SELECT %{schema_name}, '';
232
222
  EOT
233
- def create_query(option = @option)
234
- TABLE_EXISTENCE_CHECK_QUERY_TMPLT % {database: option[:database], table_names: option[:tables].collect{|tn| "'#{tn}'"}.join(',')}
235
- end
236
-
237
- def check_result(result, option = @option)
238
- existing_tables = []
239
- result.each {|r| existing_tables << r['table_name']}
240
- missing_tables = option[:tables] - existing_tables
241
-
242
- unless missing_tables.empty?
243
- raise FlydataCore::MysqlCompatibilityError,
244
- "These tables are missing. Create these tables on your database or remove them from the data entry : #{missing_tables.join(", ")}"
245
- end
246
- end
247
223
  end
248
224
 
249
225
  class MySqlVersion57CompabilityChecker < MysqlCompatibilityChecker
@@ -298,45 +274,46 @@ EOT
298
274
  end
299
275
 
300
276
  class PrimaryKeyChecker < MysqlCompatibilityChecker
277
+ include FlydataCore::CompatibilityCheck::PrimaryKeyCheck
301
278
  PK_CHECK_QUERY_TMPLT = <<EOT
302
279
  SELECT
280
+ t.table_schema,
303
281
  t.table_name,
304
282
  SUM(IF(tc.constraint_type='PRIMARY KEY' AND col.is_nullable='NO', 1, 0)) as num_pk,
305
283
  SUM(IF(tc.constraint_type='UNIQUE' AND col.is_nullable='NO', 1, 0)) as num_uk
306
284
  FROM
307
- (select * from information_schema.tables where table_schema = '%{database}' AND table_name in (%{table_names})) t
285
+ (select * from information_schema.tables where table_schema = %{schema_name} AND table_name in (%{table_names})) t
308
286
  LEFT OUTER JOIN
309
- (select * from information_schema.table_constraints where table_schema = '%{database}' AND table_name in (%{table_names})) tc
287
+ (select * from information_schema.table_constraints where table_schema = %{schema_name} AND table_name in (%{table_names})) tc
310
288
  USING (table_schema, table_name)
311
289
  LEFT JOIN
312
- (select * from information_schema.key_column_usage where constraint_schema = '%{database}') kc
290
+ (select * from information_schema.key_column_usage where constraint_schema = %{schema_name}) kc
313
291
  USING (table_schema, table_name, constraint_name)
314
292
  LEFT JOIN
315
- (select * from information_schema.columns where table_schema = '%{database}' AND table_name in (%{table_names})) col
293
+ (select * from information_schema.columns where table_schema = %{schema_name} AND table_name in (%{table_names})) col
316
294
  ON t.table_schema = col.table_schema AND t.table_name = col.table_name AND kc.column_name = col.column_name
317
295
  GROUP BY
318
296
  t.table_schema, t.table_name
319
297
  HAVING
320
298
  num_pk = 0 and num_uk = 0;
321
299
  EOT
300
+ end
322
301
 
323
- def create_query(option = @option)
324
- pk_override = option[:pk_override] || {}
325
- tables = option[:tables].select{|tbl| pk_override[tbl.to_s].nil? } # exclude tables having pk_override
326
- return nil if tables.empty?
327
- PK_CHECK_QUERY_TMPLT % {database: option[:database], table_names: tables.collect{|tn| "'#{tn}'"}.join(',')}
328
- end
329
-
330
- def check_result(result, option = @option)
331
- missing_pk_tables = []
332
- result.each {|r| missing_pk_tables << r['table_name']}
333
-
334
- unless missing_pk_tables.empty?
335
- raise FlydataCore::MysqlCompatibilityError,
336
- "Primary key is required for tables to sync. " +
337
- "Add primary key or remove following tables from data entry: #{missing_pk_tables.join(", ")}"
338
- end
339
- end
302
+ class PkOverrideChecker < MysqlCompatibilityChecker
303
+ include FlydataCore::CompatibilityCheck::PkOverrideCheck
304
+ PK_OVERRIDE_QUERY_TMPLT = <<EOT
305
+ SELECT
306
+ t.table_schema,
307
+ t.table_name,
308
+ col.column_name
309
+ FROM
310
+ (select * from information_schema.tables where table_schema = %{schema_name} AND table_name in (%{table_names})) t
311
+ LEFT JOIN
312
+ (select * from information_schema.columns where table_schema = %{schema_name} AND table_name in (%{table_names})) col
313
+ ON t.table_schema = col.table_schema AND t.table_name = col.table_name
314
+ ORDER BY
315
+ t.table_name;
316
+ EOT
340
317
  end
341
318
 
342
319
  class NonRdsRetentionChecker < MysqlCompatibilityChecker
@@ -1,114 +1,105 @@
1
1
  require 'flydata-core/errors'
2
+ require 'flydata-core/postgresql/config'
3
+ require 'flydata-core/compatibility_check/db_compatibility_checker'
2
4
  require 'flydata-core/postgresql/pg_client'
3
5
 
4
6
  module FlydataCore
5
7
  module Postgresql
6
- class CompatibilityChecker
7
- def initialize(option = {})
8
- option ||= {}
9
- @option = option.merge FlydataCore::Postgresql::Config.build_db_opts(option)
10
- end
8
+ class PostgresqlCompatibilityChecker < FlydataCore::CompatibilityCheck::DbCompatibilityChecker
9
+ COMPATIBILITY_ERROR_CLASS = PostgresqlCompatibilityError
11
10
 
12
- def do_check(option = @option, &block)
13
- result = block.call create_query(option)
14
- check_result(result, option)
11
+ def initialize(option = {})
12
+ super(option.merge FlydataCore::Postgresql::Config.build_db_opts(option))
15
13
  end
16
14
 
17
15
  # Override
18
- #def create_query(option = @option)
19
- #end
20
-
21
- # Override
22
- #def validate_result(result, option = @option)
23
- #end
24
- end
25
-
26
- class PostgresqlCompatibilityChecker < CompatibilityChecker
27
- def do_check(option = @option, &block)
28
- query = create_query(option)
29
- result = if block
30
- block.call query
31
- else
32
- exec_query(query)
33
- end
34
- check_result(result, option)
35
- end
36
-
37
- def exec_query(query)
38
- begin
39
- client = PGClient.new(@option)
40
- client.query(query)
41
- ensure
42
- client.close rescue nil if client
43
- end
16
+ def build_db_client(option)
17
+ PGClient.new(@option)
44
18
  end
45
19
 
46
20
  def schema_and_table_in_query(option = @option)
47
21
  schema = FlydataCore::Postgresql::QueryHelper.schema_as_value(option[:schema])
48
22
  {
49
23
  schema_name: schema,
50
- table_names: option[:tables].collect{|tn| "'#{tn}'"}.join(',')
24
+ table_names: table_names_in_query(option[:tables]),
51
25
  }
52
26
  end
53
27
  end
54
28
 
55
- class TableExistenceChecker < PostgresqlCompatibilityChecker
56
- TABLE_EXISTENCE_CHECK_QUERY_TMPLT = <<EOT
29
+ class SnapshotFunctionChecker < PostgresqlCompatibilityChecker
30
+ TXID_CURRENT_SNAPSHOT_CHECK_SQL = <<EOS
57
31
  SELECT
58
- table_name
32
+ p.proname
59
33
  FROM
60
- information_schema.tables
34
+ pg_proc p
35
+ JOIN pg_namespace n ON p.pronamespace = n.oid
61
36
  WHERE
62
- table_schema in (%{schema_name})
63
- AND
64
- table_name in (%{table_names});
65
- EOT
37
+ n.nspname = 'pg_catalog'
38
+ AND p.proname = 'txid_current_snapshot';
39
+ EOS
66
40
 
67
41
  def create_query(option = @option)
68
- TABLE_EXISTENCE_CHECK_QUERY_TMPLT % schema_and_table_in_query(option)
42
+ TXID_CURRENT_SNAPSHOT_CHECK_SQL
69
43
  end
70
44
 
71
45
  def check_result(result, option = @option)
72
- existing_tables = []
73
- result.each {|r| existing_tables << r['table_name']}
74
- missing_tables = option[:tables] - existing_tables
75
-
76
- unless missing_tables.empty?
46
+ unless result.first
77
47
  raise FlydataCore::PostgresqlCompatibilityError,
78
- "These tables are missing. Create these tables on your database or remove them from the data entry : #{missing_tables.join(", ")}"
48
+ "Unsupported PostgreSQL version. The version must be 8.3 or later."
79
49
  end
80
50
  end
81
51
  end
82
52
 
53
+ class TableExistenceChecker < PostgresqlCompatibilityChecker
54
+ include FlydataCore::CompatibilityCheck::TableExistenceCheck
55
+ TABLE_EXISTENCE_CHECK_QUERY_TMPLT = <<EOT
56
+ SELECT
57
+ table_schema,
58
+ table_name
59
+ FROM
60
+ information_schema.tables
61
+ WHERE
62
+ table_schema = %{schema_name}
63
+ AND
64
+ table_name in (%{table_names})
65
+ UNION
66
+ SELECT %{schema_name}, '';
67
+ EOT
68
+ end
69
+
83
70
  class PrimaryKeyChecker < PostgresqlCompatibilityChecker
71
+ include FlydataCore::CompatibilityCheck::PrimaryKeyCheck
84
72
  PK_CHECK_QUERY_TMPLT = <<EOT
85
73
  SELECT
74
+ t.table_schema,
86
75
  t.table_name
87
76
  FROM
88
- (select * from information_schema.tables where table_schema in (%{schema_name}) AND table_name in (%{table_names})) t
77
+ (select * from information_schema.tables where table_schema = %{schema_name} AND table_name in (%{table_names})) t
89
78
  LEFT OUTER JOIN
90
- (select * from information_schema.table_constraints where table_schema in (%{schema_name}) AND table_name in (%{table_names})) tc
79
+ (select * from information_schema.table_constraints where table_schema = %{schema_name} AND table_name in (%{table_names})) tc
91
80
  USING (table_schema, table_name)
92
81
  GROUP BY
93
82
  t.table_schema, t.table_name
94
83
  HAVING
95
84
  SUM(CASE WHEN tc.constraint_type='PRIMARY KEY' THEN 1 ELSE 0 END) = 0;
96
85
  EOT
86
+ end
97
87
 
98
- def create_query(option = @option)
99
- PK_CHECK_QUERY_TMPLT % schema_and_table_in_query(option)
100
- end
101
-
102
- def check_result(result, option = @option)
103
- missing_pk_tables = []
104
- result.each {|r| missing_pk_tables << r['table_name']}
105
-
106
- unless missing_pk_tables.empty?
107
- raise FlydataCore::PostgresqlCompatibilityError,
108
- "Primary key is required for tables to sync. " +
109
- "Add primary key or remove following tables from data entry: #{missing_pk_tables.join(", ")}"
110
- end
111
- end
88
+ class PkOverrideChecker < PostgresqlCompatibilityChecker
89
+ include FlydataCore::CompatibilityCheck::PkOverrideCheck
90
+ PK_OVERRIDE_QUERY_TMPLT = <<EOT
91
+ SELECT
92
+ t.table_schema,
93
+ t.table_name,
94
+ col.column_name
95
+ FROM
96
+ (select * from information_schema.tables where table_schema = %{schema_name} AND table_name in (%{table_names})) t
97
+ LEFT JOIN
98
+ (select * from information_schema.columns where table_schema = %{schema_name} AND table_name in (%{table_names})) col
99
+ ON t.table_schema = col.table_schema AND t.table_name = col.table_name
100
+ ORDER BY
101
+ t.table_name;
102
+ EOT
112
103
  end
113
104
  end
114
105
  end
@@ -3,7 +3,7 @@ require 'flydata-core/mysql/compatibility_checker'
3
3
 
4
4
  module FlydataCore
5
5
  module Mysql
6
- describe CompatibilityChecker do
6
+ describe 'CompatibilityChecker' do
7
7
  let(:database) { 'test_db' }
8
8
  let(:mysql_host) { hostname + '.' + domain_name }
9
9
  let(:hostname) { 'test-hostname' }
@@ -87,12 +87,16 @@ module FlydataCore
87
87
  subject { subject_object.create_query }
88
88
  it { is_expected.to eq <<EOT
89
89
  SELECT
90
- t.table_name as table_name
90
+ t.table_schema,
91
+ t.table_name
91
92
  FROM
92
93
  information_schema.tables t
93
94
  WHERE
94
- t.table_schema='test_db'
95
- AND t.table_name in ('table_1','table_2','table_3');
95
+ t.table_schema = 'test_db'
96
+ AND
97
+ t.table_name in ('table_1','table_2','table_3')
98
+ UNION
99
+ SELECT 'test_db', '';
96
100
  EOT
97
101
  }
98
102
  end
@@ -133,6 +137,7 @@ EOT
133
137
  subject { subject_object.create_query }
134
138
  it { is_expected.to eq <<EOT
135
139
  SELECT
140
+ t.table_schema,
136
141
  t.table_name,
137
142
  SUM(IF(tc.constraint_type='PRIMARY KEY' AND col.is_nullable='NO', 1, 0)) as num_pk,
138
143
  SUM(IF(tc.constraint_type='UNIQUE' AND col.is_nullable='NO', 1, 0)) as num_uk
@@ -162,6 +167,7 @@ EOT
162
167
  end
163
168
  it { is_expected.to eq <<EOT
164
169
  SELECT
170
+ t.table_schema,
165
171
  t.table_name,
166
172
  SUM(IF(tc.constraint_type='PRIMARY KEY' AND col.is_nullable='NO', 1, 0)) as num_pk,
167
173
  SUM(IF(tc.constraint_type='UNIQUE' AND col.is_nullable='NO', 1, 0)) as num_uk
@@ -221,6 +227,81 @@ EOT
221
227
  end
222
228
  end
223
229
  end
230
+
231
+ describe PkOverrideChecker do
232
+ let(:subject_object) { described_class.new(option) }
233
+
234
+ describe '#create_query' do
235
+ subject { subject_object.create_query }
236
+
237
+ context 'when pk_override is given' do
238
+ before do
239
+ option[:pk_override] = {
240
+ 'table_1' => ['id'],
241
+ 'table_2' => ['name'],
242
+ }
243
+ end
244
+ it { is_expected.to eq <<EOT
245
+ SELECT
246
+ t.table_schema,
247
+ t.table_name,
248
+ col.column_name
249
+ FROM
250
+ (select * from information_schema.tables where table_schema = 'test_db' AND table_name in ('table_1','table_2')) t
251
+ LEFT JOIN
252
+ (select * from information_schema.columns where table_schema = 'test_db' AND table_name in ('table_1','table_2')) col
253
+ ON t.table_schema = col.table_schema AND t.table_name = col.table_name
254
+ ORDER BY
255
+ t.table_name;
256
+ EOT
257
+ }
258
+ end
259
+
260
+ context 'when no pk override' do
261
+ let(:tables) { %w(table_2) }
262
+ before do
263
+ option.delete(:pk_override)
264
+ end
265
+ it { is_expected.to be_nil }
266
+ end
267
+ end
268
+
269
+ describe '#check_reesult' do
270
+ subject { subject_object.check_result(result) }
271
+ let(:result) { [
272
+ {"table_name" => "table_1", "column_name" => "id"},
273
+ {"table_name" => "table_1", "column_name" => "value"},
274
+ {"table_name" => "table_2", "column_name" => "name"},
275
+ {"table_name" => "table_2", "column_name" => "age"},
276
+ ] }
277
+
278
+ before do
279
+ option[:pk_override] = pk_override
280
+ end
281
+
282
+ context 'when given pk_override exist' do
283
+ let(:pk_override) { {
284
+ 'table_1' => ['id'],
285
+ 'table_2' => ['name'],
286
+ } }
287
+ it { expect{subject}.not_to raise_error }
288
+ end
289
+
290
+ context 'when some columns do not exist' do
291
+ let(:pk_override) { {
292
+ 'table_1' => ['id'],
293
+ 'table_2' => ['id'],
294
+ } }
295
+ it do
296
+ expect{subject}.to raise_error {|e|
297
+ expect(e).to be_kind_of(FlydataCore::MysqlCompatibilityError)
298
+ expect(e.to_s.include?("table_1")).to be_falsey
299
+ expect(e.to_s.include?("table_2")).to be_truthy
300
+ }
301
+ end
302
+ end
303
+ end
304
+ end
224
305
  end
225
306
 
226
307
  describe BinlogParameterChecker do
@@ -3,13 +3,43 @@ require 'flydata-core/postgresql/compatibility_checker'
3
3
 
4
4
  module FlydataCore
5
5
  module Postgresql
6
- describe CompatibilityChecker do
6
+ describe 'CompatibilityChecker' do
7
7
  let(:database) { 'test_db' }
8
8
  let(:tables) { %w(table_1 table_2 table_3) }
9
9
  let(:option) do
10
10
  { database: database, tables: tables }
11
11
  end
12
12
 
13
+ describe SnapshotFunctionChecker do
14
+ let(:subject_object) { described_class.new(option) }
15
+
16
+ describe '#check_reesult' do
17
+ let(:result) { [] }
18
+ subject { subject_object.check_result(result) }
19
+
20
+ context 'when the function exists' do
21
+ let(:result) do
22
+ [ {'proname' => 'txid_current_snapshot'} ]
23
+ end
24
+ it { expect{subject}.not_to raise_error }
25
+ end
26
+
27
+ context 'when the function does not exist' do
28
+ let(:result) do
29
+ []
30
+ end
31
+ it do
32
+ expect{subject}.to raise_error(
33
+ FlydataCore::PostgresqlCompatibilityError,
34
+ /Unsupported PostgreSQL version/
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+
42
+
13
43
  describe TableExistenceChecker do
14
44
  let(:subject_object) { described_class.new(option) }
15
45
 
@@ -19,13 +49,16 @@ module FlydataCore
19
49
  context 'when schema is not set' do
20
50
  it { is_expected.to eq <<EOT
21
51
  SELECT
52
+ table_schema,
22
53
  table_name
23
54
  FROM
24
55
  information_schema.tables
25
56
  WHERE
26
- table_schema in ((select current_schema))
57
+ table_schema = (select current_schema)
27
58
  AND
28
- table_name in ('table_1','table_2','table_3');
59
+ table_name in ('table_1','table_2','table_3')
60
+ UNION
61
+ SELECT (select current_schema), '';
29
62
  EOT
30
63
  }
31
64
  end
@@ -33,13 +66,16 @@ EOT
33
66
  before { option.merge!(schema: 'test_schema') }
34
67
  it { is_expected.to eq <<EOT
35
68
  SELECT
69
+ table_schema,
36
70
  table_name
37
71
  FROM
38
72
  information_schema.tables
39
73
  WHERE
40
- table_schema in ('test_schema')
74
+ table_schema = 'test_schema'
41
75
  AND
42
- table_name in ('table_1','table_2','table_3');
76
+ table_name in ('table_1','table_2','table_3')
77
+ UNION
78
+ SELECT 'test_schema', '';
43
79
  EOT
44
80
  }
45
81
  end
@@ -84,11 +120,12 @@ EOT
84
120
  context 'when schema is not set' do
85
121
  it { is_expected.to eq <<EOT
86
122
  SELECT
123
+ t.table_schema,
87
124
  t.table_name
88
125
  FROM
89
- (select * from information_schema.tables where table_schema in ((select current_schema)) AND table_name in ('table_1','table_2','table_3')) t
126
+ (select * from information_schema.tables where table_schema = (select current_schema) AND table_name in ('table_1','table_2','table_3')) t
90
127
  LEFT OUTER JOIN
91
- (select * from information_schema.table_constraints where table_schema in ((select current_schema)) AND table_name in ('table_1','table_2','table_3')) tc
128
+ (select * from information_schema.table_constraints where table_schema = (select current_schema) AND table_name in ('table_1','table_2','table_3')) tc
92
129
  USING (table_schema, table_name)
93
130
  GROUP BY
94
131
  t.table_schema, t.table_name
@@ -102,11 +139,12 @@ EOT
102
139
  before { option.merge!(schema: 'test_schema') }
103
140
  it { is_expected.to eq <<EOT
104
141
  SELECT
142
+ t.table_schema,
105
143
  t.table_name
106
144
  FROM
107
- (select * from information_schema.tables where table_schema in ('test_schema') AND table_name in ('table_1','table_2','table_3')) t
145
+ (select * from information_schema.tables where table_schema = 'test_schema' AND table_name in ('table_1','table_2','table_3')) t
108
146
  LEFT OUTER JOIN
109
- (select * from information_schema.table_constraints where table_schema in ('test_schema') AND table_name in ('table_1','table_2','table_3')) tc
147
+ (select * from information_schema.table_constraints where table_schema = 'test_schema' AND table_name in ('table_1','table_2','table_3')) tc
110
148
  USING (table_schema, table_name)
111
149
  GROUP BY
112
150
  t.table_schema, t.table_name
@@ -117,7 +155,7 @@ EOT
117
155
  end
118
156
  end
119
157
 
120
- describe '#check_reesult' do
158
+ describe '#check_result' do
121
159
  let(:result) { [] }
122
160
  subject { subject_object.check_result(result) }
123
161
 
@@ -133,6 +171,7 @@ EOT
133
171
  [{"table_name" => "table_1"}, {"table_name" => "table_3"}]
134
172
  end
135
173
  it do
174
+ skip "Primary key check for postgresql currenlty not raises error due to false alarm"
136
175
  expect{subject}.to raise_error {|e|
137
176
  expect(e).to be_kind_of(FlydataCore::PostgresqlCompatibilityError)
138
177
  expect(e.to_s.include?("table_1")).to be_truthy
@@ -143,6 +182,81 @@ EOT
143
182
  end
144
183
  end
145
184
  end
185
+
186
+ describe PkOverrideChecker do
187
+ let(:subject_object) { described_class.new(option) }
188
+
189
+ describe '#create_query' do
190
+ subject { subject_object.create_query }
191
+
192
+ context 'when pk_override is given' do
193
+ before do
194
+ option[:pk_override] = {
195
+ 'table_1' => ['id'],
196
+ 'table_2' => ['name'],
197
+ }
198
+ end
199
+ it { is_expected.to eq <<EOT
200
+ SELECT
201
+ t.table_schema,
202
+ t.table_name,
203
+ col.column_name
204
+ FROM
205
+ (select * from information_schema.tables where table_schema = (select current_schema) AND table_name in ('table_1','table_2')) t
206
+ LEFT JOIN
207
+ (select * from information_schema.columns where table_schema = (select current_schema) AND table_name in ('table_1','table_2')) col
208
+ ON t.table_schema = col.table_schema AND t.table_name = col.table_name
209
+ ORDER BY
210
+ t.table_name;
211
+ EOT
212
+ }
213
+ end
214
+
215
+ context 'when no pk override' do
216
+ let(:tables) { %w(table_2) }
217
+ before do
218
+ option.delete(:pk_override)
219
+ end
220
+ it { is_expected.to be_nil }
221
+ end
222
+ end
223
+
224
+ describe '#check_reesult' do
225
+ subject { subject_object.check_result(result) }
226
+ let(:result) { [
227
+ {"table_name" => "table_1", "column_name" => "id"},
228
+ {"table_name" => "table_1", "column_name" => "value"},
229
+ {"table_name" => "table_2", "column_name" => "name"},
230
+ {"table_name" => "table_2", "column_name" => "age"},
231
+ ] }
232
+
233
+ before do
234
+ option[:pk_override] = pk_override
235
+ end
236
+
237
+ context 'when given pk_override exist' do
238
+ let(:pk_override) { {
239
+ 'table_1' => ['id'],
240
+ 'table_2' => ['name'],
241
+ } }
242
+ it { expect{subject}.not_to raise_error }
243
+ end
244
+
245
+ context 'when some columns do not exist' do
246
+ let(:pk_override) { {
247
+ 'table_1' => ['id'],
248
+ 'table_2' => ['id'],
249
+ } }
250
+ it do
251
+ expect{subject}.to raise_error {|e|
252
+ expect(e).to be_kind_of(FlydataCore::PostgresqlCompatibilityError)
253
+ expect(e.to_s.include?("table_1")).to be_falsey
254
+ expect(e.to_s.include?("table_2")).to be_truthy
255
+ }
256
+ end
257
+ end
258
+ end
259
+ end
146
260
  end
147
261
  end
148
262
  end
Binary file
@@ -84,6 +84,10 @@ module PluginSupport
84
84
  @first_empty_binlog = true
85
85
  end
86
86
  end
87
+
88
+ def reset_table_rev(table)
89
+ @context.table_revs[table] = 1
90
+ end
87
91
  end
88
92
  end
89
93
 
@@ -17,9 +17,14 @@ module PluginSupport
17
17
 
18
18
  def process(record)
19
19
  if acceptable_db?(record)
20
- $log.error "CREATE TABLE detected. Please reset and resync the following table:'#{table_info(record)[:table_name]}'. - query:'#{record["query"]}' binlog_pos:'#{binlog_pos(record)}'"
20
+ emit_record(:create_table, record) do |opt|
21
+ table_name = table_info(record)[:table_name]
22
+ $log.info "type:create_table table_name:'#{table_name}' query:'#{record["query"]}' binlog_pos:'#{binlog_pos(record)}'"
23
+
24
+ opt[:increment_table_rev] = true
25
+ { table_name: table_name, query: record["query"] }
26
+ end
21
27
  end
22
- #NOTE: No emit_record here because this record should not be sent to data servers for now
23
28
  end
24
29
  end
25
30
  end
@@ -17,9 +17,14 @@ module PluginSupport
17
17
 
18
18
  def process(record)
19
19
  if acceptable_db?(record)
20
- $log.error "DROP TABLE detected for table:'#{table_info(record)[:table_name]}'. - query:'#{record["query"]}' binlog_pos:'#{binlog_pos(record)}'"
20
+ emit_record(:drop_table, record) do |opt|
21
+ table_name = table_info(record)[:table_name]
22
+ $log.info "type:drop_table table_name:'#{table_name}' query:'#{record["query"]}' binlog_pos:'#{binlog_pos(record)}'"
23
+
24
+ opt[:increment_table_rev] = true
25
+ { table_name: table_name, query: record["query"] }
26
+ end
21
27
  end
22
- #NOTE: No emit_record here because this record should not be sent to data servers for now
23
28
  end
24
29
  end
25
30
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flydata
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.9
4
+ version: 0.7.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Fujikawa
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2016-08-05 00:00:00.000000000 Z
15
+ date: 2016-08-09 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rest-client
@@ -522,6 +522,8 @@ files:
522
522
  - flydata-core/circle.yml
523
523
  - flydata-core/lib/flydata-core.rb
524
524
  - flydata-core/lib/flydata-core/agent.rb
525
+ - flydata-core/lib/flydata-core/compatibility_check/base.rb
526
+ - flydata-core/lib/flydata-core/compatibility_check/db_compatibility_checker.rb
525
527
  - flydata-core/lib/flydata-core/config/user_maintenance.rb
526
528
  - flydata-core/lib/flydata-core/core_ext.rb
527
529
  - flydata-core/lib/flydata-core/core_ext/module.rb
@@ -880,7 +882,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
880
882
  version: '0'
881
883
  requirements: []
882
884
  rubyforge_project:
883
- rubygems_version: 2.2.2
885
+ rubygems_version: 2.4.3
884
886
  signing_key:
885
887
  specification_version: 4
886
888
  summary: FlyData Agent