purview 1.0.0.alpha

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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +143 -0
  7. data/Rakefile +11 -0
  8. data/TODO +81 -0
  9. data/lib/purview/columns/base.rb +65 -0
  10. data/lib/purview/columns/boolean.rb +11 -0
  11. data/lib/purview/columns/created_timestamp.rb +11 -0
  12. data/lib/purview/columns/date.rb +11 -0
  13. data/lib/purview/columns/float.rb +11 -0
  14. data/lib/purview/columns/id.rb +11 -0
  15. data/lib/purview/columns/integer.rb +11 -0
  16. data/lib/purview/columns/money.rb +11 -0
  17. data/lib/purview/columns/string.rb +11 -0
  18. data/lib/purview/columns/text.rb +11 -0
  19. data/lib/purview/columns/time.rb +11 -0
  20. data/lib/purview/columns/timestamp.rb +11 -0
  21. data/lib/purview/columns/updated_timestamp.rb +11 -0
  22. data/lib/purview/columns/uuid.rb +11 -0
  23. data/lib/purview/columns.rb +14 -0
  24. data/lib/purview/connections/base.rb +55 -0
  25. data/lib/purview/connections/mysql.rb +39 -0
  26. data/lib/purview/connections/postgresql.rb +27 -0
  27. data/lib/purview/connections.rb +3 -0
  28. data/lib/purview/databases/base.rb +559 -0
  29. data/lib/purview/databases/mysql.rb +207 -0
  30. data/lib/purview/databases/postgresql.rb +210 -0
  31. data/lib/purview/databases.rb +3 -0
  32. data/lib/purview/exceptions/base.rb +5 -0
  33. data/lib/purview/exceptions/could_not_acquire_lock.rb +9 -0
  34. data/lib/purview/exceptions/lock_already_released.rb +9 -0
  35. data/lib/purview/exceptions/no_table.rb +9 -0
  36. data/lib/purview/exceptions/no_window.rb +9 -0
  37. data/lib/purview/exceptions/rows_outside_window.rb +18 -0
  38. data/lib/purview/exceptions/table.rb +13 -0
  39. data/lib/purview/exceptions.rb +7 -0
  40. data/lib/purview/loaders/base.rb +154 -0
  41. data/lib/purview/loaders/mysql.rb +81 -0
  42. data/lib/purview/loaders/postgresql.rb +81 -0
  43. data/lib/purview/loaders.rb +3 -0
  44. data/lib/purview/loggers/base.rb +99 -0
  45. data/lib/purview/loggers/console.rb +11 -0
  46. data/lib/purview/loggers.rb +2 -0
  47. data/lib/purview/mixins/helpers.rb +21 -0
  48. data/lib/purview/mixins/logger.rb +21 -0
  49. data/lib/purview/mixins.rb +2 -0
  50. data/lib/purview/parsers/base.rb +39 -0
  51. data/lib/purview/parsers/csv.rb +49 -0
  52. data/lib/purview/parsers/tsv.rb +11 -0
  53. data/lib/purview/parsers.rb +3 -0
  54. data/lib/purview/pullers/base.rb +19 -0
  55. data/lib/purview/pullers/uri.rb +66 -0
  56. data/lib/purview/pullers.rb +2 -0
  57. data/lib/purview/refinements/object.rb +5 -0
  58. data/lib/purview/refinements/time.rb +5 -0
  59. data/lib/purview/refinements.rb +2 -0
  60. data/lib/purview/structs/base.rb +10 -0
  61. data/lib/purview/structs/result.rb +7 -0
  62. data/lib/purview/structs/window.rb +7 -0
  63. data/lib/purview/structs.rb +3 -0
  64. data/lib/purview/tables/base.rb +140 -0
  65. data/lib/purview/tables/raw.rb +13 -0
  66. data/lib/purview/tables.rb +2 -0
  67. data/lib/purview/types/base.rb +9 -0
  68. data/lib/purview/types/boolean.rb +9 -0
  69. data/lib/purview/types/date.rb +9 -0
  70. data/lib/purview/types/float.rb +9 -0
  71. data/lib/purview/types/integer.rb +9 -0
  72. data/lib/purview/types/money.rb +9 -0
  73. data/lib/purview/types/string.rb +9 -0
  74. data/lib/purview/types/text.rb +9 -0
  75. data/lib/purview/types/time.rb +9 -0
  76. data/lib/purview/types/timestamp.rb +9 -0
  77. data/lib/purview/types/uuid.rb +9 -0
  78. data/lib/purview/types.rb +11 -0
  79. data/lib/purview/version.rb +3 -0
  80. data/lib/purview.rb +27 -0
  81. data/purview.gemspec +29 -0
  82. data/spec/spec_helper.rb +5 -0
  83. metadata +210 -0
@@ -0,0 +1,207 @@
1
+ module Purview
2
+ module Databases
3
+ class MySQL < Base
4
+ def false_value
5
+ '0'
6
+ end
7
+
8
+ def null_value
9
+ 'NULL'
10
+ end
11
+
12
+ def true_value
13
+ '1'
14
+ end
15
+
16
+ private
17
+
18
+ def connection_opts
19
+ super.merge(:database => name)
20
+ end
21
+
22
+ def connection_type
23
+ Purview::Connections::MySQL
24
+ end
25
+
26
+ def create_index_sql(table_name, index_name, table, columns, index_opts={})
27
+ 'CREATE INDEX %s ON %s (%s)' % [
28
+ index_name,
29
+ table_name,
30
+ column_names(columns).join(', '),
31
+ ]
32
+ end
33
+
34
+ def create_table_sql(table_name, table, table_opts={})
35
+ 'CREATE TABLE %s (%s)' % [
36
+ table_name,
37
+ column_definitions(table).join(', '),
38
+ ]
39
+ end
40
+
41
+ def create_temporary_table_sql(table_name, table, table_opts={})
42
+ 'CREATE TEMPORARY TABLE %s LIKE %s' % [
43
+ table_name,
44
+ table.name,
45
+ ]
46
+ end
47
+
48
+ def default_map
49
+ super.merge(Purview::Types::Timestamp => '0')
50
+ end
51
+
52
+ def drop_index_sql(table_name, index_name, table, columns, index_opts={})
53
+ 'DROP INDEX %s' % [
54
+ index_name,
55
+ ]
56
+ end
57
+
58
+ def drop_table_sql(table_name, table, table_opts={})
59
+ 'DROP TABLE %s' % [
60
+ table_name,
61
+ ]
62
+ end
63
+
64
+ def ensure_table_metadata_exists_for_table_sql(table)
65
+ 'INSERT IGNORE INTO %s VALUES (%s, NULL, NULL, NULL, NULL)' % [
66
+ table_metadata_table_name,
67
+ quoted(table.name),
68
+ ]
69
+ end
70
+
71
+ def ensure_table_metadata_table_exists_sql
72
+ 'CREATE TABLE IF NOT EXISTS %s (%s PRIMARY KEY, %s, %s, %s, %s)' % [
73
+ table_metadata_table_name,
74
+ table_metadata_table_name_column_definition,
75
+ table_metadata_enabled_column_definition,
76
+ table_metadata_last_pulled_at_column_definition,
77
+ table_metadata_locked_at_column_definition,
78
+ table_metadata_max_timestamp_pulled_column_definition,
79
+ ]
80
+ end
81
+
82
+ def get_enabled_for_table_sql(table)
83
+ 'SELECT %s FROM %s WHERE %s = %s' % [
84
+ table_metadata_enabled_column_name,
85
+ table_metadata_table_name,
86
+ table_metadata_table_name_column_name,
87
+ quoted(table.name),
88
+ ]
89
+ end
90
+
91
+ def get_last_pulled_at_for_table_sql(table)
92
+ 'SELECT %s FROM %s WHERE %s = %s' % [
93
+ table_metadata_last_pulled_at_column_name,
94
+ table_metadata_table_name,
95
+ table_metadata_table_name_column_name,
96
+ quoted(table.name),
97
+ ]
98
+ end
99
+
100
+ def get_locked_at_for_table_sql(table)
101
+ 'SELECT %s FROM %s WHERE %s = %s' % [
102
+ table_metadata_locked_at_column_name,
103
+ table_metadata_table_name,
104
+ table_metadata_table_name_column_name,
105
+ quoted(table.name),
106
+ ]
107
+ end
108
+
109
+ def get_max_timestamp_pulled_for_table_sql(table)
110
+ 'SELECT %s FROM %s WHERE %s = %s' % [
111
+ table_metadata_max_timestamp_pulled_column_name,
112
+ table_metadata_table_name,
113
+ table_metadata_table_name_column_name,
114
+ quoted(table.name),
115
+ ]
116
+ end
117
+
118
+ def limit_map
119
+ super.merge(Purview::Types::String => 255)
120
+ end
121
+
122
+ def lock_table_sql(table, timestamp)
123
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND %s IS NULL' % [
124
+ table_metadata_table_name,
125
+ table_metadata_locked_at_column_name,
126
+ quoted(timestamp),
127
+ table_metadata_table_name_column_name,
128
+ quoted(table.name),
129
+ table_metadata_locked_at_column_name,
130
+ ]
131
+ end
132
+
133
+ def next_table_sql(timestamp)
134
+ 'SELECT %s FROM %s WHERE %s = %s AND %s IS NULL ORDER BY %s IS NULL DESC, %s LIMIT 1' % [
135
+ table_metadata_table_name_column_name,
136
+ table_metadata_table_name,
137
+ table_metadata_enabled_column_name,
138
+ quoted(true_value),
139
+ table_metadata_locked_at_column_name,
140
+ table_metadata_last_pulled_at_column_name,
141
+ table_metadata_last_pulled_at_column_name,
142
+ ]
143
+ end
144
+
145
+ def set_enabled_for_table_sql(table, enabled)
146
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND (%s IS NULL OR %s = %s)' % [
147
+ table_metadata_table_name,
148
+ table_metadata_enabled_column_name,
149
+ quoted(enabled),
150
+ table_metadata_table_name_column_name,
151
+ quoted(table.name),
152
+ table_metadata_enabled_column_name,
153
+ table_metadata_enabled_column_name,
154
+ quoted(false_value),
155
+ ]
156
+ end
157
+
158
+ def set_last_pulled_at_for_table_sql(table, timestamp)
159
+ 'UPDATE %s SET %s = %s WHERE %s = %s' % [
160
+ table_metadata_table_name,
161
+ table_metadata_last_pulled_at_column_name,
162
+ quoted(timestamp),
163
+ table_metadata_table_name_column_name,
164
+ quoted(table.name),
165
+ ]
166
+ end
167
+
168
+ def set_locked_at_for_table_sql(table, timestamp)
169
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND %s' % [
170
+ table_metadata_table_name,
171
+ table_metadata_locked_at_column_name,
172
+ quoted(timestamp),
173
+ table_metadata_table_name_column_name,
174
+ quoted(table.name),
175
+ ]
176
+ end
177
+
178
+ def set_max_timestamp_pulled_for_table_sql(table, timestamp)
179
+ 'UPDATE %s SET %s = %s WHERE %s = %s' % [
180
+ table_metadata_table_name,
181
+ table_metadata_max_timestamp_pulled_column_name,
182
+ quoted(timestamp),
183
+ table_metadata_table_name_column_name,
184
+ quoted(table.name),
185
+ ]
186
+ end
187
+
188
+ def type_map
189
+ super.merge(
190
+ Purview::Types::Money => 'decimal',
191
+ Purview::Types::UUID => 'varchar',
192
+ )
193
+ end
194
+
195
+ def unlock_table_sql(table)
196
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND %s IS NOT NULL' % [
197
+ table_metadata_table_name,
198
+ table_metadata_locked_at_column_name,
199
+ null_value,
200
+ table_metadata_table_name_column_name,
201
+ quoted(table.name),
202
+ table_metadata_locked_at_column_name,
203
+ ]
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,210 @@
1
+ module Purview
2
+ module Databases
3
+ class PostgreSQL < Base
4
+ def false_value
5
+ 'FALSE'
6
+ end
7
+
8
+ def null_value
9
+ 'NULL'
10
+ end
11
+
12
+ def true_value
13
+ 'TRUE'
14
+ end
15
+
16
+ private
17
+
18
+ def connection_opts
19
+ super.merge(:dbname => name)
20
+ end
21
+
22
+ def connection_type
23
+ Purview::Connections::PostgreSQL
24
+ end
25
+
26
+ def create_index_sql(table_name, index_name, table, columns, index_opts={})
27
+ 'CREATE INDEX %s ON %s (%s)' % [
28
+ index_name,
29
+ table_name,
30
+ column_names(columns).join(', '),
31
+ ]
32
+ end
33
+
34
+ def create_table_sql(table_name, table, table_opts={})
35
+ 'CREATE TABLE %s (%s)' % [
36
+ table_name,
37
+ column_definitions(table).join(', '),
38
+ ]
39
+ end
40
+
41
+ def create_temporary_table_sql(table_name, table, table_opts={})
42
+ 'CREATE TEMPORARY TABLE %s (LIKE %s INCLUDING ALL)' % [
43
+ table_name,
44
+ table.name,
45
+ ]
46
+ end
47
+
48
+ def drop_index_sql(table_name, index_name, table, columns, index_opts={})
49
+ 'DROP INDEX %s' % [
50
+ index_name,
51
+ ]
52
+ end
53
+
54
+ def drop_table_sql(table_name, table, table_opts={})
55
+ 'DROP TABLE %s' % [
56
+ table_name,
57
+ ]
58
+ end
59
+
60
+ def ensure_table_metadata_exists_for_table_sql(table)
61
+ 'INSERT INTO %s (%s) SELECT %s WHERE NOT EXISTS (SELECT 1 FROM %s WHERE %s = %s)' % [
62
+ table_metadata_table_name,
63
+ table_metadata_table_name_column_name,
64
+ quoted(table.name),
65
+ table_metadata_table_name,
66
+ table_metadata_table_name_column_name,
67
+ quoted(table.name),
68
+ ]
69
+ end
70
+
71
+ def ensure_table_metadata_table_exists_sql
72
+ 'CREATE TABLE IF NOT EXISTS %s (%s PRIMARY KEY, %s, %s, %s, %s)' % [
73
+ table_metadata_table_name,
74
+ table_metadata_table_name_column_definition,
75
+ table_metadata_enabled_column_definition,
76
+ table_metadata_last_pulled_at_column_definition,
77
+ table_metadata_locked_at_column_definition,
78
+ table_metadata_max_timestamp_pulled_column_definition,
79
+ ]
80
+ end
81
+
82
+ def get_enabled_for_table_sql(table)
83
+ 'SELECT %s FROM %s WHERE %s = %s' % [
84
+ table_metadata_enabled_column_name,
85
+ table_metadata_table_name,
86
+ table_metadata_table_name_column_name,
87
+ quoted(table.name),
88
+ ]
89
+ end
90
+
91
+ def get_last_pulled_at_for_table_sql(table)
92
+ 'SELECT %s FROM %s WHERE %s = %s' % [
93
+ table_metadata_last_pulled_at_column_name,
94
+ table_metadata_table_name,
95
+ table_metadata_table_name_column_name,
96
+ quoted(table.name),
97
+ ]
98
+ end
99
+
100
+ def get_locked_at_for_table_sql(table)
101
+ 'SELECT %s FROM %s WHERE %s = %s' % [
102
+ table_metadata_locked_at_column_name,
103
+ table_metadata_table_name,
104
+ table_metadata_table_name_column_name,
105
+ quoted(table.name),
106
+ ]
107
+ end
108
+
109
+ def get_max_timestamp_pulled_for_table_sql(table)
110
+ 'SELECT %s FROM %s WHERE %s = %s' % [
111
+ table_metadata_max_timestamp_pulled_column_name,
112
+ table_metadata_table_name,
113
+ table_metadata_table_name_column_name,
114
+ quoted(table.name),
115
+ ]
116
+ end
117
+
118
+ def limitless_types
119
+ super + [
120
+ Purview::Types::Money,
121
+ Purview::Types::UUID,
122
+ ]
123
+ end
124
+
125
+ def lock_table_sql(table, timestamp)
126
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND %s IS NULL' % [
127
+ table_metadata_table_name,
128
+ table_metadata_locked_at_column_name,
129
+ quoted(timestamp),
130
+ table_metadata_table_name_column_name,
131
+ quoted(table.name),
132
+ table_metadata_locked_at_column_name,
133
+ ]
134
+ end
135
+
136
+ def next_table_sql(timestamp)
137
+ 'SELECT %s FROM %s WHERE %s = %s AND %s IS NULL ORDER BY %s IS NULL DESC, %s LIMIT 1' % [
138
+ table_metadata_table_name_column_name,
139
+ table_metadata_table_name,
140
+ table_metadata_enabled_column_name,
141
+ quoted(true_value),
142
+ table_metadata_locked_at_column_name,
143
+ table_metadata_last_pulled_at_column_name,
144
+ table_metadata_last_pulled_at_column_name,
145
+ ]
146
+ end
147
+
148
+ def set_enabled_for_table_sql(table, enabled)
149
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND (%s IS NULL OR %s = %s)' % [
150
+ table_metadata_table_name,
151
+ table_metadata_enabled_column_name,
152
+ quoted(enabled),
153
+ table_metadata_table_name_column_name,
154
+ quoted(table.name),
155
+ table_metadata_enabled_column_name,
156
+ table_metadata_enabled_column_name,
157
+ quoted(false_value),
158
+ ]
159
+ end
160
+
161
+ def set_last_pulled_at_for_table_sql(table, timestamp)
162
+ 'UPDATE %s SET %s = %s WHERE %s = %s' % [
163
+ table_metadata_table_name,
164
+ table_metadata_last_pulled_at_column_name,
165
+ quoted(timestamp),
166
+ table_metadata_table_name_column_name,
167
+ quoted(table.name),
168
+ ]
169
+ end
170
+
171
+ def set_locked_at_for_table_sql(table, timestamp)
172
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND %s' % [
173
+ table_metadata_table_name,
174
+ table_metadata_locked_at_column_name,
175
+ quoted(timestamp),
176
+ table_metadata_table_name_column_name,
177
+ quoted(table.name),
178
+ ]
179
+ end
180
+
181
+ def set_max_timestamp_pulled_for_table_sql(table, timestamp)
182
+ 'UPDATE %s SET %s = %s WHERE %s = %s' % [
183
+ table_metadata_table_name,
184
+ table_metadata_max_timestamp_pulled_column_name,
185
+ quoted(timestamp),
186
+ table_metadata_table_name_column_name,
187
+ quoted(table.name),
188
+ ]
189
+ end
190
+
191
+ def type_map
192
+ super.merge(
193
+ Purview::Types::Money => 'money',
194
+ Purview::Types::UUID => 'uuid',
195
+ )
196
+ end
197
+
198
+ def unlock_table_sql(table)
199
+ 'UPDATE %s SET %s = %s WHERE %s = %s AND %s IS NOT NULL' % [
200
+ table_metadata_table_name,
201
+ table_metadata_locked_at_column_name,
202
+ null_value,
203
+ table_metadata_table_name_column_name,
204
+ quoted(table.name),
205
+ table_metadata_locked_at_column_name,
206
+ ]
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,3 @@
1
+ require 'purview/databases/base'
2
+ require 'purview/databases/mysql'
3
+ require 'purview/databases/postgresql'
@@ -0,0 +1,5 @@
1
+ module Purview
2
+ module Exceptions
3
+ class Base < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Purview
2
+ module Exceptions
3
+ class CouldNotAcquireLock < Table
4
+ def message
5
+ "Could not acquire the lock for table: #{table.name}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Purview
2
+ module Exceptions
3
+ class LockAlreadyReleased < Table
4
+ def message
5
+ "Lock already released for table: #{table.name}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Purview
2
+ module Exceptions
3
+ class NoTable < Base
4
+ def message
5
+ 'Could not find a table'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Purview
2
+ module Exceptions
3
+ class NoWindow < Table
4
+ def message
5
+ "Could not find a window for table: #{table.name}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Purview
2
+ module Exceptions
3
+ class RowsOutsideWindow < Base
4
+ def initialize(table_name, count)
5
+ @table_name = table_name
6
+ @count = count
7
+ end
8
+
9
+ def message
10
+ "#{count} row(s) outside window for table: #{table_name}"
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :count, :table_name
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module Purview
2
+ module Exceptions
3
+ class Table < Base
4
+ def initialize(table)
5
+ @table = table
6
+ end
7
+
8
+ private
9
+
10
+ attr_reader :table
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ require 'purview/exceptions/base'
2
+ require 'purview/exceptions/table'
3
+ require 'purview/exceptions/could_not_acquire_lock'
4
+ require 'purview/exceptions/lock_already_released'
5
+ require 'purview/exceptions/no_table'
6
+ require 'purview/exceptions/no_window'
7
+ require 'purview/exceptions/rows_outside_window'
@@ -0,0 +1,154 @@
1
+ module Purview
2
+ module Loaders
3
+ class Base
4
+ def initialize(opts={})
5
+ @opts = opts
6
+ end
7
+
8
+ def load(connection, rows, window)
9
+ with_context_logging("`load` for: #{table.name}") do
10
+ with_temporary_table(connection, rows, window) do |temporary_table_name|
11
+ update_result = \
12
+ connection.execute(table_update_sql(window, temporary_table_name))
13
+ delete_result = \
14
+ connection.execute(table_delete_sql(window, temporary_table_name))
15
+ insert_result = \
16
+ connection.execute(table_insert_sql(window, temporary_table_name))
17
+ logger.debug(
18
+ '%d row(s) inserted, %d row(s) updated and %d row(s) deleted in: %s' % [
19
+ insert_result.rows_affected,
20
+ update_result.rows_affected,
21
+ delete_result.rows_affected,
22
+ table.name,
23
+ ]
24
+ )
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ include Purview::Mixins::Helpers
32
+ include Purview::Mixins::Logger
33
+
34
+ attr_reader :opts
35
+
36
+ def count_column_name
37
+ 'count'
38
+ end
39
+
40
+ def create_temporary_table(connection)
41
+ database.create_temporary_table(
42
+ connection,
43
+ table,
44
+ :table => temporary_table_opts
45
+ )
46
+ end
47
+
48
+ def database
49
+ table.database
50
+ end
51
+
52
+ def id_in_sql(temporary_table_name)
53
+ raise %{All "#{Base}(s)" must override the "id_in_sql" method}
54
+ end
55
+
56
+ def in_window_sql(window)
57
+ raise %{All "#{Base}(s)" must override the "in_window_sql" method}
58
+ end
59
+
60
+ def load_temporary_table(connection, temporary_table_name, rows, window)
61
+ with_context_logging("`load_temporary_table` for: #{temporary_table_name}") do
62
+ rows.each_slice(rows_per_slice) do |rows_slice|
63
+ connection.execute(
64
+ temporary_table_insert_sql(
65
+ temporary_table_name,
66
+ rows_slice
67
+ )
68
+ )
69
+ end
70
+ end
71
+ end
72
+
73
+ def not_in_window_sql(window)
74
+ raise %{All "#{Base}(s)" must override the "not_in_window_sql" method}
75
+ end
76
+
77
+ def quoted(value)
78
+ database.quoted(value)
79
+ end
80
+
81
+ def row_values(row)
82
+ table.column_names.map { |column_name| quoted(row[column_name]) }.join(', ')
83
+ end
84
+
85
+ def rows_per_slice
86
+ opts[:rows_per_slice] || 1000
87
+ end
88
+
89
+ def table
90
+ opts[:table]
91
+ end
92
+
93
+ def table_delete_sql(window, temporary_table_name)
94
+ raise %{All "#{Base}(s)" must override the "table_delete_sql" method}
95
+ end
96
+
97
+ def table_insert_sql(window, temporary_table_name)
98
+ raise %{All "#{Base}(s)" must override the "table_insert_sql" method}
99
+ end
100
+
101
+ def table_update_sql(window, temporary_table_name)
102
+ raise %{All "#{Base}(s)" must override the "table_update_sql" method}
103
+ end
104
+
105
+ def temporary_table_insert_sql(temporary_table_name, rows)
106
+ raise %{All "#{Base}(s)" must override the "temporary_table_insert_sql" method}
107
+ end
108
+
109
+ def temporary_table_opts
110
+ {
111
+ :create_indices => true,
112
+ :name => table.temporary_name,
113
+ }
114
+ end
115
+
116
+ def temporary_table_verify_sql(temporary_table_name, rows, window)
117
+ raise %{All "#{Base}(s)" must override the "temporary_table_verify_sql" method}
118
+ end
119
+
120
+ def verify_temporary_table(connection, temporary_table_name, rows, window)
121
+ with_context_logging("`verify_temporary_table` for: #{temporary_table_name}") do
122
+ rows_outside_window = connection.execute(
123
+ temporary_table_verify_sql(
124
+ temporary_table_name,
125
+ rows,
126
+ window
127
+ )
128
+ ).rows[0][count_column_name]
129
+ raise Purview::Exceptions::RowsOutsideWindow.new(temporary_table_name, rows_outside_window) \
130
+ unless zero?(rows_outside_window)
131
+ end
132
+ end
133
+
134
+ def with_temporary_table(connection, rows, window)
135
+ yield(
136
+ create_temporary_table(connection).tap do |temporary_table_name|
137
+ load_temporary_table(
138
+ connection,
139
+ temporary_table_name,
140
+ rows,
141
+ window
142
+ )
143
+ verify_temporary_table(
144
+ connection,
145
+ temporary_table_name,
146
+ rows,
147
+ window
148
+ )
149
+ end
150
+ )
151
+ end
152
+ end
153
+ end
154
+ end