real_data_tests 0.3.14 → 0.3.16
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 +4 -4
- data/lib/real_data_tests/rspec_helper.rb +179 -49
- data/lib/real_data_tests/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbf98b8c1c4b72e614a490731c40ff4f56c36eb14b4b13cf19fc13b7c3022fe3
|
4
|
+
data.tar.gz: '029a17e72f0a42f34eae153cbf4a212e185100027e438b8a9e48efb112b7e349'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 854233f093058fe510b0c37409a6a4c3445a2c65a70dc9c862d3a8eaec12a535ba435b354a5348b8e4f41bf15291caacabcb1d9b4274b506f02d1baeec97e2b0
|
7
|
+
data.tar.gz: ce700cb666714010f2622307da5321fac568eb32afbaf6795121d855e6e47409047192a0c38d3b0ed1f3b8e82d109988135c7c8b6a6cfb63c6e8e274c79a686e
|
@@ -1,5 +1,38 @@
|
|
1
1
|
module RealDataTests
|
2
2
|
module RSpecHelper
|
3
|
+
class SqlBlock
|
4
|
+
attr_reader :type, :content, :table_name
|
5
|
+
|
6
|
+
def initialize(content)
|
7
|
+
@content = content.strip
|
8
|
+
@type = determine_block_type
|
9
|
+
@table_name = extract_table_name if @type == :insert
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def determine_block_type
|
15
|
+
case @content
|
16
|
+
when /\AINSERT INTO/i
|
17
|
+
:insert
|
18
|
+
when /\ACOPY.*FROM stdin/i
|
19
|
+
:copy
|
20
|
+
when /\AALTER TABLE/i
|
21
|
+
:alter
|
22
|
+
when /\ASET/i
|
23
|
+
:set
|
24
|
+
else
|
25
|
+
:other
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def extract_table_name
|
30
|
+
if @content =~ /INSERT INTO\s+"?([^\s"(]+)"?\s/i
|
31
|
+
$1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
3
36
|
def load_real_test_data(name)
|
4
37
|
dump_path = File.join(RealDataTests.configuration.dump_path, "#{name}.sql")
|
5
38
|
raise Error, "Test data file not found: #{dump_path}" unless File.exist?(dump_path)
|
@@ -17,11 +50,13 @@ module RealDataTests
|
|
17
50
|
end
|
18
51
|
end
|
19
52
|
|
20
|
-
# Native Ruby implementation
|
21
53
|
def load_real_test_data_native(name)
|
22
54
|
dump_path = File.join(RealDataTests.configuration.dump_path, "#{name}.sql")
|
23
55
|
raise Error, "Test data file not found: #{dump_path}" unless File.exist?(dump_path)
|
24
56
|
|
57
|
+
sql_content = File.read(dump_path)
|
58
|
+
blocks = parse_sql_blocks(sql_content)
|
59
|
+
|
25
60
|
ActiveRecord::Base.transaction do
|
26
61
|
connection = ActiveRecord::Base.connection
|
27
62
|
|
@@ -29,17 +64,8 @@ module RealDataTests
|
|
29
64
|
connection.execute('SET session_replication_role = replica;')
|
30
65
|
|
31
66
|
begin
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
statements.each_with_index do |statement, index|
|
36
|
-
begin
|
37
|
-
cleaned_statement = clean_sql_statement(statement)
|
38
|
-
puts "Executing Statement ##{index + 1}: #{cleaned_statement}" # Debug log
|
39
|
-
connection.execute(cleaned_statement)
|
40
|
-
rescue ActiveRecord::StatementInvalid => e
|
41
|
-
raise Error, "Error executing statement ##{index + 1}: #{e.message}\nSQL: #{cleaned_statement}"
|
42
|
-
end
|
67
|
+
blocks.each_with_index do |block, index|
|
68
|
+
execute_block(block, index + 1, blocks.length)
|
43
69
|
end
|
44
70
|
ensure
|
45
71
|
connection.execute('SET session_replication_role = DEFAULT;')
|
@@ -64,6 +90,147 @@ module RealDataTests
|
|
64
90
|
options.join(" ")
|
65
91
|
end
|
66
92
|
|
93
|
+
class SqlBlock
|
94
|
+
attr_reader :type, :content, :table_name
|
95
|
+
|
96
|
+
def initialize(content)
|
97
|
+
@content = content.strip
|
98
|
+
@type = determine_block_type
|
99
|
+
@table_name = extract_table_name if @type == :insert
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def determine_block_type
|
105
|
+
if @content.match?(/\AINSERT INTO/i)
|
106
|
+
:insert
|
107
|
+
elsif @content.match?(/\ACOPY.*FROM stdin/i)
|
108
|
+
:copy
|
109
|
+
elsif @content.match?(/\AALTER TABLE/i)
|
110
|
+
:alter
|
111
|
+
elsif @content.match?(/\ASET/i)
|
112
|
+
:set
|
113
|
+
else
|
114
|
+
:other
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def extract_table_name
|
119
|
+
if @content =~ /INSERT INTO\s+"?([^\s"(]+)"?\s/i
|
120
|
+
$1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def parse_sql_blocks(content)
|
126
|
+
blocks = []
|
127
|
+
current_block = []
|
128
|
+
in_copy_block = false
|
129
|
+
|
130
|
+
content.each_line do |line|
|
131
|
+
line = line.chomp
|
132
|
+
|
133
|
+
# Skip empty lines and comments unless in COPY block
|
134
|
+
next if !in_copy_block && (line.empty? || line.start_with?('--'))
|
135
|
+
|
136
|
+
# Handle start of COPY block
|
137
|
+
if !in_copy_block && line.upcase.match?(/\ACOPY.*FROM stdin/i)
|
138
|
+
current_block = [line]
|
139
|
+
in_copy_block = true
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
# Handle end of COPY block
|
144
|
+
if in_copy_block && line == '\\.'
|
145
|
+
current_block << line
|
146
|
+
blocks << SqlBlock.new(current_block.join("\n"))
|
147
|
+
current_block = []
|
148
|
+
in_copy_block = false
|
149
|
+
next
|
150
|
+
end
|
151
|
+
|
152
|
+
# Accumulate lines in COPY block
|
153
|
+
if in_copy_block
|
154
|
+
current_block << line
|
155
|
+
next
|
156
|
+
end
|
157
|
+
|
158
|
+
# Handle regular SQL statements
|
159
|
+
current_block << line
|
160
|
+
if line.end_with?(';')
|
161
|
+
blocks << SqlBlock.new(current_block.join("\n"))
|
162
|
+
current_block = []
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Handle any remaining block
|
167
|
+
blocks << SqlBlock.new(current_block.join("\n")) unless current_block.empty?
|
168
|
+
blocks
|
169
|
+
end
|
170
|
+
|
171
|
+
def execute_block(block, index, total)
|
172
|
+
case block.type
|
173
|
+
when :insert
|
174
|
+
execute_insert_block(block, index, total)
|
175
|
+
when :copy
|
176
|
+
execute_copy_block(block, index, total)
|
177
|
+
else
|
178
|
+
execute_regular_block(block, index, total)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def execute_insert_block(block, index, total)
|
183
|
+
puts "Executing INSERT block #{index}/#{total} for table: #{block.table_name}"
|
184
|
+
# Don't modify statements that already end with semicolon
|
185
|
+
statement = if block.content.strip.end_with?(';')
|
186
|
+
block.content
|
187
|
+
else
|
188
|
+
"#{block.content};"
|
189
|
+
end
|
190
|
+
|
191
|
+
begin
|
192
|
+
ActiveRecord::Base.connection.execute(statement)
|
193
|
+
rescue ActiveRecord::StatementInvalid => e
|
194
|
+
if e.message.include?('syntax error at or near "ON"')
|
195
|
+
# Try alternative formatting for ON CONFLICT
|
196
|
+
modified_statement = statement.gsub(/\)\s+ON\s+CONFLICT/, ') ON CONFLICT')
|
197
|
+
ActiveRecord::Base.connection.execute(modified_statement)
|
198
|
+
else
|
199
|
+
raise
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def execute_copy_block(block, index, total)
|
205
|
+
puts "Executing COPY block #{index}/#{total}"
|
206
|
+
ActiveRecord::Base.connection.execute(block.content)
|
207
|
+
end
|
208
|
+
|
209
|
+
def execute_regular_block(block, index, total)
|
210
|
+
puts "Executing block #{index}/#{total} of type: #{block.type}"
|
211
|
+
ActiveRecord::Base.connection.execute(block.content)
|
212
|
+
end
|
213
|
+
|
214
|
+
def normalize_insert_statement(statement)
|
215
|
+
# First clean up any excess whitespace around parentheses
|
216
|
+
statement = statement.gsub(/\(\s+/, '(')
|
217
|
+
.gsub(/\s+\)/, ')')
|
218
|
+
.gsub(/\)\s+ON\s+CONFLICT/, ') ON CONFLICT')
|
219
|
+
|
220
|
+
# Ensure proper spacing around ON CONFLICT
|
221
|
+
if statement =~ /(.*?)\s*ON\s+CONFLICT\s+(.*?)\s*(?:DO\s+.*?)?\s*;\s*\z/i
|
222
|
+
base = $1.strip
|
223
|
+
conflict_part = $2.strip
|
224
|
+
action_part = $3&.strip || 'DO NOTHING'
|
225
|
+
|
226
|
+
# Rebuild the statement with consistent formatting
|
227
|
+
"#{base} ON CONFLICT #{conflict_part} #{action_part};"
|
228
|
+
else
|
229
|
+
# If no ON CONFLICT clause, just clean up the spacing
|
230
|
+
statement.strip.sub(/;?\s*$/, ';')
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
67
234
|
def split_sql_statements(sql)
|
68
235
|
statements = []
|
69
236
|
current_statement = ''
|
@@ -206,41 +373,4 @@ module RealDataTests
|
|
206
373
|
end
|
207
374
|
end
|
208
375
|
end
|
209
|
-
|
210
|
-
def import
|
211
|
-
@logger.info "Starting SQL import..."
|
212
|
-
|
213
|
-
ActiveRecord::Base.transaction do
|
214
|
-
begin
|
215
|
-
# Disable foreign key checks and triggers temporarily
|
216
|
-
ActiveRecord::Base.connection.execute('SET session_replication_role = replica;')
|
217
|
-
|
218
|
-
# Split the SQL content into individual statements
|
219
|
-
statements = split_sql_statements(@sql_content)
|
220
|
-
|
221
|
-
statements.each_with_index do |statement, index|
|
222
|
-
next if statement.strip.empty?
|
223
|
-
|
224
|
-
begin
|
225
|
-
@logger.info "Executing statement #{index + 1} of #{statements.length}"
|
226
|
-
cleaned_statement = clean_sql_statement(statement)
|
227
|
-
ActiveRecord::Base.connection.execute(cleaned_statement)
|
228
|
-
rescue ActiveRecord::StatementInvalid => e
|
229
|
-
@logger.error "Error executing statement #{index + 1}: #{e.message}"
|
230
|
-
@logger.error "Statement: #{cleaned_statement[0..100]}..."
|
231
|
-
raise
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
@logger.info "Successfully imported all SQL statements"
|
236
|
-
rescue StandardError => e
|
237
|
-
@logger.error "Error during import: #{e.message}"
|
238
|
-
@logger.error e.backtrace.join("\n")
|
239
|
-
raise
|
240
|
-
ensure
|
241
|
-
# Re-enable foreign key checks and triggers
|
242
|
-
ActiveRecord::Base.connection.execute('SET session_replication_role = DEFAULT;')
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
376
|
end
|