Skip to content

Commit 840919b

Browse files
committed
Fix Alembic BM25 expression normalization
1 parent ffc72a6 commit 840919b

2 files changed

Lines changed: 53 additions & 8 deletions

File tree

paradedb/sqlalchemy/alembic.py

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from __future__ import annotations
22

3-
import re
4-
53
from alembic.autogenerate import comparators, renderers
64
from alembic.operations import Operations
75
from alembic.operations.ops import MigrateOperation
@@ -210,12 +208,47 @@ def _normalize_bm25_expression(expr: str) -> str:
210208
normalized = "".join(expr.split())
211209
normalized = normalized.replace('"', "")
212210
normalized = normalized.replace("::text", "")
213-
# Ignore schema/table qualification differences, but keep tokenizer namespaces like `pdb.simple`.
214-
previous = None
215-
while previous != normalized:
216-
previous = normalized
217-
normalized = re.sub(r"(?<![A-Za-z0-9_])(?!pdb\b)[A-Za-z_][A-Za-z0-9_]*\.", "", normalized)
218-
return normalized
211+
return _strip_non_pdb_qualifiers(normalized)
212+
213+
214+
def _strip_non_pdb_qualifiers(expr: str) -> str:
215+
"""Strip relation qualifiers outside SQL string literals.
216+
217+
Preserves tokenizer namespaces like ``pdb.simple`` and leaves quoted literal
218+
content untouched (for example regex patterns like ``'run.*'``).
219+
"""
220+
out: list[str] = []
221+
i = 0
222+
in_single = False
223+
while i < len(expr):
224+
ch = expr[i]
225+
226+
if ch == "'":
227+
out.append(ch)
228+
# Escaped quote inside a string literal: ''.
229+
if in_single and i + 1 < len(expr) and expr[i + 1] == "'":
230+
out.append("'")
231+
i += 2
232+
continue
233+
in_single = not in_single
234+
i += 1
235+
continue
236+
237+
if not in_single and (ch.isalpha() or ch == "_"):
238+
j = i + 1
239+
while j < len(expr) and (expr[j].isalnum() or expr[j] == "_"):
240+
j += 1
241+
242+
if j < len(expr) and expr[j] == ".":
243+
token = expr[i:j]
244+
if token.lower() != "pdb":
245+
i = j + 1
246+
continue
247+
248+
out.append(ch)
249+
i += 1
250+
251+
return "".join(out)
219252

220253

221254
def _normalized_expression_list(expressions: list[str]) -> list[str]:

tests/unit/test_alembic_unit.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,15 @@ def test_suppress_standard_bm25_ops_noop_when_no_bm25():
265265

266266
pdb_alembic._suppress_standard_bm25_ops(upgrade_ops, set())
267267
assert len(upgrade_ops.ops) == 1
268+
269+
270+
def test_normalize_bm25_expression_keeps_dotted_literal_content():
271+
expr = "(description)::pdb.regex_pattern('run.*')"
272+
normalized = pdb_alembic._normalize_bm25_expression(expr)
273+
assert normalized == "(description)::pdb.regex_pattern('run.*')"
274+
275+
276+
def test_normalize_bm25_expression_strips_relation_qualifiers_only():
277+
expr = '"public"."products"."description"::pdb.simple(\'alias=description_simple\')'
278+
normalized = pdb_alembic._normalize_bm25_expression(expr)
279+
assert normalized == "description::pdb.simple('alias=description_simple')"

0 commit comments

Comments
 (0)