| 4793 | } |
| 4794 | |
| 4795 | static int execute_single(cbm_store_t *store, cbm_query_t *q, const char *project, int max_rows, |
| 4796 | result_builder_t *rb) { |
| 4797 | cbm_pattern_t *pat0 = &q->patterns[0]; |
| 4798 | |
| 4799 | /* Step 1: Scan initial nodes */ |
| 4800 | cbm_node_t *scanned = NULL; |
| 4801 | int scan_count = 0; |
| 4802 | scan_pattern_nodes(store, project, max_rows, &pat0->nodes[0], &scanned, &scan_count); |
| 4803 | |
| 4804 | /* Build initial bindings with early WHERE */ |
| 4805 | int bind_cap = scan_count > max_rows ? scan_count : (max_rows > 0 ? max_rows : SKIP_ONE); |
| 4806 | binding_t *bindings = malloc((bind_cap + SKIP_ONE) * sizeof(binding_t)); |
| 4807 | int bind_count = 0; |
| 4808 | const char *var_name = pat0->nodes[0].variable ? pat0->nodes[0].variable : "_n0"; |
| 4809 | |
| 4810 | for (int i = 0; i < scan_count && bind_count < bind_cap; i++) { |
| 4811 | if ((i & CYPHER_DEADLINE_CHECK_MASK) == 0 && cypher_deadline_exceeded()) { |
| 4812 | break; |
| 4813 | } |
| 4814 | binding_t b = {0}; |
| 4815 | b.store = store; |
| 4816 | binding_set(&b, var_name, &scanned[i]); |
| 4817 | bool pass = !q->where || eval_where(q->where, &b); |
| 4818 | if (pass) { |
| 4819 | bindings[bind_count++] = b; |
| 4820 | } else { |
| 4821 | binding_free(&b); |
| 4822 | } |
| 4823 | } |
| 4824 | |
| 4825 | /* Step 2: Expand first pattern's relationships */ |
| 4826 | expand_pattern_rels(store, pat0, &bindings, &bind_count, &bind_cap, &var_name, |
| 4827 | q->pattern_optional[0]); |
| 4828 | |
| 4829 | /* Step 2b: Additional patterns */ |
| 4830 | if (expand_additional_patterns(store, q, project, max_rows, &bindings, &bind_count, |
| 4831 | &bind_cap) != 0) { |
| 4832 | for (int bi = 0; bi < bind_count; bi++) { |
| 4833 | binding_free(&bindings[bi]); |
| 4834 | } |
| 4835 | free(bindings); |
| 4836 | cbm_store_free_nodes(scanned, scan_count); |
| 4837 | return CBM_NOT_FOUND; /* cross-join allocation refused/failed */ |
| 4838 | } |
| 4839 | |
| 4840 | /* Step 3: Late WHERE */ |
| 4841 | if (q->where && (pat0->rel_count > 0 || q->pattern_count > SKIP_ONE)) { |
| 4842 | filter_bindings_where(q->where, bindings, &bind_count); |
| 4843 | } |
| 4844 | |
| 4845 | /* Step 3b: WITH clause */ |
| 4846 | execute_with_clause(q, &bindings, &bind_count); |
| 4847 | |
| 4848 | /* Step 4: Project results */ |
| 4849 | rb_init(rb); |
| 4850 | if (q->ret) { |
| 4851 | execute_return_clause(q, q->ret, bindings, bind_count, max_rows, rb); |
| 4852 | } else { |
no test coverage detected