Parse the left-hand side of a WHERE condition into c. * Returns CBM_NOT_FOUND on error, 0 when an operator/value should follow, and * COND_LHS_COMPLETE when the condition is already complete (label test). */
| 1138 | * Returns CBM_NOT_FOUND on error, 0 when an operator/value should follow, and |
| 1139 | * COND_LHS_COMPLETE when the condition is already complete (label test). */ |
| 1140 | static int parse_condition_lhs(parser_t *p, cbm_condition_t *c) { |
| 1141 | if (is_multiarg_func_call(p)) { |
| 1142 | /* Multi-arg scalar function LHS: coalesce(f.depth, 0) >= 2 (#874). |
| 1143 | * Reuse the RETURN-item parser, then move ownership into the condition. */ |
| 1144 | cbm_return_item_t fitem; |
| 1145 | memset(&fitem, 0, sizeof(fitem)); |
| 1146 | if (parse_multiarg_func_item(p, &fitem) < 0) { |
| 1147 | func_item_fields_free(&fitem); |
| 1148 | return CBM_NOT_FOUND; |
| 1149 | } |
| 1150 | c->variable = fitem.variable; |
| 1151 | c->property = fitem.property; |
| 1152 | c->func = fitem.func; |
| 1153 | c->args = fitem.args; |
| 1154 | c->arg_count = fitem.arg_count; |
| 1155 | return 0; |
| 1156 | } |
| 1157 | |
| 1158 | if (check(p, TOK_IDENT) && p->pos + SKIP_ONE < p->count && |
| 1159 | p->tokens[p->pos + SKIP_ONE].type == TOK_LPAREN) { |
| 1160 | /* Unrecognised function call in WHERE — fail loudly with the supported |
| 1161 | * set instead of the misleading "unexpected operator" (#874). */ |
| 1162 | snprintf(p->error, sizeof(p->error), |
| 1163 | "unsupported function '%s' in WHERE (supported: coalesce, substring, replace, " |
| 1164 | "left, right)", |
| 1165 | peek(p)->text); |
| 1166 | return CBM_NOT_FOUND; |
| 1167 | } |
| 1168 | |
| 1169 | const cbm_token_t *var = expect(p, TOK_IDENT); |
| 1170 | if (!var) { |
| 1171 | return CBM_NOT_FOUND; |
| 1172 | } |
| 1173 | |
| 1174 | /* Label test: WHERE n:Label (openCypher, #241). Modelled as a leaf with |
| 1175 | * op="HAS_LABEL" and value=Label, evaluated against the bound node's label. */ |
| 1176 | if (check(p, TOK_COLON)) { |
| 1177 | advance(p); |
| 1178 | const cbm_token_t *lbl = expect(p, TOK_IDENT); |
| 1179 | if (!lbl) { |
| 1180 | return CBM_NOT_FOUND; |
| 1181 | } |
| 1182 | c->variable = heap_strdup(var->text); |
| 1183 | c->op = heap_strdup("HAS_LABEL"); |
| 1184 | c->value = heap_strdup(lbl->text); |
| 1185 | return COND_LHS_COMPLETE; |
| 1186 | } |
| 1187 | |
| 1188 | if (match(p, TOK_DOT)) { |
| 1189 | const cbm_token_t *prop = expect(p, TOK_IDENT); |
| 1190 | if (!prop) { |
| 1191 | return CBM_NOT_FOUND; |
| 1192 | } |
| 1193 | c->variable = heap_strdup(var->text); |
| 1194 | c->property = heap_strdup(prop->text); |
| 1195 | } else { |
| 1196 | /* No dot: bare alias (e.g. post-WITH variable like "cnt") */ |
| 1197 | c->variable = heap_strdup(var->text); |
no test coverage detected