dumpling-cli 0.4.2__tar.gz → 0.4.3__tar.gz
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.
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/CHANGELOG.md +7 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/Cargo.lock +1 -1
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/Cargo.toml +1 -1
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/PKG-INFO +1 -1
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/pyproject.toml +1 -1
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/sql.rs +37 -1
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.dumplingconf.example +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/ci.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/docs-pr.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/docs.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/platform-compat-latest.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/platform-compat-matrix.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/policy-lint.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/publish.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/release.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.github/workflows/tests.yml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/.gitignore +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/AGENTS.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/CONTRIBUTING.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/MAINTENANCE.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/README.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/assets/logo.svg +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/book.toml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/datetime_out.sql +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/datetime_sample.sql +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/docs/src/SUMMARY.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/docs/src/ci-guardrails.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/docs/src/configuration.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/docs/src/getting-started.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/docs/src/index.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/docs/src/releasing.md +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/rust-toolchain.toml +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/scripts/setup-dev.sh +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/faker_dispatch.rs +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/filter.rs +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/lint.rs +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/main.rs +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/report.rs +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/scan.rs +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/settings.rs +0 -0
- {dumpling_cli-0.4.2 → dumpling_cli-0.4.3}/src/transform.rs +0 -0
|
@@ -7,6 +7,12 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.3] - 2026-05-03
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **COPY row integrity after anonymization**: Control characters in anonymized COPY text fields are escaped so tab/newline/etc. cannot break column alignment or row boundaries ([#53](https://github.com/ababic/dumpling/pull/53)).
|
|
15
|
+
|
|
10
16
|
## [0.4.2] - 2026-05-03
|
|
11
17
|
|
|
12
18
|
### Fixed
|
|
@@ -68,6 +74,7 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
68
74
|
- Configurable output scan severities and per-category thresholds via `[output_scan]`.
|
|
69
75
|
- JSON report section for output scan findings including category, count, threshold, severity, and sample locations.
|
|
70
76
|
|
|
77
|
+
[0.4.3]: https://github.com/ababic/dumpling/compare/v0.4.2...v0.4.3
|
|
71
78
|
[0.4.2]: https://github.com/ababic/dumpling/compare/v0.4.1...v0.4.2
|
|
72
79
|
[0.4.1]: https://github.com/ababic/dumpling/compare/v0.4.0...v0.4.1
|
|
73
80
|
[0.4.0]: https://github.com/ababic/dumpling/compare/v0.3.0...v0.4.0
|
|
@@ -299,7 +299,8 @@ impl SqlStreamProcessor {
|
|
|
299
299
|
if repl.is_null {
|
|
300
300
|
new_fields.push(r"\N".to_string());
|
|
301
301
|
} else {
|
|
302
|
-
new_fields
|
|
302
|
+
new_fields
|
|
303
|
+
.push(escape_postgres_copy_text_field(&repl.value));
|
|
303
304
|
}
|
|
304
305
|
}
|
|
305
306
|
Err(e) => return Err(e),
|
|
@@ -1181,6 +1182,32 @@ impl Cell {
|
|
|
1181
1182
|
}
|
|
1182
1183
|
}
|
|
1183
1184
|
|
|
1185
|
+
/// Escapes a field value for PostgreSQL `COPY ... FROM stdin` **text** format so the output
|
|
1186
|
+
/// line still has one physical TAB-separated field per logical column. Without this, a
|
|
1187
|
+
/// replacement containing a literal TAB or newline would split the row on restore and surface
|
|
1188
|
+
/// as PostgreSQL errors like `missing data for column "..."`.
|
|
1189
|
+
fn escape_postgres_copy_text_field(s: &str) -> String {
|
|
1190
|
+
let mut out = String::with_capacity(s.len());
|
|
1191
|
+
for c in s.chars() {
|
|
1192
|
+
match c {
|
|
1193
|
+
'\\' => out.push_str("\\\\"),
|
|
1194
|
+
'\t' => out.push_str("\\t"),
|
|
1195
|
+
'\n' => out.push_str("\\n"),
|
|
1196
|
+
'\r' => out.push_str("\\r"),
|
|
1197
|
+
'\u{0008}' => out.push_str("\\b"),
|
|
1198
|
+
'\u{000c}' => out.push_str("\\f"),
|
|
1199
|
+
'\u{000b}' => out.push_str("\\v"),
|
|
1200
|
+
'\0' => out.push_str("\\0"),
|
|
1201
|
+
c if (c as u32) < 0x20 => {
|
|
1202
|
+
use std::fmt::Write;
|
|
1203
|
+
let _ = write!(out, "\\x{:02x}", c as u32);
|
|
1204
|
+
}
|
|
1205
|
+
c => out.push(c),
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
out
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1184
1211
|
fn render_cell(repl: &Replacement, original: &Cell) -> String {
|
|
1185
1212
|
let trailing = original.trailing_expr.as_deref().unwrap_or("");
|
|
1186
1213
|
if repl.is_null {
|
|
@@ -2119,6 +2146,15 @@ INSERT INTO public.users (id, email) VALUES
|
|
|
2119
2146
|
);
|
|
2120
2147
|
}
|
|
2121
2148
|
|
|
2149
|
+
#[test]
|
|
2150
|
+
fn escape_postgres_copy_text_field_escapes_control_chars() {
|
|
2151
|
+
assert_eq!(
|
|
2152
|
+
escape_postgres_copy_text_field("a\tb\nc\\"),
|
|
2153
|
+
"a\\tb\\nc\\\\"
|
|
2154
|
+
);
|
|
2155
|
+
assert_eq!(escape_postgres_copy_text_field("\0\u{01}"), "\\0\\x01");
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2122
2158
|
#[test]
|
|
2123
2159
|
fn domain_mapping_null_and_non_null_cross_table_consistency() {
|
|
2124
2160
|
// When the same domain spans two tables, NULL stays NULL in both, and
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|