sql-dialect-master 1.4.0__tar.gz → 1.5.0__tar.gz

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 (70) hide show
  1. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/PKG-INFO +2 -2
  2. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/README.md +1 -1
  3. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/metadata.py +2 -2
  4. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/boolean_conditions.py +118 -14
  5. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/core_extract.py +9 -0
  6. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/orchestrator.py +445 -11
  7. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/predicates.py +131 -52
  8. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/templates.py +0 -11
  9. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/pyproject.toml +1 -1
  10. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/PKG-INFO +2 -2
  11. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/LICENSE +0 -0
  12. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/__init__.py +0 -0
  13. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/__init__.py +0 -0
  14. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/auth.py +0 -0
  15. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/client_identity.py +0 -0
  16. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/main.py +0 -0
  17. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/metrics.py +0 -0
  18. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/middleware.py +0 -0
  19. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/rate_limit_store.py +0 -0
  20. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/readiness.py +0 -0
  21. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/workers.py +0 -0
  22. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/__init__.py +0 -0
  23. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/cache.py +0 -0
  24. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/column_hint_validation.py +0 -0
  25. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/config.py +0 -0
  26. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/constants.py +0 -0
  27. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/exceptions.py +0 -0
  28. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/function_call_scanner.py +0 -0
  29. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/function_semantics.py +0 -0
  30. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/functions_db.json +0 -0
  31. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/functions_lookup.py +0 -0
  32. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql.py +0 -0
  33. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/__init__.py +0 -0
  34. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/aggregations.py +0 -0
  35. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/evidence.py +0 -0
  36. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/mappings.py +0 -0
  37. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/relations.py +0 -0
  38. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/templates.py +0 -0
  39. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/tokenizer.py +0 -0
  40. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_legacy.py +0 -0
  41. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_llm.py +0 -0
  42. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/__init__.py +0 -0
  43. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/aggregations.py +0 -0
  44. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/clause_extract.py +0 -0
  45. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/dialect.py +0 -0
  46. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/intent.py +0 -0
  47. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/ir.py +0 -0
  48. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/models.py +0 -0
  49. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/renderer.py +0 -0
  50. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/resolution.py +0 -0
  51. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/suggestions.py +0 -0
  52. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/validation.py +0 -0
  53. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_schema.py +0 -0
  54. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/p1_sql_scanner.py +0 -0
  55. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/p2_data_validation.py +0 -0
  56. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/parser.py +0 -0
  57. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/post_processor.py +0 -0
  58. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/rules.py +0 -0
  59. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/semantic_diff.py +0 -0
  60. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/sql_statements.py +0 -0
  61. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/transpiler.py +0 -0
  62. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/type_mapping.json +0 -0
  63. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/type_mapping.py +0 -0
  64. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/utils/__init__.py +0 -0
  65. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/utils/validators.py +0 -0
  66. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/setup.cfg +0 -0
  67. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/SOURCES.txt +0 -0
  68. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/dependency_links.txt +0 -0
  69. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/requires.txt +0 -0
  70. {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql-dialect-master
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: SQL Dialect Master - Multi-database SQL conversion engine
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -38,7 +38,7 @@ Dynamic: license-file
38
38
 
39
39
  Enterprise-grade multi-database SQL conversion engine supporting 12 database dialects.
40
40
 
41
- Current release: 1.4.0
41
+ Current release: 1.5.0
42
42
 
43
43
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
44
44
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -2,7 +2,7 @@
2
2
 
3
3
  Enterprise-grade multi-database SQL conversion engine supporting 12 database dialects.
4
4
 
5
- Current release: 1.4.0
5
+ Current release: 1.5.0
6
6
 
7
7
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -17,8 +17,8 @@ Design rules:
17
17
  from __future__ import annotations
18
18
 
19
19
  # ── Release version facts (sole source of truth) ────────────────────────
20
- PROJECT_VERSION = "1.4.0"
21
- API_VERSION = "1.4.0"
20
+ PROJECT_VERSION = "1.5.0"
21
+ API_VERSION = "1.5.0"
22
22
  # Frontend v2 architecture — independent of the product release version.
23
23
  FRONTEND_ARCHITECTURE_VERSION = 2
24
24
 
@@ -7,39 +7,41 @@ user-supplied parentheses.
7
7
  import re
8
8
  from typing import List, Optional, Tuple
9
9
 
10
+ from ..p1_sql_scanner import mask_non_executable
11
+
10
12
 
11
13
  _COMPARISON_PATTERNS = (
12
14
  re.compile(
13
15
  r"(?<![A-Za-z0-9_])(price|quantity|amount|age|score|rating|views|clicks)\s+"
14
16
  r"(greater(?:\s+than)?|more\s+than|above|over|less(?:\s+than)?|"
15
17
  r"below|under|equal(?:\s+to)?|equals)\s+"
16
- r"([0-9]+(?:\.[0-9]+)?)",
18
+ r"(-?[0-9]+(?:\.[0-9]+)?)",
17
19
  re.IGNORECASE,
18
20
  ),
19
21
  re.compile(
20
22
  r"(?<![A-Za-z0-9_])(price|quantity|amount|age|score|rating|views|clicks)\s*"
21
- r"(>=|<=|!=|=|>|<)\s*([0-9]+(?:\.[0-9]+)?)",
23
+ r"(>=|<=|!=|<>|=|>|<)\s*(-?[0-9]+(?:\.[0-9]+)?)",
22
24
  re.IGNORECASE,
23
25
  ),
24
26
  re.compile(
25
27
  r"(价格|数量|金额|年龄|得分|评分|浏览量|点击量)\s*"
26
28
  r"(大于等于|小于等于|不等于|大于|小于|超过|低于|等于)\s*"
27
- r"([0-9]+(?:\.[0-9]+)?)"
29
+ r"(-?[0-9]+(?:\.[0-9]+)?)"
28
30
  ),
29
31
  )
30
32
 
31
33
  _RANGE_PATTERN = re.compile(
32
34
  r"(?<![A-Za-z0-9_])(price|quantity|amount|age|score|rating|views|clicks)\s+"
33
- r"(?:between\s+([0-9]+(?:\.[0-9]+)?)\s+and\s+([0-9]+(?:\.[0-9]+)?)|"
34
- r"in\s+(?:the\s+)?range\s+([0-9]+(?:\.[0-9]+)?)\s*(?:to|-)\s*"
35
- r"([0-9]+(?:\.[0-9]+)?))",
35
+ r"(?:between\s+(-?[0-9]+(?:\.[0-9]+)?)\s+and\s+(-?[0-9]+(?:\.[0-9]+)?)|"
36
+ r"in\s+(?:the\s+)?range\s+(-?[0-9]+(?:\.[0-9]+)?)\s*(?:to|-)\s*"
37
+ r"(-?[0-9]+(?:\.[0-9]+)?))",
36
38
  re.IGNORECASE,
37
39
  )
38
40
 
39
41
  _CN_RANGE_PATTERN = re.compile(
40
42
  r"(价格|数量|金额|年龄|得分|评分|浏览量|点击量)\s*"
41
- r"(?:在|介于)\s*([0-9]+(?:\.[0-9]+)?)\s*(?:和|到|至|-)\s*"
42
- r"([0-9]+(?:\.[0-9]+)?)\s*(?:之间|范围内|以内)?"
43
+ r"(?:在|介于)\s*(-?[0-9]+(?:\.[0-9]+)?)\s*(?:和|到|至|-)\s*"
44
+ r"(-?[0-9]+(?:\.[0-9]+)?)\s*(?:之间|范围内|以内)?"
43
45
  )
44
46
 
45
47
  _TEXT_PATTERN = re.compile(
@@ -101,6 +103,29 @@ _STATUS_CONDITIONS = {
101
103
  "未支付": "status = 'unpaid'",
102
104
  }
103
105
 
106
+ _STATUS_VALUES = {
107
+ "active": "active",
108
+ "inactive": "inactive",
109
+ "enabled": "active",
110
+ "disabled": "inactive",
111
+ "valid": "active",
112
+ "invalid": "inactive",
113
+ "completed": "completed",
114
+ "done": "completed",
115
+ "pending": "pending",
116
+ "paid": "paid",
117
+ "unpaid": "unpaid",
118
+ "有效": "active",
119
+ "无效": "inactive",
120
+ "已完成": "completed",
121
+ "未完成": "pending",
122
+ "已支付": "paid",
123
+ "未支付": "unpaid",
124
+ }
125
+ _STATUS_VALUE_PATTERN = "(?:" + "|".join(
126
+ sorted((re.escape(value) for value in _STATUS_VALUES), key=len, reverse=True)
127
+ ) + ")"
128
+
104
129
  _CN_COLUMNS = {
105
130
  "价格": "price",
106
131
  "数量": "quantity",
@@ -168,15 +193,16 @@ def _format_set_values(raw_values: str) -> str:
168
193
 
169
194
  def _comparison_matches(text: str) -> List[Tuple[int, int, str]]:
170
195
  matches: List[Tuple[int, int, str]] = []
196
+ executable = mask_non_executable(text)
171
197
 
172
198
  for pattern in _COMPARISON_PATTERNS:
173
- for match in pattern.finditer(text):
199
+ for match in pattern.finditer(executable):
174
200
  first, raw_operator, value = match.groups()
175
201
  column = _CN_COLUMNS.get(first, first)
176
202
  matches.append((match.start(), match.end(), f"{column} {_normalize_operator(raw_operator)} {value}"))
177
203
 
178
204
  for pattern in (_RANGE_PATTERN, _CN_RANGE_PATTERN):
179
- for match in pattern.finditer(text):
205
+ for match in pattern.finditer(executable):
180
206
  groups = match.groups()
181
207
  if len(groups) == 5:
182
208
  first, low1, high1, low2, high2 = groups
@@ -193,7 +219,7 @@ def _comparison_matches(text: str) -> List[Tuple[int, int, str]]:
193
219
  matches.append((match.start(), match.end(), _text_sql(column, value)))
194
220
 
195
221
  for pattern in (_NULL_PATTERN, _CN_NULL_PATTERN):
196
- for match in pattern.finditer(text):
222
+ for match in pattern.finditer(executable):
197
223
  first, raw_predicate = match.groups()
198
224
  column = _CN_COLUMNS.get(first, first)
199
225
  normalized = raw_predicate.replace(" ", "").lower()
@@ -229,9 +255,7 @@ def _comparison_matches(text: str) -> List[Tuple[int, int, str]]:
229
255
  operator = "NOT IN" if operator_text == "不在" else "IN"
230
256
  matches.append((match.start(), value_end, f"{_CN_SET_COLUMNS[column]} {operator} ({values})"))
231
257
 
232
- for match in _STATUS_PATTERN.finditer(text):
233
- status = match.group(1).lower()
234
- matches.append((match.start(), match.end(), _STATUS_CONDITIONS[status]))
258
+ matches.extend(_status_matches(text))
235
259
 
236
260
  matches.sort(key=lambda item: (item[0], item[1]))
237
261
  deduped: List[Tuple[int, int, str]] = []
@@ -242,6 +266,86 @@ def _comparison_matches(text: str) -> List[Tuple[int, int, str]]:
242
266
  return deduped
243
267
 
244
268
 
269
+ def _status_matches(text: str) -> List[Tuple[int, int, str]]:
270
+ """Extract status predicates without matching quoted values."""
271
+ executable = mask_non_executable(text)
272
+ matches: list[Tuple[int, int, str]] = []
273
+ explicit_patterns = [
274
+ (
275
+ re.compile(
276
+ rf"\bstatus\s*(?:is\s+not|isn't|not|!=|<>)\s*({_STATUS_VALUE_PATTERN})\b",
277
+ re.IGNORECASE,
278
+ ),
279
+ "!=",
280
+ ),
281
+ (
282
+ re.compile(rf"\bstatus\s*(?:is|=)\s*({_STATUS_VALUE_PATTERN})\b", re.IGNORECASE),
283
+ "=",
284
+ ),
285
+ (
286
+ re.compile(rf"\bstatus\s+({_STATUS_VALUE_PATTERN})\b", re.IGNORECASE),
287
+ "=",
288
+ ),
289
+ (
290
+ re.compile(rf"状态\s*(?:不是|不为|!=|<>)\s*({_STATUS_VALUE_PATTERN})"),
291
+ "!=",
292
+ ),
293
+ (
294
+ re.compile(rf"状态\s*(?:为|是|=)\s*({_STATUS_VALUE_PATTERN})"),
295
+ "=",
296
+ ),
297
+ ]
298
+ for pattern, operator in explicit_patterns:
299
+ for match in pattern.finditer(executable):
300
+ raw_value = match.group(1)
301
+ canonical = _STATUS_VALUES[raw_value.casefold() if raw_value.isascii() else raw_value]
302
+ matches.append((match.start(), match.end(), f"status {operator} '{canonical}'"))
303
+
304
+ occupied = [(start, end) for start, end, _ in matches]
305
+ text_spans = [
306
+ (match.start(), match.end())
307
+ for pattern in (_TEXT_PATTERN, _CN_TEXT_PATTERN)
308
+ for match in pattern.finditer(executable)
309
+ ]
310
+ for raw_value, canonical in sorted(_STATUS_VALUES.items(), key=lambda item: -len(item[0])):
311
+ if raw_value.isascii():
312
+ pattern = re.compile(rf"(?<![\w$]){re.escape(raw_value)}(?![\w$])", re.IGNORECASE)
313
+ else:
314
+ pattern = re.compile(re.escape(raw_value))
315
+ for match in pattern.finditer(executable):
316
+ if any(start <= match.start() < end or match.start() <= start < match.end() for start, end in occupied):
317
+ continue
318
+ if any(start <= match.start() < end for start, end in text_spans):
319
+ continue
320
+ prefix = executable[:match.start()]
321
+ negative = bool(
322
+ re.search(r"(?:\b(?:not|except|excluding)\s*)$", prefix, re.IGNORECASE)
323
+ or re.search(r"(?:不是|不为|非)\s*$", prefix)
324
+ )
325
+ operator = "!=" if negative else "="
326
+ matches.append((match.start(), match.end(), f"status {operator} '{canonical}'"))
327
+ occupied.append((match.start(), match.end()))
328
+
329
+ deleted_patterns = [
330
+ (
331
+ re.compile(r"(?:未删除|没有删除|不是删除|非删除|\bnot\s+deleted\b)", re.IGNORECASE),
332
+ "is_deleted = 0",
333
+ ),
334
+ (re.compile(r"(?:已删除|\bdeleted\b|\bremoved\b)", re.IGNORECASE), "is_deleted = 1"),
335
+ ]
336
+ for pattern, condition in deleted_patterns:
337
+ for match in pattern.finditer(executable):
338
+ if any(start <= match.start() < end or match.start() <= start < match.end() for start, end, _ in matches):
339
+ continue
340
+ matches.append((match.start(), match.end(), condition))
341
+ return matches
342
+
343
+
344
+ def extract_status_conditions(text: str) -> list[str]:
345
+ """Return standalone status predicates for the single-condition path."""
346
+ return [condition for _, _, condition in _status_matches(text)]
347
+
348
+
245
349
  def extract_boolean_conditions(text: str) -> Optional[List[str]]:
246
350
  """Return one boolean SQL expression preserving connector precedence/grouping."""
247
351
  matches = _comparison_matches(text)
@@ -38,6 +38,15 @@ def detect_operation(text: str, keywords: Dict[str, str]) -> str:
38
38
  Defaults to SELECT.
39
39
  """
40
40
  intent_text = mask_non_executable(text)
41
+ # Deletion-state phrases describe a SELECT predicate. Mask only the
42
+ # phrase so an independent leading "删除" in e.g. "删除已删除用户"
43
+ # remains a genuine DELETE intent.
44
+ intent_text = re.sub(
45
+ r"(?:已删除|未删除|没有删除|不是删除|非删除|already\s+deleted|not\s+deleted)",
46
+ lambda match: " " * len(match.group(0)),
47
+ intent_text,
48
+ flags=re.IGNORECASE,
49
+ )
41
50
  for keyword, op in keywords.items():
42
51
  if _pattern_match(intent_text, keyword) and op in ["SELECT", "INSERT", "UPDATE", "DELETE"]:
43
52
  return op