exwiw 0.2.1 → 0.2.3
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 +12 -0
- data/README.md +7 -2
- data/lib/exwiw/adapter/postgresql_adapter.rb +41 -0
- data/lib/exwiw/ddl_postprocessor.rb +19 -0
- data/lib/exwiw/explain_runner.rb +9 -7
- data/lib/exwiw/runner.rb +11 -6
- data/lib/exwiw/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: db5d466378dd55180ac229c2276c4e5e228a067a69b648ed7979d142f5b12ce7
|
|
4
|
+
data.tar.gz: 2be8b1ffe33cdaa1d9443ec7a3f36f7108f506addfbadb3fa06ac8ba312a89a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 17ae95279fe11b231c50a2c0d2e2ead9adcf2db6eb3fb79f7ee54f1af816c26dae23ace12d6fa901655817b26101777af04d70fd76751c6e45bc576d99ef7503
|
|
7
|
+
data.tar.gz: 99415623bdf900053d466d58c8a45fb1be380167a9d66cf9d265c8122c9fd6e3c96c8e48cb5e9e971e2783db595b63b1d525a850a3871e294a751263747b12b1
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.2.3] - 2026-05-27
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- PostgreSQL: schema dump now includes `CREATE TYPE ... AS ENUM` for custom enum types referenced by dumped tables. Previously `pg_dump --table` excluded schema-level type definitions, causing `type "..." does not exist` errors on import. ([#22](https://github.com/heyinc/exwiw/pull/22))
|
|
10
|
+
|
|
11
|
+
## [0.2.2] - 2026-05-26
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- `skip: true` table config now still emits the table's DDL into `insert-000-schema.{sql,js}` so downstream schemas stay consistent; only data extraction (`insert-*` / `delete-*`) is skipped. Previously the table was excluded from the schema file as well.
|
|
16
|
+
|
|
5
17
|
## [0.2.1] - 2026-05-23
|
|
6
18
|
|
|
7
19
|
### Added
|
data/README.md
CHANGED
|
@@ -203,7 +203,7 @@ Note: Ruby hooks are evaluated via `instance_eval` inside the exwiw process —
|
|
|
203
203
|
|
|
204
204
|
### Skip a table
|
|
205
205
|
|
|
206
|
-
Set `"skip": true` on a table's config JSON to
|
|
206
|
+
Set `"skip": true` on a table's config JSON to exclude it from data extraction. The table's DDL is still emitted into `insert-000-schema.{sql,js}` so the schema stays consistent, but no `insert-*` / `delete-*` files are generated for it and the table is never queried.
|
|
207
207
|
|
|
208
208
|
```json
|
|
209
209
|
{
|
|
@@ -347,7 +347,12 @@ At runtime:
|
|
|
347
347
|
|
|
348
348
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
349
349
|
|
|
350
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
350
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
351
|
+
|
|
352
|
+
To release a new version:
|
|
353
|
+
|
|
354
|
+
1. Run the **Release PR** workflow from the Actions tab with the new version number (e.g. `0.2.3`). This creates a PR that bumps `version.rb` and `CHANGELOG.md`.
|
|
355
|
+
2. Merge the PR. The **Release** workflow runs automatically, creating a git tag and publishing the gem to [rubygems.org](https://rubygems.org).
|
|
351
356
|
|
|
352
357
|
## Contributing
|
|
353
358
|
|
|
@@ -52,6 +52,13 @@ module Exwiw
|
|
|
52
52
|
raise "pg_dump failed (exit #{status.exitstatus}): #{stderr}"
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
enum_types = query_enum_types(table_names)
|
|
56
|
+
unless enum_types.empty?
|
|
57
|
+
enum_ddl = DdlPostprocessor.create_type_enum_statements(enum_types)
|
|
58
|
+
@logger.debug(" Found #{enum_types.size} enum type(s) to prepend.")
|
|
59
|
+
stdout = enum_ddl + stdout
|
|
60
|
+
end
|
|
61
|
+
|
|
55
62
|
idempotent = stdout
|
|
56
63
|
idempotent = DdlPostprocessor.add_if_not_exists_to_create_schema(idempotent)
|
|
57
64
|
idempotent = DdlPostprocessor.add_if_not_exists_to_create_sequence(idempotent)
|
|
@@ -264,6 +271,40 @@ module Exwiw
|
|
|
264
271
|
end
|
|
265
272
|
end
|
|
266
273
|
|
|
274
|
+
private def query_enum_types(table_names)
|
|
275
|
+
return [] if table_names.empty?
|
|
276
|
+
|
|
277
|
+
placeholders = table_names.each_with_index.map { |_, i| "$#{i + 1}" }.join(', ')
|
|
278
|
+
sql = <<~SQL
|
|
279
|
+
SELECT DISTINCT
|
|
280
|
+
n.nspname AS type_schema,
|
|
281
|
+
t.typname AS type_name,
|
|
282
|
+
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS enum_labels
|
|
283
|
+
FROM pg_attribute a
|
|
284
|
+
JOIN pg_class c ON c.oid = a.attrelid
|
|
285
|
+
JOIN pg_namespace cn ON cn.oid = c.relnamespace
|
|
286
|
+
JOIN pg_type t ON t.oid = a.atttypid
|
|
287
|
+
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
288
|
+
JOIN pg_enum e ON e.enumtypid = t.oid
|
|
289
|
+
WHERE c.relname IN (#{placeholders})
|
|
290
|
+
AND a.attnum > 0
|
|
291
|
+
AND NOT a.attisdropped
|
|
292
|
+
AND t.typtype = 'e'
|
|
293
|
+
GROUP BY n.nspname, t.typname
|
|
294
|
+
ORDER BY n.nspname, t.typname
|
|
295
|
+
SQL
|
|
296
|
+
|
|
297
|
+
result = connection.exec_params(sql, table_names)
|
|
298
|
+
decoder = PG::TextDecoder::Array.new
|
|
299
|
+
result.map do |row|
|
|
300
|
+
{
|
|
301
|
+
schema: row['type_schema'],
|
|
302
|
+
name: row['type_name'],
|
|
303
|
+
labels: decoder.decode(row['enum_labels']),
|
|
304
|
+
}
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
267
308
|
private def connection
|
|
268
309
|
@connection ||=
|
|
269
310
|
begin
|
|
@@ -57,5 +57,24 @@ module Exwiw
|
|
|
57
57
|
SQL
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
|
+
|
|
61
|
+
# Generate idempotent CREATE TYPE ... AS ENUM statements.
|
|
62
|
+
# +enum_types+ is an Array of Hashes with keys :schema, :name, :labels.
|
|
63
|
+
def create_type_enum_statements(enum_types)
|
|
64
|
+
return "" if enum_types.empty?
|
|
65
|
+
|
|
66
|
+
stmts = enum_types.map do |t|
|
|
67
|
+
qualified_name = "\"#{t[:schema]}\".\"#{t[:name]}\""
|
|
68
|
+
labels_sql = t[:labels].map { |l| "'#{l.gsub("'", "''")}'" }.join(', ')
|
|
69
|
+
<<~SQL.chomp
|
|
70
|
+
DO $exwiw$ BEGIN
|
|
71
|
+
CREATE TYPE #{qualified_name} AS ENUM (#{labels_sql});
|
|
72
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
73
|
+
END $exwiw$;
|
|
74
|
+
SQL
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
stmts.join("\n\n") + "\n\n"
|
|
78
|
+
end
|
|
60
79
|
end
|
|
61
80
|
end
|
data/lib/exwiw/explain_runner.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Exwiw
|
|
|
19
19
|
def run
|
|
20
20
|
adapter = Adapter.build(@connection_config, @logger)
|
|
21
21
|
configs = load_table_config(adapter.class.table_config_class)
|
|
22
|
-
|
|
22
|
+
validate_skipped(configs)
|
|
23
23
|
|
|
24
24
|
table_by_name = configs.each_with_object({}) { |config, hash| hash[config.name] = config }
|
|
25
25
|
|
|
@@ -31,8 +31,13 @@ module Exwiw
|
|
|
31
31
|
|
|
32
32
|
total_size = ordered_table_names.size
|
|
33
33
|
ordered_table_names.each_with_index do |table_name, idx|
|
|
34
|
-
@logger.debug("Explaining '#{table_name}'... (#{idx + 1}/#{total_size})")
|
|
35
34
|
table = table_by_name.fetch(table_name)
|
|
35
|
+
if table.skip
|
|
36
|
+
@logger.debug("Skipping explain for '#{table_name}' (skip:true)")
|
|
37
|
+
next
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@logger.debug("Explaining '#{table_name}'... (#{idx + 1}/#{total_size})")
|
|
36
41
|
|
|
37
42
|
query_ast = adapter.build_query(table, @dump_target, table_by_name)
|
|
38
43
|
sql = adapter.compile_ast(query_ast)
|
|
@@ -54,9 +59,9 @@ module Exwiw
|
|
|
54
59
|
end
|
|
55
60
|
end
|
|
56
61
|
|
|
57
|
-
private def
|
|
62
|
+
private def validate_skipped(configs)
|
|
58
63
|
skipped_names = configs.select { |c| c.skip }.map(&:name).to_set
|
|
59
|
-
return
|
|
64
|
+
return if skipped_names.empty?
|
|
60
65
|
|
|
61
66
|
configs.each do |config|
|
|
62
67
|
next if config.skip
|
|
@@ -75,9 +80,6 @@ module Exwiw
|
|
|
75
80
|
raise ArgumentError,
|
|
76
81
|
"--target-table '#{@dump_target.table_name}' is marked skip:true and cannot be used as a dump target."
|
|
77
82
|
end
|
|
78
|
-
|
|
79
|
-
skipped_names.each { |n| @logger.info("Skipping table '#{n}' (skip:true)") }
|
|
80
|
-
configs.reject { |c| c.skip }
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
end
|
data/lib/exwiw/runner.rb
CHANGED
|
@@ -30,7 +30,7 @@ module Exwiw
|
|
|
30
30
|
adapter = Adapter.build(@connection_config, @logger)
|
|
31
31
|
configs = load_table_config(adapter.class.table_config_class)
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
validate_skipped(configs)
|
|
34
34
|
|
|
35
35
|
table_by_name = configs.each_with_object({}) { |config, hash| hash[config.name] = config }
|
|
36
36
|
|
|
@@ -51,9 +51,15 @@ module Exwiw
|
|
|
51
51
|
|
|
52
52
|
total_size = ordered_table_names.size
|
|
53
53
|
ordered_table_names.each_with_index do |table_name, idx|
|
|
54
|
-
@logger.info("Processing table '#{table_name}'... (#{idx + 1}/#{total_size})")
|
|
55
54
|
table = table_by_name.fetch(table_name)
|
|
56
55
|
|
|
56
|
+
if table.skip
|
|
57
|
+
@logger.info("Skipping data extraction for '#{table_name}' (skip:true)")
|
|
58
|
+
next
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@logger.info("Processing table '#{table_name}'... (#{idx + 1}/#{total_size})")
|
|
62
|
+
|
|
57
63
|
query_ast = adapter.build_query(table, @dump_target, table_by_name)
|
|
58
64
|
results = adapter.execute(query_ast)
|
|
59
65
|
record_num = results.size
|
|
@@ -123,9 +129,9 @@ module Exwiw
|
|
|
123
129
|
end
|
|
124
130
|
end
|
|
125
131
|
|
|
126
|
-
private def
|
|
132
|
+
private def validate_skipped(configs)
|
|
127
133
|
skipped_names = configs.select { |c| c.skip }.map(&:name).to_set
|
|
128
|
-
return
|
|
134
|
+
return if skipped_names.empty?
|
|
129
135
|
|
|
130
136
|
configs.each do |config|
|
|
131
137
|
next if config.skip
|
|
@@ -145,8 +151,7 @@ module Exwiw
|
|
|
145
151
|
"--target-table '#{@dump_target.table_name}' is marked skip:true and cannot be used as a dump target."
|
|
146
152
|
end
|
|
147
153
|
|
|
148
|
-
skipped_names.each { |n| @logger.info("
|
|
149
|
-
configs.reject { |c| c.skip }
|
|
154
|
+
skipped_names.each { |n| @logger.info("Table '#{n}' is marked skip:true (schema will be included, data extraction skipped)") }
|
|
150
155
|
end
|
|
151
156
|
end
|
|
152
157
|
end
|
data/lib/exwiw/version.rb
CHANGED