sql-dialect-master 1.1.1__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 (86) hide show
  1. backend/__init__.py +1 -0
  2. backend/api/__init__.py +1 -0
  3. backend/api/main.py +732 -0
  4. backend/api/middleware.py +274 -0
  5. backend/api/rate_limit_store.py +105 -0
  6. backend/api/readiness.py +178 -0
  7. backend/core/__init__.py +57 -0
  8. backend/core/cache.py +261 -0
  9. backend/core/column_hint_validation.py +22 -0
  10. backend/core/config.py +546 -0
  11. backend/core/constants.py +84 -0
  12. backend/core/exceptions.py +124 -0
  13. backend/core/function_call_scanner.py +64 -0
  14. backend/core/function_semantics.py +148 -0
  15. backend/core/functions_db.json +601 -0
  16. backend/core/functions_lookup.py +332 -0
  17. backend/core/metadata.py +71 -0
  18. backend/core/nl2sql.py +85 -0
  19. backend/core/nl2sql_components/__init__.py +72 -0
  20. backend/core/nl2sql_components/aggregations.py +35 -0
  21. backend/core/nl2sql_components/boolean_conditions.py +279 -0
  22. backend/core/nl2sql_components/core_extract.py +106 -0
  23. backend/core/nl2sql_components/evidence.py +122 -0
  24. backend/core/nl2sql_components/mappings.py +185 -0
  25. backend/core/nl2sql_components/relations.py +180 -0
  26. backend/core/nl2sql_components/templates.py +130 -0
  27. backend/core/nl2sql_components/tokenizer.py +187 -0
  28. backend/core/nl2sql_legacy.py +22 -0
  29. backend/core/nl2sql_pipeline/__init__.py +13 -0
  30. backend/core/nl2sql_pipeline/aggregations.py +26 -0
  31. backend/core/nl2sql_pipeline/clause_extract.py +97 -0
  32. backend/core/nl2sql_pipeline/dialect.py +139 -0
  33. backend/core/nl2sql_pipeline/intent.py +85 -0
  34. backend/core/nl2sql_pipeline/ir.py +70 -0
  35. backend/core/nl2sql_pipeline/models.py +30 -0
  36. backend/core/nl2sql_pipeline/orchestrator.py +576 -0
  37. backend/core/nl2sql_pipeline/predicates.py +182 -0
  38. backend/core/nl2sql_pipeline/renderer.py +248 -0
  39. backend/core/nl2sql_pipeline/resolution.py +69 -0
  40. backend/core/nl2sql_pipeline/suggestions.py +30 -0
  41. backend/core/nl2sql_pipeline/templates.py +371 -0
  42. backend/core/nl2sql_pipeline/validation.py +28 -0
  43. backend/core/p1_sql_scanner.py +196 -0
  44. backend/core/p2_data_validation.py +72 -0
  45. backend/core/parser.py +280 -0
  46. backend/core/post_processor.py +129 -0
  47. backend/core/rules.py +808 -0
  48. backend/core/semantic_diff.py +385 -0
  49. backend/core/transpiler.py +791 -0
  50. backend/core/type_mapping.json +593 -0
  51. backend/core/type_mapping.py +765 -0
  52. backend/utils/__init__.py +2 -0
  53. backend/utils/validators.py +407 -0
  54. frontend/__init__.py +10 -0
  55. frontend/app_context_v2.py +201 -0
  56. frontend/core/__init__.py +33 -0
  57. frontend/core/design_tokens.py +127 -0
  58. frontend/core/escaping.py +13 -0
  59. frontend/core/navigation.py +135 -0
  60. frontend/core/state.py +612 -0
  61. frontend/core/styles.py +523 -0
  62. frontend/core/themes.py +69 -0
  63. frontend/core/viewmodels.py +499 -0
  64. frontend/pages/__init__.py +0 -0
  65. frontend/pages/convert.py +283 -0
  66. frontend/pages/diff.py +157 -0
  67. frontend/pages/functions.py +100 -0
  68. frontend/pages/history.py +144 -0
  69. frontend/pages/lineage.py +168 -0
  70. frontend/pages/nl2sql.py +305 -0
  71. frontend/pages/query_analysis.py +120 -0
  72. frontend/pages/runtime.py +66 -0
  73. frontend/pages/settings.py +60 -0
  74. frontend/pages/templates.py +67 -0
  75. frontend/pages/types.py +110 -0
  76. frontend/templates_v2.py +53 -0
  77. frontend/ui/__init__.py +0 -0
  78. frontend/ui/command_palette.py +153 -0
  79. frontend/ui/diff.py +275 -0
  80. frontend/ui/editor.py +102 -0
  81. frontend/ui/status.py +255 -0
  82. sql_dialect_master-1.1.1.dist-info/METADATA +360 -0
  83. sql_dialect_master-1.1.1.dist-info/RECORD +86 -0
  84. sql_dialect_master-1.1.1.dist-info/WHEEL +5 -0
  85. sql_dialect_master-1.1.1.dist-info/licenses/LICENSE +21 -0
  86. sql_dialect_master-1.1.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,124 @@
1
+ """Custom exceptions for SQL Dialect Master.
2
+
3
+ Provides a hierarchy of exceptions for better error handling and debugging.
4
+ """
5
+ from enum import Enum
6
+
7
+
8
+ class ErrorCode(str, Enum):
9
+ """Stable machine-readable error categories."""
10
+ UNSUPPORTED_DIALECT = "UNSUPPORTED_DIALECT"
11
+ TRANSPILE_FAILED = "TRANSPILE_FAILED"
12
+ PARSE_FAILED = "PARSE_FAILED"
13
+ SECURITY_VIOLATION = "SECURITY_VIOLATION"
14
+ VALIDATION_FAILED = "VALIDATION_FAILED"
15
+ BAD_REQUEST = "BAD_REQUEST"
16
+ NOT_FOUND = "NOT_FOUND"
17
+ METHOD_NOT_ALLOWED = "METHOD_NOT_ALLOWED"
18
+ PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE"
19
+ RATE_LIMITED = "RATE_LIMITED"
20
+ CACHE_FAILED = "CACHE_FAILED"
21
+ CONFIGURATION_INVALID = "CONFIGURATION_INVALID"
22
+ RULE_CONFLICT = "RULE_CONFLICT"
23
+ INTERNAL_ERROR = "INTERNAL_ERROR"
24
+ UNKNOWN = "UNKNOWN"
25
+
26
+
27
+ class SDMException(Exception):
28
+ """Base exception for SQL Dialect Master."""
29
+
30
+ error_code = ErrorCode.UNKNOWN
31
+
32
+ def __init__(self, message: str, details: dict = None):
33
+ super().__init__(message)
34
+ self.message = message
35
+ self.details = details or {}
36
+
37
+ def to_dict(self) -> dict:
38
+ """Convert exception to a backward-compatible API error dictionary."""
39
+ code = self.error_code.value
40
+ return {
41
+ "error_type": self.__class__.__name__,
42
+ "error_code": code,
43
+ "code": code,
44
+ "message": self.message,
45
+ "details": self.details
46
+ }
47
+
48
+
49
+ class UnsupportedDialectError(SDMException):
50
+ """Raised when an unsupported SQL dialect is specified."""
51
+
52
+ error_code = ErrorCode.UNSUPPORTED_DIALECT
53
+
54
+ def __init__(self, dialect: str, supported: list = None):
55
+ message = f"Unsupported dialect: {dialect}"
56
+ if supported:
57
+ message += f". Supported: {', '.join(supported)}"
58
+ super().__init__(message, {"dialect": dialect, "supported": supported})
59
+
60
+
61
+ class TranspileError(SDMException):
62
+ """Raised when SQL transpilation fails."""
63
+
64
+ error_code = ErrorCode.TRANSPILE_FAILED
65
+
66
+ def __init__(self, message: str, source_sql: str = None, source_dialect: str = None, target_dialect: str = None):
67
+ details = {
68
+ "source_sql": source_sql[:200] if source_sql else None,
69
+ "source_dialect": source_dialect,
70
+ "target_dialect": target_dialect
71
+ }
72
+ super().__init__(message, details)
73
+
74
+
75
+ class ParseError(SDMException):
76
+ """Raised when SQL parsing fails."""
77
+
78
+ error_code = ErrorCode.PARSE_FAILED
79
+
80
+ def __init__(self, message: str, sql: str = None, dialect: str = None):
81
+ details = {"sql": sql[:200] if sql else None, "dialect": dialect}
82
+ super().__init__(message, details)
83
+
84
+
85
+ class SecurityViolationError(SDMException):
86
+ """Raised when SQL contains potentially dangerous patterns."""
87
+
88
+ error_code = ErrorCode.SECURITY_VIOLATION
89
+
90
+ def __init__(self, message: str, pattern: str = None):
91
+ details = {"pattern": pattern}
92
+ super().__init__(message, details)
93
+
94
+
95
+ class ValidationError(SDMException):
96
+ """Raised when input validation fails."""
97
+
98
+ error_code = ErrorCode.VALIDATION_FAILED
99
+
100
+ def __init__(self, message: str, field: str = None, value: str = None):
101
+ details = {"field": field, "value": value}
102
+ super().__init__(message, details)
103
+
104
+
105
+ class CacheError(SDMException):
106
+ """Raised when cache operations fail."""
107
+
108
+ error_code = ErrorCode.CACHE_FAILED
109
+
110
+
111
+ class ConfigurationError(SDMException):
112
+ """Raised when configuration is invalid."""
113
+
114
+ error_code = ErrorCode.CONFIGURATION_INVALID
115
+
116
+
117
+ class RuleConflictError(SDMException):
118
+ """Raised when transformation rules conflict."""
119
+
120
+ error_code = ErrorCode.RULE_CONFLICT
121
+
122
+ def __init__(self, message: str, rule1: str = None, rule2: str = None):
123
+ details = {"rule1": rule1, "rule2": rule2}
124
+ super().__init__(message, details)
@@ -0,0 +1,64 @@
1
+ """Scanner-backed function-call replacement shared by custom SQL rewrites."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Callable
5
+
6
+ from .p1_sql_scanner import mask_non_executable
7
+
8
+
9
+ def replace_function_calls(
10
+ sql: str,
11
+ function_name: str,
12
+ replacer: Callable[[str, str], str],
13
+ ) -> str:
14
+ """Replace complete calls without interpreting literals or comments as SQL."""
15
+ upper_name = function_name.upper()
16
+ name_len = len(function_name)
17
+ masked = mask_non_executable(sql)
18
+ result = []
19
+ i = 0
20
+ length = len(sql)
21
+
22
+ while i < length:
23
+ if masked[i:i + name_len].upper() != upper_name:
24
+ result.append(sql[i])
25
+ i += 1
26
+ continue
27
+
28
+ name_end = i + name_len
29
+ if i > 0 and (masked[i - 1].isalnum() or masked[i - 1] == "_"):
30
+ result.append(sql[i])
31
+ i += 1
32
+ continue
33
+
34
+ j = name_end
35
+ while j < length and masked[j].isspace():
36
+ j += 1
37
+ if j >= length or masked[j] != "(":
38
+ result.append(sql[i])
39
+ i += 1
40
+ continue
41
+
42
+ depth = 0
43
+ k = j
44
+ while k < length:
45
+ char = masked[k]
46
+ if char == "(":
47
+ depth += 1
48
+ elif char == ")":
49
+ depth -= 1
50
+ if depth == 0:
51
+ original_call = sql[i:k + 1]
52
+ args = sql[j + 1:k]
53
+ result.append(replacer(args, original_call))
54
+ i = k + 1
55
+ break
56
+ k += 1
57
+ else:
58
+ result.append(sql[i:])
59
+ break
60
+
61
+ return "".join(result)
62
+
63
+
64
+ __all__ = ["replace_function_calls"]
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python3
2
+ """Semantics-aware function equivalence metadata for SQL dialect conversion.
3
+
4
+ The existing function catalog answers "is there syntax for this function?".
5
+ This module answers the stronger question "can this function be treated as
6
+ semantically equivalent for this source/target dialect pair?".
7
+
8
+ Only explicitly-reviewed functions and dialect pairs are classified as
9
+ semantic equivalence. Unreviewed pairs remain ``unknown`` instead of being
10
+ inferred from syntax availability or fuzzy matching.
11
+ """
12
+ from typing import Any, Dict, Tuple
13
+
14
+
15
+ FunctionKey = Tuple[str, str, str]
16
+
17
+
18
+ _SEMANTIC_STATUS = {
19
+ "equivalent": "equivalent",
20
+ "supported-but-different": "supported-but-different",
21
+ "unsupported": "unsupported",
22
+ "unknown": "unknown",
23
+ }
24
+
25
+
26
+ # Explicit metadata for the initial high-risk cross-dialect corpus.
27
+ # Keep this registry intentionally small: additions require semantic review and
28
+ # a regression test rather than being inferred from functions_db.json syntax.
29
+ _FUNCTION_SEMANTICS: Dict[str, Dict[str, Any]] = {
30
+ "DATEDIFF": {
31
+ "argument_semantics": "calendar-day difference; argument order and/or unit syntax is dialect-sensitive",
32
+ "return_shape": "scalar integer-like value",
33
+ "null_behavior": "NULL-sensitive; follows source/target function semantics",
34
+ "notes": "Dialect implementations differ in argument order and unit handling.",
35
+ "pairs": {
36
+ ("mysql", "tsql"): "supported-but-different",
37
+ ("mysql", "postgres"): "supported-but-different",
38
+ },
39
+ },
40
+ "DATE_FORMAT": {
41
+ "argument_semantics": "format a date/time value using dialect-specific format tokens",
42
+ "return_shape": "string",
43
+ "null_behavior": "NULL input produces NULL",
44
+ "notes": "Formatting token languages differ across dialects.",
45
+ "pairs": {
46
+ ("mysql", "postgres"): "supported-but-different",
47
+ },
48
+ },
49
+ "LENGTH": {
50
+ "argument_semantics": "string length; byte-vs-character semantics may differ",
51
+ "return_shape": "scalar integer-like value",
52
+ "null_behavior": "NULL input produces NULL",
53
+ "notes": "MySQL LENGTH is byte-oriented while Oracle LENGTH is character-oriented.",
54
+ "pairs": {
55
+ ("mysql", "oracle"): "supported-but-different",
56
+ },
57
+ },
58
+ "CONCAT": {
59
+ "argument_semantics": "variadic string concatenation",
60
+ "return_shape": "string",
61
+ "null_behavior": "NULL propagation semantics differ by dialect",
62
+ "notes": "MySQL CONCAT propagates NULL arguments; PostgreSQL CONCAT ignores NULL arguments.",
63
+ "pairs": {
64
+ ("mysql", "postgres"): "supported-but-different",
65
+ },
66
+ },
67
+ "GET_JSON_OBJECT": {
68
+ "argument_semantics": "extract a JSON path value with dialect-specific JSON path/operator semantics",
69
+ "return_shape": "scalar text-like JSON value",
70
+ "null_behavior": "path/input failures are dialect-sensitive",
71
+ "notes": "Hive GET_JSON_OBJECT is not a semantic synonym for PostgreSQL JSON operators.",
72
+ "pairs": {
73
+ ("hive", "postgres"): "supported-but-different",
74
+ },
75
+ },
76
+ "COLLECT_LIST": {
77
+ "argument_semantics": "aggregate input values into a collection; ordering is not implied unless explicitly specified",
78
+ "return_shape": "array/list-like aggregate",
79
+ "null_behavior": "NULL inclusion/filtering is dialect-sensitive",
80
+ "notes": "Spark COLLECT_LIST and PostgreSQL ARRAY_AGG differ in output type and ordering guarantees.",
81
+ "pairs": {
82
+ ("spark", "postgres"): "supported-but-different",
83
+ },
84
+ },
85
+ "ROW_NUMBER": {
86
+ "argument_semantics": "assign sequential row numbers within an optional window specification",
87
+ "return_shape": "integer-like value",
88
+ "null_behavior": "window ordering determines numbering; NULL ordering remains dialect-sensitive",
89
+ "notes": "Core window semantics are equivalent for the reviewed pair when the same ORDER BY semantics are preserved.",
90
+ "pairs": {
91
+ ("postgres", "mysql"): "equivalent",
92
+ },
93
+ },
94
+ "REGEXP_REPLACE": {
95
+ "argument_semantics": "replace regex matches using dialect-specific regex engines and replacement rules",
96
+ "return_shape": "string",
97
+ "null_behavior": "NULL input produces NULL",
98
+ "notes": "T-SQL has no native REGEXP_REPLACE equivalent in the reviewed baseline.",
99
+ "pairs": {},
100
+ },
101
+ }
102
+
103
+
104
+ class FunctionSemanticRegistry:
105
+ """Resolve explicit semantic equivalence for reviewed SQL functions."""
106
+
107
+ def __init__(self, semantics: Dict[str, Dict[str, Any]] = None) -> None:
108
+ self._semantics = semantics if semantics is not None else _FUNCTION_SEMANTICS
109
+
110
+ def resolve(self, function: str, source: str, target: str) -> Dict[str, Any]:
111
+ """Return an explicit semantic classification for a function pair."""
112
+ function_name = function.upper().strip()
113
+ source_dialect = source.lower().strip()
114
+ target_dialect = target.lower().strip()
115
+ metadata = self._semantics.get(function_name)
116
+
117
+ if metadata is None:
118
+ return {
119
+ "function": function_name,
120
+ "source_dialect": source_dialect,
121
+ "target_dialect": target_dialect,
122
+ "status": _SEMANTIC_STATUS["unknown"],
123
+ "equivalent": False,
124
+ "reason": "Function is not present in the reviewed semantic registry.",
125
+ }
126
+
127
+ pair_status = metadata.get("pairs", {}).get((source_dialect, target_dialect))
128
+ if pair_status is None:
129
+ if function_name == "REGEXP_REPLACE" and target_dialect == "tsql":
130
+ pair_status = _SEMANTIC_STATUS["unsupported"]
131
+ else:
132
+ pair_status = _SEMANTIC_STATUS["unknown"]
133
+
134
+ return {
135
+ "function": function_name,
136
+ "source_dialect": source_dialect,
137
+ "target_dialect": target_dialect,
138
+ "status": pair_status,
139
+ "equivalent": pair_status == _SEMANTIC_STATUS["equivalent"],
140
+ "argument_semantics": metadata.get("argument_semantics", ""),
141
+ "return_shape": metadata.get("return_shape", ""),
142
+ "null_behavior": metadata.get("null_behavior", ""),
143
+ "notes": metadata.get("notes", ""),
144
+ }
145
+
146
+ def get_metadata(self, function: str) -> Dict[str, Any]:
147
+ """Return reviewed metadata without inferring semantic equivalence."""
148
+ return self._semantics.get(function.upper().strip(), {})