parse_msql raises UnboundLocalError on three or more juxtaposed conditions.
The grammar makes AND optional — statement uses wherefullcondition+, a repetition — so conditions can be juxtaposed. That works for one or two, then breaks.
Repro
from massql.msql_parser import parse_msql
parse_msql("QUERY scaninfo(MS2DATA) WHERE MS2PROD=100 MS2PROD=200 MS2PROD=300")
lark.exceptions.VisitError: Error trying to process rule "statement":
cannot access local variable 'query_dict' where it is not associated with a value
Two juxtaposed conditions are fine, so the boundary is exactly at three:
parse_msql("QUERY scaninfo(MS1DATA) WHERE RTMIN=5 RTMAX=10") # ok
parse_msql("QUERY scaninfo(MS1DATA) WHERE RTMIN=5 RTMAX=10 CHARGE=1") # raises
WHERE a b FILTER c raises for the same reason — it also produces four top-level items:
parse_msql("QUERY scaninfo(MS2DATA) WHERE MS2PROD=100 MS2PROD=200 FILTER MS2PROD=300") # raises
Mixing forms is fine as long as the count stays at three, because a AND b collapses into one item:
parse_msql("QUERY scaninfo(MS2DATA) WHERE MS2PROD=100 MS2PROD=200 AND MS2PROD=300") # ok
Cause
MassQLToJSON.statement() (massql/msql_parser.py:327) branches on len(items) for 1, 2 and 3 only, and assigns query_dict inside each branch:
def statement(self, items):
items = [item for item in items if item != "WHERE"]
items = [item for item in items if item != "QUERY"]
if len(items) == 1: ...
if len(items) == 2: ...
if len(items) == 3: ...
return query_dict
A statement with four or more top-level items (querytype + three juxtaposed conditions, or querytype + two juxtaposed + a filter list) matches no branch and falls through to return query_dict with the local unassigned.
Suggested fix
The three branches already do the same thing — take items[0] as the querytype and concatenate everything after it — so they collapse into the general case:
def statement(self, items):
items = [item for item in items if item not in ("WHERE", "QUERY")]
query_dict = {"querytype": items[0], "conditions": []}
for group in items[1:]:
query_dict["conditions"] += group
return query_dict
That reproduces the current behaviour for 1, 2 and 3 items and handles the rest. If juxtaposition past two is meant to be rejected rather than supported, an explicit error would still be better than UnboundLocalError, since the query parses cleanly and only fails in the transformer.
Context
Found while porting the parser and IR transformer to TypeScript for a browser tool. Checked against 17e8c7496df0188bc2751e13701faf06284266b7.
How often it fires, in case it helps with prioritisation: across 1,516 gold queries drawn from the community compendium, upstream's test suite and logged user requests, zero are in this form. Across 8,618 saved model-generated queries, 8 are (0.09%). It is a sharp edge rather than a common crash — the exposure is that 29% of those golds have three or more conditions, so anything that emits juxtaposition hits it immediately.
parse_msqlraisesUnboundLocalErroron three or more juxtaposed conditions.The grammar makes
ANDoptional —statementuseswherefullcondition+, a repetition — so conditions can be juxtaposed. That works for one or two, then breaks.Repro
Two juxtaposed conditions are fine, so the boundary is exactly at three:
WHERE a b FILTER craises for the same reason — it also produces four top-level items:Mixing forms is fine as long as the count stays at three, because
a AND bcollapses into one item:Cause
MassQLToJSON.statement()(massql/msql_parser.py:327) branches onlen(items)for 1, 2 and 3 only, and assignsquery_dictinside each branch:A statement with four or more top-level items (querytype + three juxtaposed conditions, or querytype + two juxtaposed + a filter list) matches no branch and falls through to
return query_dictwith the local unassigned.Suggested fix
The three branches already do the same thing — take
items[0]as the querytype and concatenate everything after it — so they collapse into the general case:That reproduces the current behaviour for 1, 2 and 3 items and handles the rest. If juxtaposition past two is meant to be rejected rather than supported, an explicit error would still be better than
UnboundLocalError, since the query parses cleanly and only fails in the transformer.Context
Found while porting the parser and IR transformer to TypeScript for a browser tool. Checked against
17e8c7496df0188bc2751e13701faf06284266b7.How often it fires, in case it helps with prioritisation: across 1,516 gold queries drawn from the community compendium, upstream's test suite and logged user requests, zero are in this form. Across 8,618 saved model-generated queries, 8 are (0.09%). It is a sharp edge rather than a common crash — the exposure is that 29% of those golds have three or more conditions, so anything that emits juxtaposition hits it immediately.