fix: 3-level NTX correctness + CDX SET INDEX TO string quoting
NTX 3-level tree (build.go):
- Hybrid approach: bulk build for ≤2 levels, insertKeyBTree for 3+
- rebuildWithInsert: creates proper B-tree via per-key insertion
- 5000-key test: Count=5000 Found=5000 (was 5004/4868)
CDX SET INDEX TO (gengo.go):
- Strip surrounding quotes from string literal in OrderListAdd
- Was: idx.OrderListAdd("\"path\"") → file not found
- Now: idx.OrderListAdd("path") → correct
All tests:
- 14 packages ALL PASS
- 82/82 NTX stress test
- 18/18 CDX cross-read
- 50K benchmark: all counts correct
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,8 +32,15 @@ func CreateIndex(path string, keyExpr string, keyLen int, unique bool, descend b
|
||||
maxItem := calculateMaxItems(itemSize)
|
||||
halfPage := maxItem / 2
|
||||
|
||||
// Determine tree depth: if 3+ levels needed, use per-key insertion
|
||||
// for correct B-tree structure. Bulk build only for ≤2 levels.
|
||||
nLeafPages := (len(keys) + maxItem - 1) / maxItem
|
||||
if nLeafPages == 0 {
|
||||
nLeafPages = 1
|
||||
}
|
||||
use3LevelFallback := nLeafPages > maxItem+1
|
||||
|
||||
// Bulk build: all pages in memory buffer, single write at end.
|
||||
// Ported from rddfive/ntx_engine.c fa_ntx_create().
|
||||
var buf pageBuffer
|
||||
buf.init()
|
||||
|
||||
@@ -166,9 +173,63 @@ func CreateIndex(path string, keyExpr string, keyLen int, unique bool, descend b
|
||||
}
|
||||
|
||||
f.Close()
|
||||
|
||||
if use3LevelFallback && len(keys) > 0 {
|
||||
// 3+ level tree: rebuild using per-key insertion for correct B-tree
|
||||
return rebuildWithInsert(path, keyExpr, keyLen, unique, descend, keys)
|
||||
}
|
||||
|
||||
return OpenIndex(path)
|
||||
}
|
||||
|
||||
// rebuildWithInsert creates an NTX using per-key insertion (proper B-tree).
|
||||
// Used for 3+ level trees where bulk build has separator duplication issues.
|
||||
func rebuildWithInsert(path, keyExpr string, keyLen int, unique, descend bool, keys []KeyRecord) (*Index, error) {
|
||||
itemSize := 8 + keyLen
|
||||
maxItem := calculateMaxItems(itemSize)
|
||||
halfPage := maxItem / 2
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootOff := int64(HeaderSize)
|
||||
emptyRoot := [BlockSize]byte{}
|
||||
initPageOffsets(emptyRoot[:], maxItem, itemSize)
|
||||
|
||||
hdr := Header{
|
||||
Type: 0x0401, Version: 1,
|
||||
Root: uint32(rootOff), NextPage: uint32(rootOff + BlockSize),
|
||||
ItemSize: uint16(itemSize), KeySize: uint16(keyLen),
|
||||
MaxItem: uint16(maxItem), HalfPage: uint16(halfPage),
|
||||
}
|
||||
copy(hdr.KeyExpr[:], keyExpr)
|
||||
if unique {
|
||||
hdr.Unique = 1
|
||||
}
|
||||
if descend {
|
||||
hdr.Descend = 1
|
||||
}
|
||||
WriteHeader(f, &hdr)
|
||||
f.WriteAt(emptyRoot[:], rootOff)
|
||||
f.Close()
|
||||
|
||||
idx, err := OpenIndex(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, kr := range keys {
|
||||
k := make([]byte, keyLen)
|
||||
copy(k, kr.Key)
|
||||
if err := idx.insertKeyBTree(k, kr.RecNo); err != nil {
|
||||
idx.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return idx, nil
|
||||
}
|
||||
|
||||
// --- Bulk build buffer (ported from rddfive/ntx_engine.c) ---
|
||||
|
||||
type pageBuffer struct {
|
||||
|
||||
Reference in New Issue
Block a user