mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-12 09:47:20 +08:00
Merge pull request #2241 from lorenzozanee/fix/optional-match-count-unbound
fix(cypher): ignore unbound OPTIONAL MATCH values in COUNT
This commit is contained in:
@@ -4450,11 +4450,33 @@ static void ret_agg_init_group(ret_agg_entry_t *entry, const char *key, int item
|
||||
}
|
||||
|
||||
/* Accumulate a binding into RETURN aggregation */
|
||||
static bool binding_has_value(binding_t *b, const char *var, const char *prop) {
|
||||
if (!var || strcmp(var, "*") == 0) {
|
||||
return true;
|
||||
}
|
||||
char full[CBM_SZ_256];
|
||||
if (prop) {
|
||||
snprintf(full, sizeof(full), "%s.%s", var, prop);
|
||||
} else {
|
||||
snprintf(full, sizeof(full), "%s", var);
|
||||
}
|
||||
for (int i = 0; i < b->var_count; i++) {
|
||||
if (strcmp(b->var_names[i], full) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return binding_get_edge(b, var) != NULL || binding_get(b, var) != NULL;
|
||||
}
|
||||
|
||||
static void ret_agg_accumulate(ret_agg_entry_t *entry, cbm_return_clause_t *ret, binding_t *b) {
|
||||
for (int ci = 0; ci < ret->count; ci++) {
|
||||
if (!is_aggregate_func(ret->items[ci].func)) {
|
||||
continue;
|
||||
}
|
||||
if (strcmp(ret->items[ci].func, "COUNT") == 0 &&
|
||||
!binding_has_value(b, ret->items[ci].variable, ret->items[ci].property)) {
|
||||
continue;
|
||||
}
|
||||
entry->counts[ci]++;
|
||||
const char *raw = binding_get_virtual(b, ret->items[ci].variable, ret->items[ci].property);
|
||||
double dv = strtod(raw, NULL);
|
||||
|
||||
@@ -1744,6 +1744,35 @@ TEST(cypher_exec_count) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(cypher_exec_optional_bound_terminal_count_ignores_unbound) {
|
||||
cbm_store_t *s = setup_cypher_store();
|
||||
cbm_cypher_result_t r = {0};
|
||||
|
||||
int rc = cbm_cypher_execute(s,
|
||||
"MATCH (f:Function) OPTIONAL MATCH (t)-[:CALLS]->(f) "
|
||||
"RETURN f.name, COUNT(t) AS cnt",
|
||||
"test", 0, &r);
|
||||
ASSERT_EQ(rc, 0);
|
||||
ASSERT_EQ(r.row_count, 4);
|
||||
|
||||
for (int i = 0; i < r.row_count; i++) {
|
||||
const char *name = r.rows[i][0];
|
||||
const char *count = r.rows[i][1];
|
||||
if (strcmp(name, "HandleOrder") == 0) {
|
||||
ASSERT_STR_EQ(count, "0");
|
||||
} else if (strcmp(name, "ValidateOrder") == 0 || strcmp(name, "SubmitOrder") == 0 ||
|
||||
strcmp(name, "LogError") == 0) {
|
||||
ASSERT_STR_EQ(count, "1");
|
||||
} else {
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
cbm_cypher_result_free(&r);
|
||||
cbm_store_close(s);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(cypher_exec_limit) {
|
||||
cbm_store_t *s = setup_cypher_store();
|
||||
cbm_cypher_result_t r = {0};
|
||||
@@ -4578,6 +4607,7 @@ SUITE(cypher) {
|
||||
RUN_TEST(cypher_exec_calls_with_where);
|
||||
RUN_TEST(cypher_exec_inbound);
|
||||
RUN_TEST(cypher_exec_count);
|
||||
RUN_TEST(cypher_exec_optional_bound_terminal_count_ignores_unbound);
|
||||
RUN_TEST(cypher_exec_limit);
|
||||
RUN_TEST(cypher_exec_order_by);
|
||||
RUN_TEST(cypher_exec_variable_length);
|
||||
|
||||
Reference in New Issue
Block a user