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.
Files changed (72) hide show
  1. ripple/__init__.py +31 -0
  2. ripple/answer.py +473 -0
  3. ripple/answer_page.py +214 -0
  4. ripple/cache.py +80 -0
  5. ripple/ci.py +422 -0
  6. ripple/ci_signature.py +374 -0
  7. ripple/cli.py +733 -0
  8. ripple/doctor.py +225 -0
  9. ripple/engine/__init__.py +111 -0
  10. ripple/engine/budget.py +86 -0
  11. ripple/engine/column_lineage.py +112 -0
  12. ripple/engine/column_ref.py +818 -0
  13. ripple/engine/cte_tracing.py +1309 -0
  14. ripple/engine/dependencies.py +466 -0
  15. ripple/engine/dialect.py +132 -0
  16. ripple/engine/dispatch.py +12 -0
  17. ripple/engine/extraction.py +27 -0
  18. ripple/engine/jinja.py +282 -0
  19. ripple/engine/json_sources.py +241 -0
  20. ripple/engine/macro_source.py +127 -0
  21. ripple/engine/pipeline.py +265 -0
  22. ripple/engine/preprocess.py +174 -0
  23. ripple/engine/safe_gen.py +21 -0
  24. ripple/engine/schema_qualification.py +151 -0
  25. ripple/engine/scope.py +488 -0
  26. ripple/engine/select_sources.py +1038 -0
  27. ripple/engine/sql_script.py +729 -0
  28. ripple/engine/statement.py +449 -0
  29. ripple/engine/tech_debt.py +169 -0
  30. ripple/engine/tsql_catalog.py +83 -0
  31. ripple/engine/tsql_scalar_vars.py +248 -0
  32. ripple/engine/tsql_tvf.py +653 -0
  33. ripple/engine/tsql_xml.py +97 -0
  34. ripple/engine/types.py +167 -0
  35. ripple/engine/unused_deps.py +555 -0
  36. ripple/engine/validation.py +158 -0
  37. ripple/graph.py +1499 -0
  38. ripple/home.py +232 -0
  39. ripple/loaders/__init__.py +7 -0
  40. ripple/loaders/dbt.py +359 -0
  41. ripple/loaders/dbt_config.py +339 -0
  42. ripple/loaders/identity.py +328 -0
  43. ripple/loaders/sidecar.py +65 -0
  44. ripple/loaders/sqldir.py +262 -0
  45. ripple/loaders/types.py +197 -0
  46. ripple/lookml.py +163 -0
  47. ripple/mcp_server.py +600 -0
  48. ripple/names.py +40 -0
  49. ripple/project.py +167 -0
  50. ripple/py.typed +0 -0
  51. ripple/render.py +426 -0
  52. ripple/render_shims.py +209 -0
  53. ripple/schemas.py +155 -0
  54. ripple/semantic.py +232 -0
  55. ripple/server.py +184 -0
  56. ripple/sourcefiles.py +64 -0
  57. ripple/star_resolution.py +100 -0
  58. ripple/static/answer.css +146 -0
  59. ripple/static/answer.html +358 -0
  60. ripple/static/answer_twin.js +299 -0
  61. ripple/static/explore.js +133 -0
  62. ripple/usage/__init__.py +18 -0
  63. ripple/usage/cli.py +78 -0
  64. ripple/usage/collect.py +315 -0
  65. ripple/usage/discover.py +190 -0
  66. ripple/usage/ingest.py +414 -0
  67. ripple/usage/report.py +131 -0
  68. ripple_sql-0.1.0.dist-info/METADATA +285 -0
  69. ripple_sql-0.1.0.dist-info/RECORD +72 -0
  70. ripple_sql-0.1.0.dist-info/WHEEL +4 -0
  71. ripple_sql-0.1.0.dist-info/entry_points.txt +3 -0
  72. 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