Firemedic15's picture
download
raw
15.6 kB
#!/usr/bin/env python3
"""Generate a small synthetic 'Squirrel-mini' benchmark.
This is a toy proxy for the Squirrel/SQLBench construction pipeline (Section 3 of
the paper). The real seed corpus (1,000+ proprietary enterprise SQL scripts) is not
public, so this script synthesizes long, multi-CTE ETL-style SQL scripts across
several business domains, then injects syntax and semantic bugs following the
paper's bug taxonomy (Table 3/4), producing (issue_sql, reference_sql, error/query)
triples we can use to probe LLM debugging ability and to measure script-complexity
statistics (lines, tokens, functions, AST width/depth) for comparison with Table 1.
"""
from __future__ import annotations
import json
import re
from pathlib import Path
DOMAINS = [
("ecommerce", "order_events", "customer_profile", "product_catalog"),
("finance", "transaction_ledger", "account_profile", "instrument_catalog"),
("healthcare", "visit_events", "patient_profile", "procedure_catalog"),
("logistics", "shipment_events", "carrier_profile", "route_catalog"),
("education", "enrollment_events", "student_profile", "course_catalog"),
("telecom", "usage_events", "subscriber_profile", "plan_catalog"),
("real_estate", "listing_events", "agent_profile", "property_catalog"),
("entertainment", "stream_events", "viewer_profile", "title_catalog"),
("travel", "booking_events", "traveler_profile", "fare_catalog"),
("energy", "meter_events", "site_profile", "tariff_catalog"),
]
N_METRIC_COLS = 14
N_DIM_COLS = 10
def build_reference_sql(domain: str, fact: str, dim1: str, dim2: str) -> str:
"""Build a long multi-CTE enterprise ETL SQL script (Hive/Spark-style)."""
metric_cols = [f"metric_{i}_amount" for i in range(N_METRIC_COLS)]
dim_cols = [f"dim_{i}_code" for i in range(N_DIM_COLS)]
lines = []
lines.append(f"-- ETL pipeline for {domain} daily aggregation")
lines.append("WITH base_events AS (")
lines.append(" SELECT")
lines.append(f" {fact}.entity_id,")
lines.append(f" {fact}.event_timestamp,")
for c in dim_cols[:5]:
lines.append(f" {fact}.{c},")
for c in metric_cols[:7]:
lines.append(f" {fact}.{c},")
lines.append(f" {fact}.status_flag")
lines.append(f" FROM {domain}_db.{fact} {fact}")
lines.append(f" WHERE {fact}.event_date BETWEEN '${{start_date}}' AND '${{end_date}}'")
lines.append(" AND status_flag = 'ACTIVE'")
lines.append("),")
lines.append("enriched_events AS (")
lines.append(" SELECT")
lines.append(" base_events.entity_id,")
lines.append(" base_events.event_timestamp,")
for c in dim_cols[:5]:
lines.append(f" base_events.{c},")
for c in metric_cols[:7]:
lines.append(
f" COALESCE(base_events.{c}, 0) AS {c},"
)
lines.append(f" {dim1}.segment_code,")
lines.append(f" {dim1}.region_code,")
lines.append(f" {dim1}.tier_level,")
lines.append(f" {dim2}.category_code,")
lines.append(f" {dim2}.subcategory_code")
lines.append(" FROM base_events")
lines.append(f" JOIN {domain}_db.{dim1} {dim1}")
lines.append(f" ON base_events.entity_id = {dim1}.entity_id")
lines.append(f" LEFT JOIN {domain}_db.{dim2} {dim2}")
lines.append(f" ON base_events.dim_0_code = {dim2}.dim_0_code")
lines.append("),")
lines.append("windowed_metrics AS (")
lines.append(" SELECT")
lines.append(" entity_id,")
lines.append(" segment_code,")
lines.append(" region_code,")
lines.append(" category_code,")
for c in metric_cols[:7]:
lines.append(f" {c},")
lines.append(
" ROW_NUMBER() OVER (PARTITION BY entity_id ORDER BY event_timestamp DESC) AS recency_rank,"
)
lines.append(
" SUM(metric_0_amount) OVER (PARTITION BY entity_id, segment_code) AS segment_total,"
)
lines.append(
" AVG(metric_1_amount) OVER (PARTITION BY region_code ORDER BY event_timestamp "
"ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS rolling_avg_metric_1"
)
lines.append(" FROM enriched_events")
lines.append("),")
lines.append("aggregated AS (")
lines.append(" SELECT")
lines.append(" segment_code,")
lines.append(" region_code,")
lines.append(" category_code,")
lines.append(" COUNT(DISTINCT entity_id) AS entity_count,")
for i, c in enumerate(metric_cols[:7]):
lines.append(f" SUM({c}) AS total_{c},")
lines.append(f" MAX(CASE WHEN recency_rank = 1 THEN {c} ELSE NULL END) AS latest_{c},")
lines.append(" MAX(segment_total) AS segment_total,")
lines.append(" AVG(rolling_avg_metric_1) AS avg_rolling_metric_1")
lines.append(" FROM windowed_metrics")
lines.append(" GROUP BY")
lines.append(" segment_code,")
lines.append(" region_code,")
lines.append(" category_code")
lines.append("),")
lines.append("classified AS (")
lines.append(" SELECT")
lines.append(" segment_code,")
lines.append(" region_code,")
lines.append(" category_code,")
lines.append(" entity_count,")
lines.append(" total_metric_0_amount,")
lines.append(" total_metric_1_amount,")
lines.append(" segment_total,")
lines.append(" avg_rolling_metric_1,")
lines.append(" CASE")
lines.append(" WHEN total_metric_0_amount > 100000 THEN 'HIGH'")
lines.append(" WHEN total_metric_0_amount > 10000 THEN 'MEDIUM'")
lines.append(" ELSE 'LOW'")
lines.append(" END AS value_tier,")
lines.append(
" CONCAT_WS('_', segment_code, region_code) AS segment_region_key,"
)
lines.append(
" ROUND(total_metric_1_amount / NULLIF(entity_count, 0), 2) AS per_entity_metric_1"
)
lines.append(" FROM aggregated")
lines.append("),")
lines.append("legacy_snapshot AS (")
lines.append(" SELECT")
lines.append(" segment_code,")
lines.append(" region_code,")
lines.append(" category_code,")
lines.append(" entity_count AS entity_count,")
lines.append(" total_metric_0_amount AS total_metric_0_amount,")
lines.append(" 'legacy' AS data_source")
lines.append(f" FROM {domain}_db.legacy_{fact}_snapshot")
lines.append(" WHERE snapshot_date = '${end_date}'")
lines.append(")")
lines.append("INSERT OVERWRITE TABLE fake_base_test.{}_daily_summary".format(domain))
lines.append("PARTITION (processing_date = '${end_date}')")
lines.append("SELECT")
lines.append(" classified.segment_code,")
lines.append(" classified.region_code,")
lines.append(" classified.category_code,")
lines.append(" classified.entity_count,")
lines.append(" classified.total_metric_0_amount,")
lines.append(" classified.total_metric_1_amount,")
lines.append(" classified.segment_total,")
lines.append(" classified.avg_rolling_metric_1,")
lines.append(" classified.value_tier,")
lines.append(" classified.segment_region_key,")
lines.append(" classified.per_entity_metric_1,")
lines.append(" 'current' AS data_source")
lines.append("FROM classified")
lines.append("UNION ALL")
lines.append("SELECT")
lines.append(" legacy_snapshot.segment_code,")
lines.append(" legacy_snapshot.region_code,")
lines.append(" legacy_snapshot.category_code,")
lines.append(" legacy_snapshot.entity_count,")
lines.append(" legacy_snapshot.total_metric_0_amount,")
lines.append(" 0 AS total_metric_1_amount,")
lines.append(" 0 AS segment_total,")
lines.append(" 0 AS avg_rolling_metric_1,")
lines.append(" 'LEGACY' AS value_tier,")
lines.append(
" CONCAT_WS('_', legacy_snapshot.segment_code, legacy_snapshot.region_code) AS segment_region_key,"
)
lines.append(" 0 AS per_entity_metric_1,")
lines.append(" legacy_snapshot.data_source")
lines.append("FROM legacy_snapshot;")
return "\n".join(lines)
def build_ddl(domain: str, fact: str, dim1: str, dim2: str) -> str:
return (
f"CREATE TABLE IF NOT EXISTS {domain}_db.{fact} (entity_id STRING, event_timestamp TIMESTAMP, "
+ ", ".join(f"dim_{i}_code STRING" for i in range(N_DIM_COLS))
+ ", "
+ ", ".join(f"metric_{i}_amount DOUBLE" for i in range(N_METRIC_COLS))
+ ", status_flag STRING, event_date STRING);\n"
f"CREATE TABLE IF NOT EXISTS {domain}_db.{dim1} (entity_id STRING, segment_code STRING, "
"region_code STRING, tier_level STRING);\n"
f"CREATE TABLE IF NOT EXISTS {domain}_db.{dim2} (dim_0_code STRING, category_code STRING, "
"subcategory_code STRING);\n"
f"CREATE TABLE IF NOT EXISTS {domain}_db.legacy_{fact}_snapshot (segment_code STRING, "
"region_code STRING, category_code STRING, entity_count BIGINT, total_metric_0_amount DOUBLE, "
"snapshot_date STRING);"
)
# ---- Bug injectors (mirroring Table 3 / Table 4 taxonomy) ----
def inject_syntax_bug(sql: str):
"""Return (issue_sql, level1, level2, level3, error_message) or None."""
# CASE Expression: Missing END (most frequent syntax bug type in Table 4, count=72)
m = list(re.finditer(r"CASE\n(.*?)\n(\s*)END AS (\w+)", sql, re.S))
if m:
m = m[0]
issue = sql[: m.start()] + f"CASE\n{m.group(1)}\n{m.group(2)}{m.group(3)}" + sql[m.end():]
err = (
f"ParseException: mismatched input 'AS' expecting 'END' near '{m.group(3)}'"
)
return issue, "Grammar & Structure", "CASE Expression", "Missing END in CASE WHEN", err
return None
def inject_syntax_bug_group_by(sql: str):
# Grammar & Structure / Clause Structure: trailing comma before FROM in GROUP BY
m = re.search(r"(GROUP BY\n\s+segment_code,\n\s+region_code,\n\s+category_code)\n(\))", sql)
if m:
issue = sql[: m.start()] + m.group(1) + ",\n" + m.group(2) + sql[m.end():]
err = "ParseException: extraneous input ')' expecting {identifier} near GROUP BY clause"
return issue, "Grammar & Structure", "Clause Structure", "Trailing comma in GROUP BY", err
return None
def inject_semantic_bug_join_type(sql: str):
# Joins & Grouping / JOIN Type Selection: LEFT JOIN -> INNER JOIN drops unmatched rows
if "LEFT JOIN" in sql:
issue = sql.replace("LEFT JOIN", "JOIN", 1)
query = (
"Some entities that should appear in the daily summary (e.g. those without a "
"matching category record) are missing from the output entirely. Expected: all "
"entities from the base events should be retained even if category enrichment is "
"unavailable. Please fix the bug."
)
return issue, "Joins & Grouping", "JOIN Type Selection", "Using INNER JOIN when LEFT JOIN is needed", query
return None
def inject_semantic_bug_union(sql: str):
# Semantics & Logic / Set Operations: UNION ALL -> UNION causes silent row loss via dedup
if "UNION ALL" in sql:
issue = sql.replace("UNION ALL", "UNION", 1)
query = (
"The daily summary table sometimes has fewer rows than expected when the current "
"period and legacy snapshot happen to produce identical rows for a segment; those "
"duplicate-looking rows are actually distinct facts and should both be kept. Please fix the bug."
)
return issue, "Semantics & Logic", "Set Operations", "UNION vs UNION ALL misuse", query
return None
def inject_semantic_bug_groupby_missing(sql: str):
# Joins & Grouping / GROUP BY Logic: aggregate SELECT includes non-grouped info via wrong join key
m = re.search(r"ON base_events\.entity_id = " + r"(\w+)\.entity_id", sql)
if m:
dim1 = m.group(1)
issue = sql.replace(
f"ON base_events.entity_id = {dim1}.entity_id",
f"ON base_events.dim_0_code = {dim1}.entity_id",
1,
)
query = (
"The join between base events and the profile dimension looks wrong: segment_code "
"and region_code values in the output don't correspond to the correct entity anymore, "
"producing mismatched enrichment. Please fix the bug so profile attributes are joined "
"on the correct key."
)
return issue, "Joins & Grouping", "JOIN Logic", "Wrong join key used", query
return None
def _parses_ok(sql: str) -> bool:
import sqlglot
try:
sqlglot.parse(sql, read="hive")
return True
except Exception:
return False
SYNTAX_INJECTORS = [inject_syntax_bug, inject_syntax_bug_group_by]
SEMANTIC_INJECTORS = [
inject_semantic_bug_join_type,
inject_semantic_bug_union,
inject_semantic_bug_groupby_missing,
]
def main():
tasks = []
seeds = []
for i, (domain, fact, dim1, dim2) in enumerate(DOMAINS):
ref_sql = build_reference_sql(domain, fact, dim1, dim2)
ddl = build_ddl(domain, fact, dim1, dim2)
seeds.append({"domain": domain, "reference_sql": ref_sql, "ddl": ddl})
result = None
for syn_fn in SYNTAX_INJECTORS[i % 2 :] + SYNTAX_INJECTORS[: i % 2]:
candidate = syn_fn(ref_sql)
if candidate and not _parses_ok(candidate[0]):
result = candidate
break
if result is None:
result = inject_syntax_bug(ref_sql)
if result:
issue_sql, l1, l2, l3, err = result
tasks.append(
{
"task_id": f"{domain}-syntax-{i}",
"type": "syntax",
"domain": domain,
"ddl": ddl,
"issue_sql": issue_sql,
"reference_sql": ref_sql,
"error_message": err,
"level1_error_type": l1,
"level2_error_type": l2,
"level3_error_type": l3,
}
)
sem_fn = SEMANTIC_INJECTORS[i % len(SEMANTIC_INJECTORS)]
result = sem_fn(ref_sql)
if result:
issue_sql, l1, l2, l3, query = result
tasks.append(
{
"task_id": f"{domain}-semantic-{i}",
"type": "semantic",
"domain": domain,
"ddl": ddl,
"issue_sql": issue_sql,
"reference_sql": ref_sql,
"user_query": query,
"level1_error_type": l1,
"level2_error_type": l2,
"level3_error_type": l3,
}
)
out_dir = Path(__file__).resolve().parent.parent / "data"
out_dir.mkdir(exist_ok=True)
(out_dir / "squirrel_mini_seeds.json").write_text(json.dumps(seeds, indent=2))
(out_dir / "squirrel_mini_tasks.json").write_text(json.dumps(tasks, indent=2))
print(f"Generated {len(seeds)} seed scripts and {len(tasks)} tasks")
print(f" syntax tasks: {sum(1 for t in tasks if t['type'] == 'syntax')}")
print(f" semantic tasks: {sum(1 for t in tasks if t['type'] == 'semantic')}")
if __name__ == "__main__":
main()

Xet Storage Details

Size:
15.6 kB
·
Xet hash:
7b2b790a7a452b3fcb96f9636ad859a22dfb4df3e281951ee2e8d0dd380b91bf

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.