exwiw 0.3.5 → 0.3.7

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
  SHA256:
3
- metadata.gz: 77c28c9a405ca554e6349cc4900a509b255856a19751def01ac46bce748530e6
4
- data.tar.gz: 40656c295bec322a179e3016a84eb080596868e78c6fb5d95723e809f0505a51
3
+ metadata.gz: 91f070d56a04f45680e1c858109fd6828d69220373ad725d07c248d4c8ed2f10
4
+ data.tar.gz: a2dc34318e0c0a15ec8af84d75330b92ce5278c14a5b88d6e824e26bc5b1f6a7
5
5
  SHA512:
6
- metadata.gz: a28aa9c5faa8e391f1cfd3de20bf79150173cb41a0578d02883809b186e5ad94d920432cec253893bfdaca339f6a7fbfda1b498263113574c050bc321a68763a
7
- data.tar.gz: f2b372d7bcf5367ee1c7e5bf10a68f910c1b40d218c133b8e00c12f59be4d4838c7e326ad9de0798c50201f2f7bbed0265cef92fd95cf6597775b8d7fab431f4
6
+ metadata.gz: 5700d2fd7287c365ef6c9759fd7f5e85188db6e568b32f8c29a4a0f9f3479d967014a1f77bbf64e1d0e20fdfd75004d05e86e5152d781d6fcae8a5cbf82bca1b
7
+ data.tar.gz: '09d1f99b70a0f7d0a8e12fbb0f69de776b9932db9fdc386b71fb3822ee118eda786be79737e3a2e85c340d8c6ce15e5bca472e61d4f4f6517f0bc34d133db9d1'
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.3.7] - 2026-06-01
6
+
7
+ - bug fix https://github.com/heyinc/exwiw/pull/67
8
+
9
+ ## [0.3.6] - 2026-06-01
10
+
5
11
  ## [0.3.5] - 2026-06-01
6
12
 
7
13
  ### Fixed
@@ -120,6 +120,12 @@ module Exwiw
120
120
  raise NotImplementedError, "MongodbAdapter does not support explain yet"
121
121
  end
122
122
 
123
+ def describe_query(query)
124
+ "find collection=#{query.collection} filter=#{query.filter.inspect} projection=#{query.projection.inspect}"
125
+ rescue => e
126
+ "<unavailable: #{e.class}: #{e.message}>"
127
+ end
128
+
123
129
  def output_extension
124
130
  'jsonl'
125
131
  end
data/lib/exwiw/adapter.rb CHANGED
@@ -139,6 +139,23 @@ module Exwiw
139
139
  def commented_sql(query_ast)
140
140
  "#{sql_query_comment(query_ast)} #{compile_ast(query_ast)}"
141
141
  end
142
+
143
+ # One-line, human-readable description of the extraction query, used by the
144
+ # Runner in error messages so a failure during INSERT/COPY generation (or
145
+ # query execution) can be traced back to the query that produced the data.
146
+ # SQL adapters expose the compiled, comment-prefixed SELECT; non-SQL
147
+ # adapters (e.g. MongodbAdapter) override or fall back to the query object's
148
+ # own inspect output. Best-effort: never raise from here, since it runs on
149
+ # an error path.
150
+ def describe_query(query_ast)
151
+ if respond_to?(:compile_ast)
152
+ commented_sql(query_ast)
153
+ else
154
+ query_ast.inspect
155
+ end
156
+ rescue => e
157
+ "<unavailable: #{e.class}: #{e.message}>"
158
+ end
142
159
  end
143
160
 
144
161
  # @params [Exwiw::QueryAst] query_ast
data/lib/exwiw/runner.rb CHANGED
@@ -60,51 +60,67 @@ module Exwiw
60
60
  @logger.info("Processing table '#{table_name}'... (#{idx + 1}/#{total_size})")
61
61
 
62
62
  query_ast = adapter.build_query(table, @dump_target, table_by_name)
63
- results = adapter.execute(query_ast)
64
- record_num = results.size
65
63
 
66
- if record_num.zero?
67
- @logger.info(" No records matched. skip this table.")
68
- next
69
- end
70
- insert_idx = (idx + 1).to_s.rjust(3, '0')
71
-
72
- if @output_format == 'copy'
73
- @logger.debug(" Generate COPY statement...")
74
- copy_sql = adapter.to_copy_from_stdin(results, table)
75
- @logger.info(" Generated COPY statement for #{record_num} records.")
76
-
77
- File.open(File.join(@output_dir, "insert-#{insert_idx}-#{table_name}.#{adapter.output_extension}"), 'w') do |file|
78
- file.puts(copy_sql)
79
- post = adapter.post_insert_sql(table)
80
- file.puts(post) if post
64
+ # Track which phase we are in so that, if an error is raised while
65
+ # turning the fetched rows into SQL/JSONL, the rescue below can report
66
+ # both the failing step and the exact extraction query that produced the
67
+ # data being processed.
68
+ phase = "executing extraction query"
69
+ begin
70
+ results = adapter.execute(query_ast)
71
+ record_num = results.size
72
+
73
+ if record_num.zero?
74
+ @logger.info(" No records matched. skip this table.")
75
+ next
81
76
  end
82
- else
83
- @logger.debug(" Generate INSERT statement...")
84
- chunk_size = table.bulk_insert_chunk_size
85
- chunks = chunk_size ? results.each_slice(chunk_size).to_a : [results]
86
- insert_sql = chunks.map { |chunk_rows| adapter.to_bulk_insert(chunk_rows, table) }.join("\n")
87
-
88
- @logger.info(" Generated INSERT statement for #{record_num} records (#{chunks.size} statement(s)).")
89
- File.open(File.join(@output_dir, "insert-#{insert_idx}-#{table_name}.#{adapter.output_extension}"), 'w') do |file|
90
- file.puts(insert_sql)
91
- post = adapter.post_insert_sql(table)
92
- file.puts(post) if post
93
- end
94
- end
95
-
96
- if adapter.supports_bulk_delete? && !@insert_only && !(table.respond_to?(:rails_managed?) && table.rails_managed?)
97
- @logger.debug(" Generate DELETE statement...")
98
- delete_sql = adapter.to_bulk_delete(query_ast, table)
99
- if @logger.debug?
100
- @logger.debug(" Generated DELETE statement:\n#{delete_sql}")
77
+ insert_idx = (idx + 1).to_s.rjust(3, '0')
78
+
79
+ if @output_format == 'copy'
80
+ phase = "generating COPY statement"
81
+ @logger.debug(" Generate COPY statement...")
82
+ copy_sql = adapter.to_copy_from_stdin(results, table)
83
+ @logger.info(" Generated COPY statement for #{record_num} records.")
84
+
85
+ File.open(File.join(@output_dir, "insert-#{insert_idx}-#{table_name}.#{adapter.output_extension}"), 'w') do |file|
86
+ file.puts(copy_sql)
87
+ post = adapter.post_insert_sql(table)
88
+ file.puts(post) if post
89
+ end
101
90
  else
102
- @logger.info(" Generated DELETE statement.")
91
+ phase = "generating INSERT statement"
92
+ @logger.debug(" Generate INSERT statement...")
93
+ chunk_size = table.bulk_insert_chunk_size
94
+ chunks = chunk_size ? results.each_slice(chunk_size).to_a : [results]
95
+ insert_sql = chunks.map { |chunk_rows| adapter.to_bulk_insert(chunk_rows, table) }.join("\n")
96
+
97
+ @logger.info(" Generated INSERT statement for #{record_num} records (#{chunks.size} statement(s)).")
98
+ File.open(File.join(@output_dir, "insert-#{insert_idx}-#{table_name}.#{adapter.output_extension}"), 'w') do |file|
99
+ file.puts(insert_sql)
100
+ post = adapter.post_insert_sql(table)
101
+ file.puts(post) if post
102
+ end
103
103
  end
104
- delete_idx = (total_size - idx).to_s.rjust(3, '0')
105
- File.open(File.join(@output_dir, "delete-#{delete_idx}-#{table_name}.#{adapter.output_extension}"), 'w') do |file|
106
- file.puts(delete_sql)
104
+
105
+ if adapter.supports_bulk_delete? && !@insert_only && !(table.respond_to?(:rails_managed?) && table.rails_managed?)
106
+ phase = "generating DELETE statement"
107
+ @logger.debug(" Generate DELETE statement...")
108
+ delete_sql = adapter.to_bulk_delete(query_ast, table)
109
+ if @logger.debug?
110
+ @logger.debug(" Generated DELETE statement:\n#{delete_sql}")
111
+ else
112
+ @logger.info(" Generated DELETE statement.")
113
+ end
114
+ delete_idx = (total_size - idx).to_s.rjust(3, '0')
115
+ File.open(File.join(@output_dir, "delete-#{delete_idx}-#{table_name}.#{adapter.output_extension}"), 'w') do |file|
116
+ file.puts(delete_sql)
117
+ end
107
118
  end
119
+ rescue => e
120
+ @logger.error("Error while #{phase} for table '#{table_name}' (#{idx + 1}/#{total_size}): #{e.class}: #{e.message}")
121
+ @logger.error(" Extraction query that produced the data being processed:")
122
+ @logger.error(" #{adapter.describe_query(query_ast)}")
123
+ raise
108
124
  end
109
125
  end
110
126
 
@@ -61,6 +61,20 @@ module Exwiw
61
61
  def reject_ignored_members!
62
62
  self.belongs_tos = belongs_tos.reject(&:ignore)
63
63
  self.columns = columns.reject(&:ignore)
64
+
65
+ # A table that will be extracted needs at least one effective column;
66
+ # otherwise the generated SELECT (`SELECT FROM ...`) and INSERT
67
+ # (`INSERT INTO t () ...`) are syntactically broken. Checked here, after
68
+ # rejection, so it catches both a genuinely empty `columns: []` and a list
69
+ # left empty because every column was ignore:true. Schema generation does
70
+ # not call this method, so regenerating a broken config still works.
71
+ if !rails_managed? && !ignore && columns.empty?
72
+ raise ArgumentError,
73
+ "Table '#{name}' has no columns to extract " \
74
+ "(it may be empty in the config or have all columns set to ignore:true); " \
75
+ "define or unignore at least one column."
76
+ end
77
+
64
78
  self
65
79
  end
66
80
 
data/lib/exwiw/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Exwiw
4
- VERSION = "0.3.5"
4
+ VERSION = "0.3.7"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exwiw
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shia