Skip to content

Commit 880b87a

Browse files
authored
Merge pull request #1380 from DeusData/fix/type-like-sql-allowlists
fix(store): route label allowlists through the canonical type-like set
2 parents d698db8 + 619f702 commit 880b87a

5 files changed

Lines changed: 80 additions & 10 deletions

File tree

‎Makefile.cbm‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,19 @@ PROD_SRCS = $(FOUNDATION_SRCS) $(STORE_SRCS) $(CYPHER_SRCS) $(MCP_SRCS) $(DAEMON
417417
EXISTING_C_SRCS = $(EXTRACTION_SRCS) $(LSP_SRCS) $(TS_RUNTIME_SRC) \
418418
$(GRAMMAR_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC)
419419

420+
# Project headers, as prerequisites for the targets built in one compiler
421+
# invocation from sources (test-runner, test-repro-runner, test-runner-tsan,
422+
# codebase-memory-mcp, test-foundation). Those rules list only .c files, so
423+
# editing a header changed nothing make could see and the target was NOT
424+
# rebuilt — an incremental build then silently tested the OLD header value.
425+
# Found while revert-checking a constants.h change: the check passed because
426+
# nothing had recompiled.
427+
#
428+
# Vendored trees are deliberately excluded — they are pinned, they carry their
429+
# own explicit dep lists (LSP_UNITY_DEPS, TS_RUNTIME_DEPS), and globbing tens of
430+
# thousands of grammar headers would cost more than it protects.
431+
PROJECT_HDRS = $(wildcard src/*.h src/*/*.h $(CBM_DIR)/*.h tests/*.h tests/repro/*.h)
432+
420433
# ── Test sources ─────────────────────────────────────────────────
421434

422435
TEST_FOUNDATION_SRCS = \
@@ -645,7 +658,7 @@ $(BUILD_DIR):
645658

646659
# ── Foundation-only test (fast, no extraction) ───────────────────
647660

648-
$(BUILD_DIR)/test-foundation: $(TEST_FOUNDATION_SRCS) $(FOUNDATION_SRCS) | $(BUILD_DIR)
661+
$(BUILD_DIR)/test-foundation: $(TEST_FOUNDATION_SRCS) $(FOUNDATION_SRCS) $(PROJECT_HDRS) | $(BUILD_DIR)
649662
$(CC) $(CFLAGS_TEST) -o $@ $(TEST_FOUNDATION_SRCS) $(FOUNDATION_SRCS) $(LDFLAGS_TEST)
650663

651664
test-foundation: $(BUILD_DIR)/test-foundation
@@ -761,7 +774,7 @@ $(UNIXCODER_OBJ): $(UNIXCODER_BLOB_SRC) vendored/nomic/code_vectors.bin | $(BUIL
761774
OBJS_VENDORED_TEST = $(MIMALLOC_OBJ_TEST) $(SQLITE3_OBJ_TEST) $(TRE_OBJ_TEST) $(GRAMMAR_OBJS_TEST) $(TS_RUNTIME_OBJ_TEST) $(LSP_OBJ_TEST) $(PP_OBJ_TEST) $(LZ4_OBJ_TEST) $(ZSTD_OBJ_TEST) $(UNIXCODER_OBJ)
762775
OBJS_VENDORED_TSAN = $(MIMALLOC_OBJ_TSAN) $(SQLITE3_OBJ_TSAN) $(TRE_OBJ_TSAN) $(GRAMMAR_OBJS_TSAN) $(TS_RUNTIME_OBJ_TSAN) $(LSP_OBJ_TSAN) $(PP_OBJ_TSAN) $(LZ4_OBJ_TSAN) $(ZSTD_OBJ_TSAN) $(UNIXCODER_OBJ)
763776

764-
$(BUILD_DIR)/test-runner: $(ALL_TEST_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_TEST) | $(BUILD_DIR)
777+
$(BUILD_DIR)/test-runner: $(ALL_TEST_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_TEST) $(PROJECT_HDRS) | $(BUILD_DIR)
765778
$(CC) $(CFLAGS_TEST) -Itests -Itests/repro -o $@ \
766779
$(ALL_TEST_SRCS) $(PROD_SRCS) \
767780
$(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) \
@@ -789,7 +802,7 @@ test-focused: $(BUILD_DIR)/test-runner
789802
# Mirrors test-runner's link line but uses repro_main.c (own main + counters)
790803
# and TEST_REPRO_SRCS instead of ALL_TEST_SRCS. Exits non-zero while any bug is
791804
# still reproduced (the expected state); bug-repro.yml surfaces it as a board.
792-
$(BUILD_DIR)/test-repro-runner: $(TEST_REPRO_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_TEST) | $(BUILD_DIR)
805+
$(BUILD_DIR)/test-repro-runner: $(TEST_REPRO_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_TEST) $(PROJECT_HDRS) | $(BUILD_DIR)
793806
$(CC) $(CFLAGS_TEST) -Itests -o $@ \
794807
$(TEST_REPRO_SRCS) $(PROD_SRCS) \
795808
$(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) \
@@ -831,7 +844,7 @@ TSAN_OPTIONS ?= halt_on_error=1
831844
# Normal ASan/native/soak paths remain uncapped. High-worker TSan diagnostics
832845
# invoke test-runner-tsan directly so release gates cannot drift accidentally.
833846

834-
$(BUILD_DIR)/test-runner-tsan: $(ALL_TEST_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_TSAN) | $(BUILD_DIR)
847+
$(BUILD_DIR)/test-runner-tsan: $(ALL_TEST_SRCS) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_TSAN) $(PROJECT_HDRS) | $(BUILD_DIR)
835848
$(CC) $(CFLAGS_TSAN) -Itests -Itests/repro -o $@ \
836849
$(ALL_TEST_SRCS) $(PROD_SRCS) \
837850
$(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) \
@@ -904,7 +917,7 @@ $(BUILD_DIR)/.build-config: build-config-check | $(BUILD_DIR)
904917
else mv -f $@.tmp $@; rm -f $(BUILD_DIR)/codebase-memory-mcp; \
905918
echo "=== build config changed -> full rebuild: $(BUILD_CONFIG_SIG) ==="; fi
906919

907-
$(BUILD_DIR)/codebase-memory-mcp: $(MAIN_SRC) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_PROD) $(BUILD_DIR)/.build-config | $(BUILD_DIR)
920+
$(BUILD_DIR)/codebase-memory-mcp: $(MAIN_SRC) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) $(OBJS_VENDORED_PROD) $(BUILD_DIR)/.build-config $(PROJECT_HDRS) | $(BUILD_DIR)
908921
$(CC) $(CFLAGS_PROD) -o $@ \
909922
$(MAIN_SRC) $(PROD_SRCS) \
910923
$(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) $(SQLITE_WRITER_SRC) \

‎src/foundation/constants.h‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,19 @@ enum {
9494
/* Common offset constants (used across many files). */
9595
enum { SKIP_ONE = 1, PAIR_LEN = 2 };
9696

97+
/* ── Label allowlists for SQL ────────────────────────────────────
98+
* SQL mirror of cbm_label_is_type_like() (internal/cbm/helpers.c). That
99+
* function is documented as the single source of truth for type-like labels
100+
* "instead of scattering `|| strcmp(label,\"Struct\")==0` across the tree" —
101+
* but a SQL string literal cannot call it, so several queries hardcoded
102+
* ('Function','Method','Class') and silently stopped matching the moment
103+
* Struct/Interface/Enum/Type/Trait began being emitted.
104+
*
105+
* Use these instead of inlining a label list. tests/test_store_nodes.c pins
106+
* them against cbm_label_is_type_like(), so adding a type-like label there
107+
* without updating these fails CI rather than quietly shrinking query results. */
108+
#define CBM_SQL_TYPE_LIKE_LABELS "'Class','Struct','Interface','Enum','Type','Trait'"
109+
#define CBM_SQL_CALLABLE_LABELS "'Function','Method'"
110+
#define CBM_SQL_CALLABLE_OR_TYPE_LABELS CBM_SQL_CALLABLE_LABELS "," CBM_SQL_TYPE_LIKE_LABELS
111+
97112
#endif /* CBM_CONSTANTS_H */

‎src/mcp/mcp.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2863,7 +2863,7 @@ static char *bm25_search(cbm_store_t *store, const char *project, const char *qu
28632863
" (fts.base_rank "
28642864
" - CASE WHEN n.label IN ('Function','Method') THEN 10.0 "
28652865
" WHEN n.label = 'Route' THEN 8.0 "
2866-
" WHEN n.label IN ('Class','Interface','Type','Enum') THEN 5.0 "
2866+
" WHEN n.label IN (" CBM_SQL_TYPE_LIKE_LABELS ") THEN 5.0 "
28672867
" ELSE 0.0 END) AS rank "
28682868
"FROM ("
28692869
" SELECT rowid, bm25(nodes_fts) AS base_rank"

‎src/store/store.c‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5276,7 +5276,7 @@ static int arch_boundaries(cbm_store_t *s, const char *project, const char *path
52765276
char nsqlbuf[ST_SQL_BUF];
52775277
const char *nbase =
52785278
"SELECT id, qualified_name, file_path FROM nodes WHERE project=?1 AND label IN "
5279-
"('Function','Method','Class')";
5279+
"(" CBM_SQL_CALLABLE_OR_TYPE_LABELS ")";
52805280
if (scoped) {
52815281
snprintf(nsqlbuf, sizeof(nsqlbuf), "%s%s ORDER BY id", nbase, arch_path_scope_sql());
52825282
} else {
@@ -5419,7 +5419,7 @@ static int arch_packages_from_qn(cbm_store_t *s, const char *project, const char
54195419
bool scoped = arch_path_prepare(path, norm, sizeof(norm), like, sizeof(like));
54205420
char qsqlbuf[ST_SQL_BUF];
54215421
const char *qbase = "SELECT qualified_name FROM nodes WHERE project=?1 AND label IN "
5422-
"('Function','Method','Class')";
5422+
"(" CBM_SQL_CALLABLE_OR_TYPE_LABELS ")";
54235423
if (scoped) {
54245424
snprintf(qsqlbuf, sizeof(qsqlbuf), "%s%s", qbase, arch_path_scope_sql());
54255425
} else {
@@ -6686,7 +6686,7 @@ static int arch_clusters(cbm_store_t *s, const char *project, const char *path,
66866686
bool scoped = arch_path_prepare(path, norm, sizeof(norm), like, sizeof(like));
66876687
char nsqlbuf[ST_SQL_BUF];
66886688
const char *nbase = "SELECT id, name, qualified_name, file_path FROM nodes "
6689-
"WHERE project=?1 AND label IN ('Function','Method','Class')";
6689+
"WHERE project=?1 AND label IN (" CBM_SQL_CALLABLE_OR_TYPE_LABELS ")";
66906690
if (scoped) {
66916691
snprintf(nsqlbuf, sizeof(nsqlbuf), "%s%s ORDER BY id LIMIT ?4", nbase,
66926692
arch_path_scope_sql());
@@ -7760,7 +7760,7 @@ int cbm_store_vector_search(cbm_store_t *s, const char *project, const char **ke
77607760
" FROM node_vectors v"
77617761
" INNER JOIN nodes n ON n.id = v.node_id"
77627762
" WHERE v.project = ?2"
7763-
" AND n.label IN ('Function','Method','Class')"
7763+
" AND n.label IN (" CBM_SQL_CALLABLE_OR_TYPE_LABELS ")"
77647764
" ORDER BY score DESC"
77657765
" LIMIT ?3";
77667766

‎tests/test_store_nodes.c‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,52 @@
66
*/
77
#include "test_framework.h"
88
#include <store/store.h>
9+
#include <foundation/constants.h>
10+
#include <cbm.h>
911
#include <sqlite3.h>
1012
#include <string.h>
1113
#include <stdlib.h>
1214
#include <stdio.h>
1315

16+
/* ── Label allowlist / SQL drift guard ──────────────────────────── */
17+
18+
/* CONTRACT PIN. `cbm_label_is_type_like()` is documented in cbm.h as the single
19+
* source of truth for type-like labels, "instead of scattering
20+
* `|| strcmp(label,\"Struct\")==0` across the tree". A SQL string literal cannot
21+
* call it, so four queries in store.c and the BM25 ranking in mcp.c hardcoded
22+
* their own label lists — and silently stopped matching once Struct (Rust, Go,
23+
* Swift, D) began being emitted. get_architecture and vector search dropped
24+
* every struct in the project; search_code under-ranked them.
25+
*
26+
* This pins the SQL mirrors to the C predicate in BOTH directions, so the next
27+
* type-like label fails here instead of quietly shrinking query results. */
28+
TEST(sql_label_allowlists_match_cbm_label_is_type_like) {
29+
/* Every label the C predicate accepts must appear in the SQL fragment. */
30+
static const char *const type_like[] = {"Class", "Struct", "Interface",
31+
"Enum", "Type", "Trait"};
32+
for (size_t i = 0; i < sizeof(type_like) / sizeof(type_like[0]); i++) {
33+
ASSERT_TRUE(cbm_label_is_type_like(type_like[i]));
34+
char quoted[64];
35+
snprintf(quoted, sizeof(quoted), "'%s'", type_like[i]);
36+
ASSERT_NOT_NULL(strstr(CBM_SQL_TYPE_LIKE_LABELS, quoted));
37+
ASSERT_NOT_NULL(strstr(CBM_SQL_CALLABLE_OR_TYPE_LABELS, quoted));
38+
}
39+
/* And nothing the predicate rejects may be smuggled into the type-like
40+
* fragment — otherwise the SQL would widen past the C contract. */
41+
static const char *const not_type_like[] = {"Function", "Method", "Module",
42+
"File", "Folder", "Variable"};
43+
for (size_t i = 0; i < sizeof(not_type_like) / sizeof(not_type_like[0]); i++) {
44+
ASSERT_FALSE(cbm_label_is_type_like(not_type_like[i]));
45+
char quoted[64];
46+
snprintf(quoted, sizeof(quoted), "'%s'", not_type_like[i]);
47+
ASSERT_NULL(strstr(CBM_SQL_TYPE_LIKE_LABELS, quoted));
48+
}
49+
/* The callable fragment carries exactly Function and Method on top. */
50+
ASSERT_NOT_NULL(strstr(CBM_SQL_CALLABLE_OR_TYPE_LABELS, "'Function'"));
51+
ASSERT_NOT_NULL(strstr(CBM_SQL_CALLABLE_OR_TYPE_LABELS, "'Method'"));
52+
PASS();
53+
}
54+
1455
/* ── Schema / Open / Close ──────────────────────────────────────── */
1556

1657
TEST(store_open_memory) {
@@ -1930,6 +1971,7 @@ SUITE(store_nodes) {
19301971
RUN_TEST(store_coverage_meta_zero_row_truncation_and_delete);
19311972
RUN_TEST(store_coverage_replace_rejects_invalid_row_arguments);
19321973
RUN_TEST(store_coverage_replace_rolls_back_when_shadow_rebuild_fails);
1974+
RUN_TEST(sql_label_allowlists_match_cbm_label_is_type_like);
19331975
RUN_TEST(store_open_memory);
19341976
RUN_TEST(store_close_null);
19351977
RUN_TEST(store_open_memory_twice);

0 commit comments

Comments
 (0)