exwiw 0.1.3 → 0.1.5
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/CHANGELOG.md +22 -0
- data/README.md +7 -0
- data/lib/exwiw/adapter/mysql2_adapter.rb +1 -1
- data/lib/exwiw/adapter/postgresql_adapter.rb +1 -1
- data/lib/exwiw/adapter/sqlite3_adapter.rb +2 -2
- data/lib/exwiw/cli.rb +19 -6
- data/lib/exwiw/query_ast.rb +3 -1
- data/lib/exwiw/query_ast_builder.rb +5 -0
- data/lib/exwiw/runner.rb +4 -2
- data/lib/exwiw/table_config.rb +2 -0
- data/lib/exwiw/version.rb +1 -1
- data/lib/tasks/exwiw.rake +3 -2
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f9fb9e57ed24338ac642aa9f0aa200980d8b57fd2d73851249467f9ec9e251b
|
|
4
|
+
data.tar.gz: 2cb68bd84f67b9a683116d83daacab6a96b65e4802f8b67afb81ef97ebccf3c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59bc123c523bf1f97dd97b6c18aa93972926366f121dda9dad9105cf651a783a77b38d7547fad99b7ab1c9509452123bf1c8423203cb0757cf3161d64d296c84
|
|
7
|
+
data.tar.gz: 6ea7278185d5f92bea6b43d3fb164bb23ae6557ce5d1f3d27e2a86a5147e665a7228ae848e2340c41fd1c5d43ccbdb81decf71804c4d68283de3c61b17be701c
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add `bulk_insert_chunk_size` table config to split the generated `INSERT` statement into chunks of the specified size. ([#8](https://github.com/riseshia/exwiw/pull/8))
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Bump minimum required Ruby version to 3.3.0 and drop Ruby 3.2 from the CI matrix (3.2 reached EOL on 2026-03-31).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Fix MySQL host access for local rspec runs and switch local dev scripts to inject the password via `MYSQL_PWD` env on `docker compose exec` instead of the `-p` CLI flag. ([#5](https://github.com/riseshia/exwiw/pull/5))
|
|
16
|
+
- Expand `~` in path arguments and validate the existence of `--config-dir`. ([#6](https://github.com/riseshia/exwiw/pull/6))
|
|
17
|
+
- Fix incorrect left-side table in `JOIN ... ON` clause for join chains with 3+ hops, which caused `no such column` / `column does not exist` errors at execute time. ([#7](https://github.com/riseshia/exwiw/pull/7))
|
|
18
|
+
|
|
19
|
+
## [0.1.4] - 2026-04-04
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Skip models whose table does not exist in `exwiw:schema:generate` task.
|
|
24
|
+
- Add trailing newline to generated schema files.
|
|
25
|
+
- Fixed foreign key constraint errors when exporting child tables with filters on intermediate tables. Filters from intermediate tables are now correctly included in JOIN clauses. ([#3](https://github.com/riseshia/exwiw/pull/3))
|
|
26
|
+
|
|
5
27
|
## [0.1.3] - 2025-04-02
|
|
6
28
|
|
|
7
29
|
### Fixed
|
data/README.md
CHANGED
|
@@ -98,6 +98,7 @@ This is an example of the one table schema:
|
|
|
98
98
|
"name": "users",
|
|
99
99
|
"primary_key": "id",
|
|
100
100
|
"filter": "users.id > 0",
|
|
101
|
+
"bulk_insert_chunk_size": 1000,
|
|
101
102
|
"belongs_to": [{
|
|
102
103
|
"name": "companies",
|
|
103
104
|
"foreign_key": "company_id"
|
|
@@ -115,6 +116,12 @@ This is an example of the one table schema:
|
|
|
115
116
|
|
|
116
117
|
`--config-dir` will use all json files in the specified directory.
|
|
117
118
|
|
|
119
|
+
### Bulk insert chunk size
|
|
120
|
+
|
|
121
|
+
`bulk_insert_chunk_size` splits the generated `INSERT` statement into multiple statements, each containing at most the specified number of rows. This is useful when the number of records per table is large enough to hit limits like MySQL's `max_allowed_packet`.
|
|
122
|
+
|
|
123
|
+
If omitted, all records for a table are emitted as a single `INSERT` statement.
|
|
124
|
+
|
|
118
125
|
### Filter
|
|
119
126
|
|
|
120
127
|
Some case, you don't need full records related to target. e.g. dump user access logs only for the last year.
|
|
@@ -79,7 +79,7 @@ module Exwiw
|
|
|
79
79
|
sql += " FROM #{query_ast.from_table_name}"
|
|
80
80
|
|
|
81
81
|
query_ast.join_clauses.each do |join|
|
|
82
|
-
sql += " JOIN #{join.join_table_name} ON #{
|
|
82
|
+
sql += " JOIN #{join.join_table_name} ON #{join.base_table_name}.#{join.foreign_key} = #{join.join_table_name}.#{join.primary_key}"
|
|
83
83
|
|
|
84
84
|
join.where_clauses.each do |where|
|
|
85
85
|
compiled_where_condition = compile_where_condition(where, join.join_table_name)
|
|
@@ -79,7 +79,7 @@ module Exwiw
|
|
|
79
79
|
sql += " FROM #{query_ast.from_table_name}"
|
|
80
80
|
|
|
81
81
|
query_ast.join_clauses.each do |join|
|
|
82
|
-
sql += " JOIN #{join.join_table_name} ON #{
|
|
82
|
+
sql += " JOIN #{join.join_table_name} ON #{join.base_table_name}.#{join.foreign_key} = #{join.join_table_name}.#{join.primary_key}"
|
|
83
83
|
|
|
84
84
|
join.where_clauses.each do |where|
|
|
85
85
|
compiled_where_condition = compile_where_condition(where, join.join_table_name)
|
|
@@ -79,7 +79,7 @@ module Exwiw
|
|
|
79
79
|
sql += " FROM #{query_ast.from_table_name}"
|
|
80
80
|
|
|
81
81
|
query_ast.join_clauses.each do |join|
|
|
82
|
-
sql += " JOIN #{join.join_table_name} ON #{
|
|
82
|
+
sql += " JOIN #{join.join_table_name} ON #{join.base_table_name}.#{join.foreign_key} = #{join.join_table_name}.#{join.primary_key}"
|
|
83
83
|
|
|
84
84
|
join.where_clauses.each do |where|
|
|
85
85
|
compiled_where_condition = compile_where_condition(where, join.join_table_name)
|
|
@@ -157,7 +157,7 @@ module Exwiw
|
|
|
157
157
|
@connection ||=
|
|
158
158
|
begin
|
|
159
159
|
require 'sqlite3'
|
|
160
|
-
SQLite3::Database.new(@connection_config.database_name)
|
|
160
|
+
SQLite3::Database.new(File.expand_path(@connection_config.database_name))
|
|
161
161
|
end
|
|
162
162
|
end
|
|
163
163
|
end
|
data/lib/exwiw/cli.rb
CHANGED
|
@@ -79,10 +79,6 @@ module Exwiw
|
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
if @config_dir.nil?
|
|
83
|
-
$stderr.puts "Config dir is required"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
82
|
if @database_password.nil? || @database_password.empty?
|
|
87
83
|
$stderr.puts "environment variable 'DATABASE_PASSWORD' is required"
|
|
88
84
|
exit 1
|
|
@@ -95,6 +91,21 @@ module Exwiw
|
|
|
95
91
|
exit 1
|
|
96
92
|
end
|
|
97
93
|
|
|
94
|
+
if @config_dir.nil?
|
|
95
|
+
$stderr.puts "Config dir is required"
|
|
96
|
+
exit 1
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
unless Dir.exist?(@config_dir)
|
|
100
|
+
$stderr.puts "Config dir does not exist: #{@config_dir}"
|
|
101
|
+
exit 1
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
if Dir.glob(File.join(@config_dir, "*.json")).empty?
|
|
105
|
+
$stderr.puts "Config dir contains no .json files: #{@config_dir}"
|
|
106
|
+
exit 1
|
|
107
|
+
end
|
|
108
|
+
|
|
98
109
|
if @target_table_name.nil? || @target_table_name.empty?
|
|
99
110
|
$stderr.puts "Target table is required"
|
|
100
111
|
exit 1
|
|
@@ -130,10 +141,12 @@ module Exwiw
|
|
|
130
141
|
opts.on("-p", "--port=PORT", "Target database port") { |v| @database_port = v }
|
|
131
142
|
opts.on("-u", "--user=USERNAME", "Target database user") { |v| @database_user = v }
|
|
132
143
|
opts.on("-o", "--output-dir=[DUMP_DIR_PATH]", "Output file path. default is dump/") do |v|
|
|
133
|
-
|
|
144
|
+
v = v.end_with?("/") ? v[0..-2] : v
|
|
145
|
+
@output_dir = File.expand_path(v)
|
|
134
146
|
end
|
|
135
147
|
opts.on("-c", "--config-dir=CONFIG_DIR_PATH", "Config dir path.") do |v|
|
|
136
|
-
|
|
148
|
+
v = v.end_with?("/") ? v[0..-2] : v
|
|
149
|
+
@config_dir = File.expand_path(v)
|
|
137
150
|
end
|
|
138
151
|
opts.on("-a", "--adapter=ADAPTER", "Database adapter") { |v| @database_adapter = v }
|
|
139
152
|
opts.on("--database=DATABASE", "Target database name") { |v| @database_name = v }
|
data/lib/exwiw/query_ast.rb
CHANGED
|
@@ -20,7 +20,9 @@ module Exwiw
|
|
|
20
20
|
join_table_name: join_table_name,
|
|
21
21
|
primary_key: primary_key,
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
if where_clauses.size.positive?
|
|
24
|
+
hash[:where_clauses] = where_clauses.map { |wc| wc.is_a?(String) ? wc : wc.to_h }
|
|
25
|
+
end
|
|
24
26
|
hash
|
|
25
27
|
end
|
|
26
28
|
end
|
data/lib/exwiw/runner.rb
CHANGED
|
@@ -46,9 +46,11 @@ module Exwiw
|
|
|
46
46
|
end
|
|
47
47
|
@logger.debug(" Generate INSERT SQL...")
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
chunk_size = table.bulk_insert_chunk_size
|
|
50
|
+
chunks = chunk_size ? results.each_slice(chunk_size).to_a : [results]
|
|
51
|
+
insert_sql = chunks.map { |chunk_rows| adapter.to_bulk_insert(chunk_rows, table) }.join("\n")
|
|
50
52
|
|
|
51
|
-
@logger.info(" Generated INSERT SQL for #{record_num} records.")
|
|
53
|
+
@logger.info(" Generated INSERT SQL for #{record_num} records (#{chunks.size} statement(s)).")
|
|
52
54
|
insert_idx = (idx + 1).to_s.rjust(3, '0')
|
|
53
55
|
File.open(File.join(@output_dir, "insert-#{insert_idx}-#{table_name}.sql"), 'w') do |file|
|
|
54
56
|
file.puts(insert_sql)
|
data/lib/exwiw/table_config.rb
CHANGED
|
@@ -9,6 +9,7 @@ module Exwiw
|
|
|
9
9
|
attribute :filter, optional(String), skip_serializing_if_nil: true
|
|
10
10
|
attribute :belongs_tos, array(BelongsTo)
|
|
11
11
|
attribute :columns, array(TableColumn)
|
|
12
|
+
attribute :bulk_insert_chunk_size, optional(Integer), skip_serializing_if_nil: true
|
|
12
13
|
|
|
13
14
|
def self.from_symbol_keys(hash)
|
|
14
15
|
from(JSON.parse(hash.to_json))
|
|
@@ -74,6 +75,7 @@ module Exwiw
|
|
|
74
75
|
merged_table.primary_key = passed_table.primary_key
|
|
75
76
|
merged_table.filter = filter
|
|
76
77
|
merged_table.belongs_tos = passed_table.belongs_tos
|
|
78
|
+
merged_table.bulk_insert_chunk_size = passed_table.bulk_insert_chunk_size
|
|
77
79
|
|
|
78
80
|
receiver_column_by_name = columns.each_with_object({}) { |column, hash| hash[column.name] = column }
|
|
79
81
|
|
data/lib/exwiw/version.rb
CHANGED
data/lib/tasks/exwiw.rake
CHANGED
|
@@ -14,6 +14,7 @@ namespace :exwiw do
|
|
|
14
14
|
|
|
15
15
|
ActiveRecord::Base.descendants.each do |model|
|
|
16
16
|
next if model.abstract_class?
|
|
17
|
+
next unless model.table_exists?
|
|
17
18
|
next if table_by_name[model.table_name]
|
|
18
19
|
|
|
19
20
|
belongs_tos = model.reflect_on_all_associations(:belongs_to).map do |assoc|
|
|
@@ -51,9 +52,9 @@ namespace :exwiw do
|
|
|
51
52
|
if File.exist?(path)
|
|
52
53
|
current_config = Exwiw::TableConfig.from(JSON.parse(File.read(path)))
|
|
53
54
|
merged_config = current_config.merge(table)
|
|
54
|
-
File.write(path, JSON.pretty_generate(merged_config.to_hash))
|
|
55
|
+
File.write(path, JSON.pretty_generate(merged_config.to_hash) + "\n")
|
|
55
56
|
else
|
|
56
|
-
File.write(path, JSON.pretty_generate(table.to_hash))
|
|
57
|
+
File.write(path, JSON.pretty_generate(table.to_hash) + "\n")
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: exwiw
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shia
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: serdes
|
|
@@ -66,14 +66,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
66
66
|
requirements:
|
|
67
67
|
- - ">="
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: 3.
|
|
69
|
+
version: 3.3.0
|
|
70
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
72
|
- - ">="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
74
|
version: '0'
|
|
75
75
|
requirements: []
|
|
76
|
-
rubygems_version: 3.6.
|
|
76
|
+
rubygems_version: 3.6.9
|
|
77
77
|
specification_version: 4
|
|
78
78
|
summary: Ruby gem that allows you to export records from a database to a dump file.
|
|
79
79
|
test_files: []
|