@@ -25,6 +25,10 @@ static int ref_ok(kvlangKv_t *k) {
2525 return k -> ref_on && kvspaceResolveRef && kvspaceGetByRef ;
2626}
2727
28+ static int parent_ok (kvlangKv_t * k ) {
29+ return ref_ok (k ) && k -> parent_on ;
30+ }
31+
2832static kvlangRefEnt_t * pref_find (kvlangKv_t * k , const char * dir , size_t dl ) {
2933 for (int i = 0 ; i < k -> npref ; i ++ ) {
3034 const char * pk = k -> pref [i ].key ;
@@ -150,7 +154,7 @@ static int dir_is_member(const char *dir, size_t dl) {
150154
151155static void parent_put (kvlangKv_t * k , const char * dir , size_t dl , const kvspaceRef_t * rr ) {
152156 kvlangRefEnt_t * e ;
153- if (!dir || !dl || !rr -> parent_id || !rr -> depth )
157+ if (!parent_ok ( k ) || ! dir || !dl || !rr -> parent_id || !rr -> depth )
154158 return ;
155159 if (dl >= 5 && memcmp (dir , "/lib/" , 5 ) == 0 )
156160 return ;
@@ -211,6 +215,18 @@ static int last_dir_sep(const char *key, size_t *seplen) {
211215 return -1 ;
212216}
213217
218+ /* Nested key + parent_id==0 → backend only wrote leaf fields; disable parent cache. */
219+ static void parent_note (kvlangKv_t * k , const char * key , const kvspaceRef_t * rr ) {
220+ size_t seplen = 0 ;
221+ if (k -> parent_probed || !key )
222+ return ;
223+ if (last_dir_sep (key , & seplen ) < 0 )
224+ return ;
225+ k -> parent_probed = 1 ;
226+ if (!rr -> parent_id )
227+ k -> parent_on = 0 ;
228+ }
229+
214230static int parent_prefix_ok (const kvlangRefEnt_t * e , const char * key ) {
215231 size_t dl ;
216232 const char * p ;
@@ -316,6 +332,7 @@ kvlangKv_t *kvlangKvConnect(const char *dsn) {
316332 return NULL ;
317333 }
318334 k -> ref_on = 1 ;
335+ k -> parent_on = 1 ;
319336 return k ;
320337}
321338
@@ -334,7 +351,7 @@ int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out) {
334351 kvlangXvalueZero (out );
335352 uint8_t * d ;
336353 uint32_t len ;
337- if (ref_ok (k ) && k -> npref && key ) {
354+ if (parent_ok (k ) && k -> npref && key ) {
338355 kvlangRefEnt_t * pe = (k -> npref == 1 )
339356 ? (parent_prefix_ok (& k -> pref [0 ], key ) ? & k -> pref [0 ] : NULL )
340357 : pref_cover (k , key );
@@ -383,7 +400,7 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv
383400 memcpy (key , dir , dl );
384401 memcpy (key + dl , name , nl );
385402 key [dl + nl ] = 0 ;
386- if (ref_ok (k ) && !hot_name_ok (name ) && k -> fpar .key &&
403+ if (parent_ok (k ) && !hot_name_ok (name ) && k -> fpar .key &&
387404 memcmp (k -> fpar .key , dir , k -> fpar .klen ) == 0 && dir [k -> fpar .klen ] == 0 ) {
388405 kvspaceRef_t r = { k -> fpar .block_id , k -> fpar .gen , 0 , 0 };
389406 if (kvspaceGetByRef (k -> h , & r , key , & d , & len ) == 0 && d && len > 0 ) {
@@ -410,7 +427,7 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv
410427 return 0 ;
411428 }
412429 }
413- if (ref_ok (k )) {
430+ if (parent_ok (k )) {
414431 int hit = 0 ;
415432 if (nl >= MEMBER_SEP_LEN && memchr (name , 0xC2 , nl )) {
416433 if (k -> npref )
@@ -527,16 +544,17 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui
527544 const char * slash = strrchr (key , '/' );
528545 const char * rest = slash ? slash + 1 : key ;
529546 int is_member = rest && memchr (rest , 0xC2 , strlen (rest )) != NULL ;
530- if (is_member && (pref_cover (k , key ) ||
547+ if (is_member && (! parent_ok ( k ) || pref_cover (k , key ) ||
531548 (key [0 ] == '/' && strncmp (key , "/lib/" , 5 ) == 0 )))
532549 continue ;
533- kvspaceRef_t rr ;
550+ kvspaceRef_t rr = { 0 } ;
534551 if (kvspaceResolveRef (k -> h , key , & rr ) == 0 ) {
552+ parent_note (k , key , & rr );
535553 if (!is_member ) {
536554 ref_put (k , key , & rr );
537555 hot_put (k , rest , key , rr .block_id , rr .gen );
538556 }
539- if (is_member || !k -> fpar .key ) {
557+ if (parent_ok ( k ) && ( is_member || !k -> fpar .key ) ) {
540558 size_t seplen = 0 ;
541559 int si = last_dir_sep (key , & seplen );
542560 if (si >= 0 )
0 commit comments