1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-10-23 21:49:02 +03:00

Import code for vec1 into ext/vec1/.

FossilOrigin-Name: 92709f54d4c58cf174f305b58971a01878af313157a11549b588a25216701f5c
This commit is contained in:
dan
2026-10-01 11:21:02 +00:00
65 changed files with 21639 additions and 14 deletions

View File

@@ -108,6 +108,7 @@ LDFLAGS.readline = @LDFLAGS_READLINE@
CFLAGS.readline = @CFLAGS_READLINE@
LDFLAGS.icu = @LDFLAGS_ICU@
LDFLAGS.rt = @LDFLAGS_RT@
CFLAGS.vec1.avx2 = @CFLAGS_VEC1_AVX2@
CFLAGS.icu = @CFLAGS_ICU@
LDFLAGS.libsqlite3.soname = @LDFLAGS_LIBSQLITE3_SONAME@
# soname: see https://sqlite.org/src/forumpost/5a3b44f510df8ded

View File

@@ -2474,6 +2474,36 @@ fts5_ext.lo: fts5.c $(HDR) $(EXTHDR)
fts5.dll: fts5_ext.lo
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ fts5_ext.lo
# vec1 loadable extension.
#
# If VEC1_AVX2 is non-zero (the default, except when targeting ARM),
# vec1.c is compiled twice - once with scalar code and once with AVX2 -
# and both objects linked into a single DLL that picks one at runtime
# based on the CPU. Otherwise a single scalar-only build is done.
#
!IFNDEF VEC1_AVX2
!IF "$(PLATFORM)"=="arm" || "$(PLATFORM)"=="ARM" || "$(PLATFORM)"=="arm64" || "$(PLATFORM)"=="ARM64"
VEC1_AVX2 = 0
!ELSEIF "$(VSCMD_ARG_TGT_ARCH)"=="arm" || "$(VSCMD_ARG_TGT_ARCH)"=="arm64"
VEC1_AVX2 = 0
!ELSE
VEC1_AVX2 = 1
!ENDIF
!ENDIF
VEC1_SRC = $(TOP)\ext\vec1\vec1.c
!IF $(VEC1_AVX2)!=0
vec1.dll: $(VEC1_SRC) $(SQLITE3H)
$(TCC) -DVEC1SIMD=SCALAR -Fovec1scalar.lo -Fdvec1.pdb -c $(VEC1_SRC)
$(TCC) -DVEC1SIMD=AVX2 -arch:AVX2 -Fovec1avx2.lo -Fdvec1.pdb -c $(VEC1_SRC)
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ vec1scalar.lo vec1avx2.lo
!ELSE
vec1.dll: $(VEC1_SRC) $(SQLITE3H)
$(TCC) -Fovec1.lo -Fdvec1.pdb -c $(VEC1_SRC)
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ vec1.lo
!ENDIF
sqlite3rbu.lo: $(TOP)\ext\rbu\sqlite3rbu.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\rbu\sqlite3rbu.c
@@ -2954,6 +2984,7 @@ clean:
del /Q kvtest.exe ossshell.exe scrub.exe 2>NUL
del /Q showshm.exe sqlite3_checker.* sqlite3_expert.exe 2>NUL
del /Q fts5.* fts5parse.* 2>NUL
del /Q vec1.* 2>NUL
del /q src-verify.exe 2>NUL
del /q jimsh.exe jimsh0.exe 2>NUL
-$(TCLSH_CMD) test/testrunner.tcl clean

View File

@@ -66,4 +66,17 @@ sqlite-configure canonical {
proj-define-for-opt static-tclsqlite3 STATIC_TCLSQLITE3 "Statically link tclsqlite3?"
proj-define-for-opt static-cli-shell STATIC_CLI_SHELL "Statically link CLI shell?"
# vec1 (ext/vec1) is only built on request ("make vec1.so"). If the
# compiler supports AVX2 it is built as a multi-arch extension that
# selects AVX2 or scalar code at runtime. Use [cctest] rather than
# [cc-check-flags] so the flags are not appended to later tests.
msg-checking "Checking for AVX2 flags for vec1... "
if {[cctest -cflags {-mavx2 -mfma}]} {
msg-result "-mavx2 -mfma"
define CFLAGS_VEC1_AVX2 {-mavx2 -mfma}
} else {
msg-result "no"
define CFLAGS_VEC1_AVX2 ""
}
}; # sqlite-configure

63
ext/vec1/Makefile Normal file
View File

@@ -0,0 +1,63 @@
CC = gcc
CFLAGS += -O3
CFLAGS += -DNDEBUG
CFLAGS += -Wextra -Werror
CFLAGS += -g
#CFLAGS += -DVEC1_THREADS=0
#CFLAGS += -fprofile-arcs -ftest-coverage -fprofile-update=atomic -lgcov
#CFLAGS += -fno-inline
#ASANFLAGS = -fsanitize=address,undefined -fno-sanitize-recover=undefined
#CFLAGS += $(ASANFLAGS)
SIMDCFLAGS = -mavx2 -mfma
SRC = vec1.c
vec1.so: $(SRC)
$(CC) $(CFLAGS) $(SIMDCFLAGS) $(SRC) -o vec1.so -shared -fPIC -lm
vec1.s: $(SRC)
$(CC) $(CFLAGS) -g0 $(SIMDCFLAGS) $(SRC) -S
# Method for building multi-arch loadable libraries.
#
# To build a multi-arch library, the "vec1.c" file must be compiled twice.
# Once with VEC1SIMD defined as SCALAR, and once as AVX2. The -DVEC1SMD=AVX2
# compilation should pass flags to enable AVX2 support, the -DVEC1SMD=SCALAR
# build should not pass any such flags. Then both objects are linked into
# a single *.so file. The result works on hardware that supports AVX2, and
# also on hardware that does not.
#
# Eventually, when AVX-512 support is added, there will be a third
# compilation of vec1.c with flags to support AVX-512.
#
vec1multi.so: $(SRC)
$(CC) -DVEC1SIMD=SCALAR -c $(CFLAGS) $(SRC) -o vec1scalar.o -fPIC
$(CC) -DVEC1SIMD=AVX2 -c -mavx2 -mfma $(CFLAGS) $(SRC) -o vec1avx2.o -fPIC
$(CC) vec1scalar.o vec1avx2.o -o vec1multi.so -shared -fPIC
vec1_faiss.so: vec1_faiss.cpp
g++ $(CFLAGS) vec1_faiss.cpp -o vec1_faiss.so -shared -fPIC -lfaiss
vec1testscalar.so: $(SRC)
$(CC) $(CFLAGS) -DVEC1_TEST_SCALAR $(SRC) -o vec1testscalar.so -shared -fPIC -lm
vec1.a: $(SRC)
$(CC) -DVEC1_STATIC $(CFLAGS) $(SIMDCFLAGS) -c $(SRC) -fPIC
ar rcs vec1.a vec1.o
vec1_algo_test: tool/vec1_algo_test.c $(SRC)
$(CC) -I. $(CFLAGS) $(SIMDCFLAGS) tool/vec1_algo_test.c -o vec1_algo_test -lm
clean:
rm -f vec1.so vec1_faiss.so vec1multi.so vec1testscalar.so
rm -f vec1.a
rm -f vec1.o vec1scalar.o vec1avx2.o
rm -f vec1.so-vec1.gcda vec1.so-vec1.gcno
rm -f vec1.s

19
ext/vec1/Makefile.msc Normal file
View File

@@ -0,0 +1,19 @@
CFLAGS = /Zi /O2 /DNDEBUG /W4
SIMDCFLAGS = /arch:AVX2
all: vec1.dll vec1multi.dll
vec1.dll: vec1.c
cl $(CFLAGS) $(SIMDCFLAGS) vec1.c -link -dll -out:vec1.dll
vec1multi.dll: vec1.c
cl /c /DVEC1SIMD=SCALAR $(CFLAGS) vec1.c /Fovec1scalar.obj
cl /c /DVEC1SIMD=AVX2 $(CFLAGS) /arch:AVX2 vec1.c /Fovec1avx2.obj
link /DLL vec1scalar.obj vec1avx2.obj /OUT:vec1multi.dll
clean:
del /Q vec1.dll
del /Q vec1scalar.obj
del /Q vec1multi.obj

View File

@@ -0,0 +1,125 @@
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. .. test]
}
source $testdir/tester.tcl
set dir [file dirname [info nameofexecutable]]
set vec1_shlib [file join $dir vec1]
proc vec1_load_only {db} {
if {[catch {$db one {SELECT vec1_info()}}]} {
global vec1_shlib
$db enable_load_extension 1
if {[catch { sqlite3_load_extension $db $vec1_shlib }]} {
$db eval { SELECT load_extension($vec1_shlib) }
}
$db enable_load_extension 0
}
}
sqlite3 db_test_vec1 ""
set res [catch { vec1_load_only db_test_vec1 }]
db_test_vec1 close
if {$res} {
finish_test
return -code return
}
proc vec1_load {db} {
if {[catch {$db one {SELECT vec1_info()}}]} {
global vec1_shlib
$db enable_load_extension 1
$db eval { SELECT load_extension($vec1_shlib) }
$db enable_load_extension 0
}
$db func random_vector random_vector
$db func random_vector2 random_vector2
$db func rvec rvec
}
proc vec1_load_scalar {db} {
global vec1_shlib
$db enable_load_extension 1
set scalar "${vec1_shlib}testscalar"
sqlite3_load_extension $db $scalar
$db enable_load_extension 0
}
proc random_vector {n {scale 1.0}} {
set v [list]
for {set i 0} {$i < $n} {incr i} {
lappend v [expr {((rand() * 2.0) - 1.0) * $scale}]
}
binary format f$n $v
}
proc random_vector2 {n} {
set v [list]
for {set i 0} {$i < $n} {incr i} {
lappend v [expr {abs(rand() * 10.0)}]
}
binary format f$n $v
}
proc rvec {n i} {
set lPrime {
23 29 31 37 41 43 47 53
59 61 67 71 73 79 83 89
97 101 103 107 109 113 127 131
137 139 149 151 157 163 167 173
179 181 191 193 197 199 211 223
227 229 233 239 241 251 257 263
269 271 277 281 283 293 307 311
313 317 331 337 347 349 353 359
367 373 379 383 389 397 401 409
419 421 431 433 439 443 449 457
461 463 467 479 487 491 499 503
509 521 523 541 547 557 563 569
571 577 587 593 599 601 607 613
617 619 631 641 643 647 653 659
661 673 677 683 691 701 709 719
727 733 739 743 751 757 761 769
}
if {$n>[llength $lPrime]} {
error "n value too large ($n)"
}
set i [expr $i * 769]
set lDim [list]
for {set iDim 0} {$iDim<$n} {incr iDim} {
set x [lindex $lPrime $iDim]
lappend lDim [expr ($i % $x) - $x/2]
}
binary format r* $lDim
}
db func rvec rvec
proc model2tbl {nm vec1tbl} {
set model [db one "SELECT val FROM ${vec1tbl}_model WHERE id=1"]
binary scan $model IIIIII iVersion flags nElem nCodebook nBucket eDistance
set nBytePerVector [expr $nElem*4]
set iOff [expr 4 * 6]
if {($flags & 0x08)==0 && $nCodebook>0} {
incr iOff [expr 4 * $nElem * 256]
}
#puts "nBytePerVector=$nBytePerVector"
#puts "nBucket=$nBucket"
#puts "sz=[string length $model]"
#puts "iOff=$iOff"
db eval "CREATE TABLE $nm (ibucket INTEGER PRIMARY KEY, coarse BLOB)"
for {set iBucket 0} {$iBucket<$nBucket} {incr iBucket} {
set vec [string range $model $iOff [expr $iOff+$nBytePerVector-1]]
db eval "INSERT INTO $nm VALUES(\$iBucket, \$vec)"
incr iOff $nBytePerVector
}
}

264
ext/vec1/test/vec1aa.test Normal file
View File

@@ -0,0 +1,264 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1aa
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_execsql_test 1.0.1 { SELECT vec1_config('nthread'); } {1}
do_execsql_test 1.0.2 { SELECT vec1_config('nprobe'); } {0.05}
do_execsql_test 1.1 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1(rowid, vector) VALUES(
1, vec1_from_json('[1,2,3,4,5,6,7,8]')
);
}
do_execsql_test 1.2 { PRAGMA integrity_check } {ok}
do_execsql_test 1.3 {
DELETE FROM v1_base;
PRAGMA integrity_check;
} {{v1: %_idx row 1 missing from %_base}}
do_execsql_test 1.4 {
DROP TABLE v1;
}
do_execsql_test 1.5 {
CREATE VIRTUAL TABLE v1 USING vec1;
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1(rowid, vector) VALUES(1, vec1_from_json('[1,2,3,4]'));
BEGIN;
SAVEPOINT aaa;
INSERT INTO v1(rowid, vector) VALUES(2, vec1_from_json('[1,2,3,4]'));
ROLLBACK TO aaa;
COMMIT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 1.6 {
BEGIN;
INSERT INTO v1(rowid, vector) VALUES(3, vec1_from_json('[1,2,3,4]'));
ROLLBACK;
INSERT INTO v1(rowid, vector) VALUES(4, vec1_from_json('[1,2,3,4]'));
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
#reset_db
#vec1_load db
#do_execsql_test 2.0 {
# CREATE VIRTUAL TABLE v1 USING vec1;
# INSERT INTO v1(cmd, vector) VALUES('rebuild', 'flat');
# INSERT INTO v1(rowid, vector) VALUES(1, vec1_from_json('[1,2,3,4]'));
# INSERT INTO v1(rowid, vector) VALUES(2, vec1_from_json('[4,3,2,1]'));
# SELECT count(*) fROM v1_idx;
#} {1}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
BEGIN;
INSERT INTO v1(rowid, vector) VALUES(1, random_vector(8));
INSERT INTO v1(rowid, vector) VALUES(2, random_vector(8));
INSERT INTO v1(rowid, vector) VALUES(3, random_vector(8));
COMMIT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 3.1 {
DELETE FROM v1 WHERE rowid=2;
PRAGMA integrity_check;
} {ok}
do_execsql_test 3.2 {
SELECT rowid FROM v1
} {1 3}
do_execsql_test 3.3 {
SELECT rowid FROM v1( random_vector(8), 5 ) ORDER BY 1;
} {1 3}
do_execsql_test 3.4 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO v1 SELECT random_vector(8);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO v1 SELECT random_vector(8);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO v1 SELECT random_vector(8);
}
do_execsql_test 3.5 {
DELETE FROM v1 WHERE rowid IN (5, 105, 205, 77, 98, 222);
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 4.1 {
BEGIN;
INSERT INTO v1 (rowid, vector) VALUES( 0x1_0000_0001, random_vector(8) );
INSERT INTO v1 (rowid, vector) VALUES( 0x1_0000_0002, random_vector(8) );
INSERT INTO v1 (rowid, vector) VALUES( 0x1_0000_0003, random_vector(8) );
INSERT INTO v1 (rowid, vector) VALUES( 0x1_0000_0004, random_vector(8) );
COMMIT;
}
do_execsql_test 4.2 {
PRAGMA integrity_check;
} {ok}
do_execsql_test 4.3 {
DELETE FROM v1 WHERE rowid = 0x1_0000_0003;
} {}
do_execsql_test 4.4 {
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 4.1 {
INSERT INTO v1 VALUES( random_vector( 16 ) );
}
do_execsql_test 4.2 {
INSERT INTO v1 VALUES( random_vector( 16 ) );
SELECT count(*) FROM v1_idx;
} {1}
do_execsql_test 4.3 {
PRAGMA integrity_check
} {ok}
#-------------------------------------------------------------------------
#
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 5.1 {
INSERT INTO v1 VALUES( random_vector( 16 ) );
INSERT INTO v1 VALUES( random_vector( 16 ) );
INSERT INTO v1 VALUES( random_vector( 16 ) );
}
do_execsql_test 5.2 {
BEGIN;
INSERT INTO v1(rowid, vector) VALUES(11, random_vector( 16 ) );
INSERT INTO v1(rowid, vector) VALUES(12, random_vector( 16 ) );
DELETE FROM v1 WHERE rowid=11;
COMMIT;
} {}
do_execsql_test 5.3 {
PRAGMA integrity_check
} {ok}
#-------------------------------------------------------------------------
#
reset_db
vec1_load db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE v1 USING vec1(x);
INSERT INTO v1 VALUES( random_vector(22) );
}
do_catchsql_test 6.1 {
INSERT INTO v1 VALUES( random_vector(23) );
} {1 {vec1: unexpected vector blob size 92 bytes, expected 88}}
#-------------------------------------------------------------------------
#
reset_db
vec1_load db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE v1 USING vec1(x);
}
do_catchsql_test 6.1 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{distance:"l2"}');
} {1 {vec1: required parameter missing: index}}
do_execsql_test 6.2 {
INSERT INTO v1 VALUES(random_vector(16));
INSERT INTO v1 VALUES(random_vector(16));
INSERT INTO v1 VALUES(random_vector(16));
INSERT INTO v1 VALUES(random_vector(16));
INSERT INTO v1 VALUES(random_vector(16));
UPDATE v1_base SET vector = random_vector(15) WHERE id=4;
}
do_catchsql_test 6.1 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat",distance:"l2"}');
} {1 {vec1: unexpected vector size in v1_base: 60}}
#-------------------------------------------------------------------------
#
reset_db
vec1_load db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE v1 USING vec1(x);
}
do_catchsql_test 7.1 {
INSERT INTO v1(x) VALUES('sometextvalu');
} {1 {vec1: unexpected vector type TEXT, expected BLOB}}
do_execsql_test 7.2 {
INSERT INTO v1(x) VALUES( random_vector(4) );
} {}
do_catchsql_test 7.3 {
INSERT INTO v1(x) VALUES('sometextvaluABCD');
} {1 {vec1: unexpected vector type TEXT, expected BLOB}}
do_catchsql_test 7.4 {
UPDATE v1 SET x='sometextvaluABCD';
} {1 {vec1: unexpected vector type TEXT, expected BLOB}}
finish_test

115
ext/vec1/test/vec1ab.test Normal file
View File

@@ -0,0 +1,115 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1ab
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO v1(rowid, vector) SELECT NULL, random_vector(128) FROM s;
}
foreach {tn training} {
1.1 "flat"
1.2 "{ codesize: 16 }"
1.3 "{ codesize: 16 }"
1.4 "{ codesize: 8 }"
1.5 "{ codesize: 8 , nbucket: 32 }"
1.6 "{ codesize: 16 , nbucket: 32 }"
1.7 "{ codesize: 8 , nbucket: 32 }"
} {
do_test $tn.1 {
if {$training=="flat"} {
set T {{index:"flat"}}
} else {
set T [db one {
SELECT vec1_train(vector, $training) FROM v1 WHERE rowid < 1000
}]
}
set {} {}
} {}
do_execsql_test $tn.2 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', $T);
}
do_execsql_test $tn.3 {
PRAGMA integrity_check
} ok
db close
sqlite3 db test.db
vec1_load db
do_execsql_test $tn.4 {
PRAGMA integrity_check
} ok
do_execsql_test $tn.5 {
SELECT sum(good) FROM (
SELECT rowid IN (
SELECT rowid FROM v1(o.vector, '{ K: 10 }')
) AS good FROM v1 AS o LIMIT 100
);
} {100}
}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v2 USING vec1;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO v2(rowid, vector)
SELECT 0x3_FFFF_FFFF + i, random_vector(128) FROM s;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50
)
INSERT INTO v2(rowid, vector)
SELECT i - 0x3_FFFF_FFFF, random_vector(128) FROM s;
}
do_execsql_test 2.1 {
INSERT INTO v2(cmd, vector) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 2.2 {
SELECT sum(good) FROM (
SELECT rowid IN (
SELECT rowid FROM v2(o.vector, '{ K: 10 }')
) AS good FROM v2 AS o LIMIT 100
);
} {100}
do_execsql_test 2.3 {
INSERT INTO v2(cmd, vector) VALUES('rebuild', (
SELECT vec1_train(vector, '{ codesize: 16 }') FROM (
SELECT vector FROM v2 LIMIT 1000
)
));
}
do_execsql_test 2.4 {
SELECT sum(good) FROM (
SELECT rowid IN (
SELECT rowid FROM v2(o.vector, '{ K: 10 }')
) AS good FROM v2 AS o LIMIT 100
);
} {100}
finish_test

100
ext/vec1/test/vec1ac.test Normal file
View File

@@ -0,0 +1,100 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1ac
vec1_load db
db func normalize normalize
proc normalize {vec} {
binary scan $vec f* aVal
set sum 0.0
foreach v $aVal {
set sum [expr $sum + ($v*$v)]
}
set sz [expr sqrt($sum)]
set aNew [list]
foreach v $aVal {
lappend aNew [expr $v/$sz]
}
binary format f* $aNew
}
db func rescale rescale
proc rescale {vec mul} {
binary scan $vec f* aVal
set aNew [list]
foreach v $aVal {
lappend aNew [expr $v*$mul]
}
binary format f* $aNew
}
do_execsql_test 1.0 {
CREATE TABLE t1(k INTEGER PRIMARY KEY, v VECTOR);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO t1 SELECT NULL, random_vector2(128) FROM s;
CREATE VIRTUAL TABLE v1 USING vec1;
INSERT INTO v1(rowid, vector) SELECT k, v FROM t1;
}
foreach {tn cfg} {
1 { {nbucket:0, codesize:0, distance:"cos"} }
2 { {nbucket:0, codesize:32, distance:"cos"} }
3 { {nbucket:32, codesize:32, distance:"cos"} }
} {
do_execsql_test 1.$tn.1 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', (
SELECT vec1_train(vector, $cfg) FROM v1
));
} {}
do_execsql_test 1.$tn.2 {
SELECT sum(
rowid IN ( SELECT rowid FROM v1( normalize( o.vector ), '{K:10}' ) )
)
FROM v1 AS o WHERE rowid BETWEEN 600 AND 700;
} {101}
do_execsql_test 1.$tn.3 {
SELECT sum(
rowid IN ( SELECT rowid FROM v1( rescale( o.vector, 0.01 ), '{K:10}' ) )
)
FROM v1 AS o WHERE rowid BETWEEN 600 AND 700;
} {101}
}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v2 USING vec1;
INSERT INTO v2(rowid, vector) SELECT k, v FROM t1;
INSERT INTO v2(cmd, vector) VALUES('rebuild', (
SELECT vec1_train(vector, '{nbucket:32, codesize:32, distance:"cos"}')
FROM v2
));
}
do_execsql_test 2.1 {
PRAGMA integrity_check;
} {ok}
do_execsql_test 2.2 {
DELETE FROM v2 WHERE (rowid % 45)==0;
PRAGMA integrity_check;
} {ok}
finish_test

View File

@@ -0,0 +1,80 @@
# 2026-08-04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1alter
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
INSERT INTO v1(rowid, v) VALUES(11, random_vector(8));
INSERT INTO v1(rowid, v) VALUES(12, random_vector(8));
}
do_execsql_test 1.1 {
ALTER TABLE v1 RENAME TO v2;
}
do_execsql_test 1.2 {
SELECT rowid FROM v2
} {11 12}
do_execsql_test 1.3 {
PRAGMA integrity_check
} {ok}
do_execsql_test 1.4 {
PRAGMA writable_schema;
} {0}
do_execsql_test 1.5 {
PRAGMA writable_schema = 1;
PRAGMA writable_schema;
} {1}
do_execsql_test 1.6 {
ALTER TABLE v2 RENAME TO v3;
}
do_execsql_test 1.7 {
PRAGMA writable_schema;
} {1}
do_execsql_test 1.8 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
INSERT INTO v1(rowid, v) VALUES(-14, random_vector(18));
INSERT INTO v1(rowid, v) VALUES(-15, random_vector(18));
}
do_execsql_test 1.9 {
SELECT rowid FROM v1
} {-15 -14}
do_test 1.10 { sqlite3_db_config db DEFENSIVE } {0}
do_test 1.11 { sqlite3_db_config db DEFENSIVE 1 } {1}
do_execsql_test 1.12 {
ALTER TABLE v3 RENAME TO v4;
}
do_test 1.13 { sqlite3_db_config db DEFENSIVE } {1}
do_execsql_test 1.14 {
SELECT tbl_name, name, sql FROM sqlite_schema WHERE name='v4_idx_idx'
} {
v4_idx v4_idx_idx
{CREATE INDEX 'v4_idx_idx' ON 'v4_idx'(bucket, first, last)}
}
finish_test

View File

@@ -0,0 +1,162 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1binary
foreach {tn nElem codesize} {
1 16 2
2 16 1
3 48 6
4 48 3
} {
reset_db
vec1_load db
expr srand(0)
do_execsql_test 1.$tn.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
}
set nRow [expr 40]
set nLimit [expr 40]
do_execsql_test 1.$tn.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow
)
INSERT INTO v1 SELECT random_vector($nElem) FROM s;
}
set jarg "{quantizer:\"bq\", nbucket: 8, codesize: $codesize}"
do_execsql_test 1.$tn.2 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, $jarg) FROM v1
));
}
set v [db one {SELECT random_vector($nElem)}]
set res [db eval {
SELECT rowid FROM v1 ORDER by vec1_l2_distance($v, v) LIMIT $nLimit
}]
do_execsql_test 1.$tn.2 {
SELECT rowid FROM v1($v, '{K:50, nprobe:8}')
ORDER BY vec1_l2_distance(v, $v)
LIMIT $nLimit
} $res
do_execsql_test 1.$tn.3 {
PRAGMA integrity_check
} {ok}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.1 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow )
SELECT vec1_train(random_vector(8), '{quantizer:"bq", nbucket:8}') FROM s
));
INSERT INTO v1 VALUES( random_vector(8) );
}
set v [db one {SELECT random_vector(8)}]
do_execsql_test 2.2 {
SELECT rowid FROM v1($v, '{nprobe:8}') LIMIT $nLimit
} {1}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.1 {
CREATE VIRTUAL TABLE v1 USING vec1(abc);
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow )
SELECT vec1_train(random_vector(64), '{quantizer:"bq", nbucket:2}') FROM s
));
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1 SELECT random_vector(64) FROM s;
}
do_execsql_test 2.2 {
PRAGMA integrity_check
} {ok}
foreach id [db eval {SELECT rowid FROM v1}] {
db eval { DELETE FROM v1 WHERE rowid = $id }
if {($id % 1)==0} {
do_execsql_test 2.3.$id {
PRAGMA integrity_check
} {ok}
}
}
#-------------------------------------------------------------------------
# Now check the version number in model headers. This should be 7 when
# using a binary quantizer, or 4 otherwise.
#
reset_db
vec1_load db
foreach {tn jarg iVersion} {
1 "{nbucket:2, codesize:8}" 4
2 "{nbucket:2, codesize:8, quantizer:\"bq\"}" 7
3 "{nbucket:2, codesize:8, quantizer:\"pq\"}" 4
} {
do_test 2.$tn {
set M [db one {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512
)
SELECT vec1_train(random_vector(16), $jarg) FROM s;
}]
binary scan $M I version
set version
} $iVersion
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.1 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 )
INSERT INTO v1 SELECT random_vector(100) FROM s;
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{quantizer:"bq", nbucket:2, codesize:8}') FROM v1
));
}
set v [db one {SELECT random_vector(100)}]
do_execsql_test 3.2 {
SELECT count(*) FROM (
SELECT rowid FROM v1($v, '{nprobe:2}') LIMIT 5
);
} {5}
finish_test

View File

@@ -0,0 +1,95 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1binary2
vec1_load db
expr srand(0)
proc random_item {L} {
set ii [expr {int(abs(rand()) * [llength $L])}]
lindex $L $ii
}
proc random_binvec {n {opts {1 -1}}} {
set lRet [list]
for {set ii 0} {$ii < $n} {incr ii} {
lappend lRet [random_item $opts]
}
binary format f$n $lRet
}
db func random_binvec random_binvec
set nQuery 100
foreach {tn N opts nbucket} {
1 8 {1 -1} 0
2 16 {1 -1} 0
3 24 {1 -1} 0
4 29 {1 -1} 0
5 9 {1 -1} 0
6 201 {1 -1} 0
7 8 {2 3} 1
8 33 {-400 -155} 1
} {
do_execsql_test 1.$tn.0 {
DROP TABLE IF EXISTS v1;
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<5
)
INSERT INTO v1(v) SELECT random_binvec($N, $opts) FROM s;
}
set spec "{quantizer:\"bq\", nbucket:$nbucket, wht:0}"
do_execsql_test 1.$tn.1 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, $spec) FROM v1
));
} {}
do_execsql_test 1.$tn.2 {
PRAGMA integrity_check
} {ok}
for {set j 0} {$j < $nQuery} {incr j} {
set q [random_binvec $N $opts]
set expect [db one {
SELECT r FROM (
SELECT group_concat(rowid, ' ') AS r, vec1_l2_distance($q, v) FROM v1
GROUP BY 2
ORDER BY 2 ASC
LIMIT 1
)
}]
set nExpect [llength $expect]
do_execsql_test 1.$tn.2.$j. {
SELECT r FROM (
SELECT rowid AS r FROM v1($q, $nExpect)
) ORDER BY 1
} $expect
}
}
finish_test

View File

@@ -0,0 +1,65 @@
# 2026-04-02
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1blocksize
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_catchsql_test 1.1 {
INSERT INTO v1(cmd, arg) VALUES('blocksize', 64);
} {1 {'blocksize' requires an integer value between 1024 and 16777216}}
do_catchsql_test 1.2 {
INSERT INTO v1(cmd, arg) VALUES('blocksize', 1023);
} {1 {'blocksize' requires an integer value between 1024 and 16777216}}
do_catchsql_test 1.3 {
INSERT INTO v1(cmd, arg) VALUES('blocksize', 16777217);
} {1 {'blocksize' requires an integer value between 1024 and 16777216}}
do_execsql_test 1.4 {
INSERT INTO v1(cmd, arg) VALUES('blocksize', 1024);
INSERT INTO v1(cmd, arg) VALUES('blocksize', 16777216);
} {}
do_catchsql_test 2.1 {
INSERT INTO v1(cmd, arg) VALUES('blocksize-min', 64);
} {1 {'blocksize-min' requires an integer value between 1024 and 16777216}}
do_catchsql_test 2.2 {
INSERT INTO v1(cmd, arg) VALUES('blocksize-min', 1023);
} {1 {'blocksize-min' requires an integer value between 1024 and 16777216}}
do_catchsql_test 2.3 {
INSERT INTO v1(cmd, arg) VALUES('blocksize-min', 16777217);
} {1 {'blocksize-min' requires an integer value between 1024 and 16777216}}
do_execsql_test 2.4 {
INSERT INTO v1(cmd, arg) VALUES('blocksize-min', 1024);
INSERT INTO v1(cmd, arg) VALUES('blocksize-min', 16777216);
} {}
do_catchsql_test 3.0 {
INSERT INTO v1(cmd, arg) VALUES('not-blocksize', 1024);
} {1 {vec1: unknown command: not-blocksize}}
do_catchsql_test 3.1 {
INSERT INTO v1(cmd, arg) VALUES('blocksize', 'not integer');
} {1 {'blocksize' requires an integer value between 1024 and 16777216}}
finish_test

247
ext/vec1/test/vec1cat.test Normal file
View File

@@ -0,0 +1,247 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1cat
vec1_load db
do_execsql_test 1.0 {
PRAGMA table_info = vec1cat
} {
0 database {} 0 {} 0
1 name {} 0 {} 0
2 model {} 0 {} 0
3 qprofile {} 0 {} 0
}
do_execsql_test 1.1 {
CREATE VIRTUAL TABLE x1 USING vec1(x);
SELECT database, name FROM vec1cat;
} {
main x1
}
db close
sqlite3 db test.db
vec1_load db
do_execsql_test 1.2 {
SELECT database, name, model FROM vec1cat;
} {
main x1 {{"index": "none", "distance": "l2"}}
}
do_execsql_test 1.3 {
INSERT INTO x1(cmd, arg) VALUES( 'rebuild', '{index:"flat", distance:"cos"}');
}
do_execsql_test 1.4 {
SELECT database, name, model FROM vec1cat;
} {
main x1 {{"index": "flat", "distance": "cos"}}
}
db close
sqlite3 db test.db
vec1_load db
do_execsql_test 1.5 {
SELECT database, name, model FROM vec1cat;
} {
main x1 {{"index": "flat", "distance": "cos"}}
}
do_execsql_test 1.6 {
INSERT INTO x1(cmd, arg) VALUES('rebuild', (
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000)
SELECT vec1_train(random_vector(16), '{nbucket:8, codesize:8}') FROM s
));
SELECT database, name, model FROM vec1cat;
} {
main x1
{{"index": "trained", "distance": "l2", "nbucket": 8, "quantizer": "pq", "codesize": 8, "residual": true, "wht": false}}
}
do_execsql_test 1.7 {
CREATE VIRTUAL TABLE temp.x2 USING vec1(x);
SELECT rowid, database, name, model FROM vec1cat;
} {
1 temp x2
{{"index": "none", "distance": "l2"}}
2 main x1
{{"index": "trained", "distance": "l2", "nbucket": 8, "quantizer": "pq", "codesize": 8, "residual": true, "wht": false}}
}
foreach {tn spec res} {
1 {nbucket:0, codesize:8} {
"index": "trained", "distance": "l2", "nbucket": 0,
"quantizer": "pq", "codesize": 8,
"residual": false, "wht": false
}
2 {nbucket:8, codesize:0} {
"index": "trained", "distance": "l2", "nbucket": 8,
"quantizer": "none", "codesize": 0,
"residual": false, "wht": false
}
3 {nbucket:0, codesize:8, opq:1} {
"index": "trained", "distance": "l2", "nbucket": 0,
"quantizer": "opq", "codesize": 8,
"residual": false, "wht": false
}
4 {codesize:2, quantizer: "bq", distance: "cos"} {
"index": "trained", "distance": "cos", "nbucket": 0,
"quantizer": "bq", "codesize": 2,
"residual": false, "wht": true
}
5 {codesize:1, nbucket: 16, quantizer: "bq", distance: "l2", wht: 0} {
"index": "trained", "distance": "l2", "nbucket": 16,
"quantizer": "bq", "codesize": 1,
"residual": true, "wht": false
}
6 {codesize:0, nbucket: 0} {
"index":"flat", "distance":"l2"
}
7 {} {
"index":"flat", "distance":"l2"
}
} {
set model [db one {
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000)
SELECT vec1_train(random_vector(16), '{'||$spec||'}') FROM s
}]
set output [db one { SELECT json('{' || $res || '}') }]
do_execsql_test 1.8.$tn.1 {
INSERT INTO x1(cmd, arg) VALUES( 'rebuild', $model );
SELECT json(model) FROM vec1cat WHERE name='x1';
} [list $output]
if {[db one {SELECT json_extract($output, '$.index')=='trained'}]} {
set model [db one {
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000)
SELECT vec1_train(random_vector(16), $output) FROM s
}]
} else {
set model $output
}
do_execsql_test 1.8.$tn.2 {
INSERT INTO x1(cmd, arg) VALUES( 'rebuild', $model );
SELECT json(model) FROM vec1cat WHERE name='x1';
} [list $output]
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE x1 USING vec1(x);
CREATE VIRTUAL TABLE x2 USING vec1(y);
CREATE VIRTUAL TABLE x3 USING vec1(z);
}
do_execsql_test 2.1 {
SELECT rowid, name FROM vec1cat
} {
1 x3 2 x2 3 x1
}
do_test 2.2 {
list [catch {
db eval {
SELECT rowid, name FROM vec1cat
} {
if {$name=="x2"} {
db eval {
DROP TABLE x2;
DROP TABLE x1;
}
}
}
} msg] $msg
} {1 {database table is locked}}
do_execsql_test 2.3 {
SELECT qprofile FROM vec1cat WHERE name='x1';
} {{{
"total": {"cnt": 0, "cycle": 0},
"init": {"cnt": 0, "cycle": 0},
"coarse": {"cnt": 0, "cycle": 0},
"idxread": {"cnt": 0, "cycle": 0},
"metaread": {"cnt": 0, "cycle": 0},
"metascan": {"cnt": 0, "cycle": 0},
"lut": {"cnt": 0, "cycle": 0},
"pqscan": {"cnt": 0, "cycle": 0},
"finalsort": {"cnt": 0, "cycle": 0}
}}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE x2 USING vec1(y);
INSERT INTO x2 VALUES( random_vector(13) );
INSERT INTO x2 VALUES( random_vector(13) );
INSERT INTO x2 VALUES( random_vector(13) );
INSERT INTO x2 VALUES( random_vector(13) );
INSERT INTO x2 VALUES( random_vector(13) );
INSERT INTO x2(cmd, arg) VALUES('rebuild', (
WITH s(i) AS ( VALUES (1) UNION ALL SELECT i+1 FROM s WHERE i<512 )
SELECT vec1_train( random_vector(13), '{nbucket:8}' ) FROM s
))
}
do_execsql_test 3.1 {
SELECT count(distance) FROM x2;
} 5
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE x1 USING vec1(y, a);
INSERT INTO x1(cmd, arg) VALUES('rebuild', (
WITH s(i) AS ( VALUES (1) UNION ALL SELECT i+1 FROM s WHERE i<512 )
SELECT vec1_train(
random_vector(32), '{nbucket:8, codesize:8, distance:"cos"}'
) FROM s
))
}
do_execsql_test 4.1 {
INSERT INTO x1 (rowid, y, a) VALUES(10, random_vector(32), 4556);
}
do_execsql_test 4.2 {
SELECT key FROM json_each( (SELECT distance FROM x1) )
} {bucket coarse_error reconstruction_error code}
do_execsql_test 4.3 {
SELECT typeof(distance) FROM x1(random_vector(32), '{nprobe:8, K:5}')
} {real}
finish_test

125
ext/vec1/test/vec1cat2.test Normal file
View File

@@ -0,0 +1,125 @@
# 2026-08-20
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1cat2
vec1_load db
foreach {tn training expect} {
1 { codesize: 16 } {
"index": "trained",
"distance": "l2",
"nbucket": 0,
"quantizer": "pq",
"codesize": 16,
"residual": false,
"wht": false
}
2 { quantizer: "pq" } {
"index": "trained",
"distance": "l2",
"nbucket": 0,
"quantizer": "pq",
"codesize": 16,
"residual": false,
"wht": false
}
3 { quantizer: "opq" } {
"index": "trained",
"distance": "l2",
"nbucket": 0,
"quantizer": "opq",
"codesize": 32,
"residual": false,
"wht": false
}
4 { quantizer: "bq" } {
"index": "trained",
"distance": "l2",
"nbucket": 0,
"quantizer": "bq",
"codesize": 16,
"residual": false,
"wht": true
}
5 { nbucket: 10 } {
"index": "trained",
"distance": "l2",
"nbucket": 10,
"quantizer": "none",
"codesize": 0,
"residual": false,
"wht": false
}
6 { nbucket: 10, quantizer: "pq" } {
"index": "trained",
"distance": "l2",
"nbucket": 10,
"quantizer": "pq",
"codesize": 16,
"residual": true,
"wht": false
}
7 { quantizer: "pq", codesize: 0 } {
"index": "trained",
"distance": "l2",
"nbucket": 0,
"quantizer": "pq",
"codesize": 16,
"residual": false,
"wht": false
}
} {
do_execsql_test 1.$tn.1 {
DROP TABLE IF EXISTS v1;
CREATE VIRTUAL TABLE v1 USING vec1 (v);
}
do_execsql_test 1.$tn.2 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512 ),
data(v) AS ( SELECT random_vector(128) FROM s)
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{' || $training || '}') FROM data
));
SELECT json(model) FROM vec1cat;
} [db eval { SELECT json( '{' || $expect || '}' )} ]
do_execsql_test 1.$tn.3 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512 ),
data(v) AS ( SELECT random_vector(128) FROM s)
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, (SELECT model FROM vec1cat)) FROM data
));
SELECT json(model) FROM vec1cat;
} [db eval { SELECT json( '{' || $expect || '}' )} ]
}
finish_test

View File

@@ -0,0 +1,113 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1config
vec1_load db
do_execsql_test 1.0 { SELECT vec1_config('nthread'); } {1}
do_execsql_test 1.1 { SELECT vec1_config('nprobe'); } {0.05}
do_catchsql_test 1.2 {
SELECT vec1_config('nosuch');
} {1 {vec1: no such parameter: nosuch}}
do_catchsql_test 1.3.1 {
SELECT vec1_config('nthread', 0);
} {1 {vec1: nthread requires an integer value between 1 and 128}}
do_catchsql_test 1.3.2 {
SELECT vec1_config('nthread', 129);
} {1 {vec1: nthread requires an integer value between 1 and 128}}
do_test 1.3.3 {
catch { execsql { SELECT vec1_config('nthread', 128) } }
} {0}
do_catchsql_test 1.3.4 {
SELECT vec1_config('nthread', 1);
} {0 1}
do_catchsql_test 1.4.1 {
SELECT vec1_config('nprobe', -1);
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 1.4.2 {
SELECT vec1_config('nprobe', 0.0);
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 1.4.3 {
SELECT vec1_config('nprobe', 4.0);
} {0 4.0}
do_catchsql_test 1.4.4 {
SELECT vec1_config('nprobe', 'hello');
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 1.4.5 {
SELECT vec1_config('nprobe', '1hello');
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 1.4.6 {
SELECT vec1_config('nprobe', NULL);
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 1.5.1 {
SELECT vec1_config('nthread', 'notaninteger');
} {1 {vec1: nthread requires an integer value between 1 and 128}}
#-------------------------------------------------------------------------
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE x1 USING vec1(v);
}
do_catchsql_test 2.1.1 {
SELECT * FROM x1(random_vector(8), '{nprobe: -1, K: 500}')
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 2.1.2 {
SELECT * FROM x1(random_vector(8), '{nprobe: 0.0, K: 500}')
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 2.1.3 {
SELECT * FROM x1(random_vector(8), '{nprobe: "world", K: 500}')
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 2.1.4 {
SELECT * FROM x1(random_vector(8), '{nprobe: "2world", K: 500}')
} {1 {vec1: nprobe must be a numeric value greater than 0.0}}
do_catchsql_test 2.1.5 {
SELECT * FROM x1(random_vector(8), '{nprobe: 0.5, K: 500}')
} {0 {}}
do_catchsql_test 2.2.1 {
SELECT * FROM x1(random_vector(8), '{nprobe: 0.2, K: -1}')
} {1 {vec1: K must be an integer value between 0 and 33554432}}
do_catchsql_test 2.2.2 {
SELECT * FROM x1(random_vector(8), -1)
} {1 {vec1: K must be an integer value between 0 and 33554432}}
do_catchsql_test 2.2.3 {
SELECT * FROM x1(random_vector(8), '1hello')
} {1 {malformed JSON}}
do_catchsql_test 2.2.4 {
SELECT * FROM x1(random_vector(8), '5')
} {0 {}}
do_catchsql_test 2.2.5 {
SELECT * FROM x1(random_vector(8), 5.0)
} {0 {}}
do_catchsql_test 2.2.6 {
SELECT * FROM x1(random_vector(8), '64.0')
} {0 {}}
do_catchsql_test 2.2.7 {
SELECT * FROM x1(random_vector(8), 4.5)
} {1 {vec1: K must be an integer value between 0 and 33554432}}
do_catchsql_test 2.2.8 {
SELECT * FROM x1(random_vector(8), '{K:4.5}')
} {1 {vec1: K must be an integer value between 0 and 33554432}}
finish_test

View File

@@ -0,0 +1,57 @@
# 2026-08-21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1corrupt
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO v1(rowid, vector) SELECT NULL, random_vector(128) FROM s;
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 1.1 {
PRAGMA integrity_check
} {ok}
do_execsql_test 1.2 {
UPDATE v1_idx SET val = unhex(
substr( hex(val), 1, 8 ) || '40' || substr( hex(val), 11 )
);
} {}
do_catchsql_test 1.3 {
SELECT count(rowid) FROM v1( random_vector(128), 100 );
} {1 {database disk image is malformed}}
do_execsql_test 1.3 {
PRAGMA integrity_check
} {{v1: %_idx entry id=1 is corrupt}}
#-------------------------------------------------------------------------
finish_test

View File

@@ -0,0 +1,114 @@
# 2026-08-19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1config
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1("vector column", "meta column");
}
do_execsql_test 1.1 {
SELECT v1."vector column", v1."meta column" FROM v1
}
foreach {tn cols err} {
1 { vector BLOB } {vec1: trailing characters after column name}
2 { v, col BLOB } {vec1: trailing characters after column name}
3 { v, [col BLOB]x } {vec1: trailing characters after column name}
4 { "vector col" BLOB } {vec1: trailing characters after column name}
5 { v, +i } {vec1: near "+": syntax error}
} {
execsql { DROP TABLE IF EXISTS v1 }
do_catchsql_test 1.2.$tn "
CREATE VIRTUAL TABLE v1 USING vec1( $cols )
" [list 1 $err]
}
foreach {tn cols lCol} {
1 { vector } {vector}
2 { "vector col" } {{vector col}}
3 { v, 'meta 1' } {v {meta 1}}
4 { v, 'Dave''s col' } {v {Dave's col}}
5 { v, [column [[[] } {v {column [[[}}
} {
execsql { DROP TABLE IF EXISTS v1 }
do_test 1.3.$tn {
execsql "CREATE VIRTUAL TABLE v1 USING vec1( $cols );"
set ret [list]
db eval { PRAGMA table_info = v1 } a {
lappend ret $a(name)
}
set ret
} $lCol
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE "Count Duckula's 11" USING vec1(
"vector column", "meta column"
);
}
do_execsql_test 2.1 {
SELECT * FROM "Count Duckula's 11";
}
do_execsql_test 2.2 {
ALTER TABLE "Count Duckula's 11" RENAME TO "Bill's table";
}
do_execsql_test 2.3 {
INSERT INTO "Bill's table" VALUES(random_vector(10), 1100);
INSERT INTO "Bill's table" VALUES(random_vector(10), 'text');
INSERT INTO "Bill's table"(cmd, arg) VALUES('rebuild', '{index:"flat"}');
PRAGMA integrity_check;
} {ok}
do_execsql_test 2.4 {
ALTER TABLE "Bill's table" RENAME TO "a b c ''' d e f";
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
# Try to create tables with reserved column names "rowid" or "distance".
# Report https://sqlite.org/vec1/forumpost/05e888d619
#
reset_db
vec1_load db
breakpoint
foreach {tn cols err} {
1 "rowid" {1 {vec1: reserved column name: rowid}}
2 "ROWID" {1 {vec1: reserved column name: ROWID}}
3 " [rowid] " {1 {vec1: reserved column name: [rowid]}}
4 " \"rowid\" " {1 {vec1: reserved column name: "rowid"}}
5 " 'roWid' " {1 {vec1: reserved column name: 'roWid'}}
6 " [ rowid]" {0 {}}
5 " 'ro''Wid' " {0 {}}
} {
execsql { DROP TABLE IF EXISTS g1 }
do_catchsql_test 3.$tn "
CREATE VIRTUAL TABLE g1 USING vec1 ( $cols )
" $err
}
finish_test

View File

@@ -0,0 +1,279 @@
# 2026-04-02
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1delete
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_execsql_test 1.1 {
INSERT INTO v1(rowid, vector) VALUES(1, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(2, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(3, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(4, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(5, random_vector(128));
INSERT INTO v1(cmd, vector) VALUES('rebuild', (
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512
) SELECT vec1_train( random_vector(128), '{nbucket:4,codesize:8}' ) FROM s
));
}
do_execsql_test 1.2 {
INSERT INTO v1_base VALUES(6, random_vector(128));
}
do_catchsql_test 1.3 {
DELETE FROM v1 WHERE rowid=6
} {1 {database disk image is malformed}}
do_execsql_test 1.4 {
DELETE FROM v1_base WHERE rowid=6;
UPDATE v1_base SET vector = random_vector(32) WHERE id=5;
} {}
do_catchsql_test 1.5 {
DELETE FROM v1 WHERE rowid=5
} {1 {database disk image is malformed}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_execsql_test 2.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1
) INSERT INTO v1(rowid, vector) SELECT i, random_vector(32) FROM s;
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 2.2 {
DELETE FROM v1 WHERE rowid=1;
}
do_execsql_test 2.3 {
SELECT rowid FROM v1(random_vector(32), 10);
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
INSERT INTO v1(cmd, vector) VALUES('rebuild', (
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
) SELECT vec1_train( random_vector(32), '{codesize:8}' ) FROM s
));
}
do_execsql_test 2.1 {
BEGIN;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
) INSERT INTO v1(rowid, vector) SELECT i, random_vector(32) FROM s;
}
do_test 2.2 {
for {set ii 1} {$ii < 1000} {incr ii 2} {
execsql {
DELETE FROM v1 WHERE rowid=$ii
}
}
execsql COMMIT
} {}
do_execsql_test 2.3 {
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE v1 USING vec1(a, b);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
) INSERT INTO v1(rowid, a) SELECT i, random_vector(10) FROM s;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
) INSERT INTO v1(rowid, a) SELECT i*10_000_000_000, random_vector(10) FROM s;
UPDATE v1 SET b=rowid;
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 3.1.1 {
SELECT rowid FROM v1(random_vector(10)) WHERE b=250 LIMIT 10;
} {250}
do_execsql_test 3.1.2 {
DELETE FROM v1 WHERE rowid = 250;
} {}
do_execsql_test 3.1.3 {
SELECT rowid FROM v1(random_vector(10)) WHERE b=250 LIMIT 10;
} {}
do_execsql_test 3.2.0 {
SELECT rowid FROM v1 WHERE b=250*10_000_000_000 LIMIT 10;
} {2500000000000}
do_execsql_test 3.2.1 {
SELECT rowid FROM v1 (random_vector(10)) WHERE b=250*10_000_000_000 LIMIT 10;
} {2500000000000}
do_execsql_test 3.2.2 {
DELETE FROM v1 WHERE rowid = 250*10_000_000_000;
} {}
do_execsql_test 3.2.3 {
SELECT rowid FROM v1(random_vector(10)) WHERE b=250*10_000_000_000 LIMIT 10;
} {}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE v1 USING vec1(a, b);
INSERT INTO v1(rowid, a, b)
VALUES(250*10_000_000_000, random_vector(10), 250*10_000_000_000);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 4.1.0 {
SELECT rowid FROM v1 WHERE b=250*10_000_000_000 LIMIT 10;
} {2500000000000}
do_execsql_test 4.1.1 {
SELECT rowid FROM v1 (random_vector(10)) WHERE b=250*10_000_000_000 LIMIT 10;
} {2500000000000}
do_execsql_test 4.1.2 {
DELETE FROM v1 WHERE rowid = 250*10_000_000_000;
} {}
do_execsql_test 4.1.3 {
SELECT rowid FROM v1(random_vector(10)) WHERE b=250*10_000_000_000 LIMIT 10;
} {}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE v1 USING vec1(a, b);
INSERT INTO v1 VALUES(random_vector(32), 'one');
INSERT INTO v1 VALUES(random_vector(32), 'two');
INSERT INTO v1 VALUES(random_vector(32), 'three');
INSERT INTO v1 VALUES(random_vector(32), 'four');
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 5.1 {
BEGIN;
INSERT INTO v1(rowid, a, b)
VALUES(-20_000_000_000, random_vector(32), 'five');
INSERT INTO v1(rowid, a, b)
VALUES(-30_000_000_000, random_vector(32), 'six');
INSERT INTO v1(rowid, a, b)
VALUES(-40_000_000_000, random_vector(32), 'seven');
DELETE FROM v1 WHERE rowid=3;
}
do_execsql_test 5.2 {
SELECT rowid, length(a), b FROM v1 ORDER BY 1
} {
-40000000000 128 seven
-30000000000 128 six
-20000000000 128 five
1 128 one
2 128 two
4 128 four
}
do_execsql_test 5.3 {
COMMIT;
PRAGMA integrity_check
} {ok}
do_execsql_test 5.4 {
INSERT INTO v1_base(id, vector, c0) VALUES(11, 0, 'nan');
}
do_execsql_test 5.5 {
SELECT length(a) FROM v1 WHERE rowid=11
} {{}}
do_execsql_test 5.6 {
SELECT distance FROM v1 WHERE rowid=11
} {{}}
do_execsql_test 5.7 {
SELECT distance FROM v1 WHERE rowid=4
} {{{bucket:0, coarse_error:0.000000, reconstruction_error:0.000000, code: null}}}
do_execsql_test 5.8 {
SELECT distance, distance FROM v1 WHERE rowid=4
} {
{{bucket:0, coarse_error:0.000000, reconstruction_error:0.000000, code: null}}
{{bucket:0, coarse_error:0.000000, reconstruction_error:0.000000, code: null}}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE x1 USING vec1( v );
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<400
)
INSERT INTO x1(rowid, v)
SELECT CASE WHEN i%2 THEN -i ELSE +i END, random_vector(8) FROM s;
INSERT INTO x1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 6.1 {
PRAGMA integrity_check
} {ok}
do_execsql_test 6.2 {
INSERT INTO x1(rowid, v) VALUES(0, random_vector(8));
} {}
do_execsql_test 6.3 {
DELETE FROM x1 WHERE abs(rowid)%10 = 1;
} {}
do_execsql_test 6.4 {
PRAGMA integrity_check
} {ok}
do_execsql_test 6.5 {
DELETE FROM x1 WHERE rowid = 0;
} {}
do_execsql_test 6.6 {
PRAGMA integrity_check
} {ok}
finish_test

View File

@@ -0,0 +1,85 @@
# 2026-08-04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1error
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
INSERT INTO v1(rowid, v) VALUES(11, random_vector(8));
INSERT INTO v1(rowid, v) VALUES(12, random_vector(8));
}
do_catchsql_test 1.1 {
SELECT rowid FROM v1(random_vector(8), '{K:10, nprobes:10}');
} {1 {unrecognized option 'nprobes', should be one of nprobe, K, streaming, or nprobe_slack}}
do_catchsql_test 1.2 {
SELECT vec1_train(v, '{quantizer:"donuts"}') FROM v1;
} {1 {unrecognized quantizer 'donuts', should be one of none, pq, opq, or bq}}
do_catchsql_test 1.3 {
SELECT vec1_train(v, '{nthread:0}') FROM v1;
} {1 {vec1: nthread must be set to an integer value between 1 and 128}}
#-------------------------------------------------------------------------
# Ensure that INSERT is not confused by a vector for which the squared
# L2 is close to INFINITY for all centroids.
#
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v1 USING vec1(a);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1(rowid, a) SELECT i, random_vector(4) FROM s;
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{nbucket:8}') FROM v1
));
}
do_execsql_test 2.1 {
INSERT INTO v1(a) VALUES( vec1_from_json('[1e30, 1e30, 1e30, 1e30]') );
}
#-------------------------------------------------------------------------
#
reset_db
vec1_load db
do_test 3.1 { sqlite3_db_config db TRUSTED_SCHEMA 0 } {0}
do_execsql_test 3.2 {
CREATE VIRTUAL TABLE t1 USING vec1(x);
INSERT INTO t1 VALUES( random_vector(1024) );
INSERT INTO t1 VALUES( random_vector(1024) );
CREATE VIEW v1 AS SELECT rowid, x FROM t1;
}
do_execsql_test 3.3 {
SELECT rowid FROM t1
} {1 2}
do_execsql_test 3.4 {
SELECT rowid FROM v1
} {1 2}
finish_test

View File

@@ -0,0 +1,213 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault1
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE mytbl USING vec1(a, b);
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
execsql { PRAGMA table_info = mytbl }
} -body {
execsql {
INSERT INTO mytbl VALUES(random_vector(128), 555);
}
} -test {
faultsim_test_result {0 {}} \
{1 {unable to open a temporary database file for storing temporary tables}}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE mytbl USING vec1(a, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<600 )
INSERT INTO mytbl SELECT random_vector(8), i FROM s;
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{codesize:8, nbucket:2}') FROM mytbl
));
}
do_faultsim_test 2 -faults oom* -body {
execsql {
PRAGMA integrity_check
}
} -test {
faultsim_test_result {0 ok} \
{1 {unable to open a temporary database file for storing temporary tables}}
}
#-------------------------------------------------------------------------
# OOM error while loading the extension.
#
reset_db
breakpoint
if {[catch {db one {SELECT vec1_info()}}]} {
do_faultsim_test 3 -faults oom* -prep {
reset_db
} -body {
vec1_load_only db
} -test {
faultsim_test_result {0 {}} {1 {}} {1 {error during initialization: }}
}
}
#-------------------------------------------------------------------------
# OOM error while deleting lots of rows
#
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE mytbl USING vec1(a);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO mytbl SELECT random_vector(8) FROM s;
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
faultsim_save_and_close
do_faultsim_test 4 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
db eval { PRAGMA table_info = mytbl }
} -body {
execsql {
DELETE FROM mytbl WHERE rowid BETWEEN 30 AND 60
}
} -test {
faultsim_test_result {0 {}}
}
#-------------------------------------------------------------------------
# OOM error in vec1cat query
#
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE mytbl USING vec1(a, b);
CREATE VIRTUAL TABLE x1 USING vec1(a, b);
}
set P {{{
"total": {"cnt": 0, "cycle": 0},
"init": {"cnt": 0, "cycle": 0},
"coarse": {"cnt": 0, "cycle": 0},
"idxread": {"cnt": 0, "cycle": 0},
"metaread": {"cnt": 0, "cycle": 0},
"metascan": {"cnt": 0, "cycle": 0},
"lut": {"cnt": 0, "cycle": 0},
"pqscan": {"cnt": 0, "cycle": 0},
"finalsort": {"cnt": 0, "cycle": 0}
}}}
do_faultsim_test 5 -faults oom-* -prep {
execsql { PRAGMA table_info = mytbl }
} -body {
execsql {
SELECT rowid, model, qprofile FROM vec1cat
}
} -test {
faultsim_test_result [subst {0 {1 {{"index": "none", "distance": "l2"}} $::P 2 {{"index": "none", "distance": "l2"}} $::P}}]
}
#-------------------------------------------------------------------------
# OOM error in vec1FlatToNone()
#
reset_db
vec1_load db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE hello USING vec1(a, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO hello SELECT random_vector(8), i*100 FROM s;
INSERT INTO hello(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_faultsim_test 6 -faults oom-* -prep {
sqlite3 db test.db
vec1_load db
execsql { PRAGMA table_info = mytbl }
} -body {
execsql {
INSERT INTO hello(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
} -test {
faultsim_test_result {0 {}}
}
#-------------------------------------------------------------------------
# OOM error in vec1FlatToNone()
#
reset_db
vec1_load db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE hello USING vec1(a, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1100 )
INSERT INTO hello SELECT random_vector(8), i*100 FROM s;
}
set mod [db one {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<600
)
SELECT vec1_train(random_vector(8), '{codesize:8}') FROM s
}]
do_faultsim_test 7 -faults oom* -prep {
sqlite3 db test.db
vec1_load db
execsql { PRAGMA table_info = mytbl }
} -body {
execsql {
INSERT INTO hello(cmd, arg) VALUES('rebuild', $::mod);
}
} -test {
faultsim_test_result {0 {}}
}
set mod [db one {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<600
)
SELECT vec1_train(random_vector(8), '{nbucket:8}') FROM s
}]
do_faultsim_test 8 -faults oom* -prep {
sqlite3 db test.db
vec1_load db
execsql { PRAGMA table_info = mytbl }
} -body {
execsql {
INSERT INTO hello(cmd, arg) VALUES('rebuild', $::mod);
}
} -test {
faultsim_test_result {0 {}}
}
finish_test

View File

@@ -0,0 +1,49 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault2
#-------------------------------------------------------------------------
# OOM error in multi-threaded 'rebuild'.
#
reset_db
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE mytbl USING vec1(a, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<600 )
INSERT INTO mytbl SELECT random_vector(8), i FROM s;
}
faultsim_save_and_close
proc progress {percent msg} { }
do_faultsim_test 1 -faults oom-t* -prep {
faultsim_restore_and_reopen
vec1_load db
db func progress progress
db eval { PRAGMA table_info = mytbl }
db eval { SELECT vec1_config('nthread', 2) }
} -body {
execsql {
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{codesize:8, progress: "progress"}') FROM mytbl
));
}
} -test {
faultsim_test_result {0 {}}
}
finish_test

View File

@@ -0,0 +1,106 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault3
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING vec1(a, b, c, d, e);
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom-t* -prep {
faultsim_restore_and_reopen
vec1_load db
db eval { PRAGMA table_info = t1 }
execsql {
INSERT INTO t1 VALUES(random_vector(8), NULL, $f, 'helloworld', X'123456')
}
} -body {
execsql BEGIN
for {set ii 0} {$ii < 1000} {incr ii} {
set f [expr rand()]
execsql {
INSERT INTO t1 VALUES(random_vector(8), NULL, $f, 'helloworld', X'123456')
}
}
execsql COMMIT
} -test {
faultsim_test_result {0 {}}
catchsql ROLLBACK
}
do_faultsim_test 2 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
BEGIN;
INSERT INTO t1(rowid, a) VALUES(5555, random_vector(24));
INSERT INTO t1(rowid, a) VALUES(20_000_000_000, random_vector(24));
COMMIT;
}
} -test {
faultsim_test_result {0 {}}
catchsql ROLLBACK
}
#--------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING vec1(a, b);
INSERT INTO t1(cmd, arg) VALUES('rebuild', (
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
) SELECT vec1_train( random_vector(16), '{codesize:8}' ) FROM s
));
}
faultsim_save_and_close
do_faultsim_test 3.1 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
execsql {
BEGIN;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
) INSERT INTO t1(a, b) SELECT random_vector(16), i FROM s;
}
} -body {
for {set ii 1} {$ii < 30} {incr ii} {
execsql { DELETE FROM t1 WHERE rowid=$ii }
}
} -test {
faultsim_test_result {0 {}}
catchsql ROLLBACK
}
do_faultsim_test 3.2 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
execsql { PRAGMA table_info = t1 }
} -body {
execsql {
INSERT INTO t1 VALUES( random_vector(16), 1100 );
}
} -test {
faultsim_test_result {0 {}}
}
finish_test

View File

@@ -0,0 +1,149 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault4
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING vec1(a, b);
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO t1 SELECT random_vector(13), i+0.5 FROM s;
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
db eval { PRAGMA table_info = t1 }
} -body {
execsql {
SELECT count(*) FROM t1(random_vector(13), 13);
}
} -test {
faultsim_test_result {0 13}
}
do_faultsim_test 2 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
db eval { PRAGMA table_info = t1 }
} -body {
execsql {
SELECT count(*) FROM t1(random_vector(13), '{K:5000,streaming:true}');
}
} -test {
faultsim_test_result {0 10000}
}
do_execsql_test 2.1 {
INSERT INTO t1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{nbucket:8}') FROM t1 WHERE rowid<1000
));
}
faultsim_save_and_close
do_faultsim_test 2.2 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
db eval { PRAGMA table_info = t1 }
} -body {
execsql {
SELECT count(*) FROM t1(
random_vector(13), '{K:5000,streaming:true,nprobe:2}'
);
}
} -test {
faultsim_test_result {0 10000}
}
faultsim_restore_and_reopen
vec1_load db
do_execsql_test 3.0 {
INSERT INTO t1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{codesize:13}') FROM t1 WHERE rowid<1000
));
}
faultsim_save_and_close
breakpoint
do_faultsim_test 3 -faults oom-t* -prep {
faultsim_restore_and_reopen
vec1_load db
db eval { PRAGMA table_info = t1 }
} -body {
execsql {
SELECT count(*) FROM t1(random_vector(13), 13);
}
} -test {
faultsim_test_result {0 13}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t1 USING vec1(a, b);
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO t1(a, b) VALUES( random_vector(13), 1.123 );
}
faultsim_save_and_close
do_faultsim_test 4 -faults oom-t* -prep {
faultsim_restore_and_reopen
vec1_load db
db eval { PRAGMA table_info = t1 }
} -body {
execsql {
INSERT INTO t1(a, b) VALUES( random_vector(13), 'text' );
}
} -test {
faultsim_test_result {0 {}}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE TABLE t1(a);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1 SELECT random_vector(12) FROM s;
}
proc progress {args} {}
faultsim_save_and_close
do_faultsim_test 5 -faults oom-t* -prep {
faultsim_restore_and_reopen
vec1_load db
db func progress progress
db eval { SELECT count(*) FROM t1 }
} -body {
execsql {
SELECT length( vec1_train(a, '{nbucket:8, progress:"progress"}') ) FROM t1
}
} -test {
faultsim_test_result {0 408}
}
finish_test

View File

@@ -0,0 +1,42 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault5
vec1_load db
do_execsql_test 1.0 {
CREATE TABLE t1(a);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512
) INSERT INTO t1 SELECT random_vector(16) FROM s;
}
do_execsql_test 1.1 {
SELECT min(length(a)) FROM t1
} 64
do_execsql_test 1.2 {
SELECT length( vec1_train(a, '{codesize:8, opq:1, nopq_round:2,nbucket:2}') ) FROM t1;
} {17560}
do_faultsim_test 1.3 -faults oom-t* -prep {
} -body {
execsql {
SELECT length( vec1_train(a, '{codesize:8, opq:1, nopq_round:2,nbucket:2}') ) FROM t1;
}
} -test {
faultsim_test_result {0 17560}
}
finish_test

View File

@@ -0,0 +1,46 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault6
vec1_load db
faultsim_save_and_close
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
CREATE VIRTUAL TABLE v1 USING vec1(v, a, b);
}
} -test {
faultsim_test_result {0 {}}
}
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE x1 USING vec1(a, b, c);
}
faultsim_save_and_close
do_faultsim_test 2 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql { DROP TABLE x1 }
} -test {
faultsim_test_result {0 {}}
}
finish_test

View File

@@ -0,0 +1,65 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault7
vec1_load db
do_execsql_test 1 {
CREATE VIRTUAL TABLE t1 USING vec1(v, b);
INSERT INTO t1 VALUES(random_vector(8), 10);
INSERT INTO t1 VALUES(random_vector(8), 20);
INSERT INTO t1 VALUES(random_vector(8), 30);
INSERT INTO t1 VALUES(random_vector(8), 40);
INSERT INTO t1 VALUES(random_vector(8), 50);
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
SELECT rowid, length(v)
FROM t1(random_vector(8), '{K:10}')
WHERE b IN (10, 30, 50)
ORDER BY 1;
}
} -test {
faultsim_test_result {0 {1 32 3 32 5 32}}
}
do_execsql_test 2 {
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"none"}');
}
faultsim_save_and_close
do_faultsim_test 2 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
SELECT rowid
FROM t1(random_vector(8), '{K:10}')
WHERE b IN (10, 30, 50)
ORDER BY 1;
}
} -test {
faultsim_test_result {0 {1 3 5}}
}
finish_test

View File

@@ -0,0 +1,124 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault8
vec1_load db
do_execsql_test 1 {
CREATE VIRTUAL TABLE t1 USING vec1(v, b);
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
BEGIN;
INSERT INTO t1 VALUES( random_vector(4), 1.5 );
INSERT INTO t1 VALUES( random_vector(4), 2.5 );
INSERT INTO t1 VALUES( random_vector(4), 3.5 );
INSERT INTO t1 VALUES( random_vector(4), 4.5 );
COMMIT;
}
} -test {
faultsim_test_result {0 {}}
}
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING vec1(v, b);
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO t1 VALUES( random_vector(10), -10_000_000_000 );
}
faultsim_save_and_close
do_faultsim_test 2 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
DELETE FROM t1 WHERE rowid=1;
}
} -test {
faultsim_test_result {0 {}}
}
#--------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING vec1(v, b);
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO t1 VALUES( random_vector(10), 'hello');
INSERT INTO t1 VALUES( random_vector(10), 'world');
INSERT INTO t1 VALUES( random_vector(10), 'today');
}
do_execsql_test 3.1 {
SELECT rowid, length(v), length(v) FROM t1(random_vector(10), 5)
WHERE b IN ('hello', 'world', 'today')
ORDER BY 1
} {1 40 40 2 40 40 3 40 40}
faultsim_save_and_close
do_faultsim_test 3.1 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
SELECT rowid, length(v) FROM t1(random_vector(10), 5)
WHERE b IN ('hello', 'world', 'today')
ORDER BY 1
}
} -test {
faultsim_test_result {0 {1 40 2 40 3 40}}
}
do_faultsim_test 3.3 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
SELECT rowid, length(distance) BETWEEN 60 AND 80, b FROM t1;
}
} -test {
faultsim_test_result {0 {1 1 hello 2 1 world 3 1 today}}
}
do_faultsim_test 3.4 -faults oom* -prep {
faultsim_restore_and_reopen
vec1_load db
} -body {
execsql {
SELECT rowid, b FROM t1(random_vector(10), 5) ORDER BY 1;
}
} -test {
faultsim_test_result {0 {1 hello 2 world 3 today}}
}
reset_db
vec1_load db
do_faultsim_test 4 -faults oom* -body {
execsql {
SELECT vec1_info() IS NOT NULL
}
} -test {
faultsim_test_result {0 1}
}
finish_test

View File

@@ -0,0 +1,82 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fault9
vec1_load db
do_faultsim_test 1 -faults oom* -body {
execsql {
SELECT vec1_to_json( vec1_from_json('[1,2,3,4,5,6,7,8,9,10]') );
}
} -test {
faultsim_test_result {0 {{[1,2,3,4,5,6,7,8,9,10]}}}
}
do_execsql_test 2 {
CREATE VIRTUAL TABLE x1 USING vec1(v, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<20 )
INSERT INTO x1
SELECT rvec(12, i), CASE WHEN i%2 THEN 'one' ELSE 'two' END FROM s;
INSERT INTO x1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_faultsim_test 2 -faults oom* -body {
sqlite3 db test.db
sqlite3_db_config_lookaside db 0 0 0
vec1_load db
set one one
execsql {
SELECT rowid FROM x1(rvec(12, 4000), 200) WHERE b=$one ORDER BY 1
}
} -test {
faultsim_test_result \
{0 {1 3 5 7 9 11 13 15 17 19}} \
{1 {}} \
{1 {error during initialization: }}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3 {
CREATE VIRTUAL TABLE x1 USING vec1(v, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<20 )
INSERT INTO x1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
faultsim_save_and_close
breakpoint
do_faultsim_test 3 -faults oom* -prep {
faultsim_restore_and_reopen
sqlite3_db_config_lookaside db 0 0 0
vec1_load db
} -body {
set value "this_is_a_good_value"
execsql {
INSERT INTO x1 VALUES( rvec(44, 156), $value );
}
} -test {
if {$testrc==0} {
set r [db one {
SELECT rowid FROM x1(rvec(44, 13), 10) WHERE b='this_is_a_good_value';
}]
if {$r!=1} { error "unexected!" }
}
faultsim_test_result {0 {}}
}
finish_test

View File

@@ -0,0 +1,111 @@
# 2026-04-02
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# A vec1 table "fixes" the vector size when either:
#
# * the first vector is written to the table, or
# * the table is trained by a model that specifies a vector size.
#
# This file contains tests of these two cases.
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1fixed
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_execsql_test 1.1 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', (
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512
) SELECT vec1_train( random_vector(128), '{nbucket:4}' ) FROM s
));
}
do_catchsql_test 1.2 {
INSERT INTO v1 VALUES(random_vector(127));
} {1 {vec1: unexpected vector blob size 508 bytes, expected 512}}
do_catchsql_test 1.3 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', (
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512
) SELECT vec1_train( random_vector(64), '{codesize:16}' ) FROM s
));
} {1 {vec1: model/data vector size mismatch: 64/128}}
do_catchsql_test 1.4 {
SELECT rowid FROM v1(random_vector(129))
} {1 {vec1: unexpected vector blob size 516 bytes, expected 512}}
#-------------------------------------------------------------------------
# Similar to tests 1.*, except fix the vector size by inserting a row.
#
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_execsql_test 2.1 {
INSERT INTO v1 VALUES( random_vector(128) );
}
do_catchsql_test 2.2 {
INSERT INTO v1 VALUES(random_vector(127));
} {1 {vec1: unexpected vector blob size 508 bytes, expected 512}}
do_catchsql_test 2.3 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', (
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<512
) SELECT vec1_train( random_vector(64), '{codesize:16}' ) FROM s
));
} {1 {vec1: model/data vector size mismatch: 64/128}}
do_catchsql_test 2.4 {
SELECT rowid FROM v1(random_vector(129))
} {1 {vec1: unexpected vector blob size 516 bytes, expected 512}}
do_execsql_test 2.5 {
INSERT INTO v1 VALUES( random_vector(128) );
}
#-------------------------------------------------------------------------
#
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_catchsql_test 3.1 {
INSERT INTO v1 VALUES( random_vector(1) );
} {1 {invalid vector size: 4 bytes}}
do_catchsql_test 3.2 {
INSERT INTO v1 VALUES( random_vector(4097) );
} {1 {invalid vector size: 16388 bytes}}
do_catchsql_test 3.3 {
INSERT INTO v1 VALUES( randomblob(207) );
} {1 {invalid vector size: 207 bytes}}
do_execsql_test 3.4 {
INSERT INTO v1 VALUES( random_vector(4096) );
}
finish_test

390
ext/vec1/test/vec1flat.test Normal file
View File

@@ -0,0 +1,390 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1flat
vec1_load db
do_execsql_test 1.0 {
CREATE TABLE base(vector);
CREATE VIRTUAL TABLE v1 USING vec1;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO base(vector) SELECT random_vector(32) FROM s;
INSERT INTO v1(vector) SELECT vector FROM base;
}
set rnd [db one {SELECT random_vector(32)}]
foreach {tn idx func type} {
1 { {index:"flat", distance: "l2"} } vec1_l2_distance integer
2 { {index:"flat", distance: "cos"} } vec1_cos_distance integer
3 { {index:"none", distance: "l2"} } vec1_l2_distance blob
4 { {index:"none", distance: "cos"} } vec1_cos_distance blob
} {
do_execsql_test 1.$tn.1 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', $idx);
}
do_execsql_test 1.$tn.2 {
PRAGMA integrity_check
} {ok}
set expect [db eval "
SELECT rowid FROM base ORDER BY $func (vector, \$rnd) LIMIT 50
"]
do_execsql_test 1.$tn.3 {
SELECT rowid FROM v1($rnd, '{K:50}')
} $expect
do_execsql_test 1.$tn.4 {
SELECT distinct typeof(vector) FROM v1_base
} $type
do_execsql_test 1.$tn.5 {
DELETE FROM v1;
SELECT rowid FROM v1($rnd, '{K:50}');
} {}
do_execsql_test 1.$tn.6 {
INSERT INTO v1(vector) SELECT vector FROM base;
SELECT rowid FROM v1($rnd, '{K:50}');
} $expect
do_execsql_test 1.$tn.7 {
SELECT distinct typeof(vector) FROM v1_base
} $type
set expect [db eval "
SELECT rowid, $func (vector, \$rnd) FROM base
ORDER BY $func (vector, \$rnd) LIMIT 50
"]
do_execsql_test 1.$tn.8 {
SELECT rowid, distance FROM v1($rnd, '{K:50}')
} $expect
do_execsql_test 1.$tn.9 {
SELECT count( distance ) FROM v1
} {1000}
}
do_execsql_test 1.5 {
DELETE FROM v1;
INSERT INTO v1(vector) SELECT vector FROM base LIMIT 10;
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 1.5.1 {
SELECT rowid FROM v1;
} {1 2 3 4 5 6 7 8 9 10}
do_execsql_test 1.5.2 {
BEGIN;
DELETE FROM v1 WHERE rowid IN (3, 7);
SELECT rowid FROM v1;
} {1 2 4 5 6 8 9 10}
do_execsql_test 1.5.3 {
COMMIT;
SELECT rowid FROM v1;
} {1 2 4 5 6 8 9 10}
do_execsql_test 1.5.4 {
INSERT INTO v1(rowid, vector) VALUES(11, random_vector(32));
SELECT rowid FROM v1;
} {1 2 4 5 6 8 9 10 11}
do_catchsql_test 1.5.5 {
INSERT INTO v1(rowid, vector) VALUES(8, random_vector(32));
} {1 {constraint failed}}
do_execsql_test 1.6.1 {
SELECT rowid, length(vector) FROM v1 WHERE rowid=5;
} {5 128}
do_execsql_test 1.6.2 {
SELECT rowid, length(vector) FROM v1 WHERE rowid=7;
} {}
#------------------------------------------------------------------------
# Now try tests on {codesize:0, nbucket:32} indexes. These are also
# flat, as they store full vectors in the index.
#
reset_db
vec1_load db
set rnd [db one {SELECT random_vector(32)}]
do_execsql_test 2.0 {
CREATE TABLE base(vector);
CREATE VIRTUAL TABLE v1 USING vec1;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO base(vector) SELECT random_vector(32) FROM s;
INSERT INTO v1(vector) SELECT vector FROM base;
}
do_execsql_test 2.1 {
INSERT INTO v1(cmd, vector) VALUES('rebuild', (
SELECT vec1_train(vector, '{codesize:0, nbucket:32}') FROM base
));
}
do_execsql_test 2.2 {
PRAGMA integrity_check
} {ok}
do_execsql_test 2.3.1 {
SELECT rowid FROM v1 WHERE rowid=123
} {123}
do_execsql_test 2.3.2 {
DELETE FROM v1 WHERE rowid=123;
PRAGMA integrity_check;
} {ok}
do_execsql_test 2.3.3 {
SELECT rowid FROM v1 WHERE rowid=123
} {}
do_execsql_test 2.4.1 {
SELECT rowid, length(vector) FROM v1 WHERE rowid=789
} {789 128}
do_execsql_test 2.4.2 {
SELECT count(*), sum(v1.vector=base.vector)
FROM v1 CROSS JOIN base
WHERE v1.rowid=base.rowid;
} {999 999}
do_execsql_test 2.4.3 {
SELECT count(*), sum(v1.vector=base.vector)
FROM base CROSS JOIN v1
WHERE v1.rowid=base.rowid;
} {999 999}
do_execsql_test 2.5.1 {
DELETE FROM v1;
INSERT INTO v1(vector) SELECT vector FROM base;
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE "vector table" USING vec1(a);
} {}
do_catchsql_test 3.1 {
INSERT INTO "vector table"(cmd, arg) VALUES('rebuild', '{index=flat}');
} {1 {vec1: malformed JSON}}
do_catchsql_test 3.2 {
INSERT INTO "vector table"(cmd, arg) VALUES('rebuild', X'001122334455');
} {1 {vec1: model too small - 6 bytes}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE vvv USING vec1(a, b);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO vvv(rowid, a, b)
SELECT i * 1_000_000_000, random_vector(25), i FROM s;
SELECT sum(rowid) FROM vvv;
} {5050000000000}
do_execsql_test 4.1 {
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {}
do_execsql_test 4.2 {
DELETE FROM vvv WHERE rowid IN (
21 * 1_000_000_000, 53 * 1_000_000_000, 2 * 1_000_000_000
);
SELECT sum(rowid) FROM vvv;
} {4974000000000}
do_execsql_test 4.3 {
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"none"}');
SELECT sum(rowid) FROM vvv;
} {4974000000000}
do_execsql_test 4.4 {
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"flat"}');
SELECT sum(rowid) FROM vvv;
} {4974000000000}
# Corrupt the index entry
do_execsql_test 4.5.1 {
SELECT rowid FROM vvv_idx
} {1 2}
do_execsql_test 4.5.2 {
UPDATE vvv_idx SET val = substr(val, 1, 5000) WHERE rowid=2;
} {}
do_catchsql_test 4.5.3 {
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"none"}');
} {1 {database disk image is malformed}}
do_catchsql_test 4.5.4 {
SELECT rowid FROM vvv(random_vector(25)) LIMIT 100
} {1 {database disk image is malformed}}
do_execsql_test 4.6.1 {
UPDATE vvv_idx SET val = substr(val, 1, 11) WHERE rowid=2;
} {}
do_catchsql_test 4.6.2 {
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"none"}');
} {1 {database disk image is malformed}}
do_catchsql_test 4.6.3 {
SELECT rowid FROM vvv(random_vector(25)) LIMIT 100
} {1 {database disk image is malformed}}
#-------------------------------------------------------------------------
# This time corrupt the meta-values list.
#
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE vvv USING vec1(a, b);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO vvv(a, b) SELECT random_vector(25), i FROM s;
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {}
do_execsql_test 5.1 {
UPDATE vvv_meta SET val = substr(val, 1, 50);
}
do_catchsql_test 5.2 {
SELECT rowid FROM vvv(random_vector(25)) WHERE b=222 LIMIT 10
} {1 {database disk image is malformed}}
#-------------------------------------------------------------------------
#
reset_db
vec1_load db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE vvv USING vec1(a, b);
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'first' );
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'second' );
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'third' );
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {}
do_execsql_test 6.1 {
SELECT rowid, length(a) FROM vvv(random_vector(25), 3) ORDER BY 1
} {1 100 2 100 3 100}
do_execsql_test 6.2 {
DELETE FROM vvv_base WHERE id=2;
}
do_catchsql_test 6.3 {
SELECT rowid, length(a) FROM vvv(random_vector(25), 3) ORDER BY 1
} {1 {database disk image is malformed}}
do_execsql_test 6.4 {
INSERT INTO vvv_base(id, vector, c0) VALUES(2, 0, 'second');
SELECT rowid, length(a) FROM vvv(random_vector(25), 3) ORDER BY 1
} {1 100 2 100 3 100}
do_execsql_test 6.5 {
INSERT INTO vvv(rowid, a, b) VALUES(400, random_vector(25), 'four hundred');
INSERT INTO vvv_base(id, vector, c0) VALUES(4, 0, 'fourth');
SELECT rowid, length(a) FROM vvv(random_vector(25), 4) ORDER BY 1
} {1 100 2 100 3 100 400 100}
do_execsql_test 6.6 {
SELECT rowid, length(a) FROM vvv ORDER BY 1
} {1 100 2 100 3 100 4 {} 400 100}
do_execsql_test 6.7 {
DELETE FROM vvv_base WHERE id=4;
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'fifth' );
}
do_execsql_test 6.8 {
UPDATE vvv_idx SET val = substr(val, 1, 400);
}
do_catchsql_test 6.9 {
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'sixth' );
} {1 {database disk image is malformed}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE vvv USING vec1(a, b);
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'first' );
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'second' );
INSERT INTO vvv(a, b) VALUES( random_vector(25), 'third' );
INSERT INTO vvv(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {}
do_execsql_test 7.1 {
INSERT INTO vvv(cmd, arg) VALUES('rebuild', NULL);
}
do_execsql_test 7.2 {
PRAGMA integrity_check
} {ok}
do_execsql_test 7.3 {
BEGIN;
INSERT INTO vvv(cmd, arg) VALUES('rebuild', NULL);
}
do_execsql_test 7.4 {
PRAGMA integrity_check
} {ok}
do_execsql_test 7.5 {
COMMIT
}
do_execsql_test 7.6 {
PRAGMA integrity_check
} {ok}
do_catchsql_test 7.7 {
BEGIN;
INSERT INTO vvv(cmd, arg) VALUES('rebuild', (
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
)
SELECT vec1_train(random_vector(22), '{nbucket:4}') FROM s
));
} {1 {vec1: model/data vector size mismatch: 22/25}}
do_test 7.7.1 {
sqlite3_get_autocommit db
} 0
do_execsql_test 7.8 {
COMMIT;
PRAGMA integrity_check;
} {ok}
finish_test

115
ext/vec1/test/vec1func.test Normal file
View File

@@ -0,0 +1,115 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1func
vec1_load db
do_execsql_test 1.0 {
SELECT vec1_to_json(
vec1_from_json('[1.0, 2.0, 3.0, 4.0]')
)
} {{[1,2,3,4]}}
do_execsql_test 1.1 {
SELECT quote(vec1_from_json('[1.0, 2.0, 3.0, 4.0]'))
} {X'0000803F000000400000404000008040'}
do_catchsql_test 1.2 {
SELECT vec1_to_json(X'')
} {1 {vec1: invalid vector argument}}
do_catchsql_test 1.3 {
SELECT vec1_to_json(X'0000803F000000400000404000008040FF')
} {1 {vec1: invalid vector argument}}
do_execsql_test 1.4 {
SELECT vec1_to_json(X'0000803F000000400000404000008040')
} {[1,2,3,4]}
do_execsql_test 1.5 {
SELECT vec1_to_json_i(X'0000803F000000400000404000008040')
} {[1065353216,1073741824,1077936128,1082130432]}
#-------------------------------------------------------------------------
foreach {tn in out} {
1 " [1,2,3] " "0 {{[1,2,3]}}"
2 " [ 1 , 2 , 3 ] " "0 {{[1,2,3]}}"
3 " {1,2,3} " "1 {error parsing json vector}"
4 " [1,2,3" "1 {error parsing json vector}"
5 " [1,2, ]" "1 {error parsing json vector}"
6 " [1,2,3] x " "1 {error parsing json vector}"
} {
do_catchsql_test 2.$tn {
SELECT vec1_to_json( vec1_from_json($in) )
} $out
}
#-------------------------------------------------------------------------
do_execsql_test 3.0 {
SELECT vec1_cos_distance(random_vector(9), random_vector(9))
BETWEEN 0.0 AND 2.0
} {1}
do_catchsql_test 3.1 {
SELECT vec1_cos_distance(random_vector(9), random_vector(8))
} {1 {vec1_cos_distance: bad arguments}}
do_catchsql_test 3.2 {
SELECT vec1_cos_distance(randomblob(37), randomblob(37))
} {1 {vec1_cos_distance: bad arguments}}
do_catchsql_test 3.3 {
SELECT vec1_l2_distance(random_vector(9), random_vector(8))
} {1 {vec1_l2_distance: bad arguments}}
do_catchsql_test 3.4 {
SELECT vec1_l2_distance(randomblob(37), randomblob(37))
} {1 {vec1_l2_distance: bad arguments}}
#-------------------------------------------------------------------------
# Test that vectors of zero magnitude are considered orthogonal to all
# others, including itself.
do_execsql_test 4.0 {
SELECT vec1_cos_distance(random_vector(9), zeroblob(4*9));
} {1.0}
do_execsql_test 4.1 {
SELECT vec1_cos_distance(zeroblob(4*9), random_vector(9));
} {1.0}
#-------------------------------------------------------------------------
# Test that non-blob arguments are errors.
#
foreach {tn a2 bErr} {
0 { (X'12345678', X'12345678') } 0
1 { (X'12345678', '12345678') } 1
2 { ('12345678', '12345678') } 1
3 { ('12345678', X'12345678') } 1
} {
do_test 5.$tn.1 {
lindex [catchsql "SELECT vec1_l2_distance $a2"] 0
} $bErr
do_test 5.$tn.2 {
lindex [catchsql "SELECT vec1_cos_distance $a2"] 0
} $bErr
}
#-------------------------------------------------------------------------
#
do_execsql_test 6.0 { SELECT vec1_from_json(NULL) } {{}}
do_execsql_test 6.1 { SELECT vec1_from_json(NULL) IS NULL } {1}
finish_test

View File

@@ -0,0 +1,114 @@
# 2026-03-31
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests for vec1. Specifically, it tests that the
# index does not grow indefinitely if there are lots of inserts/deletes
# and updates on the table.
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1growth
vec1_load db
foreach {tn nMeta} {
1 0
2 1
3 2
} {
set lMeta [list]
set cols ""
set exprs ""
for {set ii 0} {$ii<$nMeta} {incr ii} {
lappend lMeta c$ii
append cols ", c$ii"
append exprs ", i+$ii"
}
do_execsql_test 1.$tn.0 "
DROP TABLE IF EXISTS v1;
CREATE VIRTUAL TABLE v1 USING vec1(vec $cols);
"
do_execsql_test 1.1 "
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1_000
)
INSERT INTO v1(rowid, vec $cols) SELECT i, random_vector(32) $exprs FROM s;
"
do_execsql_test 1.2 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(vec, '{nbucket:16, codesize:0}') FROM v1
));
}
do_execsql_test 1.2.ic {
PRAGMA integrity_check
} {ok}
# Get the original number of bytes of index data.
#
set nOrig [db one { SELECT sum( length(val) ) FROM v1_idx }]
set nMax [expr {($nOrig * 6)/5}]
proc randint {min max} {
expr { int(abs(rand()) * (1+$max-$min)) }
}
for {set iIter 0} {$iIter<20} {incr iIter} {
do_test 1.$tn.3.$iIter {
for {set ii 1} {$ii <= 1000} {incr ii} {
set r [randint 1 1000]
execsql { UPDATE v1 SET vec=random_vector(32) WHERE rowid=$r }
}
set nByte [db one { SELECT sum( length(val) ) FROM v1_idx }]
expr $nByte<=$nMax
} {1}
do_execsql_test 1.$tn.3.$iIter.ic.(sz=$nByte,max=$nMax) {
PRAGMA integrity_check
} {ok}
}
}
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE vec USING vec1(a, b);
INSERT INTO vec(cmd, arg) VALUES('rebuild', '{index:"flat"}');
BEGIN;
}
do_test 2.1 {
for {set ii 1} {$ii<=128} {incr ii} {
execsql { INSERT INTO vec(a, b) VALUES( random_vector(16), NULL ) }
}
} {}
do_test 2.1 {
for {set ii 128} {$ii>1} {incr ii -1} {
execsql { DELETE FROM vec WHERE rowid=$ii }
}
} {}
do_execsql_test 2.2 {
COMMIT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 2.3 {
SELECT count(*) FROM vec;
} 1
finish_test

View File

@@ -0,0 +1,293 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1intck
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE mytbl USING vec1(a, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 )
INSERT INTO mytbl SELECT random_vector(32), 'val'||i FROM s;
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{codesize:8}') FROM mytbl
));
}
do_execsql_test 1.1 {
SELECT length(val) FROM mytbl_idx;
} {12076}
do_execsql_test 1.2 {
PRAGMA integrity_check
} {ok}
proc set_ntombstone {val n} {
set be32 [binary format I $n]
return [string replace $val 8 11 $be32]
}
db funct set_ntombstone set_ntombstone
do_execsql_test 1.3 {
UPDATE mytbl_idx SET val = set_ntombstone(val, 4);
} {}
do_execsql_test 1.4 {
PRAGMA integrity_check
} {{mytbl: bad tombstone count}}
do_execsql_test 2.0 {
INSERT INTO mytbl(cmd) VALUES('rebuild');
}
proc zero_pq {val i} {
set iOff [expr 12 + 1000*4 + $i]
set ret $val
for {set jj 0} {$jj < 8} {incr jj} {
set ret [string replace $ret $iOff $iOff [binary format c 0]]
incr iOff 16
}
return $ret
}
db func zero_pq zero_pq
do_execsql_test 2.1 {
UPDATE mytbl_idx SET val = zero_pq(val, 4);
} {}
do_execsql_test 2.2 {
PRAGMA integrity_check
} {{mytbl: %_idx PQ does not match calculated PQ for row 5}}
#------------------------------------------------------------------
do_execsql_test 3.0 {
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{nbucket:16,codesize:32}') FROM mytbl
));
}
# Replace the vector for row 555 with a copy of the bucket vector that
# is most distant from the current vector. The idea being to pick a
# value guaranteed to be assigned to a different bucket than the current
# vector.
model2tbl model mytbl
do_execsql_test 3.1 {
UPDATE mytbl_base SET vector =
(SELECT coarse FROM model ORDER BY vec1_l2_distance(vector, coarse) DESC)
WHERE id=555;
}
do_execsql_test 3.2 {
PRAGMA integrity_check
} {{mytbl: vector from row 555 is in wrong bucket}}
do_execsql_test 3.3 {
UPDATE mytbl_base SET vector = random_vector(31) WHERE id=555;
}
do_execsql_test 3.4 {
PRAGMA integrity_check
} {{mytbl: vector in %_base row 555 is wrong size}}
do_execsql_test 3.5 {
UPDATE mytbl_base SET vector = random_vector(32);
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{nbucket:16,codesize:32}') FROM mytbl
));
}
do_execsql_test 3.6 {
UPDATE mytbl_base SET c0 = 'hello world' WHERE id=555
}
do_execsql_test 3.7 {
PRAGMA integrity_check
} {{mytbl: meta-value value mismatch for row 555}}
do_execsql_test 3.8 {
UPDATE mytbl_base SET vector = random_vector(32);
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{codesize:16}') FROM mytbl
));
}
proc shorten {val} {
string range $val 0 end-1
}
db func shorten shorten
do_execsql_test 3.9 {
UPDATE mytbl_idx SET val = shorten(val);
PRAGMA integrity_check;
} {
{mytbl: %_idx entry id=1 is corrupt}
}
do_execsql_test 3.10 {
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{nbucket:16}') FROM mytbl
));
}
do_execsql_test 3.11 {
UPDATE mytbl_base SET vector = vector+1 WHERE id=555;
PRAGMA integrity_check;
} {
{mytbl: bucket value in %_base row 555 is incorrect}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE v1 USING vec1(x, a, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 )
INSERT INTO v1(rowid, x, a, b)
SELECT 10_000_000_000 + i, random_vector(8), 20_000_000_000+i, i FROM s;
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 4.1 {
SELECT count(*) FROM v1;
DELETE FROM v1 WHERE rowid = 10_000_000_000 + 35;
SELECT count(*) FROM v1;
} {100 99}
do_execsql_test 4.2 {
PRAGMA integrity_check
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE v1 USING vec1(x, a);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1 VALUES(random_vector(8), 'helloworld');
INSERT INTO v1 VALUES(random_vector(8), 4.5);
INSERT INTO v1 VALUES(random_vector(8), 1000);
INSERT INTO v1 VALUES(random_vector(8), X'12345678');
PRAGMA integrity_check
} {ok}
foreach {tn update err} {
1 { UPDATE v1_base SET c0='helloWORLD' WHERE id=1 }
{v1: meta-value value mismatch for row 1}
2 { UPDATE v1_base SET c0='helloworld2' WHERE id=1 }
{v1: meta-value value mismatch for row 1}
3 { UPDATE v1_base SET c0=4.5 WHERE id=1 }
{v1: meta-value value mismatch for row 1}
4 { UPDATE v1_base SET c0=5.5 WHERE id=2 }
{v1: meta-value value mismatch for row 2}
5 { UPDATE v1_base SET c0=1001 WHERE id=3 }
{v1: meta-value value mismatch for row 3}
6 { UPDATE v1_base SET c0=5 WHERE id=2 }
{v1: meta-value value mismatch for row 2}
7 { UPDATE v1_base SET c0='textvalue' WHERE id=2 }
{v1: meta-value value mismatch for row 2}
8 { UPDATE v1_base SET c0=X'ABCDEF' WHERE id=2 }
{v1: meta-value value mismatch for row 2}
9 { UPDATE v1_base SET c0=X'ABCDEF' WHERE id=4 }
{v1: meta-value value mismatch for row 4}
10 { UPDATE v1_base SET c0=X'00ABCDEF' WHERE id=4 }
{v1: meta-value value mismatch for row 4}
11 { DELETE FROM v1_base WHERE id=1 }
{v1: %_idx row 1 missing from %_base}
12 { INSERT INTO v1_base(id, vector, c0) VALUES(555,random_vector(8),'xyz') }
{v1: wrong number of entries in %_base - have 5, expect 4}
} {
execsql BEGIN
execsql $update
do_execsql_test 5.$tn {
PRAGMA integrity_check
} [list $err]
execsql ROLLBACK
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
execsql { SELECT vec1_config('nthread', 4) }
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE v1 USING vec1(x, a);
INSERT INTO v1 VALUES(random_vector(8), 1.2);
INSERT INTO v1 VALUES(random_vector(8), 4.5);
INSERT INTO v1 VALUES(random_vector(8), 1000);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.1 {
UPDATE v1 SET a = (rowid*1000);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1 VALUES(random_vector(8), NULL);
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.2.1 {
INSERT INTO v1 VALUES(random_vector(8), 1.234);
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.2.2 {
INSERT INTO v1 VALUES(random_vector(8), 'text');
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.3 {
UPDATE v1 SET a = (rowid*1000);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1 VALUES(random_vector(8), 10_000_000_000);
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.4 {
UPDATE v1 SET a = (rowid*1000);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1 VALUES(random_vector(8), -10_000_000_000);
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.5 {
UPDATE v1 SET a = rowid;
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1 VALUES(random_vector(8), -1);
PRAGMA integrity_check;
} {ok}
finish_test

View File

@@ -0,0 +1,40 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1intck2
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE mytbl USING vec1(a, b);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 )
INSERT INTO mytbl SELECT random_vector(32), 'val'||i FROM s;
}
foreach {tn spec} {
1 "{codesize: 8, nbucket: 8}"
2 "{codesize: 8, nbucket: 8, residual: 0}"
} {
do_execsql_test 1.$tn.1 {
INSERT INTO mytbl(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, $spec) FROM mytbl
));
}
do_execsql_test 1.$tn.2 {
PRAGMA integrity_check
} {ok}
}
finish_test

View File

@@ -0,0 +1,63 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1flat
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1(v) SELECT random_vector(32) FROM s;
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{nbucket:24, codesize:0}') FROM v1
));
}
db close
sqlite3 db test.db
vec1_load db
do_execsql_test 1.1 {
SELECT length(v) FROM v1 LIMIT 1
} {128}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v2 USING vec1(v);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v2(v) SELECT random_vector(7) FROM s;
}
breakpoint
do_execsql_test 2.1 {
INSERT INTO v2(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{nbucket:24, codesize:0}') FROM v2
));
}
set v [db one { SELECT v FROM v2 WHERE rowid = 499 }]
do_execsql_test 2.1 {
}
finish_test

View File

@@ -0,0 +1,46 @@
# 2026-08-04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1join
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(a);
INSERT INTO v1(rowid, a) VALUES(1, vec1_from_json('[1,0,0,0]'));
INSERT INTO v1(rowid, a) VALUES(2, vec1_from_json('[0,1,0,0]'));
INSERT INTO v1(rowid, a) VALUES(3, vec1_from_json('[0,0,1,0]'));
INSERT INTO v1(rowid, a) VALUES(4, vec1_from_json('[0,0,0,1]'));
INSERT INTO v1(rowid, a) VALUES(5, vec1_from_json('[1,1,0,0]'));
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
CREATE TABLE q(id INTEGER PRIMARY KEY, qv BLOB);
INSERT INTO q VALUES(1, vec1_from_json('[1,0,0,0]'));
}
# Q1: literal query vector
do_execsql_test 1.1 {
SELECT rowid FROM v1(vec1_from_json('[1,0,0,0]'), '{K:2}');
} {1 5}
# Q2: vector from table q, via CROSS JOIN
do_execsql_test 1.2 {
SELECT v.rowid FROM q CROSS JOIN v1(q.qv, '{K:2}') AS v;
} {1 5}
# Q3: vector from table q, via CROSS JOIN
do_execsql_test 1.3 {
SELECT v.rowid FROM q, v1(q.qv, '{K:2}') AS v;
} {1 5}
finish_test

View File

@@ -0,0 +1,97 @@
# 2026-03-11
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1limit
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, a, b);
INSERT INTO v1 VALUES(random_vector(16), 555, 111);
DELETE FROM v1;
}
do_catchsql_test 1.1 {
SELECT * FROM v1(:q)
} {1 {vec1: unexpected vector blob size 0 bytes, expected 64}}
set q [db one {SELECT random_vector(16)}]
foreach {tn sql} {
1 { SELECT * FROM v1($q) }
2 { SELECT * FROM v1($q) ORDER BY vec1_l2_distance(v, $q) LIMIT 10 }
3 { SELECT * FROM v1($q) WHERE a!=b LIMIT 10 }
4 { SELECT * FROM v1($q) LIMIT -1 }
5 { SELECT * FROM v1($q) WHERE a LIKE 'p%' LIMIT 10 }
} {
do_catchsql_test 1.2.$tn $sql [list \
1 {vec1: no K value or visible LIMIT clause}
]
}
foreach {tn sql} {
1 { SELECT * FROM v1($q) LIMIT 10 }
2 { SELECT * FROM v1($q) WHERE a=555 LIMIT 10 }
} {
do_execsql_test 1.3.$tn $sql {}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE TABLE corpus(id INTEGER PRIMARY KEY, v BLOB, title TEXT, cat TEXT);
INSERT INTO corpus(id,v,title,cat)
SELECT i, vec1_from_json(
'['|| (i%17) ||','|| (i%13) ||','|| (i%7) ||','|| (i%5)
||','|| (i%23) ||','|| (i%3) ||','|| (i%11) ||','|| (i%19) ||']'
), 'runbook '||i,
CASE WHEN i%3=0 THEN 'ops' WHEN i%3=1 THEN 'kb' ELSE 'security' END
FROM (
WITH RECURSIVE r(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<2000)
SELECT i FROM r
);
CREATE VIRTUAL TABLE docs USING vec1(embedding, title, category);
INSERT INTO docs(rowid, embedding, title, category)
SELECT id, v, title, cat FROM corpus;
INSERT INTO docs(cmd,arg) VALUES('rebuild',(
SELECT vec1_train(v,'{nbucket:8}') FROM corpus
));
}
foreach {tn K} {
1 576460752303423616
2 1152921504606852096
3 1152921504606847232
4 1152921504606848000
5 1152921504606847232
} {
set J "{K:$K, nprobe: 99}"
set K [expr $K]
do_catchsql_test 2.1.$tn.1 {
SELECT count(*) FROM docs( vec1_from_json('[1,2,3,4,5,6,7,8]'), $J );
} {1 {vec1: K must be an integer value between 0 and 33554432}}
do_catchsql_test 2.1.$tn.2 {
SELECT count(*) FROM docs( vec1_from_json('[1,2,3,4,5,6,7,8]'), $K );
} {1 {vec1: K must be an integer value between 0 and 33554432}}
}
finish_test

252
ext/vec1/test/vec1meta.test Normal file
View File

@@ -0,0 +1,252 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1meta
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, a, b);
}
do_execsql_test 1.1 {
SELECT v, a, b FROM v1
}
do_execsql_test 1.2 {
PRAGMA table_info = v1_base;
} {
0 id INTEGER 0 {} 1
1 vector BLOB 0 {} 0
2 c0 {} 0 {} 0
3 c1 {} 0 {} 0
}
do_execsql_test 1.3 {
PRAGMA table_xinfo = v1;
} {
0 cmd {} 0 {} 0 1
1 arg {} 0 {} 0 1
2 distance {} 0 {} 0 1
3 v BLOB 0 {} 0 0
4 a {} 0 {} 0 0
5 b {} 0 {} 0 0
}
do_execsql_test 1.4 {
INSERT INTO v1 VALUES(random_vector(32), 'A', 'B');
}
do_execsql_test 1.5 {
SELECT length(v), a, b FROM v1
} {128 A B}
#-------------------------------------------------------------------------
reset_db
vec1_load db
# There should be a limit of 256 meta-value columns. Check that this is
# enforced.
do_test 2.0 {
set lInt [list]
set res ""
for {set ii 0} {$ii < 5000} {incr ii} {
lappend lInt "c$ii"
set ct "CREATE VIRTUAL TABLE v$ii USING vec1 (v, [join $lInt ,])"
if {[catch {execsql $ct} res]} {
break
}
}
list $ii $res
} {256 {too many meta-data columns (max is 256)}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, meta, m2);
INSERT INTO v1(cmd, v) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1 VALUES(random_vector(32), 1234, 0);
INSERT INTO v1 VALUES(random_vector(32), 4321, 1);
}
do_execsql_test 3.1 {
PRAGMA integrity_check;
} {ok}
do_execsql_test 3.2 {
INSERT INTO v1(cmd, v) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 3.3 {
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.1 {
CREATE VIRTUAL TABLE v1 USING vec1(v, m1, m2);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<5000
)
INSERT INTO v1 SELECT random_vector(64), i, i+1 FROM s;
PRAGMA integrity_check;
} {ok}
set T [db one {SELECT vec1_train(v, '{nbucket:32, codesize:16}') FROM v1}]
do_execsql_test 4.2 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', $T);
PRAGMA integrity_check;
} {ok}
execsql { SELECT vec1_config('nthread', 2) }
do_execsql_test 4.3 {
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.1 {
CREATE VIRTUAL TABLE v1 USING vec1(v, m1, m2);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
BEGIN;
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 1, 10);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 2, 20);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 3, 10);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 1, 20);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 2, 10);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 3, 20);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 1, 10);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 2, 20);
INSERT INTO v1(v, m1, m2) VALUES(random_vector(16), 3, 10);
COMMIT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 5.2.1 {
SELECT rowid FROM v1(random_vector(16), 3) WHERE m1=3 ORDER BY 1
} {3 6 9}
do_execsql_test 5.2.2 {
SELECT rowid FROM v1(random_vector(16), 4) WHERE m2=20 ORDER BY 1
} {2 4 6 8}
do_execsql_test 5.2.3 {
SELECT rowid FROM v1(random_vector(16), 3) WHERE m1=3.0 ORDER BY 1
} {3 6 9}
do_execsql_test 5.2.4 {
SELECT rowid FROM v1(random_vector(16), 4) WHERE m2=20.0 ORDER BY 1
} {2 4 6 8}
do_execsql_test 5.3 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"none"}');
}
do_execsql_test 5.4.1 {
SELECT rowid FROM v1(random_vector(16), 3) WHERE m1=3 ORDER BY 1
} {3 6 9}
do_execsql_test 5.4.2 {
SELECT rowid FROM v1(random_vector(16), 4) WHERE m2=20 ORDER BY 1
} {2 4 6 8}
do_execsql_test 5.4.3 {
SELECT rowid FROM v1(random_vector(16), 3) WHERE m1=3.0 ORDER BY 1
} {3 6 9}
do_execsql_test 5.4.4 {
SELECT rowid FROM v1(random_vector(16), 4) WHERE m2=20.0 ORDER BY 1
} {2 4 6 8}
do_execsql_test 5.5 {
SELECT rowid FROM v1(random_vector(16), 4) WHERE m2=x'1234' ORDER BY 1
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
set nRow [expr 200]
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE x1 USING vec1(v, x);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow
)
INSERT INTO x1 SELECT random_vector(8), i FROM s;
INSERT INTO x1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_test 6.1 {
for {set ii 1} {$ii<=$nRow} {incr ii} {
set val [db one {
SELECT rowid FROM x1(random_vector(8), 1) WHERE x=$ii
}]
if {$val!=$ii} { error "failed for row $ii ($val)" }
}
} {}
do_execsql_test 6.2 {
PRAGMA integrity_check
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
set nRow [expr 200]
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE x1 USING vec1(v, x);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow
)
INSERT INTO x1 SELECT random_vector(8), (i%7) FROM s;
UPDATE x1 SET x=NULL WHERE x=6;
INSERT INTO x1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 7.1 {
PRAGMA integrity_check
} {ok}
set nExpect [expr {$nRow/7}]
do_execsql_test 7.2 {
SELECT count(*) FROM x1(random_vector(8), $nExpect) WHERE x IS NULL
} $nExpect
for {set ii 0} {$ii<6} {incr ii} {
do_execsql_test 7.3.$ii {
SELECT count(*) FROM x1(random_vector(8), $nExpect) WHERE x = $ii
} $nExpect
}
set nExpect [expr $nExpect+1]
do_execsql_test 7.4 {
SELECT rowid FROM x1(random_vector(8), $nExpect)
WHERE x IS NULL ORDER BY 1
} [db eval { SELECT rowid FROM x1 WHERE (rowid%7)==6 }]
for {set ii 0} {$ii<6} {incr ii} {
do_execsql_test 7.5.$ii {
SELECT rowid FROM x1(random_vector(8), $nExpect)
WHERE x = $ii ORDER BY 1
} [db eval { SELECT rowid FROM x1 WHERE (rowid%7)==$ii }]
}
finish_test

View File

@@ -0,0 +1,394 @@
# 2026-03-12
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1meta2
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, m1);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1(rowid, v, m1) VALUES
(1, random_vector(8), '456'),
(2, random_vector(8), 456),
(3, random_vector(8), 456.0),
(4, random_vector(8), NULL),
(5, random_vector(8), 'some text'),
(6, random_vector(8), 'some other text'),
(7, random_vector(8), 'this text'),
(8, random_vector(8), X'ABCDEF'),
(9, random_vector(8), X'AC');
PRAGMA integrity_check;
} {ok}
foreach {tn where res} {
1 "m1 = 456" {2 3}
2 "m1 = 'this text'" {7}
3 "m1 = 'some text'" {5}
4 "m1 = NULL" {}
5 "m1 IS ?" {4}
6 "m1 > 555" {1 5 6 7 8 9}
7 "m1 >= 'some text'" {5 7 8 9}
8 "m1 = '456'" {1}
9 "m1 = X'ABCDEF'" {8}
10 "m1 = X'ac'" {9}
11 "m1 >= X'abcdef'" {8 9}
12 "m1 > X'abcdef'" {9}
13 "m1 IS NULL" {4}
14 "m1 IS NOT NULL" {1 2 3 5 6 7 8 9}
15 "m1 < 555" {2 3}
16 "m1 <= '456'" {1 2 3}
17 "m1 < 123.2" {}
18 "m1 < 123" {}
19 "m1 < 1123.2" {2 3}
20 "m1 < 456.0" {}
21 "m1 IS 456" {2 3}
22 "m1 > 'this'" {7 8 9}
23 "m1 = ''" {}
24 "m1 IS 'this text is'" {}
22 "m1 > X'ABCDEF1234'" {9}
23 "m1 > X'ABCD'" {8 9}
24 "m1 >= X''" {8 9}
25 "m1 ISNULL" {4}
} {
set K [llength $res]
if {$K==0} {set K 100}
do_execsql_test 1.1.$tn "
SELECT * FROM (
SELECT rowid FROM v1(random_vector(8)) WHERE $where LIMIT $K
) ORDER BY 1 LIMIT -1
" $res
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
proc rval {} {
expr (rand() * 10.0)
}
db func rval rval
do_execsql_test 2.0 {
CREATE TABLE t1(v BLOB, m1, m2);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1 SELECT random_vector(12), rval(), rval() FROM s;
UPDATE t1 SET m1=NULL WHERE (rowid%7)==0;
UPDATE t1 SET m2=NULL WHERE (rowid%8)==0;
CREATE VIRTUAL TABLE v1 USING vec1(v, m1, m2);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO v1(rowid, v, m1, m2) SELECT rowid, v, m1, m2 FROM t1;
}
# Check the flags on the meta value arrays are set to 0x18.
#
# 0x08=real values.
# 0x10=contains null.
#
do_execsql_test 2.1 {
SELECT hex( substr(val, 1, 4) ) FROM v1_meta
} {00000018 00000018}
foreach t {2 3} {
foreach {tn expr} {
1 "m1 IS NULL"
2 "m1 < 1.0"
3 "m1 BETWEEN -2.5 AND 2.5"
4 "m1 < 4 AND m2 >= 4"
5 "m1 <= 'abc'"
6 "m1 IS NOT NULL"
7 "m1 < 1.1 AND m2 > 2.1"
} {
set expect [db eval "SELECT rowid FROM t1 WHERE $expr ORDER BY 1"]
do_execsql_test 2.$t.$tn.[llength $expect] "
SELECT rowid FROM v1(random_vector(12), 100) WHERE $expr ORDER BY 1
" $expect
}
do_execsql_test 2.$t.x {
PRAGMA integrity_check
} {ok}
do_execsql_test 2.$t.y {
INSERT INTO t1 VALUES(random_vector(12), 'abc', NULL);
INSERT INTO v1(rowid, v, m1, m2)
SELECT rowid, v, m1, m2 FROM t1 WHERE rowid=last_insert_rowid();
SELECT hex( substr(val, 1, 4) ) FROM v1_meta
} {00000011 00000018}
}
db func rval rval
proc rval {} {
expr {int(rand() * 255.0)}
}
do_execsql_test 2.4 {
DELETE FROM t1;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<300
)
INSERT INTO t1 SELECT random_vector(12), rval(), rval() FROM s;
UPDATE t1 SET m1=NULL WHERE m1<6 OR rowid=43;
UPDATE t1 SET m2=NULL WHERE m2<6 OR rowid=43;
DELETE FROM v1;
INSERT INTO v1(rowid, v, m1, m2) SELECT rowid, v, m1, m2 FROM t1;
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 2.5 {
SELECT hex( substr(val, 1, 4) ) FROM v1_meta
} {00000012 00000012}
foreach t {6 7 8} {
foreach {tn expr} {
1 "m1 IS NULL"
2 "m1 = 11"
3 "m1 < 22"
4 "m1 < 254"
5 "m1 > 180"
6 "m1 IS NULL AND m2 IS NULL"
7 "m1 = 10 AND m2>10"
8 "m1>0 AND m2<100"
9 "m1>=0"
10 "m1>=130"
11 "m1<=130"
12 "m1<='text'"
13 "m1>'text'"
14 "m1>1000"
15 "m1 = 100.0"
16 "m1 > 100.0"
17 "m1 < 100.0"
18 "m1 > 89.2"
19 "m1 < 89.9"
20 "m1 >= 89.9"
21 "m1 BETWEEN -1 AND 201.5"
22 "m1 = 101.2"
23 "m2 IS NOT NULL"
24 "m2 < NULL"
25 "m1 >= -1"
26 "m1 <= 255"
27 "m1 < 255.5"
28 "m1 < 253.5"
29 "m1 = NULL"
30 "m2 >= 0"
31 "m2 >= 3.99"
32 "m2 >= 13.99"
33 "m2 >= 200"
34 "m2 >= 300"
35 "m2 <= 0"
36 "m2 <= 3.99"
37 "m2 <= 135.99"
38 "m2 <= 200"
39 "m2 <= 300"
40 "m2 <= -10_000_000_000"
41 "m1 IN (10, 20, 30)"
42 "m1 IN ('10', 20, 30)"
43 "m1 IN (NULL, NULL)"
44 "m1 IN (NULL, 100, 101, 102)"
45 "m1 IN (1000, NULL, 8, -456.123)"
46 "m2 IN (10, 20, 30)"
47 "m2 IN ('10', 20, 30)"
48 "m2 IN (NULL, NULL)"
49 "m2 IN (NULL, 100, 101, 102)"
50 "m2 IN (1000, NULL, 8, -456.123)"
51 "m2 = X'ABCD'"
52 "m1 < X'ABCD'"
53 "m1 IS 132.5"
54 "m1 > -4.5"
55 "m2 >= -4.5"
56 "m2 < -4.5"
57 "m2 <= -4.5"
58 "m2 IS 132"
} {
set expect [db eval "SELECT rowid FROM t1 WHERE $expr ORDER BY 1"]
do_execsql_test 2.$t.$tn.[llength $expect] "
SELECT rowid FROM (
SELECT rowid FROM v1(random_vector(12)) WHERE $expr LIMIT 1000
) ORDER BY 1 LIMIT -1;
" $expect
}
do_execsql_test 2.$t.x {
PRAGMA integrity_check
} {ok}
if {$t==6} {
do_execsql_test 2.$t.y {
INSERT INTO t1 VALUES(random_vector(12), 1234, 'xyz');
INSERT INTO v1(rowid, v, m1, m2)
SELECT rowid, v, m1, m2 FROM t1 WHERE rowid=last_insert_rowid();
SELECT hex( substr(val, 1, 4) ) FROM v1_meta
} {00000014 00000011}
} else {
do_execsql_test 2.$t.z {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"none"}');
}
}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
proc rval {} {
expr int(rand() * 100) * 100
}
db func rval rval
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, m1, m2);
CREATE TABLE t1(v, m1, m2);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<300
)
INSERT INTO t1 SELECT random_vector(15), rval(), rval() FROM s;
UPDATE t1 SET m1=NULL WHERE m1<500 OR rowid=43;
UPDATE t1 SET m2=NULL WHERE m2<500 OR rowid=43;
INSERT INTO v1 SELECT * FROM t1;
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
foreach t {1 2} {
foreach {tn expr} {
1 "m1 = 4500"
2 "m1 IS NULL"
3 "m1 IS NOT NULL"
4 "m1 < 1000"
5 "m2 > -1000"
4 "m1 < 'text'"
5 "m2 > 'text'"
6 "m1 < 4500.5"
7 "m1 <= 4500.5"
8 "m1 = NULL"
9 "m2 IN (1000, 1100, 1200, 1300)"
10 "m2 IN (3000, 3100, 3200, 3300, NULL, 'text')"
11 "m2 IN (10_000_000_000, 2147483647, -2147483649)"
} {
set expect [db eval "SELECT rowid FROM t1 WHERE $expr ORDER BY 1"]
do_execsql_test 3.$t.$tn.[llength $expect] "
SELECT rowid FROM (
SELECT rowid FROM v1(random_vector(15)) WHERE $expr LIMIT 1000
) ORDER BY 1 LIMIT -1;
" $expect
}
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE vec1tbl USING vec1(v, t);
INSERT INTO vec1tbl VALUES(
random_vector(8), hex(randomblob(100000))
);
SELECT length(t) FROM vec1tbl;
} {200000}
do_execsql_test 4.1 {
INSERT INTO vec1tbl(cmd, arg) VALUES('rebuild', '{index:"flat"}');
PRAGMA integrity_check;
} {ok}
do_execsql_test 4.2 {
DELETE FROM vec1tbl_meta;
PRAGMA integrity_check;
} {{vec1tbl: error reading meta-list id=1,meta=0}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE vec1tbl USING vec1(v, t, u);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<317 )
INSERT INTO vec1tbl SELECT random_vector(15), 120, NULL FROM s;
INSERT INTO vec1tbl(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {}
foreach {tn expr} {
1 "t = 120"
2 "u > 120"
3 "t > 100"
4 "t >= 101"
5 "u >= 101"
6 "t IN (100, 120, 140)"
} {
set res [db eval "SELECT rowid FROM vec1tbl WHERE $expr ORDER BY 1"]
do_execsql_test 5.1.$tn "
SELECT rowid FROM vec1tbl(random_vector(15), '{K:500}')
WHERE $expr ORDER BY 1
" $res
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE vec1tbl USING vec1(v, t, u, x);
INSERT INTO vec1tbl VALUES(random_vector(15), NULL, 2147483647, 1);
INSERT INTO vec1tbl VALUES(random_vector(15), 520, 2147483646, 2);
INSERT INTO vec1tbl VALUES(random_vector(15), NULL, 2147483645, 255);
INSERT INTO vec1tbl VALUES(random_vector(15), NULL, NULL, NULL);
INSERT INTO vec1tbl(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {}
foreach {tn expr} {
1 "u = 2147483647"
2 "u IS NULL"
3 "t = 2147483647"
4 "t IS NULL"
5 "x = 255"
6 "x IS NULL"
} {
set res [db eval "SELECT rowid FROM vec1tbl WHERE $expr ORDER BY 1"]
do_execsql_test 5.1.$tn "
SELECT rowid FROM vec1tbl(random_vector(15), '{K:500}')
WHERE $expr ORDER BY 1
" $res
}
finish_test

View File

@@ -0,0 +1,304 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1meta3
vec1_load db
proc create_table {name nMeta} {
set meta ""
for {set i 0} {$i<$nMeta} {incr i} {
append meta ", m$i"
}
execsql "CREATE VIRTUAL TABLE $name USING vec1(v $meta)"
}
do_test 1.0 {
create_table v1 100
} {}
do_test 1.1 {
create_table v2 256
} {}
do_test 1.2 {
list [catch { create_table v3 257 } msg] $msg
} {1 {too many meta-data columns (max is 256)}}
do_test 1.3 {
set vals ""
for {set i 0} {$i<256} {incr i} {
append vals ", $i"
}
execsql "INSERT INTO v2 VALUES( random_vector(8) $vals )"
} {}
do_test 1.4 {
for {set i 0} {$i<256} {incr i} {
set r [execsql "SELECT rowid FROM v2( random_vector(8), 1 ) WHERE m$i = $i"]
if {$r!="1"} { error "unexpected: $r" }
}
set {} {}
} {}
do_execsql_test 1.5 {
SELECT rowid FROM v2( random_vector(8), 1 )
WHERE m1 = 1 AND m2 = 2 AND m5 = 5 AND m4 = 4 AND m3 = 3;
} {1}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE x1 USING vec1(v, a, b);
CREATE TABLE t1(z);
INSERT INTO x1 VALUES(random_vector(8), 10, 20);
INSERT INTO t1 VALUES(10);
}
do_execsql_test 2.1 {
SELECT x1.b FROM x1(random_vector(8), 5), t1 WHERE z=a;
} {20}
do_execsql_test 2.1 {
SELECT x1.b FROM x1(random_vector(8), 5) ORDER BY x1.rowid LIMIT 5
} {20}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE x1 USING vec1(v, a, b);
INSERT INTO x1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
INSERT INTO x1 VALUES(random_vector(8), 10, 20);
INSERT INTO x1 VALUES(random_vector(8), 20, 30);
}
do_execsql_test 3.1 {
INSERT INTO x1 VALUES(random_vector(8), 20_000_000_000, 40);
} {}
#-------------------------------------------------------------------------
# https://sqlite.org/vec1/forumpost/0fd4e91444
#
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE d_blob USING vec1(embedding, title, checksum);
CREATE VIRTUAL TABLE d_text USING vec1(embedding, title, checksum);
CREATE VIRTUAL TABLE d_none USING vec1(embedding, title, checksum);
INSERT INTO d_blob(cmd,arg) VALUES('rebuild','{index:"flat"}');
INSERT INTO d_text(cmd,arg) VALUES('rebuild','{index:"flat"}');
INSERT INTO d_none(cmd,arg) VALUES('rebuild','{index:"none"}');
}
do_execsql_test 4.1 {
WITH RECURSIVE r(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<200 )
INSERT INTO d_blob(rowid, embedding, title, checksum)
SELECT i, vec1_from_json('['||i||','||(i*2)||',0,0]'), 'runbook '||i,
CAST(printf('%08d', i) AS BLOB)
FROM r;
WITH RECURSIVE r(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<200 )
INSERT INTO d_text(rowid, embedding, title, checksum)
SELECT i, vec1_from_json('['||i||','||(i*2)||',0,0]'), 'runbook '||i,
printf('%08d', i) FROM r;
WITH RECURSIVE r(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<200 )
INSERT INTO d_none(rowid, embedding, title, checksum)
SELECT i, vec1_from_json('['||i||','||(i*2)||',0,0]'), 'runbook '||i,
CAST(printf('%08d', i) AS BLOB)
FROM r;
}
foreach tab {d_none d_text d_blob} {
do_execsql_test 4.2.$tab.1 "
DELETE FROM $tab WHERE rowid%2<>0;
"
# Check that the filtered query can see the 100 survivors
do_execsql_test 4.2.$tab.2 [subst -nocommands {
SELECT count(*)
FROM $tab (vec1_from_json('[9,18,0,0]'), 900)
WHERE checksum IS NOT NULL
}] {100}
}
do_execsql_test 4.3.1 {
SELECT count(*)
FROM d_blob(vec1_from_json('[9,18,0,0]'),'{K:900}')
WHERE checksum = CAST('00000100' AS BLOB)
} 1
do_execsql_test 4.3.2 {
SELECT count(*)
FROM d_none(vec1_from_json('[9,18,0,0]'),'{K:900}')
WHERE checksum = CAST('00000100' AS BLOB)
} 1
do_execsql_test 4.3.3 {
SELECT count(*)
FROM d_text(vec1_from_json('[9,18,0,0]'),'{K:900}')
WHERE checksum = '00000100'
} 1
do_execsql_test 4.4 {
PRAGMA integrity_check
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, a);
INSERT INTO v1 VALUES(random_vector(8), 1);
INSERT INTO v1 VALUES(random_vector(8), 2);
INSERT INTO v1 VALUES(random_vector(8), 3);
INSERT INTO v1 VALUES(random_vector(8), 4);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 5.1 {
SELECT rowid FROM v1( random_vector(8), 100 ) WHERE a=500;
}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 6.1.1 {
CREATE VIRTUAL TABLE v1 USING vec1(v, t);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 )
INSERT INTO v1 SELECT random_vector(16), 'tag' || hex(randomblob(1000)) FROM s;
}
do_execsql_test 6.1.2 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.1.3 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"none"}');
} {}
sqlite3_limit db SQLITE_LIMIT_LENGTH 1000000
do_catchsql_test 6.1.4 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {1 {string or blob too big}}
do_execsql_test 6.1.5 {
PRAGMA integrity_check;
} {ok}
sqlite3_limit db SQLITE_LIMIT_LENGTH 100000000
do_execsql_test 6.1.6 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
PRAGMA integrity_check;
} {ok}
do_execsql_test 6.2.0 {
DROP TABLE IF EXISTS v1;
CREATE VIRTUAL TABLE v1 USING vec1(v, t);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 )
INSERT INTO v1 SELECT random_vector(200), 'tag' || hex(randomblob(1)) FROM s;
}
sqlite3_limit db SQLITE_LIMIT_LENGTH 500000
do_catchsql_test 6.2.1 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
} {1 {string or blob too big}}
do_execsql_test 6.2.2 {
PRAGMA integrity_check;
} {ok}
sqlite3_limit db SQLITE_LIMIT_LENGTH 100000000
do_execsql_test 6.2.3 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
UPDATE v1_idx SET val = NULL;
}
do_execsql_test 6.2.4 {
PRAGMA integrity_check;
} {{v1: %_idx entry id=1 is corrupt}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE b1 USING vec1(c1, c2);
INSERT INTO b1 VALUES(random_vector(10), 'hello');
INSERT INTO b1 VALUES(random_vector(10), 'world');
INSERT INTO b1 VALUES(random_vector(10), 'one');
INSERT INTO b1 VALUES(random_vector(10), 'one');
INSERT INTO b1 VALUES(random_vector(10), 'world');
INSERT INTO b1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 7.1 {
SELECT rowid FROM b1(random_vector(10), 10)
WHERE c2 = 'ONE' COLLATE nocase
ORDER BY 1
} {3 4}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 8.0 {
CREATE VIRTUAL TABLE docs USING vec1(embedding, title, size_kb, priority);
INSERT INTO docs(rowid, embedding, title, size_kb, priority)
SELECT i,
vec1_from_json('['||i||',0,0,0]'),
'runbook '||i, (i%5)*100, (i%3)+1
FROM (
WITH RECURSIVE r(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<60
)
SELECT i FROM r
);
}
foreach {tn where res} {
1 "priority IN (1,2) AND size_kb=100" 8
2 "size_kb=100 AND priority IN (1,2)" 8
4 "size_kb IN (100,200) AND priority = 1" 8
5 "priority IN (1,2)" 20
6 "size_kb = 100" 12
3 "priority IN (1,2) AND size_kb > 50" 20
} {
breakpoint
do_execsql_test 8.1.$tn "
SELECT count(*)
FROM docs( vec1_from_json('\[1,0,0,0\]'), 20 )
WHERE $where
" [list $res]
}
finish_test

View File

@@ -0,0 +1,48 @@
# 2026-08-04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1nan
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE vv USING vec1(v);
}
do_execsql_test 1.1 {
SELECT * FROM vv( vec1_from_json('[1,2,3,4,5,6,7,8,9]'), 10);
} {}
do_catchsql_test 1.2 {
SELECT * FROM vv(vec1_from_json('[1,2,3,4,5,6,7,8,NaN]'), 10);
} {1 {vec1: non-finite value not allowed in vector}}
do_catchsql_test 1.3 {
INSERT INTO vv(v) VALUES(vec1_from_json('[1,2,3,4,NaN,5,6,7,8,9]'));
} {1 {vec1: non-finite value not allowed in vector}}
do_execsql_test 1.4 {
CREATE TABLE tr(x);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO tr SELECT random_vector(9) FROM s;
INSERT INTO tr(x) VALUES(vec1_from_json('[1,2,3,4,NaN,5,6,7,8]'));
}
do_catchsql_test 1.5 {
SELECT vec1_train(x, '{codesize:4}') FROM tr
} {1 {vec1_train: non-finite value not allowed in vector}}
finish_test

143
ext/vec1/test/vec1pq.test Normal file
View File

@@ -0,0 +1,143 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1pq
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, a1);
}
do_execsql_test 1.1 {
CREATE TABLE t1(x);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t1 SELECT random_vector(24) FROM s;
}
do_execsql_test 1.2 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(x, '{codesize:8}') FROM t1
));
}
do_test 1.3 {
foreach {rowid v} [db eval {SELECT rowid, x FROM t1}] {
db eval { INSERT INTO v1 VALUES($v, $rowid) }
}
execsql { PRAGMA integrity_check }
} {ok}
do_execsql_test 1.4 {
SELECT rowid, a1 FROM v1(random_vector(24), '{K:5}') WHERE a1 = 555
} {555 555}
do_execsql_test 1.5 {
DELETE FROM v1 WHERE rowid=444;
SELECT rowid, a1 FROM v1(random_vector(24), '{K:5}') WHERE a1 = 444;
}
do_execsql_test 1.6 {
BEGIN;
INSERT INTO v1(rowid, v) VALUES(10_000_000_000, random_vector(24));
INSERT INTO v1(rowid, v) VALUES(0, random_vector(24));
INSERT INTO v1(rowid, v) VALUES(15_000_000_000, random_vector(24));
COMMIT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v2 USING vec1(v, a2);
}
do_execsql_test 2.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v2(rowid, v, a2) SELECT
i*10_000_000_000, random_vector(27), i*10_000_000_000
FROM s;
}
do_execsql_test 2.2 {
INSERT INTO v2(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{codesize:9,nbucket:2,residual:0}') FROM v2
));
SELECT vec1_config('nprobe', 2);
} {2.0}
do_execsql_test 2.3 {
SELECT rowid, a2 FROM v2(random_vector(27), '{K:5}')
WHERE a2 = 5_550_000_000_000
} {5550000000000 5550000000000}
do_execsql_test 2.4 {
DELETE FROM v2 WHERE rowid=5550000000000;
SELECT rowid, a2 FROM v2(random_vector(27), '{K:5}')
WHERE a2 = 5_550_000_000_000
} {}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.1 {
CREATE VIRTUAL TABLE v1 USING vec1(x);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1 SELECT random_vector(32) FROM s;
}
do_execsql_test 3.2 {
SELECT length( vec1_train(x, '{codesize:11}') ) FROM v1;
} {33816}
do_execsql_test 3.3 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(x, '{codesize:11}') FROM v1
));
} {}
do_execsql_test 3.4 {
PRAGMA integrity_check
} {ok}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE docs USING vec1(e);
INSERT INTO docs(rowid, e) VALUES(1, random_vector(200, 10000.0));
WITH RECURSIVE r(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<600
)
INSERT INTO docs(rowid, e) SELECT i+1, random_vector(200) FROM r;
}
do_execsql_test 4.1 {
INSERT INTO docs(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(e, '{nbucket:2, quantizer:"pq"}') FROM docs
));
}
do_execsql_test 4.2 {
PRAGMA integrity_check
} {ok}
finish_test

View File

@@ -0,0 +1,147 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1query
vec1_load db
set lCentroid [list]
proc allocate_centroids {nCentroid nElem} {
global lCentroid
for {set ii 0} {$ii < $nCentroid} {incr ii} {
lappend lCentroid [randvec $nElem 10.0]
}
}
proc randvec {nElem {scale 1.0}} {
set vec [list]
for {set ii 0} {$ii < $nElem} {incr ii} {
lappend vec [expr {rand() * $scale}]
}
return $vec
}
proc addvec {a b} {
if {[llength $a]!=[llength $b]} {
error "vectors are different lengths"
}
set ret [list]
foreach ia $a ib $b {
lappend ret [expr $ia + $ib]
}
return $ret
}
proc cvec {nDim} {
global lCentroid
if {[llength $lCentroid]==0} {
allocate_centroids 16 $nDim
}
set ii [expr int(abs(rand()) * [llength $lCentroid])]
set ret [addvec [lindex $lCentroid $ii] [randvec $nDim 1.0]]
binary format r* $ret
}
db func cvec cvec
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
}
do_execsql_test 1.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1 SELECT cvec(24) FROM s;
}
do_execsql_test 1.2 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{codesize:0, nbucket:20, distance:"l2"}') FROM v1
));
}
set nRep 100
proc testquery {v p} {
db one {SELECT
(SELECT rowid FROM v1($v, $p) ORDER BY vec1_l2_distance(v, $v))
==
(SELECT rowid FROM v1 ORDER BY vec1_l2_distance(v, $v))
}
}
do_test 1.3 {
set nTry 0
set nFind 0
for {set ii 0} {$ii < $nRep} {incr ii} {
set v [cvec 24]
set p "{K:50, nprobe:4}"
incr nTry 1
incr nFind [testquery $v $p]
}
expr {((double($nFind) / $nTry) >= 0.98) ? "ok" : "$nFind/$nTry"}
} {ok}
do_test 1.4 {
set nTry 0
set nFind 0
for {set ii 0} {$ii < $nRep} {incr ii} {
set v [random_vector 24]
set p "{K:50, nprobe:4}"
incr nTry 1
incr nFind [testquery $v $p]
}
expr {((double($nFind) / $nTry) >= 0.99) ? "ok" : "$nFind/$nTry"}
} {ok}
model2tbl model v1
if {1 || [set_test_counter errors]>0} {
set ii 0
foreach c $lCentroid {
set bin [binary format r* $c]
puts -nonewline "CENTROID $ii: "
set L [
db eval {
SELECT format('%.1f', vec1_l2_distance($bin, coarse)) AS d FROM model
}
]
puts [lrange [lsort $L] 0 0]
incr ii
}
}
do_execsql_test 1.5 {
PRAGMA integrity_check
} {ok}
set v [db one {SELECT random_vector(24)}]
set p "{K:50,nprobe:null}"
do_catchsql_test 1.6 {
SELECT (SELECT rowid FROM v1($v, $p) ORDER BY vec1_cos_distance(v, $v))
==(SELECT rowid FROM v1 ORDER BY vec1_cos_distance(v, $v))
} {1 {vec1: null value in json object}}
do_catchsql_test 1.7 {
SELECT rowid FROM v1($v, -10)
} {1 {vec1: K must be an integer value between 0 and 33554432}}
finish_test

View File

@@ -0,0 +1,151 @@
# 2026-08-04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1rollback
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
}
do_catchsql_test 1.1 {
INSERT INTO v1(rowid, v) VALUES
(1, random_vector(8)),
(2, random_vector(8)),
(3, random_vector(7));
} {1 {vec1: unexpected vector blob size 28 bytes, expected 32}}
do_execsql_test 1.2 {
INSERT INTO v1(rowid, v) VALUES(4, random_vector(8));
}
sqlite3 db2 test.db
vec1_load db2
do_test 1.3 {
catchsql {
INSERT INTO v1(rowid, v) VALUES(5, random_vector(4));
} db2
} {1 {vec1: unexpected vector blob size 16 bytes, expected 32}}
db2 close
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
}
do_catchsql_test 2.1 {
BEGIN;
INSERT INTO v1(rowid, v) VALUES
(1, random_vector(8)),
(2, random_vector(8)),
(3, random_vector(7));
} {1 {vec1: unexpected vector blob size 28 bytes, expected 32}}
do_execsql_test 2.2 {
INSERT INTO v1(rowid, v) VALUES(4, random_vector(8));
COMMIT;
}
sqlite3 db2 test.db
vec1_load db2
do_test 2.3 {
catchsql {
INSERT INTO v1(rowid, v) VALUES(5, random_vector(4));
} db2
} {1 {vec1: unexpected vector blob size 16 bytes, expected 32}}
db2 close
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1 SELECT random_vector(24) FROM s;
}
do_execsql_test 2.1 {
BEGIN;
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{codesize:8}') FROM v1
));
ROLLBACK;
}
sqlite3 db2 test.db
vec1_load db2
do_execsql_test -db db2 2.2 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{codesize:4}') FROM v1
));
}
do_execsql_test 2.3 {
INSERT INTO v1(v) VALUES( random_vector(24) );
}
do_execsql_test 2.4 {
PRAGMA integrity_check;
} {ok}
do_execsql_test 2.5 {
BEGIN;
SAVEPOINT one;
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{codesize:8}') FROM v1
));
ROLLBACK TO one;
COMMIT;
}
do_execsql_test -db db2 2.6 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{codesize:4}') FROM v1
));
}
do_execsql_test 2.7 {
INSERT INTO v1(v) VALUES( random_vector(24) );
}
do_execsql_test 2.8 {
PRAGMA integrity_check;
} {ok}
db2 close
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
BEGIN;
CREATE VIRTUAL TABLE staging USING vec1(a, b, c);
ROLLBACK;
}
do_execsql_test 3.1 {
SAVEPOINT nightly;
CREATE VIRTUAL TABLE staging USING vec1(a, b, c);
ROLLBACK TO nightly;
}
finish_test

View File

@@ -0,0 +1,56 @@
# 2026-03-14
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1rotate
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1(rowid, v) SELECT i, random_vector(14) FROM s;
INSERT INTO v1(cmd, arg)
SELECT
'rebuild',
vec1_train(v, '{codesize:14, opq:true, svd_verify:true, distance:"cos"}')
FROM v1;
}
set v [db one {SELECT random_vector(14)}]
do_execsql_test 1.1 {
SELECT
(SELECT rowid FROM v1($v, 1))==
(SELECT rowid FROM v1 ORDER BY vec1_cos_distance($v, v))
;
} {1}
do_execsql_test 2.0 {
DROP TABLE IF EXISTS v1;
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO v1(rowid, v) SELECT i, random_vector(64) FROM s;
INSERT INTO v1(cmd, arg)
SELECT
'rebuild',
vec1_train(v, '{codesize:8, opq:true, svd_verify:true}')
FROM v1;
}
finish_test

View File

@@ -0,0 +1,42 @@
# 2026-03-14
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1rotate2
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<34000
)
INSERT INTO v1(rowid, v) SELECT i, random_vector(256) FROM s;
}
proc progress {percent msg} {
puts "${percent}%: $msg"
}
db func progress progress
do_test 1.1 {
set T [db one {
SELECT
vec1_train(v, '{codesize:16, opq:1, nopq_round:1, progress:"progress"}')
FROM v1
}]
set {} {}
} {}
finish_test

View File

@@ -0,0 +1,303 @@
# 2026-08-21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1round
vec1_load db
proc write_db {db} {
$db eval {
CREATE VIRTUAL TABLE docs USING vec1(embedding, title, category);
INSERT INTO docs(rowid,embedding,title,category) VALUES(757,
vec1_from_json('[78,15,13,9,3,1,2,1]'),
'runbook 757: rotate the search index signing key','ops'
);
INSERT INTO docs(cmd, arg) VALUES('rebuild', unhex(
'0000000400000001000000080000000400000020000000010000A841000018420000BA4200008040' ||
'0000504200004E42000090410000084100008A4200000442ABAA1642ABAA12420000264200006841' ||
'00008942000048420000A44200008C416EDB7E42499224410000D4400000434233339D426666E640' ||
'000080400000A8410000B04200002C420000BF420000784155559042ABAA86410000364200003242' ||
'0000DC410000D8416666BE42333327420000F841000080405555B74255551942B76DA142DBB6D541' ||
'6EDB6E429224254200003442000090410000A642000040400000924200009C410000404000001442' ||
'66661042CDCCCC41000038420000D841000028410000803F00003842000012420000C64100005841' ||
'00007A4200008C410000724200004C42000010414992E4400000004200002042ABAA8C42ABAA4E42' ||
'666688429A99B94000001642000036420000B2420000C441000084420000A8410000404200004040' ||
'00005A4200004A42000089420000264200009642000040420000B6420000E0410000994200002242' ||
'00000E42000008410000544200000A420000764200000A4255556142ABAA2A400000DC41ABAA8641' ||
'0080B24200000C4200008E42000000416EDB06429224054200005C420000E041ABAA2A4155551142' ||
'000058410000AC4100002041000032420000B7420000CC410000C0400000FC410000184200003041' ||
'0000003F00000841000068420000F8416666AE41CDCC4C4055559E42ABAABE41ABAAAA41ABAA3A42' ||
'0000AF4200004E420000803F00002B420000A441000010410000F040000078419A99993FCDCCD441' ||
'000040410000E841ABAA7A42ABAAAA3E00004841000040410000BD4200003D420000884100002A42' ||
'0000A54200003A4200008C410000CC410000B0400000C03F0000704100000040CDCC48426666FE41' ||
'000028420000484200009A420000B64100007442000040420000A841000038410000164200009441' ||
'000094410000B04000002C420000C0410000B84100002041ABAA584200002E420000844100000642' ||
'0000A742000038410000F4410000F041000032420000FC419A99B1419A99A1410000BF4200000242' ||
'0000884100004E420000D04100004E420000914200001242000010410000D0410000E44100001242' ||
'000098420000984100002E420000E0400000CE4100007441B76D91424992A43F0000BC420000B041' ||
'ABAA6042000036420000D0400000BC410000B3420000003FABAAAE42ABAA06420000B84200004041' ||
'00008B4200006040000022420000AC410000A8410000E8410000AC42000060410000B34200008C41' ||
'CDCC2C4233330742ABAA6A400000384200005841000040420000824200003A42CDCC1C426666EE41' ||
'CDCC2C419A99194100004A420000B04000006E420000684133332742666622420000B84000009840' ||
'000095420000EC413333634133331F4200004A425555434200004E420000C44100000A4200002A42' ||
'0000A1420000003F3333A9423333A3410000B342000002420000FC410000944166668E419A998941' ||
'0000AE420000B44100001C420000A0410000AB420000C84055558642000050410000F4410000A841' ||
'B76D4F42DBB625420000BF4200001A42ABAA5642ABAAAA3E00009C4200004C42008076420000D840' ||
'00004442000034420000E0410000803F33334342CDCC1C42000094420000E0400000804000004041' ||
'0000B24200001041922461426EDB164200001E420000DA416666A6426666EE41000036420000003F' ||
'0000C0400000204200008242000018420000B74200003742000040420000A84100008C420000C841' ||
'0000004100004E4200008F42000032420000803F0000904100000C420000F8410000CA410000BB41' ||
'00002040000020400000004000002041000016420000094200007C420000D84100009F4200002E42' ||
'ABAA7A41555575419A999742666686400000B64200000040ABAABA42ABAA1E4200009A420000D040' ||
'0000C441000002420000BF420000D04000009B42000002425555B54155554142333327426666F641' ||
'92240542DBB6B541000098420000F8410000944100000000000084420000604055556541ABAA5A41' ||
'0000B64200002C42000028410000944100004C4200001842ABAA5E420000803F5555854255554142' ||
'ABAA80425555D53F00009842000038415555B541ABAAB2413333A341CDCC944155554541ABAA1642' ||
'000084420000F04100009242000028410000D04100002C426666A2426666E64055559D41ABAAAA3F' ||
'000096420000AC410000204000004E420000E84100003842ABAA3A415555394255557541ABAA4642' ||
'0000A24200001841000072420000C4415555B541555555410000684255553F42000091420000DC41' ||
'55550541ABAA2A420000003F000046420000A8420000D841000068410000FC4133333F42CDCC3C42' ||
'0000BC4100002242CDCC7C413333B340CDCC68429A9999400000E8410000C8415555E541ABAA9A41' ||
'CDCC0C41CDCC08420000003F00000A4200009C42000070410000A0410000E0400000BA4200001042' ||
'33337B42CDCC3042000060400000E8410000BB420000F4410000804200004C42ABAAC241ABAA4642' ||
'0000AD420000EC41555505425555D5400000944100000E4200009C41000034420000B84200004040' ||
'00008C4100005841000088420000B8410000924200003842000085420000003F92248B4225497241' ||
'ABAAB442ABAA12420000BC420000A040ABAA204200001A42000000420000E04100009C4100007841' ||
'33335341333373400000803F0000803F0000AB42000022420000BC4200006041000042420000EC41' ||
'0000AB4200000042ABAA3042555527420000B0420000F8410000AB42000046420000DE4100004441' ||
'0000003F000040409A9909410000104100003041000060416EDB46412549B2400000E0410000C03F' ||
'0000003FABAA72410000C0410000E0400000F0410000184166660641CDCC4C405555D53FABAACA40' ||
'9A99C1419A99193F0000824100004C4155551540000078410000B841000038410000884100002041' ||
'ABAA8A405555B540666686409A99993F0000A04100002040ABAA2A41ABAA3A41ABAADA41ABAA0A41' ||
'B76DDB3E6EDB363F5555D54000008040000060410000D0400000B84100007841CDCCEC4066666641' ||
'0000F041000060405555B5415555B540ABAA2A3F000050416EDB7641499224405555D54155556541' ||
'000098415555954000007841ABAAAA3E00000000ABAA1A410000A841000018410000404000000841' ||
'00007041000080410000C44100005041ABAAE2410000E040CDCCCC419A9919400000CC4100008041' ||
'00005841000060410000D8400000444100006041000048410000004100003041ABAA3A4100000841' ||
'5555B541000000000000A041ABAA2A3E0000B040000010410000D0400000003F0000904100008041' ||
'0000803F00004041B76DAB41499264409224C9419224A94000002C410000E03F000080410000B040' ||
'00004841000080410000A041000030416EDB06419224C93F0000003F0000F0409A999941CDCC7C41' ||
'55558D41ABAAEA400000504100007041ABAAAA41ABAA6A4100002841000078410000B44100005841' ||
'000008410000504133333340CDCCCC3E00002041ABAA1A41ABAAB241ABAA0A41B76DC34192242941' ||
'0000D841555515400000EC41000000003333634133331341ABAA2A40555535400000104100002041' ||
'00004841000040405555D5410000804033338B41000000000000A0410000A0400000B6410000E840' ||
'0000A040000098409A9959400000604100002041ABAA2A3F00008E41000010400000C04100007841' ||
'ABAA824100007041CDCC4C413333234100007041ABAA3A415555954000005041000078410000F040' ||
'0000B041000020410000F0410000A0400000F041000070410000E641000050400000E040CDCCEC40' ||
'0000804000000841398ECB411CC7F1405555D541000030410000A040ABAA6A415555C54100001041' ||
'0000A8415555D5400000EC410000304100007041ABAA6A41000070410000504100009E4100006441' ||
'0000D041666616410000D84100005041000068410000804000006041000020400000E8410000C040' ||
'00000000000030410000F041ABAAEA400000F0419A9949410000E841000040410000DC4100008041' ||
'0000C040000070410000D8410000E0400000C8410000000000009441000040410000504100008040' ||
'0000E8410000C03F00003841000000009A998941CDCC8C40000008410000B0403333DB4100002041' ||
'00000040000020410000D041000048411CC7C1400000A040ABAA2A40000030410000804000004041' ||
'0000D4410000803F00009C4100000041ABAA6A400000804100008C4100003041ABAA2A410000C040' ||
'000050410000C03F000040400000C040000000000000404100000000000000400000D0410000C040' ||
'0000C441000040410000104100006841000090410000B0400000C441000060415555D53F00006041' ||
'0000C03F000080400000E041ABAAAA405555B541ABAAAA3F0000944100001041CDCCCC3E6666A640' ||
'0000C841000070410000A0400000F04000000040000000000000E040000010410000004000004041' ||
'0000A041000050410000F0410000C03F0000C0400000F0400000AC41000040415555C541ABAA6A40' ||
'5555E5415555754155558541ABAA0A410000104100004041333393410000803FABAA0A41ABAAEA40' ||
'0000D840000010400000D8410000A040000088410000C0400000D04000002041ABAA0A4100000000' ||
'0000E041000030410000803F555515415555AD41000080410000E841000010410000E84155555541' ||
'0000000000006041000000400000504100009041000070410000C040000060410000D44100000000' ||
'0000004100000041ABAA4A41ABAAEA400000803F00008041CDCCCC3F000000400000184100003041' ||
'0000E04100000041ABAA6A4055557540555585410000604100000040000000410000304100005041' ||
'000000410000404100005041000050410000D041000070410000BC41000040400000A84100005041' ||
'0000404100008040000098419A9919400000B0410000A0400000604100000041000024410000E840' ||
'000068410000A04000006041000030410000A0400000104000003041000090400000044100007C41' ||
'0000A040000080410000C8400000C0400000B841000020410000A8410000C03F000000400000A040' ||
'0000C040000040400000E040000030410000A041000010410000404100004041000098410000E040' ||
'00009841000020410000E0410000604100005041000040410000904100005841000080400000E040' ||
'0000D8410000C0400000E0410000000055559540000020410000803F00000000000070410000C040' ||
'0000A8410000A0400000404000004041ABAACA400000804100005841000000000000884100004040' ||
'55559540000000000000803F000030410000804000007041000060410000803F000098410000C040' ||
'000088410000803FABAA1A41ABAA6A400000B0410000004000000041000020410000804100008040' ||
'0000A040000030410000A0410000E04000004041ABAAAA3F00003041000018410000000000008040' ||
'0000404000005041000020410000A0400000A841000000410000B041000030410000984100005041' ||
'0000304100004040000070410000204100008841000080410000C040000030410000404100003041' ||
'000000000000000000003041000040400000A0400000A040000010410000803F000000000000A040' ||
'0000A040000000000000E0400000804000000000000000400000E040000000400000404000000040' ||
'0000E04000000000000040410000C04000000040000000400000C04000000000000020410000803F' ||
'000000400000C040000010410000C0400000004100000000000040400000A0400000E0400000803F' ||
'000000410000803F000030410000A0400000A0400000C0400000C0400000C0400000304100008040' ||
'0000803F000040400000803F0000803F00004040000000000000A040000080400000C0400000803F' ||
'000010410000A040000080400000404000004040000080400000803F0000A0400000E0400000A040' ||
'0000A040000000400000204100000000000000400000A0400000A040000040400000404100000040' ||
'0000304100000000000080400000803F000000410000404000002041000040400000204100000040' ||
'0000E040000040400000C04000000040000040410000803F00001041000000400000004000000000' ||
'0000E0400000C040000040410000804000000041000000400000C040000080400000000000004040' ||
'000000410000A04000002041000080400000104100004040000040400000803F0000004000008040' ||
'000030410000C04000000041000080400000404000004040000080400000C040000000000000C040' ||
'000080400000A0400000104100008040000040400000C0400000803F00000040000020410000A040' ||
'000030410000803F0000804000000000000000000000803F0000A0400000803F0000C04000004040' ||
'0000803F0000C0400000803F000000000000104100000000000020410000C0400000404100000000' ||
'000080400000004000003041000000400000803F000080400000004000004040000000400000803F' ||
'000040410000A0400000C0400000A040000040410000404000000000000080400000804000008040' ||
'000000410000C0400000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000000000000040000080400000803F00000040000000000000404000000040' ||
'00004040000000000000803F000000000000803F0000004000000040000000400000000000000000' ||
'000000400000803F0000804000000040000000000000803F0000803F0000803F000040400000803F' ||
'00008040000000000000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F0000803F' ||
'0000803F0000803F0000803F0000803F6F7AB342D39B0A42DFF4C641BDE9ED40210BC9400B594840' ||
'59C80240B290853FC4C383402D2D01429796F6401E1E3E41E2E1E1407878384000000040F1F0703F' ||
'9D73224274CE1942A594D240CE39A740638CD14095524A40082104400821843F322990421296FB40' ||
'877EF340877EF3407FB3B54012963B40C8A4F83F232C773FA6A59941D3D2D240D3D2BA4178783841' ||
'7878B8400F0F4F40C4C30340F1F0703FCA6BA8400DE539426C28EF401BCA03416C28DF406C284F40' ||
'A2BC0640515E833F3BB10B41ECC43641C54E804176624F410000E040C54E4C40B113FB3F4FEC843F' ||
'F1F04A42B5B422424B4BCB4197963E416969C9405A5A3A407878F83F8887873F0000E84100000040' ||
'0000204100006041000000416666664000000040CDCC4C3FB5B47C42F1F03C42B5B4B4405A5ADA40' ||
'0000C040A6A54540000000400000803F33330142CDCC12423333B7419A99A9409A99B14000003040' ||
'666606403333733F175DCC41E9A24342E9A2CB40A38B1E4174D10540D1455740000000400000803F' ||
'00C025420000AE4000001941000090400000A04000004C400000F03F0000883F254962429EE73942' ||
'5555C541310C43408661B84062184640621806407A9E673F00A06F420000DE400000BE410000D640' ||
'0000A740000046400000064000008C3F43169A4116B290410000A04091858C40F5A6B7407AD35B40' ||
'EA4DEF3F16B2903F1296A7423E8DD0410000A040734F03413E8DB04009CB3D4000000040DDD3883F' ||
'33B30A429A99CB410000B84066662A413333B740CDCC2440000008409A99793F00006841DBB63142' ||
'DBB6B5413DCFE3409EE7B94049922440499204400000803FB497004109ED65405F42FB40721CC740' ||
'ABAA8A402FA13D40D15E0240BE84763F00002E42000084410000C441555521410000C84055552540' ||
'000000400000803F008066420000FB415555E940ABAA10410000A84055554540000000405555853F' ||
'ABAA3E42BCBB314233336341555525413333D340BCBB3B40EFEEEE3F4444843FCDCCAA4254823C41' ||
'95209F4158FB06419520CF40A8043940A804F93F90A7843FDFF4DE414E6FC2416F7ACB41EA4D2F41' ||
'6F7AD3400B5948404E6FFA3F9CDE743F000091425D742F4274D1B941BAE8B2404617BD402FBA4840' ||
'5D741140175D743F0000E8400000D4410000B2410000B040ABAACE4055554D40ABAA02400000803F' ||
'721C9242ABAAC6418EE39E41ABAA16411CC7C9408EE3484000000040398E833F0AD72F423333EB41' ||
'00008041F6289C40A470DD40E17A5440000000408FC2753FB1DCAF42232C2F42734FE340B9A72141' ||
'232C9740000040409F46D83F232C773F5D74A941A38B1042BAE8E2402FBA0841E9A2CB4046173D40' ||
'175DF43F0000803F9A99B141CDCC8441ABAAAA41111171407777C7409A993940444404404444843F'
));
}
}
do_test 1.0 {
write_db db
} {}
do_execsql_test 1.1 {
PRAGMA integrity_check
} {ok}
db close
sqlite3 db test.db
if {[catch { vec1_load_scalar db }]} {
puts "Skipping tests... vec1testscalar.so not built"
finish_test
return
}
breakpoint
do_execsql_test 1.2 {
PRAGMA integrity_check
} {ok}
do_execsql_test 1.3 { DROP TABLE docs }
do_test 1.4 {
write_db db
} {}
do_execsql_test 1.5 {
PRAGMA integrity_check
} {ok}
db close
sqlite3 db test.db
vec1_load db
do_execsql_test 1.6 {
PRAGMA integrity_check
} {ok}
finish_test

View File

@@ -0,0 +1,77 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1scalar
if {[catch { vec1_load_scalar db }]} {
puts "Skipping tests... vec1testscalar.so not built"
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE docs USING vec1(embedding, title, category);
}
do_execsql_test 1.1 {
INSERT INTO docs(rowid, embedding, title, category)
SELECT i, vec1_from_json('['||(SELECT group_concat(printf('%!.9g',
sin(i*0.7+j*1.3)*0.5 + cos(i*0.11*j+0.3)*0.5),',')
FROM (WITH RECURSIVE q(j) AS (SELECT 0 UNION ALL SELECT j+1 FROM q WHERE j<31)
SELECT j FROM q))||']'),
'runbook '||i,
CASE i%4 WHEN 0 THEN 'ops' WHEN 1 THEN 'kb' WHEN 2 THEN 'sec' ELSE 'net' END
FROM (
WITH RECURSIVE r(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<700)
SELECT i FROM r
);
}
do_execsql_test 1.2 {
INSERT INTO docs(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(
embedding,'{nbucket:8,codesize:8,quantizer:"bq",residual:0}'
) FROM docs
));
}
do_execsql_test 1.3 {
PRAGMA integrity_check
} {ok}
db close
sqlite3 db test.db
vec1_load db
do_execsql_test 1.4 {
PRAGMA integrity_check
} {ok}
do_execsql_test 1.5 {
INSERT INTO docs(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(
embedding,'{nbucket:8,codesize:8,quantizer:"bq",residual:0}'
) FROM docs
));
}
db close
sqlite3 db test.db
vec1_load_scalar db
do_execsql_test 1.6 {
PRAGMA integrity_check
} {ok}
finish_test

View File

@@ -0,0 +1,121 @@
# 2026-03-14
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1stream
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v1(rowid, v) SELECT i, random_vector(16) FROM s;
INSERT INTO v1(cmd, arg)
SELECT 'rebuild', vec1_train(v, '{nbucket:32, codesize:0}') FROM v1;
}
do_test 1.1 {
llength [
db eval {
SELECT rowid FROM v1(random_vector(16), '{K:10, nprobe: 1, streaming:0}')
}
]
} {10}
do_test 1.2 {
llength [
db eval {
SELECT rowid FROM v1(
random_vector(16), '{K:1000, nprobe: 32, streaming:0}'
)
}
]
} {1000}
do_test 1.3 {
llength [
db eval {
SELECT rowid FROM v1(random_vector(16), '{K:10, nprobe: 1, streaming:1}')
}
]
} {1000}
proc repeat {str n} {
string repeat $str $n
}
db func repeat repeat
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v2 USING vec1(v, m);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO v2(rowid, v, m) SELECT i, random_vector(16), repeat(i,200) FROM s;
INSERT INTO v2(cmd, arg)
SELECT 'rebuild', vec1_train(v, '{nbucket:32, codesize:0}') FROM v1;
}
breakpoint
do_test 2.1 {
llength [
db eval {
SELECT rowid
FROM v2(random_vector(16), '{K:10, nprobe: 1, streaming:1}')
WHERE m=repeat(555,200)
}
]
} {1}
#-------------------------------------------------------------------------
reset_db
vec1_load db
db func vec vec
proc vec {args} { binary format r* $args }
proc sequence {n} {
set ret [list]
for {set i 1} {$i <= $n} {incr i} { lappend ret $i }
set ret
}
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE docs USING vec1(embedding, title, category);
WITH RECURSIVE r(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<400)
INSERT INTO docs(rowid, embedding, title, category)
SELECT i,
vec(i%37, i%53, i%71, i%29),
'runbook '||i,
CASE WHEN i%3=0 THEN 'ops' ELSE 'kb' END
FROM r;
INSERT INTO docs(cmd,arg) VALUES('rebuild', (
SELECT vec1_train(embedding,'{nbucket:16,quantizer:"none"}') FROM docs
));
}
do_execsql_test 3.1 { PRAGMA integrity_check } {ok}
for {set ii 1} {$ii <= 16} {incr ii} {
set j "{K:10,streaming:1,nprobe:$ii}"
do_execsql_test 3.2.$ii {
SELECT rowid FROM docs(vec(5,5,5,5), $j) ORDER BY 1
} [sequence 400]
}
finish_test

View File

@@ -0,0 +1,87 @@
# 2026-03-14
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1stream2
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING vec1(v);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 )
INSERT INTO t1 SELECT rvec(16, i) FROM s;
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
foreach {tn2 idx} {
1 {
INSERT INTO t1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
2 {
INSERT INTO t1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{nbucket:8}') FROM t1
));
SELECT vec1_config('nprobe', 2);
}
3 {
INSERT INTO t1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{nbucket:8, codesize:4}') FROM t1
));
SELECT vec1_config('nprobe', 4);
}
} {
foreach {tn iVec} {
1 498753
2 722456
3 462190
4 981077
5 606275
6 239435
7 508863
8 795393
9 163905
10 697229
} {
db eval $idx
for {set nRes 2} {$nRes < 100} {incr nRes 3} {
set expect [db eval { SELECT rowid FROM t1( rvec(16, $iVec), $nRes ) }]
if {$nRes>[llength $expect]} break
do_test 1.$tn2.$tn.$nRes.1 {
set J "{K:$nRes, streaming:1}"
set all [db eval { SELECT rowid FROM t1( rvec(16, $iVec), $J) }]
lrange $all 0 [expr $nRes-1]
} $expect
set expect [db eval { SELECT rowid FROM t1 ORDER BY 1 }]
do_test 1.$tn2.$tn.$nRes.2 {
set J "{K:$nRes, streaming:1}"
set all [db eval { SELECT rowid FROM t1( rvec(16, $iVec), $J) }]
lsort -integer $all
} $expect
do_test 1.$tn2.$tn.$nRes.3 {
set J "{K:$nRes, streaming:1, nprobe_slack:0.2}"
set all [db eval { SELECT rowid FROM t1( rvec(16, $iVec), $J) }]
lsort -integer $all
} $expect
}
}
}
finish_test

View File

@@ -0,0 +1,160 @@
# 2026-04-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1train
vec1_load db
do_execsql_test 1.0 {
CREATE TABLE t1(a);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1000
) INSERT INTO t1 SELECT random_vector(16) FROM s;
}
do_catchsql_test 1.1 {
SELECT vec1_train(a, '{codesize: "large"}') FROM t1
} {1 {vec1: codesize must be set to an integer value between 1 and 256, or 0}}
do_catchsql_test 1.2 {
SELECT vec1_train(a, '{codesize: 16, nthread: -1}') FROM t1
} {1 {vec1: nthread must be set to an integer value between 1 and 128}}
do_catchsql_test 1.3 {
SELECT vec1_train(a, '{codesize: 1024}') FROM t1
} {1 {vec1: codesize must be set to an integer value between 1 and 256, or 0}}
do_catchsql_test 1.4 {
SELECT vec1_train(a, '{codesize: 8, opq: "yes"}') FROM t1
} {1 {vec1: opq must be set to a boolean value}}
do_catchsql_test 1.5 {
SELECT vec1_train(a, '{codesize: 8, opq: 555}') FROM t1
} {1 {vec1: opq must be set to a boolean value}}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE u1 USING vec1(a);
}
do_catchsql_test 2.1 {
INSERT INTO u1(cmd, arg) VALUES('rebuild', '{param: 1234}');
} {1 {vec1: unknown parameter: param}}
do_execsql_test 3.0 {
CREATE TABLE t2(x);
INSERT INTO t2 VALUES(random_vector(16));
INSERT INTO t2 VALUES(random_vector(16));
INSERT INTO t2 VALUES(random_vector(16));
INSERT INTO t2 VALUES('');
}
do_catchsql_test 3.1 {
SELECT vec1_train(x, '{codesize: 8}') FROM t2
} {1 {vec1_train: bad argument}}
do_execsql_test 3.2.0 {
UPDATE t2 SET x=randomblob(7) WHERE rowid=4;
}
do_catchsql_test 3.2.1 {
SELECT vec1_train(x, '{codesize: 8}') FROM t2
} {1 {vec1_train: bad argument}}
do_execsql_test 3.3.0 {
UPDATE t2 SET x=random_vector(20) WHERE rowid=4;
}
do_catchsql_test 3.3.1 {
SELECT vec1_train(x, '{codesize: 8}') FROM t2
} {1 {vec1_train: bad argument}}
do_catchsql_test 3.3.2 {
SELECT vec1_train(x) FROM t2
} {1 {vec1_train: bad argument}}
do_execsql_test 3.4.0 {
UPDATE t2 SET x=random_vector(16) WHERE rowid=4;
}
#do_catchsql_test 3.4.1 {
# SELECT vec1_train(x, '{codesize: 12}') FROM t2
#} {1 {vec1_train: vector size (16) must be a multiple of codesize (12)}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 4.0 {
CREATE TABLE x1(v);
}
do_catchsql_test 4.1 {
SELECT vec1_train(v, '{nbucket: 8}') FROM x1
} {1 {too few training vectors (have 0, require more than that)}}
do_execsql_test 4.2 {
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100
) INSERT INTO x1 SELECT random_vector(16) FROM s;
}
do_catchsql_test 4.3 {
SELECT vec1_train(v, '{nbucket: 8, progress: "nofunc"}') FROM x1
} {1 {no such function: nofunc}}
proc errorfunc {args} {
error "this is an error!"
}
db func errorfunc errorfunc
do_catchsql_test 4.4 {
SELECT vec1_train(v, '{nbucket: 8, progress: "errorfunc"}') FROM x1
} {1 {this is an error!}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE v1 USING vec1(a);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1000
) INSERT INTO v1 SELECT random_vector(16) FROM s;
}
do_catchsql_test 5.1 {
INSERT INTO v1(cmd, arg) VALUES(
'rebuild', (SELECT substr( (
SELECT vec1_train(a, '{codesize: 8}') FROM v1
), 1, 2000))
);
} {1 {vec1: model size mismatch: have 2000, expected 16408}}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE v1 USING vec1(a);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<12
) INSERT INTO v1 SELECT random_vector(16) FROM s;
}
proc myprofile {args} {}
db func myprofile myprofile
do_catchsql_test 6.1 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(a, '{quantizer:"pq", profile: "myprofile"}') FROM v1
));
} {1 {too few training vectors (require 512, have 12)}}
finish_test

View File

@@ -0,0 +1,124 @@
# 2026-08-04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1update
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
INSERT INTO v1(rowid, v) VALUES(11, random_vector(8));
INSERT INTO v1(rowid, v) VALUES(12, random_vector(8));
INSERT INTO v1(cmd, v) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 1.1 {
PRAGMA integrity_check
} {ok}
do_catchsql_test 1.2.1 {
UPDATE v1 SET v = random_vector(9) WHERE rowid=12;
} {1 {vec1: unexpected vector blob size 36 bytes, expected 32}}
do_execsql_test 1.2.2 {
PRAGMA integrity_check
} {ok}
do_execsql_test 1.2.3 {
SELECT rowid, length(v) from v1;
} {11 32 12 32}
do_execsql_test 1.3.1 {
BEGIN;
INSERT INTO v1(rowid, v) VALUES(13, random_vector(8));
} {}
do_catchsql_test 1.3.2 {
UPDATE v1 SET v = random_vector(9) WHERE rowid=12;
} {1 {vec1: unexpected vector blob size 36 bytes, expected 32}}
do_execsql_test 1.3.3 {
COMMIT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 1.3.4 {
SELECT rowid, length(v) from v1;
} {11 32 12 32 13 32}
do_execsql_test 1.3.5 {
SELECT rowid, length(v) from v1;
} {11 32 12 32 13 32}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v);
INSERT INTO v1(rowid, v) VALUES(11, random_vector(8));
INSERT INTO v1(rowid, v) VALUES(12, random_vector(8));
INSERT INTO v1(cmd, v) VALUES('rebuild', '{index:"flat"}');
}
# explain_i { UPDATE v1 SET rowid=11 WHERE rowid=12; }
do_execsql_test 2.1 {
BEGIN;
INSERT INTO v1(rowid, v) VALUES(13, random_vector(8));
}
do_catchsql_test 2.2 {
UPDATE v1 SET rowid=11 WHERE rowid=12;
} {1 {constraint failed}}
do_execsql_test 2.3 {
COMMIT;
PRAGMA integrity_check
} {ok}
do_execsql_test 2.4 {
SELECT rowid, length(v) FROM v1
} {11 32 12 32 13 32}
#-------------------------------------------------------------------------
reset_db
vec1_load db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, t);
WITH s(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1000)
INSERT INTO v1 SELECT random_vector(16), '' FROM s;
INSERT INTO v1(cmd, arg) VALUES('rebuild', (
SELECT vec1_train(v, '{quantizer:"bq", nbucket: 1}') FROM v1
));
}
do_execsql_test 3.1 { SELECT count(distance) FROM v1 } {1000}
do_execsql_test 3.2 {
UPDATE v1 SET t='text510' WHERE rowid IN (412, 667, 2);
}
do_execsql_test 3.3 {
PRAGMA integrity_check
} {ok}
do_execsql_test 3.4 {
SELECT rowid FROM v1(random_vector(16), 100) WHERE t='text510' ORDER BY 1
} {2 412 667}
finish_test

View File

@@ -0,0 +1,61 @@
# 2026-08-04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1update2
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1(v, x);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<200
)
INSERT INTO v1 SELECT random_vector(16), 'tag' || hex(randomblob(64)) FROM s;
INSERT INTO v1(cmd, arg) VALUES('blocksize', 2048);
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 1.1 {
SELECT sum(length(val)) FROM v1_idx;
SELECT sum(length(val)) FROM v1_meta;
} {13684 26656}
do_execsql_test 1.2 {
SELECT count(*) FROM v1_idx;
SELECT count(*) FROM v1_meta;
} {7 7}
do_execsql_test 1.3 {
UPDATE v1 SET v = random_vector(16);
UPDATE v1 SET v = random_vector(16);
UPDATE v1 SET v = random_vector(16);
UPDATE v1 SET v = random_vector(16);
}
do_execsql_test 1.4 {
SELECT count(*) FROM v1_idx;
SELECT count(*) FROM v1_meta;
} {7 7}
do_execsql_test 1.5 {
INSERT INTO v1(cmd, arg) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 1.6 {
SELECT count(*) FROM v1_idx;
SELECT count(*) FROM v1_meta;
} {7 7}
finish_test

View File

@@ -0,0 +1,118 @@
# 2026-01-17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] vec1_common.tcl]
set testprefix vec1version
vec1_load db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE v1 USING vec1;
}
do_execsql_test 1.1 {
INSERT INTO v1(rowid, vector) VALUES(1, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(2, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(3, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(4, random_vector(128));
INSERT INTO v1(rowid, vector) VALUES(5, random_vector(128));
INSERT INTO v1(cmd, vector) VALUES('rebuild', '{index:"flat"}');
}
do_execsql_test 1.2 { PRAGMA integrity_check } {ok}
do_execsql_test 1.3 {
SELECT * FROM v1_config WHERE id=5;
} {5 4}
do_execsql_test 1.4 {
UPDATE v1_config SET val=2 WHERE id=5;
} {}
db close
sqlite3 db test.db
vec1_load db
do_catchsql_test 1.5 {
SELECT rowid FROM v1(random_vector(128));
} {1 {unrecognized index version}}
do_execsql_test 1.6 {
UPDATE v1_config SET val=7 WHERE id=5;
} {}
do_catchsql_test 1.7 {
SELECT rowid FROM v1(random_vector(128));
} {1 {unsupported index version - requires vec1 0.7}}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE v2 USING vec1;
INSERT INTO v2(rowid, vector) VALUES(5, random_vector(128));
INSERT INTO v2_config(id, val) VALUES(17, 'hello');
}
db close
sqlite3 db test.db
vec1_load db
do_catchsql_test 2.1 {
SELECT rowid FROM v2(random_vector(128), 4);
} {1 {unrecognized config entry: 17}}
#--------------------------------------------------------------------------
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE v3 USING vec1;
}
do_execsql_test 3.1 {
INSERT INTO v3(rowid, vector) VALUES(1, random_vector(128));
INSERT INTO v3(rowid, vector) VALUES(2, random_vector(128));
INSERT INTO v3(rowid, vector) VALUES(3, random_vector(128));
INSERT INTO v3(rowid, vector) VALUES(4, random_vector(128));
INSERT INTO v3(rowid, vector) VALUES(5, random_vector(128));
INSERT INTO v3(cmd, vector) VALUES('rebuild', '{index:"flat"}');
}
do_test 3.2 {
set model [db one {SELECT val FROM v3_model WHERE id=1}]
set model [string replace $model 0 3 [binary format I 1005]]
db eval { UPDATE v3_model SET val = $model WHERE id=1 }
} {}
db close
sqlite3 db test.db
vec1_load db
do_catchsql_test 3.3 {
SELECT rowid FROM v3(random_vector(128), 4);
} {1 {vec1: unsupported model version - require vec1 1.5}}
do_test 3.4 {
set model [db one {SELECT val FROM v3_model WHERE id=1}]
set model [string replace $model 0 3 [binary format I 2]]
db eval { UPDATE v3_model SET val = $model WHERE id=1 }
} {}
db close
sqlite3 db test.db
vec1_load db
do_catchsql_test 3.5 {
SELECT rowid FROM v3(random_vector(128), 4);
} {1 {vec1: unrecognized model version}}
finish_test

View File

@@ -0,0 +1,787 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include "vec1.c"
#ifdef VEC1_HAVE_NEON
/*
** Run nIter iterations of a loop containing N independent, back-to-back
** FMA chains (N float32x4_t accumulators, each updated once per
** iteration) and return the elapsed time on vec1HardwareTimer().
**
** N is a literal compile-time constant at every call site (see
** MAKE_FMA_TEST below), so the "for(c...)" loop below is unrolled by
** the compiler into straight-line code - there is no loop-control
** instruction diluting the measurement, and for N==1 this is a single
** dependent chain (true FMA latency); for larger N it is N independent
** chains (FMA throughput, once N is large enough to hide the latency).
*/
#define MAKE_FMA_TEST(N) \
static u64 vec1FmaTest##N(int nIter){ \
float32x4_t acc[N]; \
float32x4_t a = vdupq_n_f32(1.000001f); \
float32x4_t b = vdupq_n_f32(0.999999f); \
int c, r; \
u64 t; \
for(c=0; c<N; c++) acc[c] = vdupq_n_f32(1.0f + 0.001f*(float)c); \
t = vec1HardwareTimer(); \
for(r=0; r<nIter; r++){ \
for(c=0; c<N; c++) acc[c] = vfmaq_f32(acc[c], a, b); \
} \
t = vec1HardwareTimer() - t; \
/* Force the result to be used, so the compiler can't prove the */ \
/* whole loop is dead and delete it. */ \
{ \
float s = 0.0f; \
for(c=0; c<N; c++) s += vaddvq_f32(acc[c]); \
if( s!=s ) fprintf(stderr, "unreachable: %f\n", (double)s); \
} \
return t; \
}
MAKE_FMA_TEST(1)
MAKE_FMA_TEST(2)
MAKE_FMA_TEST(3)
MAKE_FMA_TEST(4)
MAKE_FMA_TEST(6)
MAKE_FMA_TEST(8)
MAKE_FMA_TEST(10)
MAKE_FMA_TEST(12)
MAKE_FMA_TEST(16)
MAKE_FMA_TEST(20)
MAKE_FMA_TEST(24)
MAKE_FMA_TEST(28)
MAKE_FMA_TEST(32)
#undef MAKE_FMA_TEST
/*
** Measure NEON FMA latency and throughput on this host and use
** Little's Law (accumulators_needed = latency * throughput) to derive
** the minimum number of independent accumulator chains required to
** keep the FMA pipeline(s) saturated.
*/
static void vec1FmaBench(void){
const int nIter = 200*1000*1000;
struct { int n; u64 t; } a[13];
int i;
double latency; /* ticks per FMA, N==1 (dependent chain) */
double bestRate = 0.0; /* FMAs per tick, best N tested */
int nOptimal;
a[0].n=1; a[0].t = vec1FmaTest1(nIter);
a[1].n=2; a[1].t = vec1FmaTest2(nIter);
a[2].n=3; a[2].t = vec1FmaTest3(nIter);
a[3].n=4; a[3].t = vec1FmaTest4(nIter);
a[4].n=6; a[4].t = vec1FmaTest6(nIter);
a[5].n=8; a[5].t = vec1FmaTest8(nIter);
a[6].n=10; a[6].t = vec1FmaTest10(nIter);
a[7].n=12; a[7].t = vec1FmaTest12(nIter);
a[8].n=16; a[8].t = vec1FmaTest16(nIter);
a[9].n=20; a[9].t = vec1FmaTest20(nIter);
a[10].n=24; a[10].t = vec1FmaTest24(nIter);
a[11].n=28; a[11].t = vec1FmaTest28(nIter);
a[12].n=32; a[12].t = vec1FmaTest32(nIter);
printf("%-12s %-16s %-16s %-10s\n", "nChains", "ticks/iter", "ticks/FMA", "FMA/tick");
for(i=0; i<(int)(sizeof(a)/sizeof(a[0])); i++){
double ticksPerIter = (double)a[i].t / nIter;
double ticksPerFma = ticksPerIter / a[i].n;
double fmaPerTick = 1.0 / ticksPerFma;
printf("%-12d %-16.3f %-16.4f %-10.3f\n",
a[i].n, ticksPerIter, ticksPerFma, fmaPerTick);
if( i==0 ) latency = ticksPerFma;
if( fmaPerTick>bestRate ) bestRate = fmaPerTick;
}
nOptimal = (int)ceil(latency * bestRate);
printf("\n");
printf("FMA latency (N=1, dependent chain): %.3f ticks\n", latency);
printf("FMA throughput (best of N tested): %.3f FMA/tick\n", bestRate);
printf("Little's Law: accumulators needed = latency * throughput\n");
printf(" = %.3f * %.3f = %.2f -> use %d independent accumulators\n",
latency, bestRate, latency*bestRate, nOptimal);
}
#endif /* VEC1_HAVE_NEON */
/*
** Populate aTrans[] with transposed version of the array of centroids in
** aCentroid[].
*/
static void vec1TransposeCentroids(
const float *aCentroid, /* Packed array of centroids */
int nElem, /* Number of elements in each vector */
int nVector, /* Number of centroid vectors */
int nWidth, /* Transpose width */
float *aTrans /* Populate this array with transformation */
){
int c, d;
float *pOut = aTrans;
/* nVector must be an integer multiple of nWidth */
assert( (nVector % nWidth)==0 );
for(c=0; c<nVector; c+=nWidth){
for(d=0; d<nElem; d++){
int ii;
for(ii=0; ii<nWidth; ii++){
(*pOut++) = aCentroid[ (c+ii)*nElem + d ];
}
}
}
assert( pOut==&aTrans[nElem * nVector] );
}
static double vec1RandomFloat(Vec1Random *p) {
return (vec1RandomNext(p) >> 8) * (1.0 / 16777216.0); // 24-bit mantissa
}
static void vec1RandomArray(Vec1Random *p, float *aRnd, int nRnd){
int ii;
for(ii=0; ii<nRnd; ii++){
aRnd[ii] = vec1RandomFloat(p);
}
}
static void usage(const char *zExec){
fprintf(stderr,
"usage: %s NELEM NCENTROID NTRAIN NREPEAT ALGORITHM\n", zExec
);
exit(-1);
}
static void xLloydsNaive(
int nElem,
const float *aCentroid,
int nCentroid,
const float *aTrain,
int nTrain,
int *aBest
){
int ii;
for(ii=0; ii<nTrain; ii++){
const float *pVec = &aTrain[ii*nElem];
double fBest = INFINITY;
int iBest = -1;
int jj;
for(jj=0; jj<nCentroid; jj++){
double fNew = vec1L2Dist(pVec, &aCentroid[jj*nElem], nElem);
if( fNew<fBest ){
iBest = jj;
fBest = fNew;
}
}
aBest[ii] = iBest;
}
}
static void xLloydsStride(
int nElem,
const float *aCentroid,
int nCentroid,
const float *aTrain,
int nTrain,
int *aBest
){
#define VEC1_LLOYDS_STRIDE 8
const int nStride = VEC1_LLOYDS_STRIDE;
int ii;
for(ii=0; ii<nTrain; ii+=nStride){
float aDist[VEC1_LLOYDS_STRIDE];
int aRes[VEC1_LLOYDS_STRIDE];
int cc;
int jj;
for(cc=0; cc<nStride; cc++) aDist[cc] = INFINITY;
for(jj=0; jj<nCentroid; jj++){
const float *pCent = &aCentroid[jj * nElem];
for(cc=0; cc<nStride; cc++){
float fDist = vec1L2Dist(pCent, &aTrain[(cc+ii)*nElem], nElem);
if( fDist<aDist[cc] ){
aDist[cc] = fDist;
aRes[cc] = jj;
}
}
}
memcpy(&aBest[ii], aRes, sizeof(aRes));
}
}
#if defined(VEC1_HAVE_NEON)
# define TEST_BMT_NVEC 8
/*
** NEON version of vec1BestMatchTransposeN(). Array aCentNT[] contains the
** centroids transposed in blocks of TEST_BMT_NVEC centroids (the same
** width as the number of vectors - see xLloydsTransposed2()). Each block
** of TEST_BMT_NVEC centroids occupies TEST_BMT_NREG float32x4_t registers
** per training vector.
*/
# define TEST_BMT_NREG (TEST_BMT_NVEC/4)
static void vec1BestMatchTransposeNNEON(
int nElem,
const float *aCent,
const float *aCentNT,
int nCent,
const float *aVec[TEST_BMT_NVEC],
int aBestOut[TEST_BMT_NVEC],
double *pfTotalOut
){
const int nWidth = TEST_BMT_NVEC;
float aDist[TEST_BMT_NVEC];
int aBest[TEST_BMT_NVEC];
int vv;
int iCent;
const float *pTrans = aCentNT;
/* All the code below assumes TEST_BMT_NVEC is a multiple of 4 */
assert( (TEST_BMT_NVEC % 4)==0 );
for(vv=0; vv<TEST_BMT_NVEC; vv++){
aDist[vv] = INFINITY;
aBest[vv] = 0;
}
for(iCent=0; iCent<=(nCent-nWidth); iCent+=nWidth){
float32x4_t aAcc[TEST_BMT_NVEC][TEST_BMT_NREG];
int d;
int rr; /* For looping 0..TEST_BMT_NREG-1 */
for(vv=0; vv<TEST_BMT_NVEC; vv++){
for(rr=0; rr<TEST_BMT_NREG; rr++){
aAcc[vv][rr] = vdupq_n_f32(0.0f);
}
}
for(d=0; d<nElem; d++){
float32x4_t cval[TEST_BMT_NREG];
for(rr=0; rr<TEST_BMT_NREG; rr++){
cval[rr] = vld1q_f32(&pTrans[rr*4]);
}
for(vv=0; vv<TEST_BMT_NVEC; vv++){
float32x4_t sval = vld1q_dup_f32(&aVec[vv][d]);
for(rr=0; rr<TEST_BMT_NREG; rr++){
float32x4_t diff = vsubq_f32(cval[rr], sval);
aAcc[vv][rr] = vfmaq_f32(aAcc[vv][rr], diff, diff);
}
}
pTrans += nWidth;
}
/* At this point, the TEST_BMT_NREG registers in aAcc[vv] contain the
** distances from training vector vv to centroids iCent to
** (iCent + TEST_BMT_NVEC - 1). Populate the aDist[] and aBest[] scalar
** arrays accordingly. */
for(vv=0; vv<TEST_BMT_NVEC; vv++){
float32x4_t vMin = aAcc[vv][0];
for(rr=1; rr<TEST_BMT_NREG; rr++){
vMin = vminq_f32(vMin, aAcc[vv][rr]);
}
if( vminvq_f32(vMin)<aDist[vv] ){
/* This branch runs if aAcc[vv] contains one of more values less
** than scalar value aDist[vv]. */
int is;
float aScalar[TEST_BMT_NVEC];
for(rr=0; rr<TEST_BMT_NREG; rr++){
vst1q_f32(&aScalar[rr*4], aAcc[vv][rr]);
}
for(is=0; is<TEST_BMT_NVEC; is++){
if( aScalar[is]<aDist[vv] ){
aDist[vv] = aScalar[is];
aBest[vv] = is + iCent;
}
}
}
}
}
for( ; iCent<nCent; iCent++){
const float *cent = &aCent[iCent*nElem];
for(vv=0; vv<TEST_BMT_NVEC; vv++){
float dist = vec1L2Dist(aVec[vv], cent, nElem);
if( dist<aDist[vv] ){
aDist[vv] = dist;
aBest[vv] = iCent;
}
}
}
memcpy(aBestOut, aBest, sizeof(aBest));
if( pfTotalOut ){
double total = 0.0;
for(vv=0; vv<TEST_BMT_NVEC; vv++){
total += aDist[vv];
}
*pfTotalOut += total;
}
}
# define vec1BestMatchTransposeN vec1BestMatchTransposeNNEON
#elif defined(VEC1_HAVE_AVX2)
# define TEST_BMT_NVEC VEC1_MULTIMATCH_NVEC
#endif
static void xLloydsTransposed2(
int nElem,
const float *aCentroid,
int nCentroid,
const float *aTrain,
int nTrain,
int *aBest
){
float *aTrans = 0;
int ii;
aTrans = (float*)malloc(nElem * nCentroid * sizeof(float));
vec1TransposeCentroids(
aCentroid, nElem, nCentroid, TEST_BMT_NVEC, aTrans
);
for(ii=0; ii<nTrain; ii+=TEST_BMT_NVEC){
const float *aVec[TEST_BMT_NVEC];
int vv;
for(vv=0; vv<TEST_BMT_NVEC; vv++){
aVec[vv] = &aTrain[(ii+vv)*nElem];
}
vec1BestMatchTransposeN(
nElem, aCentroid, aTrans, nCentroid, aVec, &aBest[ii], 0
);
}
free(aTrans);
}
static void xLloydsModelTDist(
int nElem,
const float *aCentroid,
int nCentroid,
const float *aTrain,
int nTrain,
int *aBest
){
float *aTrans = 0;
int ii;
aTrans = (float*)malloc(nElem * nCentroid * sizeof(float));
vec1TransposeCentroids(
aCentroid, nElem, nCentroid, VEC1_TRANSPOSE_WIDTH, aTrans
);
for(ii=0; ii<nTrain; ii++){
const float *pTrans = aTrans;
float fBestDist = INFINITY;
const float *vec = &aTrain[ii*nElem];
int K;
for(K=0; K<=(nCentroid-VEC1_TRANSPOSE_WIDTH); K+=VEC1_TRANSPOSE_WIDTH){
int cc;
float aDist[VEC1_TRANSPOSE_WIDTH];
vec1ModelTDist(pTrans, vec, nElem, aDist);
pTrans += (nElem * VEC1_TRANSPOSE_WIDTH);
for(cc=0; cc<VEC1_TRANSPOSE_WIDTH; cc++){
if( aDist[cc]<fBestDist ){
aBest[ii] = K+cc;
fBestDist = aDist[cc];
}
}
}
for(; K<nCentroid; K++){
float dist = vec1L2Dist(&aCentroid[K*nElem], vec, nElem);
if( dist<fBestDist ){
fBestDist = dist;
aBest[ii] = K;
}
}
}
free(aTrans);
}
#if 0
static void xLloydsTransposed(
int nElem,
const float *aCentroid,
int nCentroid,
const float *aTrain,
int nTrain,
int *aBest
){
#if !defined(VEC1_HAVE_AVX2) && !defined(VEC1_HAVE_NEON)
fprintf(stderr, "must define either VEC1_HAVE_AVX2 or VEC1_HAVE_NEON\n");
exit(1);
#else
float *aTrans = 0;
int ii;
/*
#if defined(VEC1_HAVE_AVX2)
# define VEC1_LLOYDS_NACC 8
# define VEC1_LLOYDS_WIDTH 8
#else
# define VEC1_LLOYDS_NACC 8
# define VEC1_LLOYDS_WIDTH 4
#endif
*/
aTrans = (float*)malloc(nElem * nCentroid * sizeof(float));
vec1TransposeCentroids(aCentroid, nElem, nCentroid, VEC1_NTRANSPOSE_WIDTH, aTrans);
for(ii=0; ii<nTrain; ii+=VEC1_LLOYDS_NACC){
const float *pTrans = aTrans;
int cc;
int iCent;
int aRes[VEC1_LLOYDS_NACC];
float aDist[VEC1_LLOYDS_NACC];
/* Initialize the aDist[] array. */
for(cc=0; cc<VEC1_LLOYDS_NACC; cc++){
aDist[cc] = INFINITY;
}
for(iCent=0; iCent<nCentroid; iCent+=VEC1_LLOYDS_WIDTH){
int d;
#if defined(VEC1_HAVE_AVX2)
/* All the AVX2 code below assumes VEC1_LLOYDS_WIDTH==8, so
** assert() that here. */
assert( VEC1_LLOYDS_WIDTH==8 );
__m256 aAcc[VEC1_LLOYDS_NACC];
for(cc=0; cc<VEC1_LLOYDS_NACC; cc++){
aAcc[cc] = _mm256_setzero_ps();
}
for(d=0; d<nElem; d++){
__m256 cval = LOADU( pTrans );
for(cc=0; cc<VEC1_LLOYDS_NACC; cc++){
__m256 sval = _mm256_set1_ps( aTrain[(ii+cc)*nElem+d] );
__m256 diff = _mm256_sub_ps( cval, sval );
aAcc[cc] = FMADD(diff, diff, aAcc[cc]);
}
pTrans += VEC1_LLOYDS_WIDTH;
}
/* At this point, each register in aAcc[] contains the distances
** from the corresponding training vector (vector ii + cc) to
** centroids iCent to (iCent + VEC1_SIMD_WIDTH). Populate the aDist[]
** and aRes[] scalar arrays accordingly. */
for(cc=0; cc<VEC1_LLOYDS_NACC; cc++){
__m256 vMin = _mm256_set1_ps(aDist[cc]);
__m256 mask = _mm256_cmp_ps(aAcc[cc], vMin, _CMP_LT_OQ);
if( _mm256_movemask_ps(mask)!=0 ){
/* This branch runs if aAcc[cc] contains one of more values less
** than scalar value aDist[cc]. */
int is;
float aScalar[VEC1_LLOYDS_WIDTH];
_mm256_storeu_ps(aScalar, aAcc[cc]);
for(is=0; is<VEC1_LLOYDS_WIDTH; is++){
if( aScalar[is]<aDist[cc] ){
aDist[cc] = aScalar[is];
aRes[cc] = is + iCent;
}
}
}
}
#else
/* All the NEON code below assumes VEC1_LLOYDS_WIDTH==4, so
** assert() that here. */
assert( VEC1_LLOYDS_WIDTH==4 );
float32x4_t aAcc[VEC1_LLOYDS_NACC];
for(cc=0; cc<VEC1_LLOYDS_NACC; cc++){
aAcc[cc] = vdupq_n_f32(0.0f);
}
for(d=0; d<nElem; d++){
float32x4_t cval = vld1q_f32( pTrans );
for(cc=0; cc<VEC1_LLOYDS_NACC; cc++){
float32x4_t sval = vdupq_n_f32( aTrain[(ii+cc)*nElem+d] );
float32x4_t diff = vsubq_f32( cval, sval );
aAcc[cc] = vfmaq_f32( aAcc[cc], diff, diff );
}
pTrans += VEC1_LLOYDS_WIDTH;
}
/* Same "did anything improve" check as the AVX2 movemask test,
** using a horizontal max over the comparison mask instead. Any
** lane less-than gives an all-1s (0xFFFFFFFF) mask in that lane,
** so a nonzero horizontal max means at least one lane improved.
** vmaxvq_u32 is aarch64-only (no 32-bit ARMv7 equivalent without
** a pairwise-max fallback). */
for(cc=0; cc<VEC1_LLOYDS_NACC; cc++){
float32x4_t vMin = vdupq_n_f32(aDist[cc]);
uint32x4_t mask = vcltq_f32(aAcc[cc], vMin);
if( vmaxvq_u32(mask)!=0 ){
int is;
float aScalar[VEC1_LLOYDS_WIDTH];
vst1q_f32(aScalar, aAcc[cc]);
for(is=0; is<VEC1_LLOYDS_WIDTH; is++){
if( aScalar[is]<aDist[cc] ){
aDist[cc] = aScalar[is];
aRes[cc] = is + iCent;
}
}
}
}
#endif
}
/* Scalar array aRes[] now contains the index of the best centroid
** for each of the VEC1_LLOYDS_NACC training vectors being considered
** by this loop. */
memcpy(&aBest[ii], aRes, sizeof(aRes));
}
#endif
}
#endif
#ifdef VEC1_USE_TRANSPOSITION
/* If VEC1_USE_TRANSPOSITION is defined in vec1.c, then there is no
** implementation of vec1BestMatchN() to import. So in this case we
** create one here. VEC1_USE_TRANSPOSITION is only ever defined on
** AVX2 builds, so the implementation may assume AVX2. */
#if !defined(VEC1_HAVE_AVX2)
# error "A non-AVX2 host with VEC1_USE_TRANSPOSITION defined..."
#endif
static void vec1BestMatchN(
int nElem, /* Number of elements in vectors */
const float *aCent, /* nCent packed centroids */
int nCent, /* Number of centroids */
const float *aVec[VEC1_MULTIMATCH_NVEC],
int aBest[VEC1_MULTIMATCH_NVEC],
double *pfTotalDist /* IN/OUT: Increment by total distortion */
){
int iCent;
int vv;
float aBestDist[VEC1_MULTIMATCH_NVEC];
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
aBestDist[vv] = INFINITY;
}
for(iCent=0; iCent<nCent; iCent++){
__m256 aAcc[VEC1_MULTIMATCH_NVEC];
float aDist[VEC1_MULTIMATCH_NVEC];
int d;
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
aAcc[vv] = _mm256_setzero_ps();
}
for(d=0; d<=(nElem-8); d+=8){
__m256 cent = LOADU( &aCent[iCent*nElem + d] );
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
__m256 diff = _mm256_sub_ps(cent, LOADU(&aVec[vv][d]));
aAcc[vv] = FMADD(diff, diff, aAcc[vv]);
}
}
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
HORIZONTAL_SUM(aDist[vv], aAcc[vv]);
}
for(; d<nElem; d++){
float cent = aCent[iCent*nElem + d];
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
float diff = cent - aVec[vv][d];
aDist[vv] += (diff * diff);
}
}
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
if( aDist[vv]<aBestDist[vv] ){
aBestDist[vv] = aDist[vv];
aBest[vv] = iCent;
}
}
}
if( pfTotalDist ){
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
*pfTotalDist += aBestDist[vv];
}
}
}
#endif
/*
** A register-blocked microkernel, GEMM-style.
*/
static void xLloydsTiled(
int nElem,
const float *aCentroid,
int nCentroid,
const float *aTrain,
int nTrain,
int *aBest
){
int ii;
for(ii=0; ii<=(nTrain-VEC1_MULTIMATCH_NVEC); ii+=VEC1_MULTIMATCH_NVEC){
const float *aVec[VEC1_MULTIMATCH_NVEC];
int vv;
for(vv=0; vv<VEC1_MULTIMATCH_NVEC; vv++){
aVec[vv] = &aTrain[(ii+vv) * nElem];
}
vec1BestMatchN(nElem, aCentroid, nCentroid, aVec, &aBest[ii], 0);
}
for(; ii<nTrain; ii++){
aBest[ii] = vec1PqBestMatch(
aCentroid, nCentroid, &aTrain[ii*nElem], nElem, 0
);
}
}
int main(int argc, char **argv){
#ifdef VEC1_HAVE_NEON
if( argc==2 && strcmp(argv[1], "fmabench")==0 ){
vec1FmaBench();
return 0;
}
#endif
const struct Alg {
const char *zName;
void (*x)(int, const float*, int, const float*, int, int*);
} aAlg[] = {
{ "naive", xLloydsNaive },
{ "strided", xLloydsStride },
{ "transposed", xLloydsTransposed2 },
{ "tiled", xLloydsTiled },
{ "modeltdist", xLloydsModelTDist },
};
/* Command line parameters */
int nElem = 0;
int nCentroid = 0;
int nTrain = 0;
int nRepeat = 0;
float *aCentroid; /* nCentroid centroids, each nElem floats */
float *aTrain; /* nTrain training vectors, each nElem f.. */
int *aRes; /* Result array */
u64 t; /* Hardware timer value */
int ii; /* Loop counter */
void (*xLloyds)(int, const float*, int, const float*, int, int*) = 0;
Vec1Random rnd;
vec1RandomInit(&rnd, vec1HardwareTimer(), 0);
if( argc!=6 ) usage(argv[0]);
nElem = atoi( argv[1] );
nCentroid = atoi( argv[2] );
nTrain = atoi( argv[3] );
nRepeat = atoi( argv[4] );
/* Vector size must be a multiple of 8 */
if( nElem<=0 || (nElem%8)!=0 ){
fprintf(stderr, "NELEM must be a multiple of 8 greater than 0\n");
return 1;
}
/* Number of centroids must be a multiple of 32 */
if( nCentroid<=0 || (nCentroid%32)!=0 ){
fprintf(stderr, "NCENTROID must be a multiple of 32 greater than 0\n");
return 1;
}
/* Number of training vectors must be a multiple of 32 */
if( nTrain<=0 || (nTrain%32)!=0 ){
fprintf(stderr, "NTRAIN must be a multiple of 32 greater than 0\n");
return 1;
}
/* Number of repeats must be greater than 0 */
if( nRepeat<=0 ){
fprintf(stderr, "NREPEAT must greater than 0\n");
return 1;
}
for(ii=0; ii<(int)(sizeof(aAlg)/sizeof(aAlg[0])); ii++){
if( strcmp(argv[5], aAlg[ii].zName)==0 ){
xLloyds = aAlg[ii].x;
}
}
if( xLloyds==0 ){
fprintf(stderr, "no such algorithm: %s\n", argv[5]);
return 1;
}
aCentroid = (float*)malloc( nCentroid * nElem * sizeof(float) );
aTrain = (float*)malloc( nTrain * nElem * sizeof(float) );
aRes = (int*)malloc( nTrain * sizeof(int) );
vec1RandomArray(&rnd, aCentroid, nCentroid * nElem);
vec1RandomArray(&rnd, aTrain, nTrain * nElem);
t = vec1HardwareTimer();
for(ii=0; ii<nRepeat; ii++){
xLloyds(nElem, aCentroid, nCentroid, aTrain, nTrain, aRes);
}
t = vec1HardwareTimer() - t;
#if 1
for(ii=0; ii<nTrain; ii++){
float *pVec = &aTrain[ii*nElem];
int iBest = 0;
double fBest = vec1L2Dist(pVec, aCentroid, nElem);
int jj;
for(jj=1; jj<nCentroid; jj++){
double fNew = vec1L2Dist(pVec, &aCentroid[jj*nElem], nElem);
if( fNew<fBest ){
iBest = jj;
fBest = fNew;
}
}
if( iBest!=aRes[ii] ){
const double fTol = 0.00001;
double fDist = vec1L2Dist(pVec, &aCentroid[aRes[ii]*nElem], nElem);
if( (fDist>fBest && (fDist-fBest)/fDist>fTol)
|| (fDist<fBest && (fBest-fDist)/fBest>fTol)
){
fprintf(stderr, "algorithm FAILED...\n");
return 1;
}
}
}
#endif
printf("Cycles: ");
if( t>(1000*1000*1000) ){
printf("%.2fG\n", (double)t / 1000000000.0);
}else if( t>(10*1000*1000) ){
printf("%dM\n", (int)(t / (1000*1000)));
}else if( t>(1*1000*1000) ){
printf("%.2fM\n", (double)t / (1000*1000));
}else{
printf("%d\n", (int)t);
}
return 0;
}

707
ext/vec1/tool/vec1bench.tcl Normal file
View File

@@ -0,0 +1,707 @@
package require sqlite3
set DEFAULT(--nthread) 16
set DEFAULT(--max-train) 100000
set DEFAULT(--train-function) vec1_train
set DEFAULT(--index) {nbucket 1024 codesize 32 opq 1}
set DEFAULT(--query) ""
set DEFAULT(--meta-data) 0
set DEFAULT(--svd-verify) 0
set DEFAULT(--report) ""
set DEFAULT(--repeat) 1
proc usage {} {
global DEFAULT
puts stderr "usage: $::argv0 ?OPTIONS? DATABASE ?SHLIB1...?"
puts stderr "options:"
foreach o [array names DEFAULT] {
set display $DEFAULT($o)
if { [string is integer $display]==0 } { set display "\"$display\"" }
puts stderr " [format {% -20s} $o](default: $display)"
}
exit 1
}
proc process_args {lArg} {
global C
global DEFAULT
array set C [array get DEFAULT]
for {set i 0} {$i < [expr [llength $lArg]-1]} {incr i} {
set a [lindex $lArg $i]
if {[string range $a 0 0]!="-"} break
if {[string range $a 1 1]!="-"} { set a "-$a" }
unset -nocomplain m
foreach o [array names C] {
if {[string match ${a}* $o]} {
if {[info exists m]} usage
set m $o
}
}
if {[info exists m]==0} usage
incr i
set C($m) [lindex $lArg $i]
}
lrange $lArg $i end
}
# Process command line arguments and open the database. And load the
# vec1.so dynamic extension(s).
#
set lExt [process_args $argv]
if {[llength $lExt]==0} usage
set C(dbname) [lindex $lExt 0]
set C(lExt) [lrange $lExt 1 end]
proc open_db {fname} {
global C
set lExt $C(lExt);
sqlite3 db $fname
db enable_load_extension 1
if {[llength $lExt]==0 && [catch {db eval {SELECT vec1_info()}}]} {
set shlib [file join [file dirname [info nameofexecutable]] vec1]
db eval { SELECT load_extension($shlib) }
} else {
foreach shlib $lExt {
db eval { SELECT load_extension($shlib) }
}
}
db enable_load_extension 0
}
proc main { } {
global C
if {$C(dbname)=="release"} {
if {[catch release_test msg]} {
puts stderr "ERROR: $msg"
}
return
}
open_db $C(dbname)
check_db_looks_ok
summarize_db
build_nearest_if_required
set r $C(--repeat)
set lResult [list]
for {set i 0} {$i < $r} {incr i} {
if {$C(--index) != ""} {
test_build_index $C(--index)
}
if {$C(--query) != ""} {
lappend lResult [test_query_index {*}$C(--query)]
}
}
if {$C(--query) != ""} {
if {$r==1} {
print_query_timings
} else {
puts "recall: [lsort -integer $lResult]"
}
exit
}
array set I $C(--index)
if {[info exists I(codesize)] && $I(codesize)==0} {
# If the command-line specified a code-size of 0, then do not bother
# varying K, as reranking won't help anyway.
foreach nprobe {16 32 48 64} {
test_query_index 1 1 $nprobe
test_query_index 10 10 $nprobe
}
} else {
foreach nprobe {16 32 48 64} {
test_query_index 1 1 $nprobe
test_query_index 10 1 $nprobe
test_query_index 50 1 $nprobe
test_query_index 100 1 $nprobe
test_query_index 200 1 $nprobe
test_query_index 300 1 $nprobe
test_query_index 10 10 $nprobe
test_query_index 50 10 $nprobe
test_query_index 100 10 $nprobe
test_query_index 200 10 $nprobe
test_query_index 300 10 $nprobe
test_query_index 100 100 $nprobe
test_query_index 200 100 $nprobe
test_query_index 400 100 $nprobe
test_query_index 600 100 $nprobe
test_query_index 800 100 $nprobe
test_query_index 1000 100 $nprobe
}
}
print_report
}
proc release_test_one {varname name index lConfig} {
global T
upvar $varname lReport
test_build_index $index
foreach {K recall nprobe} $lConfig {
test_query_index $K $recall $nprobe
}
set times [format_us $T(train)]/[format_us $T(build)]
lappend lReport "$name {$index}: train/build: $times"
foreach {K recall nprobe} $lConfig {
set r $T(recall,$recall,$nprobe,$K)
set q $T(qps,$recall,$nprobe,$K)
set res "[format {% -6s} $nprobe/$K] -> ${r}@$recall ${q}qps "
lappend lReport " $res"
}
}
proc release_test {} {
global C
set C(--nthread) 8
set lReq [list \
sift1m.db \
landmark-dino-768-cosine.db \
agnews-mxbai-1024-euclidean.db \
]
set nMinByte [expr 1024*1024]
# Check that all required databases are present in cwd
foreach f $lReq {
if {[file exists $f]==0 || [file size $f]<$nMinByte} {
error "missing required db file \"$f\""
}
}
open_db sift1m.db
build_nearest_if_required
release_test_one lRep SIFT1M "nbucket 1024 codesize 16 opq 0" {
1 1 16
100 1 16
1 1 48
100 1 48
}
release_test_one lRep SIFT1M "nbucket 0 codesize 16 opq 0" {
1 1 16
100 1 16
}
release_test_one lRep SIFT1M "nbucket 1024 codesize 0 opq 0" {
1 1 16
1 1 48
}
open_db landmark-dino-768-cosine.db
build_nearest_if_required
release_test_one lRep LANDMARK "nbucket 1024 codesize 48 opq 1" {
1 1 16
100 1 48
}
open_db agnews-mxbai-1024-euclidean.db
build_nearest_if_required
release_test_one lRep AGNEWS "nbucket 1024 codesize 32 opq 1" {
1 1 16
100 1 48
}
foreach ln $lRep { puts $ln }
set fd [open vec1.release.txt a+]
puts $fd "--------------------------------------------------------------"
foreach ln $lRep { puts $fd $ln }
close $fd
}
proc format_cycles {c} {
if {$c>(1000*1000*1000)} {
set ret "[format %.2f [expr $c/1000000000.0]]G"
} elseif {$c>(10*1000000)} {
set ret "[format %d [expr $c/1000000]]M"
} elseif {$c>(1000000)} {
set ret "[format %.2f [expr $c/1000000.0]]M"
} elseif {$c>(10*1000)} {
set ret "[format %d [expr $c/1000]]K"
} elseif {$c>1000} {
set ret "[format %.2f [expr $c/1000.0]]K"
} else {
set ret $c
}
return $ret
}
# Read the qprofile information for table "v1" from eponymous virtual
# table vec1cat. Print a report to stdout based on the values read.
#
proc print_query_timings {} {
set lTopic [list]
db eval {
SELECT fullkey, value FROM json_tree(
(SELECT qprofile FROM vec1cat WHERE name='v1')
) WHERE type='integer';
} {
if {$value>0} {
set key [string range $fullkey 2 end]
set A($key) $value
foreach {topic field} [split $key .] {
if {$field=="cnt" && $topic!="total"} { lappend lTopic $topic }
}
}
}
set nTotal $A(total.cycle);
set total_percent 0.0
foreach name $lTopic {
set cnt $A(${name}.cnt)
set cycle $A(${name}.cycle)
set percent [expr (100.0 * $cycle) / $nTotal]
set total_percent [expr $total_percent + $percent]
set c [format_cycles $cycle]
puts "[format {% -12s%8s%6.2f%%} $name: $c $percent] ($cnt)"
}
puts [format {% -12s%6.2f%%} total: $total_percent]
}
# Argument $t is some number of microseconds. Return a string making
# this easy for humans to read - e.g. "123us" or "12.1ms".
#
proc format_us {t} {
set ret "${t}us"
if {$t>(10*1000000)} {
set ret "[format %.1f [expr $t/1000000.0]]s"
} elseif {$t>1000000} {
set ret "[format %.2f [expr $t/1000000.0]]s"
} elseif {$t>100000} {
set ret "[format %d [expr $t/1000]]ms"
} elseif {$t>10000} {
set ret "[format %.1f [expr $t/1000.0]]ms"
} elseif {$t>1000} {
set ret "[format %.2f [expr $t/1000.0]]ms"
} else {
set ret "${t}us"
}
return $ret
}
# Add commas to integer argument $i to make it easier to read.
# e.g. 12345 -> "12,345"
#
proc format_int {i} {
set ret [list]
while {$i>0} {
if {$i>=1000} {
set ret [concat [format %.3d [expr $i % 1000]] $ret]
} else {
set ret [concat $i $ret]
}
set i [expr $i / 1000]
}
join $ret ,
}
proc my_time {caption script} {
puts -nonewline "$caption..."
flush stdout
set t [uplevel [list time $script]]
set t [lindex $t 0]
puts " [format_us $t]"
return $t
}
# Check that the __meta__ table looks ok. This command checks that there
# are "train", "test", "neighbors" and "distances" tables, with types
# float32, float32, int64 and float64, respectively, in the db. If not,
# it prints out and error and exits.
#
proc check_db_looks_ok {} {
db eval {
WITH expect(nm, tp) AS (
SELECT 'train', 'float32' UNION ALL
SELECT 'test', 'float32' UNION ALL
SELECT 'neighbors', 'int64' UNION ALL
SELECT 'neighbors', 'int32'
),
have(nm, tp) AS (
SELECT table_name, dtype
FROM __meta__
WHERE table_name IN ('train', 'test', 'neighbors')
)
SELECT (count(*)==3) as 'ok' FROM have WHERE (nm, tp) IN (
SELECT nm, tp FROM expect
);
} {
if {$ok==0} {
puts stderr "database does not look right. bailing out..."
exit -1
}
}
}
proc get_distance_function {} {
set dm [db one {SELECT value FROM meta WHERE field='distance'}]
set f(cosine) vec1_cos_distance
set f(angular) vec1_cos_distance
set f(euclidean) vec1_l2_distance
set f(normalized) vec1_l2_distance
if {[info exists f($dm)]==0} {
puts "Unrecognized distance metric: $dm"
exit 1
}
return $f($dm)
}
# Output a summary of the db for the user to read.
#
proc summarize_db {} {
set nQuery [db one {SELECT count(*) FROM test}]
set nVector [db one {SELECT count(*) FROM train}]
set nDim [db one {SELECT length(vec)/4 FROM train LIMIT 1}]
set dm [db one {SELECT 'dist=' || value FROM meta WHERE field='distance'}]
set cmp "$dm -> [get_distance_function]"
puts "Test is $nQuery queries against $nVector ${nDim}-d vectors ($cmp)"
}
proc vec_to_json_i64 {blob} {
binary scan $blob w* L
set ret "\[[join $L ,]\]"
return $ret
}
proc vec_to_json_i32 {blob} {
binary scan $blob i* L
set ret "\[[join $L ,]\]"
return $ret
}
proc myvar {var} {
upvar $var v
if {[info exists v]==0} { return "" }
return $v
}
proc print_report {} {
global T
global C
proc vkey_cmp {lhs rhs} {
foreach {lr lp} [split $lhs ,] {}
foreach {rr rp} [split $rhs ,] {}
if {$rr==$lr} {
if {$lp<$rp} { return -1 }
if {$lp>$rp} { return +1 }
}
return [expr $rr - $lr]
}
foreach k [array names T qps*] {
foreach {q recall nprobe k} [split $k ,] {}
set RECALL($recall) 1
set V($recall,$nprobe) 1
set H($k) 1
}
if {$C(--report)==""} {
set fd stdout
} else {
set fd [open $C(--report) a]
}
set nVector [format_int [db one {SELECT count(*) FROM train}]]
set nSample [format_int $T(nTrain)]
set nDim [db one {SELECT length(vec)/4 FROM train LIMIT 1}]
set zDataset [file tail $C(dbname)]
regsub {\..*} $zDataset {} zDataset
puts $fd "<!-- Report starts here!!! -->"
puts $fd "<h1>Dataset: $zDataset ($nVector ${nDim}d vectors)</h1>"
puts $fd "<table striped=1>"
puts $fd "<tr><th>Parameter<th>Value"
puts $fd "<tr><td>Vec1 Version<td>[db one {SELECT vec1_info()}]"
puts $fd "<tr><td>Index Parameters<td>$T(spec)"
puts $fd "<tr><td>Training time<td>[format_us $T(train)] ($nSample samples, $C(--nthread) threads)"
puts $fd "<tr><td>Build time<td>[format_us $T(build)] ($C(--nthread) threads)"
puts $fd "</table>"
puts $fd ""
foreach r [lsort -integer [array names RECALL]] {
puts $fd "<p>Recall@$r results:"
puts $fd ""
puts $fd "<table striped=1>"
catch { array unset K }
catch { array unset N }
foreach key [array names T qps*] {
foreach {q recall nprobe k} [split $key ,] {}
if {$r==$recall} {
set K($k) 1
set N($nprobe) 1
}
}
puts -nonewline $fd " <tr><th>"
foreach k [lsort -integer [array names K]] {
puts -nonewline $fd "<th colspan=2>K=$k"
}
puts $fd ""
puts $fd " <tr><th>nprobe"
foreach k [lsort -integer [array names K]] {
puts -nonewline $fd "<th>Recall<th>QPS"
}
puts $fd ""
foreach nprobe [lsort -integer [array names N]] {
puts -nonewline $fd " <tr><td>$nprobe"
foreach k [lsort -integer [array names K]] {
puts -nonewline $fd "<td>[format %.3f $T(recall,$r,$nprobe,$k)]"
puts -nonewline $fd "<td>$T(qps,$r,$nprobe,$k)"
}
puts $fd ""
}
puts $fd "</table>"
}
puts $fd "<!-- Report ends here!!! -->"
puts $fd ""
puts $fd ""
if {$fd!="stdout"} {
puts "Report output to $C(--report)"
close $fd
}
}
# See if the "nearest" table has been built yet. If not, create and
# populate it now. It contains one row per test vector.
#
proc build_nearest_if_required {} {
set dm [db one {SELECT value FROM meta WHERE field='distance'}]
set fmt [db one {SELECT dtype FROM __meta__ WHERE table_name='neighbors'}]
if {$fmt=="int64"} {
db func ivec_to_json vec_to_json_i64
} else {
db func ivec_to_json vec_to_json_i32
}
set func [get_distance_function]
db eval BEGIN
if {[db one {SELECT count(*) FROM sqlite_schema WHERE name='nearest'}]==0} {
db eval "
CREATE TABLE nearest(
id INTEGER PRIMARY KEY, nn1 JSON, nn10 JSON, nn100 JSON
);
WITH groundtruth(qid, val, dist) AS (
SELECT q.id, j.value, $func (q.vec, b.vec)
FROM test q,
neighbors g,
json_each(ivec_to_json(g.vec)) j,
train b
WHERE q.id=g.id /* AND j.key<20 */ AND b.id=j.value
),
ranked(qid, val, rank) AS (
SELECT qid, val, rank() OVER (
PARTITION BY qid ORDER BY dist
) FROM groundtruth
)
INSERT INTO nearest
SELECT qid,
json_group_array(val) FILTER (WHERE rank=1),
json_group_array(val) FILTER (WHERE rank<=10),
json_group_array(val)
FROM ranked
GROUP BY qid
"
}
db eval COMMIT
}
proc test_build_index {lSpec} {
global C
global T
set func [get_distance_function]
set DIST(vec1_l2_distance) "\"L2\""
set DIST(vec1_cos_distance) "\"cos\""
set cols ""
set values ""
if {$C(--meta-data)==1} {
set cols ", m1"
set values ", 100"
} elseif {$C(--meta-data)==4} {
set cols ", m4"
set values ", 10000"
}
# Drop any old "v1" table that may exist. And create the new one.
#
db eval "
DROP TABLE IF EXISTS v1;
CREATE VIRTUAL TABLE v1 USING vec1(vector $cols);
SELECT vec1_config('nthread', $C(--nthread) );
INSERT INTO v1(rowid, vector $cols) SELECT id, vec $values FROM train;
"
if {$lSpec=="flat"} {
db eval "
INSERT INTO v1(cmd, arg)
VALUES('rebuild', '{index:\"flat\",distance: $DIST($func)}')
"
return
}
set pairs [list]
foreach {k v} $lSpec {
set val $v
if {[string is double $val]==0} {
set val "\"$v\""
}
lappend pairs "$k:$val"
}
set showspec "{[join $pairs {, }]}"
lappend pairs "svd_verify: $C(--svd-verify)"
lappend pairs "progress: \"myprogress\""
lappend pairs "profile: \"myprofile\""
set spec "{[join $pairs {, }]}"
# Command to handle vec1_train() progress callbacks.
#
unset -nocomplain ::myprogressms
proc myprogress {percent msg} {
catch {
if {[info exists ::myprogressms]==0} {
set ::myprogressms [clock milli]
}
set ms [expr [clock milli] - $::myprogressms]
puts "${ms}ms: $percent%: $msg"
} xyz
}
db func myprogress myprogress
# Command to handle vec1_train() profile callbacks.
#
proc myprofile {p} {
puts "profile: $p"
}
db func myprofile myprofile
# Check if a "learn" table exists. If it does not, use data from "train"
# to train the model.
#
if {[db one {SELECT count(*) FROM sqlite_schema WHERE name='learn'}]==0} {
set learn "train"
} else {
set learn "learn"
}
set nTotal [db one "SELECT count(*) FROM $learn"]
set nTrain [expr {$nTotal > $C(--max-train) ? $C(--max-train) : $nTotal}]
set caption "$C(--train-function)($spec) on $nTrain/$nTotal \"$learn\" rows"
set T(nTrain) $nTrain
set T(spec) $showspec
set T(train) [my_time "Training: $caption" {
puts ""
set model [db one [subst {
SELECT $C(--train-function)(vec, '$spec') FROM $learn
WHERE (
(abs(random()) / 9223372036854775807.0) * $nTotal <= $nTrain
)
}]]
}]
set T(build) [my_time "Building index" {
db eval {
INSERT INTO v1(cmd, vector) VALUES('rebuild', $model);
}
}]
}
proc test_query_index {K {recall 1} {nprobe 0.05} } {
global T
global C
set nMmap [db one { PRAGMA mmap_size = 20000000000 }]
puts "Using [expr ${nMmap}/(1024*1024)]MB mmap region"
db eval {
PRAGMA synchronous = off;
SELECT rowid FROM v1 LIMIT 1;
SELECT vec1_config('nprobe', $nprobe);
}
set func [get_distance_function]
set nn "nn$recall"
set sql1 [subst "
SELECT vec, $nn AS nn, test.id AS id
FROM test JOIN nearest ON (test.id=nearest.id)
"]
#set spec "{K:$K,nprobe:$nprobe,nprobe_slack:0.9}"
set spec "{K:$K,nprobe:$nprobe}"
set tail ""
if {$C(--meta-data)==1} {
append tail "WHERE m1=100"
} elseif {$C(--meta-data)==4} {
append tail "WHERE m4=10000"
}
if {$K!=$recall} {
append tail [subst -novar {
ORDER BY [set func] ($vec, v1.vector)
LIMIT [set recall]}]
}
set sql2 [subst -novar {
SELECT coalesce(sum( ans IN (SELECT value FROM json_each($nn) )), 0) FROM (
SELECT v1.rowid AS ans
FROM v1($vec, $spec) [set tail]
)
}]
set nQuery [db one {SELECT count(*) FROM test}]
set caption "Testing $nQuery recall@$recall queries $spec"
set s 0
set t [my_time $caption {
set nQuery 0
db eval $sql1 {
set nGood [db one $sql2]
# if {$nGood!=$recall} { puts "id=$id nGood=$nGood" }
incr s $nGood
incr nQuery
}
puts -nonewline "($s / [expr $recall*$nQuery]) "
}]
set qps [expr ($nQuery*1000000 / $t)]
set T(qps,$recall,$nprobe,$K) $qps
set T(recall,$recall,$nprobe,$K) [expr (($s * 1.0) / ($nQuery * $recall))]
return $s
}
main
exit

View File

@@ -0,0 +1,121 @@
#
# This script edits a vec1bench database to add constant dimensions to
# the data, query and (if any) training vectors in the db. This should
# not change the results of any query, but did at one point confound
# the OPQ training code.
#
package require sqlite3
set DEFAULT(--ncol) 1
set DEFAULT(--zero) 0
proc usage {} {
global DEFAULT
puts stderr "usage: $::argv0 ?OPTIONS? DATABASE"
puts stderr "options:"
foreach o [array names DEFAULT] {
set display $DEFAULT($o)
if { [string is integer $display]==0 } { set display "\"$display\"" }
puts stderr " [format {% -20s} $o](default: $display)"
}
exit 1
}
proc process_args {lArg} {
global C
global DEFAULT
array set C [array get DEFAULT]
for {set i 0} {$i < [expr [llength $lArg]-1]} {incr i} {
set a [lindex $lArg $i]
if {[string range $a 0 0]!="-"} break
if {[string range $a 1 1]!="-"} { set a "-$a" }
unset -nocomplain m
foreach o [array names C] {
if {[string match ${a}* $o]} {
if {[info exists m]} usage
set m $o
}
}
if {[info exists m]==0} usage
incr i
set C($m) [lindex $lArg $i]
}
lrange $lArg $i end
}
# Check that the __meta__ table looks ok. This command checks that there
# are "train", "test", "neighbors" and "distances" tables, with types
# float32, float32, int64 and float64, respectively, in the db. If not,
# it prints out and error and exits.
#
proc check_db_looks_ok {} {
db eval {
WITH expect(nm, tp) AS (
SELECT 'train', 'float32' UNION ALL
SELECT 'test', 'float32' UNION ALL
SELECT 'neighbors', 'int64' UNION ALL
SELECT 'neighbors', 'int32'
),
have(nm, tp) AS (
SELECT table_name, dtype
FROM __meta__
WHERE table_name IN ('train', 'test', 'neighbors')
)
SELECT (count(*)==3) as 'ok' FROM have WHERE (nm, tp) IN (
SELECT nm, tp FROM expect
);
} {
if {$ok==0} {
puts stderr "database does not look right. bailing out..."
exit -1
}
}
}
# Process command line arguments and open the database. And load the
# vec1.so dynamic extension(s).
#
set lExt [process_args $argv]
if {[llength $lExt]!=1} usage
set C(dbname) [lindex $lExt 0]
proc main { } {
global C
sqlite3 db $C(dbname)
check_db_looks_ok
set nCol $C(--ncol)
set zstr [string repeat 00000000 $nCol]
db transaction {
foreach tbl {test train learn} {
if {$C(--zero)} {
catch { db eval "UPDATE $tbl SET vec = unhex( hex(vec) || '$zstr')" }
} else {
catch { db eval "
UPDATE $tbl SET vec = unhex( hex(vec) || substr(hex(vec), 1, 8*$nCol) )
" }
}
}
}
if {$C(--zero)} {
puts "Appended $nCol 0.0 floats to all vectors"
} else {
puts "Appended duplicate of first $nCol floats to all vectors"
}
}
main
exit

View File

@@ -0,0 +1,91 @@
import h5py
import sqlite3
import numpy as np
import os
import sys
def sqlite_type_for(dtype):
# Everything goes in as BLOB; dtype is recorded separately
return "BLOB"
def sanitize(name):
# HDF5 paths like "/train" → "train"
return name.strip("/").replace("/", "_")
def main(h5_path, sqlite_path):
if os.path.exists(sqlite_path):
os.remove(sqlite_path)
conn = sqlite3.connect(sqlite_path)
cur = conn.cursor()
with h5py.File(h5_path, "r") as f:
dm = "unknown"
cur.execute("CREATE TABLE meta(field TEXT, value ANY)")
if "distance" in f.attrs:
dm = f.attrs["distance"]
if isinstance(dm, bytes):
dm = dm.decode("utf-8")
cur.execute("INSERT INTO meta VALUES('distance', ?)", (dm,))
print("VIBE distance metric:", dm)
def visit(name, obj):
if not isinstance(obj, h5py.Dataset):
return
data = obj[()]
table = sanitize(name)
print(f"Importing {name} → table '{table}' shape={data.shape} dtype={data.dtype}")
# Create table
cur.execute(f"""
CREATE TABLE {table} (
id INTEGER PRIMARY KEY,
vec BLOB NOT NULL
)
""")
# Metadata table (created once)
cur.execute("""
CREATE TABLE IF NOT EXISTS __meta__ (
table_name TEXT PRIMARY KEY,
dtype TEXT,
shape TEXT
)
""")
cur.execute(
"INSERT INTO __meta__ VALUES (?, ?, ?)",
(table, str(data.dtype), str(data.shape))
)
# Insert rows
if data.ndim == 1:
rows = [(i, data[i].tobytes()) for i in range(data.shape[0])]
else:
rows = [(i, data[i].tobytes()) for i in range(data.shape[0])]
cur.executemany(
f"INSERT INTO {table} (id, vec) VALUES (?, ?)",
rows
)
conn.commit()
f.visititems(visit)
conn.close()
print("Done.")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("usage: hdf5_to_sqlite.py input.hdf5 output.sqlite")
sys.exit(1)
main(sys.argv[1], sys.argv[2])

12939
ext/vec1/vec1.c Normal file

File diff suppressed because it is too large Load Diff

35
main.mk
View File

@@ -1786,6 +1786,40 @@ fts5.c: $(FTS5_SRC) $(B.tclsh)
fts5.o: fts5.c $(DEPS_OBJ_COMMON) $(EXTHDR)
$(T.cc.extension) -c fts5.c
#
# vec1 loadable extension. Not part of the amalgamation or any
# distributed package; built only by explicitly running "make vec1.so".
#
# If $(CFLAGS.vec1.avx2) is not empty (configure sets it to "-mavx2
# -mfma" when the compiler accepts them), vec1.c is compiled twice -
# once with scalar code and once with AVX2 - and both objects linked
# into a single extension that picks one at runtime based on the CPU.
# Otherwise a single scalar-only build is done.
#
# Deliberately does not use $(T.cc.extension), as that defines
# SQLITE_CORE, which is wrong for a loadable extension.
#
# $(CFLAGS.vec1.opt) is appended after $(CFLAGS), so it overrides any
# -O option there (e.g. configure's default -O2). vec1 relies on the
# unrolling and inlining that -O3 provides. To build with the -O level
# from $(CFLAGS) instead, pass "CFLAGS.vec1.opt=" to make.
#
CFLAGS.vec1.avx2 ?=
CFLAGS.vec1.opt ?= -O3
T.cc.vec1 = $(T.compile) -I. -I$(TOP)/src $(CFLAGS.vec1.opt)
vec1$(T.dll): $(TOP)/ext/vec1/vec1.c sqlite3.h
@if test -n "$(CFLAGS.vec1.avx2)"; then set -x; \
$(T.cc.vec1) -DVEC1SIMD=SCALAR -c $(TOP)/ext/vec1/vec1.c \
-o vec1-scalar.o || exit $$?; \
$(T.cc.vec1) -DVEC1SIMD=AVX2 $(CFLAGS.vec1.avx2) \
-c $(TOP)/ext/vec1/vec1.c -o vec1-avx2.o || exit $$?; \
$(T.compile) $(LDFLAGS.shlib) -o $@ vec1-scalar.o vec1-avx2.o \
$(LDFLAGS.math) $(LDFLAGS.pthread) || exit $$?; \
else set -x; \
$(T.cc.vec1) $(LDFLAGS.shlib) -o $@ $(TOP)/ext/vec1/vec1.c \
$(LDFLAGS.math) $(LDFLAGS.pthread) || exit $$?; \
fi
sqlite3rbu.o: $(TOP)/ext/rbu/sqlite3rbu.c $(DEPS_OBJ_COMMON) $(EXTHDR)
$(T.cc.extension) -c $(TOP)/ext/rbu/sqlite3rbu.c
@@ -2500,6 +2534,7 @@ help:
tidy:
rm -f *.o *.obj *.c *.da *.bb *.bbg gmon.* *.rws sqlite3$(T.exe)
rm -f fts5.h keywordhash.h opcodes.h sqlite3.h sqlite3ext.h sqlite3session.h
rm -f vec1$(T.dll)
rm -rf .libs .deps tsrc .target_source
rm -f lemon$(B.exe) sqlite*.tar.gz
rm -f mkkeywordhash$(B.exe) mksourceid$(B.exe)

View File

@@ -1,13 +1,13 @@
C SAHPool\sVFS\stweaks\scontributed\soff-list\sby\sJure\sRotar:\shardening\sagainst\smalicious\sinputs\sand\sI/O\serrors.\sTruncate\sto\sthe\sshorter\slength\swhen\soverwriting\sa\sdb\sfile\swith\sa\sshorter\sone\svia\simportDb().\sCorrect\sxOpen()\sto\shonor\sthe\scombination\sof\sCREATE/EXCLUSIVE\sflags.
D 2026-10-01T08:13:58.026
C Import\scode\sfor\svec1\sinto\sext/vec1/.
D 2026-10-01T11:21:02.802
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F AGENTS.md 44ddfb38ef23e277888bd70ae378e6cbf5f5cab0e2a4420bd21d89b6caff7a4a
F LICENSE.md 6bc480fc673fb4acbc4094e77edb326267dd460162d7723c7f30bee2d3d9e97d
F Makefile.in 1235ff3793bb8a2687dfa34ed541f7d6d5dd215b5a4f36acb6311941afa58651
F Makefile.in b9e64c91cf8bea2305e1ca88bd3eb022afa93963a8561ea8290c65d4fafec03e
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
F Makefile.msc a726ea17c0b7c1e3b2e58dbcd71e89b99b970f05cb4f6672a11e172bdf704313
F Makefile.msc 7e3d397adf43dcf18efd5348970a085da7d57955f0b72c65b35776ab3f0a81e7
F README.md 8a7487de00e6d6d360cec15cc114cbb397207e1b975901683561506401c6d48c
F VERSION 99cf3be5f13d091183e4314b7fc2e0c0e69accfbe64608b45a313338bbdd7b62
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -16,7 +16,7 @@ F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F art/sqlite370.svg 40b7e2fe8aac3add5d56dd86ab8d427a4eca5bcb3fe4f8946cb3794e1821d531
F auto.def 44a0d1bf09d78355fc88251ccbf8e64e6341fd89c11de68a01c3645e53a2bade
F auto.def 313ae320e59bdd7d7e73e753c0fba8983e6e0cd963cca8e7685f43a0185d1059
F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
F autoconf/Makefile.in efab1f56c7961d301efc030baa4391b9faae38733d94385a3d56b54720a74aba
F autoconf/Makefile.msc e6c596e6e63ea17aa7a97eb8ded18cd984c8dd4203766644fbd57b8fcf720225
@@ -584,6 +584,63 @@ F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009e
F ext/session/sqlite3session.c a5723efc88d340c65b391da8a4d628ca17bec49edf0a11a55653d5d208db6ea1
F ext/session/sqlite3session.h ca7c4422c1514a95056cc8d333217df6b1829d39058126b1de85d10cd62d7a9c
F ext/session/test_session.c f465f13c661919f8ba4485a9d0690c4df5bff1cc79e962cfdafa9ff376d5119f
F ext/vec1/Makefile 791bd52e40d5b65026c63c8778dbdfc6d66083457ee50cf873d6483d98f43437
F ext/vec1/Makefile.msc 66fbdf78ebb987bdbe646e3b1c895fc8785e14c50666f6694b840a0cdd3b7f9e
F ext/vec1/test/vec1_common.tcl 72226d9ee7cea8ad331905b28496392e4771f7b7c9d2890575795eddefc19db3
F ext/vec1/test/vec1aa.test 1e30f623ae9254590b4bc538447d816809e1e4281060d756391ac616952cb5fa
F ext/vec1/test/vec1ab.test 04a19655e475dff8f9cf0be8140eb1034497fb36ecdf1f979779bf3ee7edfe6f
F ext/vec1/test/vec1ac.test d22cdefd7718e71330b23174523981be78d55645c65c273445e5b8986ce3a6a0
F ext/vec1/test/vec1alter.test 6cb7ea2677d79a4ea040f27f5cd8902060bbd00fb2f9f94f3715b7a7bab91c69
F ext/vec1/test/vec1binary.test dd618dbb9c4f522e1f85a5d58a05ddfc13402302c379cb35b8b9d33300db5886
F ext/vec1/test/vec1binary2.test 2013dec736e39439672c1ea9113cdc0ab72a36b191b1afe3600c51f4b7ab4757
F ext/vec1/test/vec1blocksize.test dd7d9460f5cac98bc5d2c18f1211a503fb0625afed8286a2becdfcfebf0afbdc
F ext/vec1/test/vec1cat.test 8cea568d8823519044053d39f4eec8d77a806bf85e1e14817b448729038306de
F ext/vec1/test/vec1cat2.test 9e0d9a9cfa33b6aade9150c73064be53e52de9d693f53b7600e7696ed8022e02
F ext/vec1/test/vec1config.test fa0d8e3c83fd5ceeb36b7c481f9207e15f17020357ecde1b243fe03b12cea1b0
F ext/vec1/test/vec1corrupt.test 8711434b742673c57154332cbfefb83f83b2d40d2399f9d7c3f1a2e8676ece25
F ext/vec1/test/vec1create.test fdf7dc7d4e62d5654c1ac27bb9aea9451f024280dce5d0ec97d2698a040b3914
F ext/vec1/test/vec1delete.test 1dc56bbc8a6753991dbc8cdca558a933ae6280c01bee261b1b2561e0390b57e6
F ext/vec1/test/vec1error.test 3d1b74cd535dd5c9d303309f0b18ec606b100139a6483cbbd495b0aa3a9c8a09
F ext/vec1/test/vec1fault1.test 8b34a9e68a9877db43707cdfdd388f229f8267793475ceb828dd931afe6fd3b6
F ext/vec1/test/vec1fault2.test 3863f5d553e19a8dafafc2b976c295a62e33075373be204b81a9b714b2062295
F ext/vec1/test/vec1fault3.test e4c4ff00b361bb9b961c134e252fd5dc364acd3e9d2abbf2b8d8ed4e223b9406
F ext/vec1/test/vec1fault4.test 3c2bf8b30769118d7158aae9d8f7c13b17faf28245ac17d1b8e1d0977422d1bc
F ext/vec1/test/vec1fault5.test 5f41482c7108b2383ec318f0f23d0b4e2bad5fbe9526f244f3237f0e32b0a706
F ext/vec1/test/vec1fault6.test 4f86d9fbd31552a1461e2dcdf350b4d5413b5536382a669a0010dea59f0c2865
F ext/vec1/test/vec1fault7.test 92ebab2b519e79c41be71ef3f9f438f4a3358b475164c41288d674be102a1d24
F ext/vec1/test/vec1fault8.test a2da772d96539b25574cc4cf0cdcf291931da1a416a6883662e10f5c92275c69
F ext/vec1/test/vec1fault9.test 150a88218168844e80f1f600c7cd3189f01e05b69f8c899c0ef42997f51846ee
F ext/vec1/test/vec1fixed.test 9f1ecc7a67bfe1afb8a3c9e173c2f7990b14f0ad1bbb869d6fe30d247d3b2271
F ext/vec1/test/vec1flat.test e6fbbe5dd6a46019d5913a427a9d80e25771694a7db0c51ecb60ecb96c8c49b1
F ext/vec1/test/vec1func.test e27a3aed8abc7dcea38ec87ccd8f91d30eba551fe1cc1e9f9b0e39cee9ce574d
F ext/vec1/test/vec1growth.test 9dc2abf5a79bd8d23e5718717dce4001ae89d807688b3b1a9f6b1f0d666ece42
F ext/vec1/test/vec1intck.test 45fd8d3619b8116d2dfc0fbc8ae76c044de176f96a316e16ea1c8dff7d2b9359
F ext/vec1/test/vec1intck2.test a25d41c108b1c63a20a90125543f5ecb3eb696e6c3716a3c2c6325cd8e18b026
F ext/vec1/test/vec1ivf.test 32ece2817587ce8a38e2b9798c7bd0c1e991bc65cf59c03bd91a655ff7a81e06
F ext/vec1/test/vec1join.test 7f13b951303ed6aebf18bc70b70673ab6dbd7ac3ca99a22a47c7cdb369245218
F ext/vec1/test/vec1limit.test 175972f00c4230896c5e09e0944c8b74ae1b39f08683a8e0538b25ced2c2b1de
F ext/vec1/test/vec1meta.test 5a3ae0338a6168bab4a381817b13425028b6ab04dff270065077be471d77a5fc
F ext/vec1/test/vec1meta2.test 4997a26728d24aaf8219b3c168209df00f8174eec521415ff94dfdd2d7db5da0
F ext/vec1/test/vec1meta3.test 91907adb451e17b777f526dd39c8cdfd2c5928a47b3a864eb2df6b812a8ce461
F ext/vec1/test/vec1nan.test 3ff3fe4b0f33a5c1f4129ba318443e7d927fd9dd6fc339349fea3c57fe140a8c
F ext/vec1/test/vec1pq.test 81d74f516971a3883782d2d443efe3f6a7501d4c9f980a758e1c2eb8b5f22885
F ext/vec1/test/vec1query.test 7120a2824b9f6563f7986c436204870c9d6e4af2bff3a39d38ef277188ef9189
F ext/vec1/test/vec1rollback.test 41565f526e153b24615ed38edda835fbb185f2f07f5e72f5c947add810b33d71
F ext/vec1/test/vec1rotate.test a90773e8ef8259f81c793cb335e3c023295231416c37d89e387d524304dadfde
F ext/vec1/test/vec1rotate2.test 7e4e06b3f970190c4ac28256db7aa924796b9a1fe1985941e35db4604c8717a0
F ext/vec1/test/vec1round.test b38735c7e5b0af833505cc639d60816cfca2143b7461a1b443815c4be2b9d3ed
F ext/vec1/test/vec1scalar.test bf2acbf26a2e9b2494de5e55f049045ae7da4a0352ddeafa6d6bdfd7e6d5a909
F ext/vec1/test/vec1stream.test 54f325ef75508f2b1ff82d883ac1c12bb4494f652468ef195b736c97640c34a7
F ext/vec1/test/vec1stream2.test ad0aa9dc70fdccd46b31d6c19352387a04394bb631437a1bc9e169292cc797de
F ext/vec1/test/vec1train.test 5f60a53963ed971b56c7dcc14bcccee433f8f554f1148ed1bff8231047e310b6
F ext/vec1/test/vec1update.test 77d5b85b859393f0d8fbc2f556a75c06bee7b23a19b5af7c3c40dc443f811e65
F ext/vec1/test/vec1update2.test 4fb868f051df89a35f7e58725699259150292fe972253088f3feb10ee7c50945
F ext/vec1/test/vec1version.test 7d17cc84c0a3c8706080abb3484bad8a6eb118c8d4946c7846cde5c4e6cfc89e
F ext/vec1/tool/vec1_algo_test.c c62b828a3e46a39a8656f26b74804906cc3160758f8afc6ed89f507b832af302
F ext/vec1/tool/vec1bench.tcl de201e8b468de035065dd8dfdaa171d64760693deb19e6d5725e23840e6cca1e
F ext/vec1/tool/vec1bench_addcols.tcl 8c509253d07b179928f2ee32e3591a2c70d13c914217e1328c01afa148717051
F ext/vec1/tool/vec1bench_vibe2db.py 84ddb0236bc3d02a0393abce41986a1b7dad7f0d83884576f8318c2883e3aed3
F ext/vec1/vec1.c 08cd74248dd2707e8a59943007e1dbcac2946e60b9d55c261f4810d1689b17fa
F ext/wasm/GNUmakefile cc28e65dec12f1b992957152d8395484f4fcf7f94e6b6736a09ee4f48e7ec048
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
@@ -665,7 +722,7 @@ F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk 54a08023012d78cc2ac7712d1c624ce66461e920c3e0241ebed5383aba6d5361
F main.mk 86f616b7ef7931c76d421bb76d05c6f90f45fa5d365eac178e19f8a54995671c
F make.bat a136fd0b1c93e89854a86d5f4edcf0386d211e5d5ec2434480f6eea436c7420c
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -707,7 +764,7 @@ F src/insert.c 68b898cb17607b36fa751b8985da06531932c502d77186f82842817286c7fd03
F src/json.c 98b654f8316e76bdff4eaf915ba6b95335a8b77e0bcd650a107c7ac86fb16080
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 5cd4cd7ec6e1a46416806899086363fbf95eecd3bfb9a388b436c9a77243c902
F src/main.c 5e366de50caa6083b0f28ca342970bff909c8086382c21f1cd7b8d92fa7b3590
F src/main.c 3bdc49a38ba0919016531fc7fa606a103731123497014c80233ff57fd44d4298
F src/malloc.c 422f7e0498e1c9ef967f06283b6f2c0b16db6b905d8e06f6dbc8baaa3e4e6c5a
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@@ -1524,7 +1581,7 @@ F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test 8a801d2b8e4b0ebb99701f026a67a9e84634c8aa24799a842c44003b93250da1
F test/pendingrace.test e99efc5ab3584da3dfc8cd6a0ec4e5a42214820574f5ea24ee93f1d84655f463
F test/percentile.test fd78896fa882fa4fbf693640097859721f3629926c2ccf804af5bcb7001fd35b
F test/permutations.test 1fd848d38291131d0c7eed41321311ee5d58ae5b7ce6f37a3d79ebaff7e1b95c
F test/permutations.test c919e9d9b9d17faccd543128ac714efbba0606c7051bcb54f264006eecdb4627
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test 7d07b7bb76e273215d6a20c4f83c3062cc28976c737ccb70a686025801e86c8f
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
@@ -2221,8 +2278,9 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
P b838769b4b115518feba1ea1f6a39f49427870cae824ec7225627565ab91de3e
R c548003c5f4748fdb47a89cb5157d9df
U stephan
Z b7a7297d541145c38b4c4d120e2488bf
P 7af745868b873daacdbb57621201887f80782cef4e10ef24243a15ae88897f0d b55faf3253cd5d2fc69b4e025f4707bda3600c553f85aff152cfd4e71e54c81e
R 642951fe9ff481d26284b1f25f51a3c6
T +closed b55faf3253cd5d2fc69b4e025f4707bda3600c553f85aff152cfd4e71e54c81e
U dan
Z 44f633989d1d519b076317587e6f49cd
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
7af745868b873daacdbb57621201887f80782cef4e10ef24243a15ae88897f0d
92709f54d4c58cf174f305b58971a01878af313157a11549b588a25216701f5c

View File

@@ -2054,7 +2054,7 @@ int sqlite3CreateFunc(
if( db->nVdbeActive ){
sqlite3ErrorWithMsg(db, SQLITE_BUSY,
"unable to delete/modify user-function due to active statements");
assert( !db->mallocFailed );
assert( !db->mallocFailed || db->nVdbeExec>0 );
return SQLITE_BUSY;
}else{
sqlite3ExpirePreparedStatements(db, 0);

View File

@@ -96,6 +96,7 @@ foreach f [glob -nocomplain \
$testdir/../ext/recover/*.test \
$testdir/../ext/rbu/*.test \
$testdir/../ext/intck/*.test \
$testdir/../ext/vec1/test/*.test \
] {
lappend alltests $f
}