MCPcopy Create free account
hub / github.com/DeusData/codebase-memory-mcp / expand_pattern_rels

Function expand_pattern_rels

src/cypher/cypher.c:3291–3359  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

3289}
3290
3291static void expand_pattern_rels(cbm_store_t *store, cbm_pattern_t *pat, binding_t **bindings,
3292 int *bind_count, int *bind_cap, const char **var_name,
3293 bool is_optional) {
3294 for (int ri = 0; ri < pat->rel_count; ri++) {
3295 /* #601: stop expanding further hops once the wall-clock budget is spent
3296 * (an unbounded expansion is exactly what blows up here). */
3297 if (cypher_deadline_exceeded()) {
3298 return;
3299 }
3300 cbm_rel_pattern_t *rel = &pat->rels[ri];
3301 cbm_node_pattern_t *target_node = &pat->nodes[ri + SKIP_ONE];
3302 const char *to_var = target_node->variable ? target_node->variable : "_n_t";
3303
3304 bool is_variable_length = (rel->min_hops != SKIP_ONE || rel->max_hops != SKIP_ONE);
3305
3306 /* #1196: the hop's output buffer GROWS to hold every matched row —
3307 * the old bind_cap*10 ceiling silently dropped edges before WHERE and
3308 * aggregation, falsifying counts. binding_out_append handles growth
3309 * and the OPTIONAL fallback shares it, so no writer can run off the
3310 * end and no row class is dropped (the old fixed sizing had exactly
3311 * those two failure modes, CWE-787 and the OPTIONAL data loss). */
3312 int new_cap = *bind_count > 0 ? *bind_count : CYP_GROWTH_10;
3313 binding_t *new_bindings = malloc((size_t)new_cap * sizeof(binding_t));
3314 if (!new_bindings) {
3315 return; /* OOM: leave existing bindings untouched rather than corrupt */
3316 }
3317 int new_count = 0;
3318
3319 for (int bi = 0; bi < *bind_count; bi++) {
3320 if ((bi & CYPHER_DEADLINE_CHECK_MASK) == 0 && cypher_deadline_exceeded()) {
3321 break;
3322 }
3323 binding_t *b = &(*bindings)[bi];
3324 cbm_node_t *src = binding_get(b, *var_name);
3325 if (!src) {
3326 continue;
3327 }
3328
3329 int match_count = 0;
3330
3331 if (is_variable_length) {
3332 expand_var_length(store, rel, target_node, b, src, to_var, &new_bindings,
3333 &new_count, &new_cap, &match_count);
3334 } else {
3335 expand_fixed_length(store, rel, target_node, b, src, to_var, &new_bindings,
3336 &new_count, &new_cap, &match_count);
3337 }
3338
3339 /* OPTIONAL MATCH: no expansion for this source, so keep the binding
3340 * with the target unbound (projection renders it ""). The shared
3341 * growable append gives every fallback row a slot. */
3342 if (is_optional && match_count == 0) {
3343 binding_t nb = {0};
3344 binding_copy(&nb, b);
3345 /* Don't set to_var — it remains unbound; projection returns "" */
3346 (void)binding_out_append(&new_bindings, &new_count, &new_cap, &nb);
3347 }
3348 }

Callers 3

cross_join_with_relsFunction · 0.85
execute_singleFunction · 0.85

Calls 7

cypher_deadline_exceededFunction · 0.85
binding_getFunction · 0.85
expand_var_lengthFunction · 0.85
expand_fixed_lengthFunction · 0.85
binding_copyFunction · 0.85
binding_out_appendFunction · 0.85
binding_freeFunction · 0.85

Tested by

no test coverage detected