dbx 0.1.1 → 0.1.2

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: 3a9156b9797726754b8258e171e2bfd19de4ccd7
4
- data.tar.gz: 2886905cce5b47d0b2675cf4249659de21666b22
3
+ metadata.gz: c52bc907eb88537752612d4c288654e737048082
4
+ data.tar.gz: d4fb5d7f94bf24c126cd896b142eb17fc585fd93
5
5
  SHA512:
6
- metadata.gz: 35784e53ee334916019dfc1df50a95a5f224767242418e8f8ff2247e778699fddf3287416dc467d738325d86aa4e09d662fe8aefd4be91aa6da50d33a09ecc5b
7
- data.tar.gz: b396f106e73027fff0a3dd6a314899bb6996bc6ef82a93c127cae5f9af8f8483fa4e7ffa134c112c68b129d13d65ff2b28f90d139711132c1b84c51fecf1a936
6
+ metadata.gz: 1755fa1dd6c372745ed3f9234fe1918bfb01f3ef76bff45d5626ff1e0be9300f6ab21fadb096cac23950a27bd2461d2f5130b28c3367e4ed5a7eb5293db0cb1b
7
+ data.tar.gz: 45cd468ce1ead1093b22fc8fb50171c45fc5f51e7c18ff3bd55b4c703f764d35d1cc950535cc53491238bd47139c92182d89819489db923e6e9c20b14791a113
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dbx (0.1.0)
4
+ dbx (0.1.1)
5
5
  activerecord (~> 4.0)
6
6
  activesupport (~> 4.0)
7
7
  thor
data/exe/dbx CHANGED
@@ -49,6 +49,7 @@ class CLI < Thor
49
49
  option :force, type: :boolean, banner: 'remove diff_ table if it exists'
50
50
  option :using, type: :array, banner: 'JOIN USING the columns list here. Ex: id'
51
51
  option :exclude_columns, type: :array, banner: 'Exclude columns from comparison and selection'
52
+ option :no_a_b, type: :boolean, banner: 'Disable the *_a and *_b columns from *_diff table'
52
53
  def diff(table_a, table_b)
53
54
  handle_global_options
54
55
  DBX::Differ.diff(
@@ -56,7 +57,8 @@ class CLI < Thor
56
57
  table_b: table_b,
57
58
  using: options[:using],
58
59
  exclude_columns: options[:exclude_columns],
59
- force: options[:force]
60
+ force: options[:force],
61
+ no_a_b: options[:no_a_b]
60
62
  )
61
63
  end
62
64
 
@@ -64,6 +66,7 @@ class CLI < Thor
64
66
  option :force, type: :boolean, banner: 'remove diff_ table if it exists'
65
67
  option :using, type: :array, banner: 'JOIN USING the columns list here. Ex: id'
66
68
  option :exclude_columns, type: :array, banner: 'Exclude columns from comparison and selection'
69
+ option :no_a_b, type: :boolean, banner: 'Disable the *_a and *_b columns from *_diff table'
67
70
  def import_diff(src_a, src_b)
68
71
  handle_global_options
69
72
  DBX::Differ.import_and_diff(
@@ -71,7 +74,8 @@ class CLI < Thor
71
74
  src_b: src_b,
72
75
  using: options[:using],
73
76
  exclude_columns: options[:exclude_columns],
74
- force: options[:force]
77
+ force: options[:force],
78
+ no_a_b: options[:no_a_b]
75
79
  )
76
80
  end
77
81
 
data/lib/dbx/differ.rb CHANGED
@@ -13,7 +13,7 @@ module DBX
13
13
  # @param [String] table B Should be newer than table A, but doesn't have to be.
14
14
  # @param [Array<String>] using is the join criteria between the 2 tables.
15
15
  # @param [Array<String>] exclude_columns are excluded from the diff comparison.
16
- def diff(table_a:, table_b:, force: false, using: ['id'], exclude_columns: nil)
16
+ def diff(table_a:, table_b:, force: false, using: ['id'], exclude_columns: nil, no_a_b: false)
17
17
  table_diff = "diff_#{table_a}_#{table_b}"
18
18
  exclude_columns ||= []
19
19
  DBX.info("Creating diff table #{table_diff}")
@@ -23,7 +23,7 @@ module DBX
23
23
  CREATE TABLE #{table_diff} AS
24
24
  SELECT
25
25
  #{using.join(', ')},
26
- #{select_columns(table_a, exclude_columns: using + exclude_columns)}
26
+ #{select_columns(table_a, exclude_columns: using + exclude_columns, no_a_b: no_a_b)}
27
27
  FROM #{table_a} AS a
28
28
  FULL OUTER JOIN #{table_b} b USING (#{using.join(',')})
29
29
  WHERE
@@ -36,10 +36,13 @@ module DBX
36
36
  (SELECT COUNT(*) FROM #{table_diff}) diffs
37
37
  SQL
38
38
  end
39
+ DBX.info("Creating diff stats: #{table_diff}")
40
+ create_diff_stats(table_diff, force: force)
41
+
39
42
  DBX.info("Diff complete. Results details in: #{table_diff}")
40
43
  end
41
44
 
42
- def import_and_diff(src_a:, src_b:, force: false, using: ['id'], exclude_columns: nil)
45
+ def import_and_diff(src_a:, src_b:, force: false, using: ['id'], exclude_columns: nil, no_a_b: false)
43
46
  DBX.info("Importing #{src_a}")
44
47
  table_a = DBX.import_table(src_a, force: force)
45
48
 
@@ -47,10 +50,39 @@ module DBX
47
50
  DBX.info("Importing #{src_b}")
48
51
  table_b = DBX.import_table(src_b, force: force)
49
52
 
50
- diff(table_a: table_a, table_b: table_b, force: force, using: using, exclude_columns: exclude_columns)
53
+ diff(table_a: table_a, table_b: table_b, force: force, using: using, exclude_columns: exclude_columns, no_a_b: no_a_b)
51
54
  end
52
55
 
53
- def select_columns(table, exclude_columns: nil)
56
+ def create_diff_stats(diff_table, force: false)
57
+ DBX.connection do |conn|
58
+ diff_stats = "#{diff_table}_stats"
59
+ conn.execute("DROP TABLE IF EXISTS #{diff_stats}") if force
60
+ selects = conn.columns(diff_table).map do |column|
61
+ header, type = column.name, column.type
62
+ col = header[/(.*)_diff$/, 1]
63
+ next unless col
64
+
65
+ if column.sql_type == 'interval'
66
+ %{SUM(#{header}) AS #{col}_sum}
67
+ else
68
+ case type
69
+ when :decimal, :integer, :date, :datetime
70
+ %{SUM(#{header}) AS #{col}_sum}
71
+ else
72
+ %{COUNT(#{header}) AS #{col}_count}
73
+ end
74
+ end
75
+ end.compact.join(",\n")
76
+ conn.execute(<<-SQL)
77
+ CREATE TABLE #{diff_stats} AS
78
+ SELECT
79
+ #{selects}
80
+ FROM #{diff_table}
81
+ SQL
82
+ end
83
+ end
84
+
85
+ def select_columns(table, exclude_columns: nil, no_a_b: false)
54
86
  exclude_columns ||= []
55
87
  DBX.connection do |conn|
56
88
  conn.columns(table).map do |column|
@@ -58,11 +90,13 @@ module DBX
58
90
  next if exclude_columns.include?(header)
59
91
  case type
60
92
  when :decimal, :integer
61
- select_difference(header)
62
- when :date, :datetime
63
- select_difference_as_text(header)
93
+ select_difference(header, no_a_b: no_a_b)
94
+ when :date
95
+ select_difference_as_int(header, no_a_b: no_a_b)
96
+ when :datetime
97
+ select_difference_as_interval(header, no_a_b: no_a_b)
64
98
  else
65
- select_boolean(header)
99
+ select_boolean(header, no_a_b: no_a_b)
66
100
  end
67
101
  end.compact.join(',')
68
102
  end
@@ -79,22 +113,39 @@ module DBX
79
113
  end.compact.join('OR')
80
114
  end
81
115
 
82
- def select_difference(column)
116
+ def select_difference(column, no_a_b: false)
117
+ a = "a.#{column}"
118
+ b = "b.#{column}"
119
+ a_b = no_a_b ? '' : "#{a} AS #{column}_a, #{b} AS #{column}_b, "
120
+ %(#{a_b}(CASE WHEN #{a} = #{b} THEN NULL WHEN #{a} IS NULL THEN #{b} WHEN #{b} IS NULL THEN #{a} ELSE NULLIF(#{b} - #{a}, 0) END) AS #{column}_diff)
121
+ end
122
+
123
+ def select_difference_as_int(column, no_a_b: false)
124
+ a = "a.#{column}"
125
+ b = "b.#{column}"
126
+ a_b = no_a_b ? '' : "#{a} AS #{column}_a, #{b} AS #{column}_b, "
127
+ %(#{a_b}(CASE WHEN #{a} = #{b} THEN NULL::bigint WHEN #{a} IS NULL THEN 1 WHEN #{b} IS NULL THEN -1 ELSE (#{b} - #{a}) END) AS #{column}_diff)
128
+ end
129
+
130
+ def select_difference_as_interval(column, no_a_b: false)
83
131
  a = "a.#{column}"
84
132
  b = "b.#{column}"
85
- %(#{a} AS #{column}_a, #{b} AS #{column}_b, (CASE WHEN #{a} IS NULL THEN #{b} WHEN #{b} IS NULL THEN #{a} ELSE NULLIF(#{b} - #{a}, 0) END) AS #{column}_diff)
133
+ a_b = no_a_b ? '' : "#{a} AS #{column}_a, #{b} AS #{column}_b, "
134
+ %(#{a_b}(CASE WHEN #{a} = #{b} THEN NULL::interval WHEN #{a} IS NULL THEN '1 day'::interval WHEN #{b} IS NULL THEN '-1 day'::interval ELSE (#{b} - #{a})::interval END) AS #{column}_diff)
86
135
  end
87
136
 
88
- def select_difference_as_text(column)
137
+ def select_difference_as_text(column, no_a_b: false)
89
138
  a = "a.#{column}"
90
139
  b = "b.#{column}"
91
- %(#{a} AS #{column}_a, #{b} AS #{column}_b, (CASE WHEN #{a} IS NULL THEN #{b}::text WHEN #{b} IS NULL THEN #{a}::text ELSE NULLIF((#{b} - #{a})::text, '0') END) AS #{column}_diff)
140
+ a_b = no_a_b ? '' : "#{a} AS #{column}_a, #{b} AS #{column}_b, "
141
+ %(#{a_b}(CASE WHEN #{a} = #{b} THEN NULL WHEN #{a} IS NULL THEN #{b}::text WHEN #{b} IS NULL THEN #{a}::text ELSE (#{b} - #{a})::text END) AS #{column}_diff)
92
142
  end
93
143
 
94
- def select_boolean(column)
144
+ def select_boolean(column, no_a_b: false)
95
145
  a = "a.#{column}"
96
146
  b = "b.#{column}"
97
- %(#{a} AS #{column}_a, #{b} AS #{column}_b, NULLIF(#{a} <> #{b}, FALSE) AS #{column}_diff)
147
+ a_b = no_a_b ? '' : "#{a} AS #{column}_a, #{b} AS #{column}_b, "
148
+ %(#{a_b}NULLIF(#{a} <> #{b}, FALSE) AS #{column}_diff)
98
149
  end
99
150
  end
100
151
  end
data/lib/dbx/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dbx
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.1.2'.freeze
3
3
  end
data/lib/dbx.rb CHANGED
@@ -58,7 +58,7 @@ module DBX
58
58
  end
59
59
 
60
60
  def parse_table_name(src)
61
- File.basename(src).sub(File.extname(src), '')
61
+ File.basename(src).sub(File.extname(src), '').downcase
62
62
  end
63
63
 
64
64
  def create_table(src, name: nil, force: false, sample_rows: config_sample_rows, csv_options: {})
@@ -77,12 +77,12 @@ module DBX
77
77
  def import_table(src, name: nil, force: false, sample_rows: config_sample_rows, csv_options: {})
78
78
  name ||= parse_table_name(src)
79
79
  connection do |conn|
80
- create_table(src, force: force, sample_rows: sample_rows, csv_options: csv_options)
80
+ create_table(src, name: name, force: force, sample_rows: sample_rows, csv_options: csv_options)
81
81
  # TODO only postgres is support at the moment
82
82
  pg = conn.instance_variable_get(:@connection)
83
83
  types = column_types(src).keys.map{|m| %("#{m}")}
84
84
 
85
- pg_stmt = "COPY #{name}(#{types.join(',')}) FROM STDIN CSV"
85
+ pg_stmt = %{COPY "#{name}"(#{types.join(',')}) FROM STDIN CSV}
86
86
  conn.logger.debug(pg_stmt)
87
87
  pg.copy_data(pg_stmt) do
88
88
  first = true
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Pierce
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-22 00:00:00.000000000 Z
11
+ date: 2018-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler