mikeboone Claude Fable 5 commited on
Commit
e32850b
·
1 Parent(s): 2d09335

fix(ddl): CHECK sanitizer no longer deletes columns named CHECKOUT_*

Browse files

The chunk-level CHECK-constraint detector used prefix matching
(upper.startswith("CHECK")), so any column whose name begins with CHECK —
CHECKOUT_INITIATED, CHECKOUT_ATTEMPTS, CHECKOUT_CONVERSION_RATE,
CHECKOUT_ABANDONMENT_RATE — was silently dropped from CREATE TABLE while the
generated dataset still carried its values. The INSERT then failed on columns
the table didn't have. Root cause of the "random" schema/data-mismatch build
failures (Cloudscape, Atlas Freight partial, Tixr run 02ec3e7baba6) — it fired
whenever the LLM blueprint author named a column CHECKOUT_*, which
checkout-funnel briefs (Tixr) invite.

Detector now requires the CHECK keyword followed by a parenthesized
expression, and the CONSTRAINT branch uses a word-boundary match so an FK
named CHECKOUT_FK survives. Inline CHECK removal already used \bCHECK\b and
was safe. 4 regression tests added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

cdw_connector.py CHANGED
@@ -258,7 +258,15 @@ class SnowflakeDeployer:
258
  continue
259
 
260
  upper = stripped.upper()
261
- is_table_check = upper.startswith("CHECK") or ("CHECK" in upper and upper.startswith("CONSTRAINT"))
 
 
 
 
 
 
 
 
262
  if is_table_check:
263
  removed_any = True
264
  continue
 
258
  continue
259
 
260
  upper = stripped.upper()
261
+ # A table-level CHECK constraint is the keyword CHECK followed by a
262
+ # parenthesized expression — NOT any chunk that merely starts with the
263
+ # letters CHECK. A column named CHECKOUT_* (e.g. CHECKOUT_ATTEMPTS
264
+ # NUMBER(38,2)) must survive; prefix matching silently deleted such
265
+ # columns from CREATE TABLE while the dataset still carried their
266
+ # values (the FACT_PLATFORM_ENGAGEMENT / CHECKOUT_INITIATED bug).
267
+ is_table_check = bool(re.match(r"CHECK\s*\(", upper)) or (
268
+ upper.startswith("CONSTRAINT") and re.search(r"\bCHECK\s*\(", upper)
269
+ )
270
  if is_table_check:
271
  removed_any = True
272
  continue
tests/test_cdw_ddl_sanitizer.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Regression tests for the CREATE TABLE CHECK-constraint sanitizer.
2
+
3
+ The bug: the chunk-level CHECK detector used prefix matching
4
+ (upper.startswith("CHECK")), so any COLUMN whose name begins with CHECK —
5
+ CHECKOUT_INITIATED, CHECKOUT_ATTEMPTS, CHECKOUT_CONVERSION_RATE... — was
6
+ silently deleted from the CREATE TABLE while the generated dataset still
7
+ carried its values. The INSERT then referenced columns the table didn't have
8
+ (the FACT_PLATFORM_ENGAGEMENT failure caught by the dataset_writer guard).
9
+ """
10
+
11
+ from cdw_connector import SnowflakeDeployer
12
+
13
+
14
+ def _sanitize(statement: str) -> str:
15
+ return SnowflakeDeployer()._sanitize_create_table_statement(statement)
16
+
17
+
18
+ def test_checkout_columns_survive_and_table_check_is_removed():
19
+ stmt = (
20
+ "CREATE TABLE FACT_PLATFORM_ENGAGEMENT (\n"
21
+ " ENGAGEMENT_KEY NUMBER NOT NULL,\n"
22
+ " CHECKOUT_ATTEMPTS NUMBER(38,0) NOT NULL,\n"
23
+ " CHECKOUT_CONVERSION_RATE NUMBER(38,2) NOT NULL,\n"
24
+ " CHECKOUT_ABANDONMENT_RATE NUMBER(38,2) NOT NULL,\n"
25
+ " CHECK (CHECKOUT_ATTEMPTS >= 0)\n"
26
+ ")"
27
+ )
28
+ out = _sanitize(stmt)
29
+ assert "CHECKOUT_ATTEMPTS NUMBER(38,0)" in out
30
+ assert "CHECKOUT_CONVERSION_RATE" in out
31
+ assert "CHECKOUT_ABANDONMENT_RATE" in out
32
+ assert "CHECK (" not in out
33
+
34
+
35
+ def test_named_constraint_starting_with_check_is_only_removed_when_it_is_a_check():
36
+ stmt = (
37
+ "CREATE TABLE T (\n"
38
+ " ID NUMBER NOT NULL,\n"
39
+ " CHECKOUT_KEY NUMBER NOT NULL,\n"
40
+ " CONSTRAINT CHECKOUT_FK FOREIGN KEY (CHECKOUT_KEY) REFERENCES DIM_CHECKOUT(CHECKOUT_KEY),\n"
41
+ " CONSTRAINT POSITIVE_ID CHECK (ID > 0)\n"
42
+ ")"
43
+ )
44
+ out = _sanitize(stmt)
45
+ # FK constraint whose NAME contains CHECK must survive.
46
+ assert "FOREIGN KEY (CHECKOUT_KEY)" in out
47
+ # Real named CHECK constraint must go.
48
+ assert "POSITIVE_ID" not in out
49
+
50
+
51
+ def test_inline_check_clause_removed_but_column_kept():
52
+ stmt = (
53
+ "CREATE TABLE T (\n"
54
+ " ID NUMBER NOT NULL,\n"
55
+ " QTY NUMBER CHECK (QTY >= 0) NOT NULL\n"
56
+ ")"
57
+ )
58
+ out = _sanitize(stmt)
59
+ assert "QTY NUMBER" in out
60
+ assert "CHECK (" not in out
61
+
62
+
63
+ def test_statement_without_checks_is_untouched():
64
+ stmt = (
65
+ "CREATE TABLE DIM_CHECKOUT (\n"
66
+ " CHECKOUT_KEY NUMBER NOT NULL,\n"
67
+ " CHECKOUT_CHANNEL VARCHAR NOT NULL\n"
68
+ ")"
69
+ )
70
+ assert _sanitize(stmt) == stmt