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.
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/PKG-INFO +2 -2
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/README.md +1 -1
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/metadata.py +2 -2
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/boolean_conditions.py +118 -14
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/core_extract.py +9 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/orchestrator.py +445 -11
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/predicates.py +131 -52
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/templates.py +0 -11
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/pyproject.toml +1 -1
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/PKG-INFO +2 -2
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/LICENSE +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/__init__.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/__init__.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/auth.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/client_identity.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/main.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/metrics.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/middleware.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/rate_limit_store.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/readiness.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/api/workers.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/__init__.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/cache.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/column_hint_validation.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/config.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/constants.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/exceptions.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/function_call_scanner.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/function_semantics.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/functions_db.json +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/functions_lookup.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/__init__.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/aggregations.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/evidence.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/mappings.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/relations.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/templates.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/tokenizer.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_legacy.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_llm.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/__init__.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/aggregations.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/clause_extract.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/dialect.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/intent.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/ir.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/models.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/renderer.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/resolution.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/suggestions.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_pipeline/validation.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_schema.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/p1_sql_scanner.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/p2_data_validation.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/parser.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/post_processor.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/rules.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/semantic_diff.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/sql_statements.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/transpiler.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/type_mapping.json +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/type_mapping.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/utils/__init__.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/utils/validators.py +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/setup.cfg +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/SOURCES.txt +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/dependency_links.txt +0 -0
- {sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/sql_dialect_master.egg-info/requires.txt +0 -0
- {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.
|
|
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.
|
|
41
|
+
Current release: 1.5.0
|
|
42
42
|
|
|
43
43
|
[](https://www.python.org/downloads/)
|
|
44
44
|
[](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.
|
|
5
|
+
Current release: 1.5.0
|
|
6
6
|
|
|
7
7
|
[](https://www.python.org/downloads/)
|
|
8
8
|
[](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.
|
|
21
|
-
API_VERSION = "1.
|
|
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"(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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)
|
{sql_dialect_master-1.4.0 → sql_dialect_master-1.5.0}/backend/core/nl2sql_components/core_extract.py
RENAMED
|
@@ -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
|