flydata 0.7.9 → 0.7.10

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