Skip to content

Commit bf12aba

Browse files
fix: Handle nested parentheses in index parsing for JSON path indexes
The compile_index() function used a regex that couldn't handle nested parentheses in JSON path expressions like: (json_value(`col`, '$.path' returning char(20))) This caused describe() output to fail round-trip parsing when the table had JSON indexes. Replace the regex with a proper parser that tracks parenthesis depth and only splits on commas at the top level. Fixes the "index reconstruction" issue in test_json.py::test_describe. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 58534fb commit bf12aba

File tree

1 file changed

+40
-1
lines changed

1 file changed

+40
-1
lines changed

src/datajoint/declare.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,45 @@ def alter(definition: str, old_definition: str, context: dict) -> tuple[list[str
581581
return sql, [e for e in external_stores if e not in external_stores_]
582582

583583

584+
def _parse_index_args(args: str) -> list[str]:
585+
"""
586+
Parse comma-separated index arguments, handling nested parentheses.
587+
588+
Parameters
589+
----------
590+
args : str
591+
The arguments string from an index declaration (e.g., ``"a, b, (func(x, y))"``)
592+
593+
Returns
594+
-------
595+
list[str]
596+
List of individual arguments with surrounding whitespace stripped.
597+
598+
Notes
599+
-----
600+
This parser correctly handles nested parentheses in expressions like
601+
``(json_value(`col`, '$.path' returning char(20)))``.
602+
"""
603+
result = []
604+
current = []
605+
depth = 0
606+
for char in args:
607+
if char == "(":
608+
depth += 1
609+
current.append(char)
610+
elif char == ")":
611+
depth -= 1
612+
current.append(char)
613+
elif char == "," and depth == 0:
614+
result.append("".join(current).strip())
615+
current = []
616+
else:
617+
current.append(char)
618+
if current:
619+
result.append("".join(current).strip())
620+
return [arg for arg in result if arg] # Filter empty strings
621+
622+
584623
def compile_index(line: str, index_sql: list[str]) -> None:
585624
"""
586625
Parse an index declaration and append SQL to index_sql.
@@ -612,7 +651,7 @@ def format_attribute(attr):
612651
raise DataJointError(f'Table definition syntax error in line "{line}"')
613652
match = match.groupdict()
614653

615-
attr_list = re.findall(r"(?:[^,(]|\([^)]*\))+", match["args"])
654+
attr_list = _parse_index_args(match["args"])
616655
index_sql.append(
617656
"{unique}index ({attrs})".format(
618657
unique="unique " if match["unique"] else "",

0 commit comments

Comments
 (0)