Skip to content

Commit 8a57d73

Browse files
kulvirgitclaude
andcommitted
refactor: remove sqlglot dependency, migrate all SQL methods to sqlguard (Rust)
- Rewire 6 `server.py` dispatch branches (`sql.analyze`, `sql.translate`, `sql.optimize`, `sql.format`, `sql.fix`, `sql.rewrite`) to call `guard_*` functions directly instead of `sqlglot`-based Python modules - Migrate `feedback_store.py` from `sqlglot` AST to regex-based fingerprinting and `guard_extract_metadata()`/`guard_complexity_score()` for cost prediction - Update `cost_gate.py` to use `guard_lint()` instead of `analyze_sql()` - Update `test_local.py` to use `guard_transpile()` instead of `translate_sql()` - Delete 8 source files (~2,700 lines): analyzer, translator, formatter, optimizer, rewriter, fixer, schema_diff, confidence - Delete 7 corresponding test files - Remove `sqlglot>=25.0` from `pyproject.toml` dependencies - Update tests to match `sqlguard` output format (`findings` key, `fixed` key, `total` complexity score) - All 676 tests pass Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fbb420e commit 8a57d73

23 files changed

Lines changed: 140 additions & 4098 deletions

packages/altimate-engine/pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ description = "Python engine for Altimate CLI - lineage, SQL execution, dbt inte
99
requires-python = ">=3.10"
1010
dependencies = [
1111
"pydantic>=2.0",
12-
"sqlglot>=25.0",
1312
"pyyaml>=6.0",
1413
]
1514

packages/altimate-engine/src/altimate_engine/ci/cost_gate.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""CI cost gate — scan changed SQL files for critical issues.
22
3-
Reads SQL files, runs existing analyze_sql(), and returns
3+
Reads SQL files, runs lint analysis, and returns
44
pass/fail based on whether CRITICAL severity issues are found.
55
66
Skips:
77
- Jinja templates ({{ }}, {% %})
8-
- sqlglot parse errors (likely Jinja or non-standard SQL)
8+
- Parse errors (likely Jinja or non-standard SQL)
99
- Non-SQL files
1010
"""
1111

@@ -15,7 +15,7 @@
1515
import re
1616
from typing import Any
1717

18-
from altimate_engine.sql.analyzer import analyze_sql
18+
from altimate_engine.sql.guard import guard_lint
1919

2020

2121
# Jinja pattern: {{ ... }} or {% ... %} or {# ... #}
@@ -119,19 +119,19 @@ def scan_files(
119119
file_issues: list[dict[str, Any]] = []
120120

121121
for stmt in statements:
122-
# Run analyzer
123-
analysis = analyze_sql(stmt, dialect)
124-
if not analysis.get("success", True):
122+
# Run lint analysis
123+
lint_result = guard_lint(stmt)
124+
if lint_result.get("error"):
125125
# Parse error — skip this statement (likely incomplete SQL)
126126
continue
127127

128-
for issue in analysis.get("issues", []):
129-
severity = issue.get("severity", "warning")
128+
for finding in lint_result.get("findings", lint_result.get("issues", [])):
129+
severity = finding.get("severity", "warning")
130130
file_issues.append({
131-
"type": issue.get("type", "UNKNOWN"),
131+
"type": finding.get("rule", finding.get("type", "UNKNOWN")),
132132
"severity": severity,
133-
"message": issue.get("message", ""),
134-
"source": "analyzer",
133+
"message": finding.get("message", ""),
134+
"source": "lint",
135135
})
136136
total_issues += 1
137137
if severity in ("error", "critical"):

packages/altimate-engine/src/altimate_engine/local/test_local.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def test_sql_local(
1212
) -> dict[str, Any]:
1313
"""Execute SQL against a local DuckDB database for validation.
1414
15-
If target_dialect differs from DuckDB, auto-transpiles via sqlglot first.
15+
If target_dialect differs from DuckDB, auto-transpiles first.
1616
1717
Args:
1818
sql: The SQL to test.
@@ -37,11 +37,12 @@ def test_sql_local(
3737

3838
if target_dialect and target_dialect.lower() not in ("duckdb", "duck"):
3939
try:
40-
from altimate_engine.sql.translator import translate_sql
40+
from altimate_engine.sql.guard import guard_transpile
4141

42-
result = translate_sql(sql, target_dialect, "duckdb")
43-
if result.get("success") and result.get("translated_sql"):
44-
test_sql = result["translated_sql"]
42+
result = guard_transpile(sql, target_dialect, "duckdb")
43+
translated = result.get("sql", result.get("translated_sql"))
44+
if result.get("success") and translated:
45+
test_sql = translated
4546
transpiled = True
4647
transpile_warnings = result.get("warnings", [])
4748
except Exception as e:

packages/altimate-engine/src/altimate_engine/server.py

Lines changed: 80 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
SqlFormatResult,
6868
SqlOptimizeParams,
6969
SqlOptimizeResult,
70+
SqlOptimizeSuggestion,
7071
SqlPredictCostParams,
7172
SqlPredictCostResult,
7273
SqlRecordFeedbackParams,
@@ -114,16 +115,9 @@
114115
SqlGuardResult,
115116
)
116117
from altimate_engine.sql.executor import execute_sql
117-
from altimate_engine.sql.analyzer import analyze_sql
118-
from altimate_engine.sql.optimizer import optimize_sql
119-
from altimate_engine.sql.translator import translate_sql
120-
from altimate_engine.sql.formatter import format_sql
121118
from altimate_engine.sql.explainer import explain_sql
122-
from altimate_engine.sql.fixer import fix_sql
123119
from altimate_engine.sql.autocomplete import autocomplete_sql
124120
from altimate_engine.sql.diff import diff_sql
125-
from altimate_engine.sql.rewriter import rewrite_sql
126-
from altimate_engine.sql.schema_diff import diff_schema
127121
from altimate_engine.ci.cost_gate import scan_files
128122
from altimate_engine.schema.inspector import inspect_schema
129123
from altimate_engine.schema.pii_detector import detect_pii
@@ -132,7 +126,7 @@
132126
from altimate_engine.dbt.manifest import parse_manifest
133127
from altimate_engine.dbt.lineage import dbt_lineage
134128
from altimate_engine.connections import ConnectionRegistry
135-
# lineage.check now delegates to sqlguard via guard_column_lineage
129+
# lineage.check delegates to guard_column_lineage
136130
from altimate_engine.sql.feedback_store import FeedbackStore
137131
from altimate_engine.schema.cache import SchemaCache
138132
from altimate_engine.finops.query_history import get_query_history
@@ -317,21 +311,23 @@ def dispatch(request: JsonRpcRequest) -> JsonRpcResponse:
317311
result = inspect_schema(SchemaInspectParams(**params))
318312
elif method == "sql.analyze":
319313
params_obj = SqlAnalyzeParams(**params)
320-
raw_result = analyze_sql(params_obj.sql, params_obj.dialect or "snowflake")
321-
# Convert raw dict result to SqlAnalyzeResult model
322314
issues = []
323-
for issue in raw_result.get("issues", []):
315+
316+
# Anti-pattern detection via sqlguard lint
317+
lint_result = guard_lint(params_obj.sql, schema_context=params_obj.schema_context)
318+
for issue in lint_result.get("findings", lint_result.get("issues", [])):
324319
issues.append(
325320
SqlAnalyzeIssue(
326-
type=issue["type"],
321+
type=issue.get("rule", issue.get("type", "LINT")),
327322
severity=issue.get("severity", "warning"),
328-
message=issue["message"],
329-
recommendation=issue.get("recommendation", ""),
323+
message=issue.get("message", ""),
324+
recommendation=issue.get("suggestion", issue.get("recommendation", "")),
330325
location=issue.get("location"),
331326
confidence=issue.get("confidence", "high"),
332327
)
333328
)
334-
# Append sqlguard semantic checks if available
329+
330+
# Semantic checks
335331
sem_result = guard_check_semantics(
336332
params_obj.sql,
337333
schema_context=params_obj.schema_context,
@@ -348,7 +344,8 @@ def dispatch(request: JsonRpcRequest) -> JsonRpcResponse:
348344
confidence=si.get("confidence", "medium"),
349345
)
350346
)
351-
# Append sqlguard safety scan if available
347+
348+
# Safety scan
352349
safety_result = guard_scan_safety(params_obj.sql)
353350
if safety_result.get("threats"):
354351
for threat in safety_result["threats"]:
@@ -362,26 +359,60 @@ def dispatch(request: JsonRpcRequest) -> JsonRpcResponse:
362359
confidence="high",
363360
)
364361
)
362+
365363
result = SqlAnalyzeResult(
366-
success=raw_result.get("success", True),
364+
success=not lint_result.get("error"),
367365
issues=issues,
368366
issue_count=len(issues),
369367
confidence=_compute_overall_confidence(issues),
370-
confidence_factors=_get_confidence_factors(raw_result),
371-
error=raw_result.get("error"),
368+
confidence_factors=[] if not lint_result.get("error") else ["Lint parse failed"],
369+
error=lint_result.get("error"),
372370
)
373371
elif method == "sql.translate":
374372
params_obj = SqlTranslateParams(**params)
375-
result_dict = translate_sql(
376-
params_obj.sql, params_obj.source_dialect, params_obj.target_dialect
373+
raw = guard_transpile(params_obj.sql, params_obj.source_dialect, params_obj.target_dialect)
374+
result = SqlTranslateResult(
375+
success=raw.get("success", True),
376+
translated_sql=raw.get("sql", raw.get("translated_sql")),
377+
source_dialect=params_obj.source_dialect,
378+
target_dialect=params_obj.target_dialect,
379+
warnings=raw.get("warnings", []),
380+
error=raw.get("error"),
377381
)
378-
result = SqlTranslateResult(**result_dict)
379382
elif method == "sql.optimize":
380383
params_obj = SqlOptimizeParams(**params)
381-
result_dict = optimize_sql(
382-
params_obj.sql, params_obj.dialect, params_obj.schema_context
384+
# Rewrite for optimization
385+
rw = guard_rewrite_sql(params_obj.sql, schema_context=params_obj.schema_context)
386+
# Lint for remaining issues
387+
lint = guard_lint(params_obj.sql, schema_context=params_obj.schema_context)
388+
389+
suggestions = []
390+
for r in rw.get("rewrites", []):
391+
suggestions.append(
392+
SqlOptimizeSuggestion(
393+
type="REWRITE",
394+
description=r.get("explanation", "Optimization rewrite"),
395+
before=r.get("original_fragment", ""),
396+
after=r.get("rewritten_fragment", ""),
397+
)
398+
)
399+
400+
anti_patterns = []
401+
for issue in lint.get("findings", lint.get("issues", [])):
402+
anti_patterns.append({
403+
"type": issue.get("rule", issue.get("type", "LINT")),
404+
"message": issue.get("message", ""),
405+
"suggestion": issue.get("suggestion", ""),
406+
})
407+
408+
result = SqlOptimizeResult(
409+
success=True,
410+
original_sql=params_obj.sql,
411+
optimized_sql=rw.get("rewritten_sql", params_obj.sql),
412+
suggestions=suggestions,
413+
anti_patterns=anti_patterns,
414+
error=rw.get("error"),
383415
)
384-
result = SqlOptimizeResult(**result_dict)
385416
elif method == "lineage.check":
386417
p = LineageCheckParams(**params)
387418
raw = guard_column_lineage(
@@ -457,46 +488,45 @@ def dispatch(request: JsonRpcRequest) -> JsonRpcResponse:
457488
result = SqlPredictCostResult(**prediction)
458489
elif method == "sql.format":
459490
fmt_params = SqlFormatParams(**params)
460-
fmt_result = format_sql(
461-
fmt_params.sql, fmt_params.dialect, fmt_params.indent
491+
raw = guard_format_sql(fmt_params.sql, fmt_params.dialect)
492+
result = SqlFormatResult(
493+
success=raw.get("success", True),
494+
formatted_sql=raw.get("formatted_sql", raw.get("sql")),
495+
statement_count=raw.get("statement_count", 1),
496+
error=raw.get("error"),
462497
)
463-
result = SqlFormatResult(**fmt_result)
464498
elif method == "sql.explain":
465499
result = explain_sql(SqlExplainParams(**params))
466500
elif method == "sql.fix":
467501
fix_params = SqlFixParams(**params)
468-
# Try sqlguard.fix first for Rust-powered fixing
469502
guard_result = guard_fix_sql(fix_params.sql)
470-
if guard_result.get("success") and guard_result.get("fixed_sql"):
503+
fixed = guard_result.get("fixed", guard_result.get("success", False))
504+
fixed_sql = guard_result.get("fixed_sql")
505+
if fixed and fixed_sql:
471506
result = SqlFixResult(
472507
success=True,
473508
original_sql=fix_params.sql,
474-
fixed_sql=guard_result["fixed_sql"],
509+
fixed_sql=fixed_sql,
475510
error_message=fix_params.error_message,
476511
suggestions=[
477512
SqlFixSuggestion(
478513
type="SQLGUARD_FIX",
479-
message="Auto-fixed by sqlguard Rust engine",
514+
message="Auto-fixed by sqlguard engine",
480515
confidence="high",
481-
fixed_sql=guard_result["fixed_sql"],
516+
fixed_sql=fixed_sql,
482517
)
483518
],
484519
suggestion_count=1,
485520
)
486521
else:
487-
# Fall back to sqlglot-based fixer
488-
fix_result = fix_sql(
489-
fix_params.sql, fix_params.error_message, fix_params.dialect
490-
)
491522
result = SqlFixResult(
492-
success=fix_result["success"],
493-
original_sql=fix_result["original_sql"],
494-
fixed_sql=fix_result.get("fixed_sql"),
495-
error_message=fix_result["error_message"],
496-
suggestions=[
497-
SqlFixSuggestion(**s) for s in fix_result["suggestions"]
498-
],
499-
suggestion_count=fix_result["suggestion_count"],
523+
success=False,
524+
original_sql=fix_params.sql,
525+
fixed_sql=fixed_sql,
526+
error_message=fix_params.error_message,
527+
suggestions=[],
528+
suggestion_count=0,
529+
error=guard_result.get("error", "Unable to auto-fix"),
500530
)
501531
elif method == "sql.autocomplete":
502532
ac_params = SqlAutocompleteParams(**params)
@@ -627,7 +657,6 @@ def dispatch(request: JsonRpcRequest) -> JsonRpcResponse:
627657
# --- SQL rewrite ---
628658
elif method == "sql.rewrite":
629659
p = SqlRewriteParams(**params)
630-
# Try sqlguard.rewrite first for Rust-powered rewriting
631660
guard_rw = guard_rewrite_sql(p.sql, schema_context=p.schema_context)
632661
if guard_rw.get("success") and guard_rw.get("rewritten_sql"):
633662
rewrites = []
@@ -648,17 +677,12 @@ def dispatch(request: JsonRpcRequest) -> JsonRpcResponse:
648677
rewrites_applied=rewrites,
649678
)
650679
else:
651-
# Fall back to sqlglot-based rewriter
652-
raw = rewrite_sql(p.sql, p.dialect, p.schema_context)
653680
result = SqlRewriteResult(
654-
success=raw["success"],
655-
original_sql=raw["original_sql"],
656-
rewritten_sql=raw.get("rewritten_sql"),
657-
rewrites_applied=[
658-
SqlRewriteRule(**r)
659-
for r in raw.get("rewrites_applied", [])
660-
],
661-
error=raw.get("error"),
681+
success=False,
682+
original_sql=p.sql,
683+
rewritten_sql=None,
684+
rewrites_applied=[],
685+
error=guard_rw.get("error", "No rewrites applicable"),
662686
)
663687
# --- sqlguard ---
664688
elif method == "sqlguard.validate":

0 commit comments

Comments
 (0)