ripple-sql 0.1.0__py3-none-any.whl
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.
- ripple/__init__.py +31 -0
- ripple/answer.py +473 -0
- ripple/answer_page.py +214 -0
- ripple/cache.py +80 -0
- ripple/ci.py +422 -0
- ripple/ci_signature.py +374 -0
- ripple/cli.py +733 -0
- ripple/doctor.py +225 -0
- ripple/engine/__init__.py +111 -0
- ripple/engine/budget.py +86 -0
- ripple/engine/column_lineage.py +112 -0
- ripple/engine/column_ref.py +818 -0
- ripple/engine/cte_tracing.py +1309 -0
- ripple/engine/dependencies.py +466 -0
- ripple/engine/dialect.py +132 -0
- ripple/engine/dispatch.py +12 -0
- ripple/engine/extraction.py +27 -0
- ripple/engine/jinja.py +282 -0
- ripple/engine/json_sources.py +241 -0
- ripple/engine/macro_source.py +127 -0
- ripple/engine/pipeline.py +265 -0
- ripple/engine/preprocess.py +174 -0
- ripple/engine/safe_gen.py +21 -0
- ripple/engine/schema_qualification.py +151 -0
- ripple/engine/scope.py +488 -0
- ripple/engine/select_sources.py +1038 -0
- ripple/engine/sql_script.py +729 -0
- ripple/engine/statement.py +449 -0
- ripple/engine/tech_debt.py +169 -0
- ripple/engine/tsql_catalog.py +83 -0
- ripple/engine/tsql_scalar_vars.py +248 -0
- ripple/engine/tsql_tvf.py +653 -0
- ripple/engine/tsql_xml.py +97 -0
- ripple/engine/types.py +167 -0
- ripple/engine/unused_deps.py +555 -0
- ripple/engine/validation.py +158 -0
- ripple/graph.py +1499 -0
- ripple/home.py +232 -0
- ripple/loaders/__init__.py +7 -0
- ripple/loaders/dbt.py +359 -0
- ripple/loaders/dbt_config.py +339 -0
- ripple/loaders/identity.py +328 -0
- ripple/loaders/sidecar.py +65 -0
- ripple/loaders/sqldir.py +262 -0
- ripple/loaders/types.py +197 -0
- ripple/lookml.py +163 -0
- ripple/mcp_server.py +600 -0
- ripple/names.py +40 -0
- ripple/project.py +167 -0
- ripple/py.typed +0 -0
- ripple/render.py +426 -0
- ripple/render_shims.py +209 -0
- ripple/schemas.py +155 -0
- ripple/semantic.py +232 -0
- ripple/server.py +184 -0
- ripple/sourcefiles.py +64 -0
- ripple/star_resolution.py +100 -0
- ripple/static/answer.css +146 -0
- ripple/static/answer.html +358 -0
- ripple/static/answer_twin.js +299 -0
- ripple/static/explore.js +133 -0
- ripple/usage/__init__.py +18 -0
- ripple/usage/cli.py +78 -0
- ripple/usage/collect.py +315 -0
- ripple/usage/discover.py +190 -0
- ripple/usage/ingest.py +414 -0
- ripple/usage/report.py +131 -0
- ripple_sql-0.1.0.dist-info/METADATA +285 -0
- ripple_sql-0.1.0.dist-info/RECORD +72 -0
- ripple_sql-0.1.0.dist-info/WHEEL +4 -0
- ripple_sql-0.1.0.dist-info/entry_points.txt +3 -0
- ripple_sql-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Column lineage validation and trust scoring.
|
|
2
|
+
|
|
3
|
+
This module provides validation functions for lineage correctness:
|
|
4
|
+
- Trust level mapping from confidence scores
|
|
5
|
+
- UNION column validation (positional vs semantic matching)
|
|
6
|
+
- SELECT column name extraction
|
|
7
|
+
|
|
8
|
+
Trust Levels:
|
|
9
|
+
- verified: 1.0 confidence - warehouse-qualified or exact match
|
|
10
|
+
- high_confidence: 0.8-0.99 - strong evidence
|
|
11
|
+
- moderate: 0.5-0.79 - heuristic match
|
|
12
|
+
- review_required: <0.5 - ambiguity or collision detected
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
from ripple.engine.safe_gen import safe_sql
|
|
19
|
+
from ripple.engine.types import TrustLevel
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from sqlglot import exp
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_trust_level(confidence: float) -> TrustLevel:
|
|
28
|
+
"""Map confidence score to trust level.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
confidence: Confidence score (0.0-1.0)
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Trust level string for API response
|
|
35
|
+
"""
|
|
36
|
+
if confidence >= 1.0:
|
|
37
|
+
return "verified"
|
|
38
|
+
elif confidence >= 0.8:
|
|
39
|
+
return "high_confidence"
|
|
40
|
+
elif confidence >= 0.5:
|
|
41
|
+
return "moderate"
|
|
42
|
+
return "review_required"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_select_column_names(
|
|
46
|
+
select_node: "exp.Select",
|
|
47
|
+
dialect: str,
|
|
48
|
+
) -> list[str]:
|
|
49
|
+
"""Extract column names/aliases from a SELECT node.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
select_node: SQLGlot Select expression
|
|
53
|
+
dialect: SQL dialect for generating SQL strings
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
List of column names in order they appear
|
|
57
|
+
"""
|
|
58
|
+
from sqlglot import exp
|
|
59
|
+
|
|
60
|
+
names = []
|
|
61
|
+
for select_expr in select_node.selects:
|
|
62
|
+
if isinstance(select_expr, exp.Alias):
|
|
63
|
+
names.append(select_expr.alias)
|
|
64
|
+
elif isinstance(select_expr, exp.Column):
|
|
65
|
+
names.append(select_expr.name)
|
|
66
|
+
elif isinstance(select_expr, exp.Star):
|
|
67
|
+
names.append("*")
|
|
68
|
+
else:
|
|
69
|
+
# Computed expression without alias - use SQL representation
|
|
70
|
+
sql = safe_sql(select_expr, dialect)
|
|
71
|
+
names.append(sql if sql is not None and len(sql) < 50 else f"expr_{len(names)}")
|
|
72
|
+
return names
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def validate_union_columns(
|
|
76
|
+
union_expr: "exp.Expression",
|
|
77
|
+
dialect: str,
|
|
78
|
+
) -> tuple[bool, list[dict]]:
|
|
79
|
+
"""Validate that UNION branches have matching column names.
|
|
80
|
+
|
|
81
|
+
SQL standard UNION uses positional matching, but column name mismatches
|
|
82
|
+
often indicate bugs. This function detects when UNION branches have
|
|
83
|
+
different column names at the same position.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
union_expr: SQLGlot Union/Except/Intersect expression
|
|
87
|
+
dialect: SQL dialect
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Tuple of (is_valid, mismatches)
|
|
91
|
+
- is_valid: True if all column names match across branches
|
|
92
|
+
- mismatches: List of mismatch details if any found
|
|
93
|
+
"""
|
|
94
|
+
from sqlglot import exp
|
|
95
|
+
|
|
96
|
+
branches: list[list[str]] = []
|
|
97
|
+
|
|
98
|
+
def collect_branches(node: exp.Expression) -> None:
|
|
99
|
+
"""Recursively collect SELECT branches from set operations."""
|
|
100
|
+
if isinstance(node, (exp.Union, exp.Except, exp.Intersect)):
|
|
101
|
+
collect_branches(node.left)
|
|
102
|
+
collect_branches(node.right)
|
|
103
|
+
elif isinstance(node, exp.Select):
|
|
104
|
+
cols = get_select_column_names(node, dialect)
|
|
105
|
+
branches.append(cols)
|
|
106
|
+
|
|
107
|
+
collect_branches(union_expr)
|
|
108
|
+
|
|
109
|
+
if len(branches) < 2:
|
|
110
|
+
return True, []
|
|
111
|
+
|
|
112
|
+
first_branch = branches[0]
|
|
113
|
+
mismatches = []
|
|
114
|
+
|
|
115
|
+
for branch_idx, branch in enumerate(branches[1:], start=2):
|
|
116
|
+
# Check column count first
|
|
117
|
+
if len(branch) != len(first_branch):
|
|
118
|
+
mismatches.append(
|
|
119
|
+
{
|
|
120
|
+
"branch": branch_idx,
|
|
121
|
+
"warning_type": "union_column_count_mismatch",
|
|
122
|
+
"expected_count": len(first_branch),
|
|
123
|
+
"actual_count": len(branch),
|
|
124
|
+
"warning": f"Branch {branch_idx} has {len(branch)} columns, expected {len(first_branch)}",
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
# Check column names
|
|
130
|
+
for pos, (expected, actual) in enumerate(zip(first_branch, branch, strict=False), start=1):
|
|
131
|
+
# Skip if either is * (can't validate)
|
|
132
|
+
if expected == "*" or actual == "*":
|
|
133
|
+
# Log warning for SELECT * in UNION
|
|
134
|
+
logger.warning(
|
|
135
|
+
f"UNION branch {branch_idx} uses SELECT * at position {pos} - "
|
|
136
|
+
"cannot validate column names"
|
|
137
|
+
)
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
if expected.lower() != actual.lower():
|
|
141
|
+
mismatches.append(
|
|
142
|
+
{
|
|
143
|
+
"branch": branch_idx,
|
|
144
|
+
"position": pos,
|
|
145
|
+
"expected": expected,
|
|
146
|
+
"actual": actual,
|
|
147
|
+
"warning_type": "union_column_name_mismatch",
|
|
148
|
+
"warning": f"Column name mismatch at position {pos}: '{expected}' vs '{actual}'",
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return len(mismatches) == 0, mismatches
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Underscore-prefixed aliases
|
|
156
|
+
_get_trust_level = get_trust_level
|
|
157
|
+
_validate_union_columns = validate_union_columns
|
|
158
|
+
_get_select_column_names = get_select_column_names
|