mirror of
https://github.com/sqlite/sqlite.git
synced 2026-10-23 21:49:02 +03:00
Import vec1 sources into directory ext/vec1/ of this project.
FossilOrigin-Name: 3a0876d973868be7b6f4156a434cd09098c7ab1abdc7f0ef4f3d12a03de68b24
This commit is contained in:
@@ -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
|
||||
|
||||
31
Makefile.msc
31
Makefile.msc
@@ -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
|
||||
|
||||
13
auto.def
13
auto.def
@@ -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
63
ext/vec1/Makefile
Normal 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
19
ext/vec1/Makefile.msc
Normal 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
|
||||
125
ext/vec1/test/vec1_common.tcl
Normal file
125
ext/vec1/test/vec1_common.tcl
Normal 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
264
ext/vec1/test/vec1aa.test
Normal 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
115
ext/vec1/test/vec1ab.test
Normal 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
100
ext/vec1/test/vec1ac.test
Normal 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
|
||||
80
ext/vec1/test/vec1alter.test
Normal file
80
ext/vec1/test/vec1alter.test
Normal 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
|
||||
|
||||
162
ext/vec1/test/vec1binary.test
Normal file
162
ext/vec1/test/vec1binary.test
Normal 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
|
||||
|
||||
95
ext/vec1/test/vec1binary2.test
Normal file
95
ext/vec1/test/vec1binary2.test
Normal 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
|
||||
|
||||
65
ext/vec1/test/vec1blocksize.test
Normal file
65
ext/vec1/test/vec1blocksize.test
Normal 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
247
ext/vec1/test/vec1cat.test
Normal 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
125
ext/vec1/test/vec1cat2.test
Normal 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
|
||||
|
||||
113
ext/vec1/test/vec1config.test
Normal file
113
ext/vec1/test/vec1config.test
Normal 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
|
||||
|
||||
57
ext/vec1/test/vec1corrupt.test
Normal file
57
ext/vec1/test/vec1corrupt.test
Normal 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
|
||||
|
||||
114
ext/vec1/test/vec1create.test
Normal file
114
ext/vec1/test/vec1create.test
Normal 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
|
||||
|
||||
279
ext/vec1/test/vec1delete.test
Normal file
279
ext/vec1/test/vec1delete.test
Normal 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
|
||||
|
||||
85
ext/vec1/test/vec1error.test
Normal file
85
ext/vec1/test/vec1error.test
Normal 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
|
||||
|
||||
213
ext/vec1/test/vec1fault1.test
Normal file
213
ext/vec1/test/vec1fault1.test
Normal 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
|
||||
|
||||
49
ext/vec1/test/vec1fault2.test
Normal file
49
ext/vec1/test/vec1fault2.test
Normal 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
|
||||
|
||||
106
ext/vec1/test/vec1fault3.test
Normal file
106
ext/vec1/test/vec1fault3.test
Normal 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
|
||||
|
||||
149
ext/vec1/test/vec1fault4.test
Normal file
149
ext/vec1/test/vec1fault4.test
Normal 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
|
||||
|
||||
42
ext/vec1/test/vec1fault5.test
Normal file
42
ext/vec1/test/vec1fault5.test
Normal 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
|
||||
|
||||
46
ext/vec1/test/vec1fault6.test
Normal file
46
ext/vec1/test/vec1fault6.test
Normal 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
|
||||
|
||||
65
ext/vec1/test/vec1fault7.test
Normal file
65
ext/vec1/test/vec1fault7.test
Normal 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
|
||||
|
||||
124
ext/vec1/test/vec1fault8.test
Normal file
124
ext/vec1/test/vec1fault8.test
Normal 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
|
||||
|
||||
82
ext/vec1/test/vec1fault9.test
Normal file
82
ext/vec1/test/vec1fault9.test
Normal 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
|
||||
|
||||
111
ext/vec1/test/vec1fixed.test
Normal file
111
ext/vec1/test/vec1fixed.test
Normal 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
390
ext/vec1/test/vec1flat.test
Normal 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
115
ext/vec1/test/vec1func.test
Normal 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
|
||||
|
||||
114
ext/vec1/test/vec1growth.test
Normal file
114
ext/vec1/test/vec1growth.test
Normal 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
|
||||
|
||||
293
ext/vec1/test/vec1intck.test
Normal file
293
ext/vec1/test/vec1intck.test
Normal 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
|
||||
|
||||
40
ext/vec1/test/vec1intck2.test
Normal file
40
ext/vec1/test/vec1intck2.test
Normal 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
|
||||
|
||||
63
ext/vec1/test/vec1ivf.test
Normal file
63
ext/vec1/test/vec1ivf.test
Normal 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
|
||||
|
||||
46
ext/vec1/test/vec1join.test
Normal file
46
ext/vec1/test/vec1join.test
Normal 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
|
||||
|
||||
97
ext/vec1/test/vec1limit.test
Normal file
97
ext/vec1/test/vec1limit.test
Normal 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
252
ext/vec1/test/vec1meta.test
Normal 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
|
||||
|
||||
394
ext/vec1/test/vec1meta2.test
Normal file
394
ext/vec1/test/vec1meta2.test
Normal 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
|
||||
304
ext/vec1/test/vec1meta3.test
Normal file
304
ext/vec1/test/vec1meta3.test
Normal 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
|
||||
|
||||
48
ext/vec1/test/vec1nan.test
Normal file
48
ext/vec1/test/vec1nan.test
Normal 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
143
ext/vec1/test/vec1pq.test
Normal 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
|
||||
|
||||
147
ext/vec1/test/vec1query.test
Normal file
147
ext/vec1/test/vec1query.test
Normal 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
|
||||
|
||||
148
ext/vec1/test/vec1rollback.test
Normal file
148
ext/vec1/test/vec1rollback.test
Normal file
@@ -0,0 +1,148 @@
|
||||
# 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}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
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}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
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}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
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
|
||||
56
ext/vec1/test/vec1rotate.test
Normal file
56
ext/vec1/test/vec1rotate.test
Normal 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
|
||||
|
||||
42
ext/vec1/test/vec1rotate2.test
Normal file
42
ext/vec1/test/vec1rotate2.test
Normal 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
|
||||
|
||||
303
ext/vec1/test/vec1round.test
Normal file
303
ext/vec1/test/vec1round.test
Normal 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
|
||||
|
||||
77
ext/vec1/test/vec1scalar.test
Normal file
77
ext/vec1/test/vec1scalar.test
Normal 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
|
||||
|
||||
121
ext/vec1/test/vec1stream.test
Normal file
121
ext/vec1/test/vec1stream.test
Normal 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
|
||||
|
||||
87
ext/vec1/test/vec1stream2.test
Normal file
87
ext/vec1/test/vec1stream2.test
Normal 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
|
||||
|
||||
160
ext/vec1/test/vec1train.test
Normal file
160
ext/vec1/test/vec1train.test
Normal 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
|
||||
|
||||
124
ext/vec1/test/vec1update.test
Normal file
124
ext/vec1/test/vec1update.test
Normal 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
|
||||
|
||||
61
ext/vec1/test/vec1update2.test
Normal file
61
ext/vec1/test/vec1update2.test
Normal 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
|
||||
|
||||
118
ext/vec1/test/vec1version.test
Normal file
118
ext/vec1/test/vec1version.test
Normal 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
|
||||
|
||||
787
ext/vec1/tool/vec1_algo_test.c
Normal file
787
ext/vec1/tool/vec1_algo_test.c
Normal 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
707
ext/vec1/tool/vec1bench.tcl
Normal 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
|
||||
|
||||
121
ext/vec1/tool/vec1bench_addcols.tcl
Normal file
121
ext/vec1/tool/vec1bench_addcols.tcl
Normal 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
|
||||
|
||||
91
ext/vec1/tool/vec1bench_vibe2db.py
Normal file
91
ext/vec1/tool/vec1bench_vibe2db.py
Normal 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])
|
||||
12938
ext/vec1/vec1.c
Normal file
12938
ext/vec1/vec1.c
Normal file
File diff suppressed because it is too large
Load Diff
35
main.mk
35
main.mk
@@ -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)
|
||||
|
||||
82
manifest
82
manifest
@@ -1,13 +1,13 @@
|
||||
C Do\snot\sinherit\sthe\sOPFS\sSAHPool's\sxSleep\smethod\sfrom\sthe\sdefault\sVFS,\sas\sdoing\sso\scan\scause\sthe\sthread\sto\sfreeze.\sThis\sVFS\sonly\sdeals\swith\scontention\soriginating\sin\sthe\ssame\sthread,\sso\sxSleep()\sneeds\sto\sbe\sa\sno-op.\sAddresses\s[forum:3f0794c5d8|bug\sreport\s2026-09-30T12:52:54Z].
|
||||
D 2026-09-30T13:23:56.388
|
||||
C Import\svec1\ssources\sinto\sdirectory\sext/vec1/\sof\sthis\sproject.
|
||||
D 2026-09-30T14:49:16.274
|
||||
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 cb2a117153eb3bd3b97a19e8fce6a880b930862b0b50d240f80569ad593a6764
|
||||
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 d6c96e5272447a08847a5031ceac121f3483a2a26283bb651f6c1d85179b0b2b
|
||||
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
|
||||
@@ -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,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
|
||||
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
|
||||
P fa44643f65d6815c8c21ec25e7aa6e39a00fdc818d1d058e03123042e45884e1
|
||||
R 3a63c51ccb58b2a22c00bdb1b8f5247c
|
||||
U stephan
|
||||
Z 901de4bdf6f0c30a65a3330271325068
|
||||
P c9dd4d88e44a9eae4ea11d0ce5491c7ac2ac2d60a3bc83ed485812234be374da
|
||||
R abde61b2c412631e87e77f443f340a02
|
||||
T *branch * vec1
|
||||
T *sym-vec1 *
|
||||
T -sym-trunk *
|
||||
U dan
|
||||
Z 907e792789daaeb3c2cc5400f2140971
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
branch trunk
|
||||
tag trunk
|
||||
branch vec1
|
||||
tag vec1
|
||||
|
||||
@@ -1 +1 @@
|
||||
c9dd4d88e44a9eae4ea11d0ce5491c7ac2ac2d60a3bc83ed485812234be374da
|
||||
3a0876d973868be7b6f4156a434cd09098c7ab1abdc7f0ef4f3d12a03de68b24
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user