diff --git a/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c b/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c index 46033982df..d5a72ca3c3 100644 --- a/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c +++ b/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c @@ -19000,6 +19000,7 @@ struct KeyInfo { * vector indices as they operate with names rather than with page numbers */ char *zIndexName; /* Name of the index (might be NULL) */ + char *zDbSName; /* Name of the database schema (might be NULL) */ u32 nRef; /* Number of references to this KeyInfo object */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nKeyField; /* Number of key columns in the index */ @@ -84921,7 +84922,7 @@ typedef struct BlobSpot BlobSpot; */ struct DiskAnnIndex { sqlite3 *db; /* Database connection */ - char *zDb; /* Database name */ + char *zDbSName; /* Database name */ char *zName; /* Index name */ char *zShadow; /* Shadow table name */ int nFormatVersion; /* DiskAnn format version */ @@ -85112,10 +85113,10 @@ int vectorOutRowsPut(VectorOutRows *, int, int, const u64 *, sqlite3_value *); void vectorOutRowsGet(sqlite3_context *, const VectorOutRows *, int, int); void vectorOutRowsFree(sqlite3 *, VectorOutRows *); -int diskAnnCreateIndex(sqlite3 *, const char *, const VectorIdxKey *, VectorIdxParams *); -int diskAnnClearIndex(sqlite3 *, const char *); -int diskAnnDropIndex(sqlite3 *, const char *); -int diskAnnOpenIndex(sqlite3 *, const char *, const VectorIdxParams *, DiskAnnIndex **); +int diskAnnCreateIndex(sqlite3 *, const char *, const char *, const VectorIdxKey *, VectorIdxParams *); +int diskAnnClearIndex(sqlite3 *, const char *, const char *); +int diskAnnDropIndex(sqlite3 *, const char *, const char *); +int diskAnnOpenIndex(sqlite3 *, const char *, const char *, const VectorIdxParams *, DiskAnnIndex **); void diskAnnCloseIndex(DiskAnnIndex *); int diskAnnInsert(const DiskAnnIndex *, const VectorInRow *, char **); int diskAnnDelete(const DiskAnnIndex *, const VectorInRow *, char **); @@ -85129,14 +85130,14 @@ typedef struct VectorIdxCursor VectorIdxCursor; int vectorIdxParseColumnType(const char *, int *, int *, const char **); -int vectorIndexCreate(Parse*, Index*, const IdList*); -int vectorIndexClear(sqlite3 *, const char *); -int vectorIndexDrop(sqlite3 *, const char *); -int vectorIndexCursorInit(sqlite3 *, VectorIdxCursor **, const char *); +int vectorIndexCreate(Parse*, Index*, const char *, const IdList*); +int vectorIndexClear(sqlite3 *, const char *, const char *); +int vectorIndexDrop(sqlite3 *, const char *, const char *); +int vectorIndexCursorInit(sqlite3 *, const char *, const char *, VectorIdxCursor **); void vectorIndexCursorClose(sqlite3 *, VectorIdxCursor *); int vectorIndexInsert(VectorIdxCursor *, const UnpackedRecord *, char **); int vectorIndexDelete(VectorIdxCursor *, const UnpackedRecord *, char **); -int vectorIndexSearch(sqlite3 *, int, sqlite3_value **, VectorOutRows *, char **); +int vectorIndexSearch(sqlite3 *, const char *, int, sqlite3_value **, VectorOutRows *, char **); #if 0 } /* end of the 'extern "C"' block */ @@ -97586,13 +97587,14 @@ case OP_OpenVectorIdx: { }else if( pOp->p4type==P4_INT32 ){ nField = pOp->p4.i; } + assert( pKeyInfo->zDbSName != NULL ); if( pOp->p5 == OPFLAG_FORDELETE ){ - rc = vectorIndexClear(db, pKeyInfo->zIndexName); + rc = vectorIndexClear(db, pKeyInfo->zDbSName, pKeyInfo->zIndexName); if( rc ){ goto abort_due_to_error; } } - rc = vectorIndexCursorInit(db, &cursor, pKeyInfo->zIndexName); + rc = vectorIndexCursorInit(db, pKeyInfo->zDbSName, pKeyInfo->zIndexName, &cursor); if( rc ) { goto abort_due_to_error; } @@ -125112,6 +125114,7 @@ static void destroyTable(Parse *pParse, Table *pTab){ Pgno iTab = pTab->tnum; Pgno iDestroyed = 0; Index *pIdx; + int iDb; #ifndef SQLITE_OMIT_VECTOR /* @@ -125125,9 +125128,12 @@ static void destroyTable(Parse *pParse, Table *pTab){ * 3. Delete index during the parsing stage (implemented variant) - it's hacky * and bit dirty but seems to me as pretty safe and easy way to delete index */ + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( IsVectorIndex(pIdx) ){ - vectorIndexDrop(pParse->db, pIdx->zName); + assert( 0 <= iDb && iDb < pParse->db->nDb ); + vectorIndexDrop(pParse->db, pParse->db->aDb[iDb].zDbSName, pIdx->zName); } } #endif @@ -126094,7 +126100,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( #ifndef SQLITE_OMIT_VECTOR - if( vectorIndexCreate(pParse, pIndex, pUsing) != SQLITE_OK ) { + if( vectorIndexCreate(pParse, pIndex, db->aDb[iDb].zDbSName, pUsing) != SQLITE_OK ) { goto exit_create_index; } idxType = pIndex->idxType; // vectorIndexCreate can update idxType to 4 (VECTOR INDEX) @@ -126451,6 +126457,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists "or PRIMARY KEY constraint cannot be dropped", 0); goto exit_drop_index; } + iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_VECTOR /* * There are several places to delete vector index: @@ -126464,10 +126471,9 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists * and bit dirty but seems to me as pretty safe and easy way to delete index */ if( IsVectorIndex(pIndex) ){ - vectorIndexDrop(pParse->db, pIndex->zName); + vectorIndexDrop(pParse->db, pParse->db->aDb[iDb].zDbSName, pIndex->zName); } #endif - iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_INDEX; @@ -127409,7 +127415,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ ** when it has finished using it. */ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ - int i; + int i, iDb; int nCol = pIdx->nColumn; int nKey = pIdx->nKeyCol; KeyInfo *pKey; @@ -127420,8 +127426,12 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); } if( pKey ){ + iDb = sqlite3SchemaToIndex(pParse->db, pIdx->pSchema); assert( sqlite3KeyInfoIsWriteable(pKey) ); pKey->zIndexName = sqlite3DbStrDup(pParse->db, pIdx->zName); + if( 0 <= iDb && iDb < pParse->db->nDb ){ + pKey->zDbSName = sqlite3DbStrDup(pParse->db, pParse->db->aDb[iDb].zDbSName); + } for(i=0; iazColl[i]; pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 : @@ -144878,6 +144888,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ p->db = db; p->nRef = 1; p->zIndexName = NULL; + p->zDbSName = NULL; memset(&p[1], 0, nExtra); }else{ return (KeyInfo*)sqlite3OomFault(db); @@ -144897,6 +144908,9 @@ SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){ if( p->zIndexName != NULL ){ sqlite3DbFree(p->db, p->zIndexName); } + if( p->zDbSName != NULL ){ + sqlite3DbFree(p->db, p->zDbSName); + } sqlite3DbNNFreeNN(p->db, p); } } @@ -209542,7 +209556,7 @@ int blobSpotCreate(const DiskAnnIndex *pIndex, BlobSpot **ppBlobSpot, u64 nRowid } // open blob in the end so we don't need to close it in error case - rc = sqlite3_blob_open(pIndex->db, pIndex->zDb, pIndex->zShadow, "data", nRowid, isWritable, &pBlobSpot->pBlob); + rc = sqlite3_blob_open(pIndex->db, pIndex->zDbSName, pIndex->zShadow, "data", nRowid, isWritable, &pBlobSpot->pBlob); rc = blobSpotConvertRc(pIndex, rc); if( rc != SQLITE_OK ){ goto out; @@ -209589,7 +209603,7 @@ int blobSpotReload(const DiskAnnIndex *pIndex, BlobSpot *pBlobSpot, u64 nRowid, pBlobSpot->isAborted = 0; pBlobSpot->nRowid = nRowid; - rc = sqlite3_blob_open(pIndex->db, pIndex->zDb, pIndex->zShadow, "data", nRowid, pBlobSpot->isWritable, &pBlobSpot->pBlob); + rc = sqlite3_blob_open(pIndex->db, pIndex->zDbSName, pIndex->zShadow, "data", nRowid, pBlobSpot->isWritable, &pBlobSpot->pBlob); rc = blobSpotConvertRc(pIndex, rc); if( rc != SQLITE_OK ){ goto abort; @@ -209786,6 +209800,7 @@ void nodeBinDebug(const DiskAnnIndex *pIndex, const BlobSpot *pBlobSpot) { int diskAnnCreateIndex( sqlite3 *db, + const char *zDbSName, const char *zIdxName, const VectorIdxKey *pKey, VectorIdxParams *pParams @@ -209830,7 +209845,8 @@ int diskAnnCreateIndex( } zSql = sqlite3MPrintf( db, - "CREATE TABLE IF NOT EXISTS %s_shadow (%s, data BLOB, PRIMARY KEY (%s))", + "CREATE TABLE IF NOT EXISTS \"%w\".%s_shadow (%s, data BLOB, PRIMARY KEY (%s))", + zDbSName, zIdxName, columnSqlDefs, columnSqlNames @@ -209840,15 +209856,15 @@ int diskAnnCreateIndex( return rc; } -int diskAnnClearIndex(sqlite3 *db, const char *zIdxName) { - char *zSql = sqlite3MPrintf(db, "DELETE FROM %s_shadow", zIdxName); +int diskAnnClearIndex(sqlite3 *db, const char *zDbSName, const char *zIdxName) { + char *zSql = sqlite3MPrintf(db, "DELETE FROM \"%w\".%s_shadow", zDbSName, zIdxName); int rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3DbFree(db, zSql); return rc; } -int diskAnnDropIndex(sqlite3 *db, const char *zIdxName){ - char *zSql = sqlite3MPrintf(db, "DROP TABLE %s_shadow", zIdxName); +int diskAnnDropIndex(sqlite3 *db, const char *zDbSName, const char *zIdxName){ + char *zSql = sqlite3MPrintf(db, "DROP TABLE \"%w\".%s_shadow", zDbSName, zIdxName); int rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3DbFree(db, zSql); return rc; @@ -209866,8 +209882,8 @@ static int diskAnnSelectRandomShadowRow(const DiskAnnIndex *pIndex, u64 *pRowid) zSql = sqlite3MPrintf( pIndex->db, - "SELECT rowid FROM %s LIMIT 1 OFFSET ABS(RANDOM()) %% MAX((SELECT COUNT(*) FROM %s), 1)", - pIndex->zShadow, pIndex->zShadow + "SELECT rowid FROM \"%w\".%s LIMIT 1 OFFSET ABS(RANDOM()) %% MAX((SELECT COUNT(*) FROM %s), 1)", + pIndex->zDbSName, pIndex->zShadow, pIndex->zShadow ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; @@ -209917,7 +209933,11 @@ static int diskAnnGetShadowRowid(const DiskAnnIndex *pIndex, const VectorInRow * rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "SELECT rowid FROM %s WHERE (%s) = (%s)", pIndex->zShadow, columnSqlNames, columnSqlPlaceholders); + zSql = sqlite3MPrintf( + pIndex->db, + "SELECT rowid FROM \"%w\".%s WHERE (%s) = (%s)", + pIndex->zDbSName, pIndex->zShadow, columnSqlNames, columnSqlPlaceholders + ); if( zSql == NULL ){ rc = SQLITE_NOMEM; goto out; @@ -209966,7 +209986,11 @@ static int diskAnnGetShadowRowKeys(const DiskAnnIndex *pIndex, u64 nRowid, const rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "SELECT %s FROM %s WHERE rowid = ?", columnSqlNames, pIndex->zShadow); + zSql = sqlite3MPrintf( + pIndex->db, + "SELECT %s FROM \"%w\".%s WHERE rowid = ?", + columnSqlNames, pIndex->zDbSName, pIndex->zShadow + ); if( zSql == NULL ){ rc = SQLITE_NOMEM; goto out; @@ -210016,7 +210040,11 @@ static int diskAnnInsertShadowRow(const DiskAnnIndex *pIndex, const VectorInRow rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "INSERT INTO %s VALUES (%s, ?) RETURNING rowid", pIndex->zShadow, columnSqlPlaceholders); + zSql = sqlite3MPrintf( + pIndex->db, + "INSERT INTO \"%w\".%s VALUES (%s, ?) RETURNING rowid", + pIndex->zDbSName, pIndex->zShadow, columnSqlPlaceholders + ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; goto out; @@ -210063,7 +210091,11 @@ static int diskAnnInsertShadowRow(const DiskAnnIndex *pIndex, const VectorInRow static int diskAnnDeleteShadowRow(const DiskAnnIndex *pIndex, i64 nRowid){ int rc; sqlite3_stmt *pStmt = NULL; - char *zSql = sqlite3MPrintf(pIndex->db, "DELETE FROM %s WHERE rowid = ?", pIndex->zShadow); + char *zSql = sqlite3MPrintf( + pIndex->db, + "DELETE FROM \"%w\".%s WHERE rowid = ?", + pIndex->zDbSName, pIndex->zShadow + ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; goto out; @@ -210586,9 +210618,6 @@ int diskAnnSearch( } rc = SQLITE_OK; out: - if( rc != SQLITE_OK ){ - vectorOutRowsFree(pIndex->db, pRows); - } diskAnnSearchCtxDeinit(&ctx); return rc; } @@ -210782,6 +210811,7 @@ int diskAnnDelete( // open index with zIdxName and pParams serialized binary parameters and set result to the ppIndex int diskAnnOpenIndex( sqlite3 *db, /* Database connection */ + const char *zDbSName, /* Database schema name */ const char *zIdxName, /* Index name */ const VectorIdxParams *pParams, /* Index parameters */ DiskAnnIndex **ppIndex /* OUT: Index */ @@ -210792,9 +210822,13 @@ int diskAnnOpenIndex( return SQLITE_NOMEM; } pIndex->db = db; - pIndex->zDb = sqlite3DbStrDup(db, db->aDb[0].zDbSName); + pIndex->zDbSName = sqlite3DbStrDup(db, zDbSName); pIndex->zName = sqlite3DbStrDup(db, zIdxName); pIndex->zShadow = sqlite3MPrintf(db, "%s_shadow", zIdxName); + if( pIndex->zShadow == NULL ){ + diskAnnCloseIndex(pIndex); + return SQLITE_NOMEM_BKPT; + } pIndex->nFormatVersion = vectorIdxParamsGetU64(pParams, VECTOR_FORMAT_PARAM_ID); pIndex->nDistanceFunc = vectorIdxParamsGetU64(pParams, VECTOR_METRIC_TYPE_PARAM_ID); pIndex->nBlockSize = vectorIdxParamsGetU64(pParams, VECTOR_BLOCK_SIZE_PARAM_ID) << DISKANN_BLOCK_SIZE_SHIFT; @@ -210803,14 +210837,13 @@ int diskAnnOpenIndex( pIndex->pruningAlpha = vectorIdxParamsGetF64(pParams, VECTOR_PRUNING_ALPHA_PARAM_ID); pIndex->insertL = vectorIdxParamsGetU64(pParams, VECTOR_INSERT_L_PARAM_ID); pIndex->searchL = vectorIdxParamsGetU64(pParams, VECTOR_SEARCH_L_PARAM_ID); - if( pIndex->zShadow == NULL || - pIndex->nDistanceFunc == 0 || + if( pIndex->nDistanceFunc == 0 || pIndex->nBlockSize == 0 || pIndex->nNodeVectorType == 0 || pIndex->nVectorDims == 0 ){ diskAnnCloseIndex(pIndex); - return SQLITE_NOMEM; + return SQLITE_ERROR; } if( pIndex->pruningAlpha == 0 ){ pIndex->pruningAlpha = VECTOR_PRUNING_ALPHA_DEFAULT; @@ -210831,8 +210864,8 @@ int diskAnnOpenIndex( } void diskAnnCloseIndex(DiskAnnIndex *pIndex){ - if( pIndex->zDb ){ - sqlite3DbFree(pIndex->db, pIndex->zDb); + if( pIndex->zDbSName ){ + sqlite3DbFree(pIndex->db, pIndex->zDbSName); } if( pIndex->zName ){ sqlite3DbFree(pIndex->db, pIndex->zName); @@ -211921,15 +211954,34 @@ int vectorIdxParseColumnType(const char *zType, int *pType, int *pDims, const ch return -1; } -int initVectorIndexMetaTable(sqlite3* db) { - static const char *zSql = "CREATE TABLE IF NOT EXISTS " VECTOR_INDEX_GLOBAL_META_TABLE " ( name TEXT PRIMARY KEY, metadata BLOB ) WITHOUT ROWID;"; - return sqlite3_exec(db, zSql, 0, 0, 0); +int initVectorIndexMetaTable(sqlite3* db, const char *zDbSName) { + int rc; + static const char *zSqlTemplate = "CREATE TABLE IF NOT EXISTS \"%w\"." VECTOR_INDEX_GLOBAL_META_TABLE " ( name TEXT PRIMARY KEY, metadata BLOB ) WITHOUT ROWID;"; + char* zSql; + + assert( zDbSName != NULL ); + + zSql = sqlite3_mprintf(zSqlTemplate, zDbSName); + if( zSql == NULL ){ + return SQLITE_NOMEM_BKPT; + } + rc = sqlite3_exec(db, zSql, 0, 0, 0); + sqlite3_free(zSql); + return rc; } -int insertIndexParameters(sqlite3* db, const char *zName, const VectorIdxParams *pParameters) { - static const char *zSql = "INSERT INTO " VECTOR_INDEX_GLOBAL_META_TABLE " VALUES (?, ?)"; - sqlite3_stmt* pStatement = NULL; +int insertIndexParameters(sqlite3* db, const char *zDbSName, const char *zName, const VectorIdxParams *pParameters) { int rc = SQLITE_ERROR; + static const char *zSqlTemplate = "INSERT INTO \"%w\"." VECTOR_INDEX_GLOBAL_META_TABLE " VALUES (?, ?)"; + sqlite3_stmt* pStatement = NULL; + char *zSql; + + assert( zDbSName != NULL ); + + zSql = sqlite3_mprintf(zSqlTemplate, zDbSName); + if( zSql == NULL ){ + return SQLITE_NOMEM_BKPT; + } rc = sqlite3_prepare_v2(db, zSql, -1, &pStatement, 0); if( rc != SQLITE_OK ){ @@ -211950,6 +212002,9 @@ int insertIndexParameters(sqlite3* db, const char *zName, const VectorIdxParams rc = SQLITE_OK; } clear_and_exit: + if( zSql != NULL ){ + sqlite3_free(zSql); + } if( pStatement != NULL ){ sqlite3_finalize(pStatement); } @@ -212043,24 +212098,31 @@ int vectorIndexGetParameters( } -int vectorIndexDrop(sqlite3 *db, const char *zIdxName) { +int vectorIndexDrop(sqlite3 *db, const char *zDbSName, const char *zIdxName) { // we want to try delete all traces of index on every attempt // this is done to prevent unrecoverable situations where index were dropped but index parameters deletion failed and second attempt will fail on first step - int rcIdx = diskAnnDropIndex(db, zIdxName); - int rcParams = removeIndexParameters(db, zIdxName); + int rcIdx, rcParams; + + assert( zDbSName != NULL ); + + rcIdx = diskAnnDropIndex(db, zDbSName, zIdxName); + rcParams = removeIndexParameters(db, zIdxName); return rcIdx != SQLITE_OK ? rcIdx : rcParams; } -int vectorIndexClear(sqlite3 *db, const char *zIdxName) { - return diskAnnClearIndex(db, zIdxName); +int vectorIndexClear(sqlite3 *db, const char *zDbSName, const char *zIdxName) { + assert( zDbSName != NULL ); + return diskAnnClearIndex(db, zDbSName, zIdxName); } -int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { +int vectorIndexCreate(Parse *pParse, Index *pIdx, const char *zDbSName, const IdList *pUsing) { int i, rc = SQLITE_OK; int dims, type; int hasLibsqlVectorIdxFn = 0, hasCollation = 0; const char *pzErrMsg; + assert( zDbSName != NULL ); + sqlite3 *db = pParse->db; Table *pTable = pIdx->pTable; struct ExprList_item *pListItem; @@ -212147,21 +212209,12 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { return SQLITE_ERROR; } - if( vectorIdxKeyGet(pTable, &idxKey, &pzErrMsg) != 0 ){ - sqlite3ErrorMsg(pParse, "failed to detect underlying table key: %s", pzErrMsg); - return SQLITE_ERROR; - } - if( idxKey.nKeyColumns != 1 ){ - sqlite3ErrorMsg(pParse, "vector index for tables without ROWID and composite primary key are not supported"); - return SQLITE_ERROR; - } - // schema is locked while db is initializing and we need to just proceed here if( db->init.busy == 1 ){ goto succeed; } - rc = initVectorIndexMetaTable(db); + rc = initVectorIndexMetaTable(db, zDbSName); if( rc != SQLITE_OK ){ return rc; } @@ -212169,12 +212222,20 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { if( rc != SQLITE_OK ){ return rc; } - rc = diskAnnCreateIndex(db, pIdx->zName, &idxKey, &idxParams); + if( vectorIdxKeyGet(pTable, &idxKey, &pzErrMsg) != 0 ){ + sqlite3ErrorMsg(pParse, "failed to detect underlying table key: %s", pzErrMsg); + return SQLITE_ERROR; + } + if( idxKey.nKeyColumns != 1 ){ + sqlite3ErrorMsg(pParse, "vector index for tables without ROWID and composite primary key are not supported"); + return SQLITE_ERROR; + } + rc = diskAnnCreateIndex(db, zDbSName, pIdx->zName, &idxKey, &idxParams); if( rc != SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to initialize diskann vector index"); return rc; } - rc = insertIndexParameters(db, pIdx->zName, &idxParams); + rc = insertIndexParameters(db, zDbSName, pIdx->zName, &idxParams); if( rc != SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to update global metadata table"); return rc; @@ -212186,7 +212247,7 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { return SQLITE_OK; } -int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows *pRows, char **pzErrMsg) { +int vectorIndexSearch(sqlite3 *db, const char* zDbSName, int argc, sqlite3_value **argv, VectorOutRows *pRows, char **pzErrMsg) { int type, dims, k, rc; const char *zIdxName; const char *zErrMsg; @@ -212197,6 +212258,8 @@ int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows VectorIdxParams idxParams; vectorIdxParamsInit(&idxParams, NULL, 0); + assert( zDbSName != NULL ); + if( argc != 3 ){ *pzErrMsg = sqlite3_mprintf("vector search must have exactly 3 parameters"); rc = SQLITE_ERROR; @@ -212242,22 +212305,22 @@ int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows rc = SQLITE_ERROR; goto out; } - pIndex = sqlite3FindIndex(db, zIdxName, db->aDb[0].zDbSName); + pIndex = sqlite3FindIndex(db, zIdxName, zDbSName); if( pIndex == NULL ){ *pzErrMsg = sqlite3_mprintf("vector index not found"); rc = SQLITE_ERROR; goto out; } + rc = diskAnnOpenIndex(db, zDbSName, zIdxName, &idxParams, &pDiskAnn); + if( rc != SQLITE_OK ){ + *pzErrMsg = sqlite3_mprintf("failed to open diskann index"); + goto out; + } if( vectorIdxKeyGet(pIndex->pTable, &pKey, &zErrMsg) != 0 ){ *pzErrMsg = sqlite3_mprintf("failed to extract table key: %s", zErrMsg); rc = SQLITE_ERROR; goto out; } - rc = diskAnnOpenIndex(db, zIdxName, &idxParams, &pDiskAnn); - if( rc != SQLITE_OK ){ - *pzErrMsg = sqlite3_mprintf("failed to open diskann index"); - goto out; - } rc = diskAnnSearch(pDiskAnn, pVector, k, &pKey, pRows, pzErrMsg); out: if( pDiskAnn != NULL ){ @@ -212303,14 +212366,17 @@ int vectorIndexDelete( int vectorIndexCursorInit( sqlite3 *db, - VectorIdxCursor **ppCursor, - const char *zIndexName + const char *zDbSName, + const char *zIndexName, + VectorIdxCursor **ppCursor ){ int rc; VectorIdxCursor* pCursor; VectorIdxParams params; vectorIdxParamsInit(¶ms, NULL, 0); + assert( zDbSName != NULL ); + if( vectorIndexGetParameters(db, zIndexName, ¶ms) != 0 ){ return SQLITE_ERROR; } @@ -212318,7 +212384,7 @@ int vectorIndexCursorInit( if( pCursor == 0 ){ return SQLITE_NOMEM_BKPT; } - rc = diskAnnOpenIndex(db, zIndexName, ¶ms, &pCursor->pIndex); + rc = diskAnnOpenIndex(db, zDbSName, zIndexName, ¶ms, &pCursor->pIndex); if( rc != SQLITE_OK ){ sqlite3DbFree(db, pCursor); return rc; @@ -212370,8 +212436,9 @@ void vectorIndexCursorClose(sqlite3 *db, VectorIdxCursor *pCursor){ typedef struct vectorVtab vectorVtab; struct vectorVtab { - sqlite3_vtab base; /* Base class - must be first */ - sqlite3 *db; /* Database connection */ + sqlite3_vtab base; /* Base class - must be first */ + sqlite3 *db; /* Database connection */ + char* zDbSName; /* Database schema name */ }; typedef struct vectorVtab_cursor vectorVtab_cursor; @@ -212394,26 +212461,36 @@ static int vectorVtabConnect( sqlite3_vtab **ppVtab, char **pzErr ){ - vectorVtab *pVtab; + char *zDbSName = NULL; + vectorVtab *pVtab = NULL; int rc; /* + * name of the database ignored by SQLite - so we don't need to provide any schema prefix here * hidden column are parameters of table-valued function (see https://www.sqlite.org/vtab.html#table_valued_functions) */ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(idx hidden, vector hidden, k hidden, id);"); if( rc != SQLITE_OK ){ return rc; } - pVtab = sqlite3_malloc( sizeof(*pVtab) ); + pVtab = sqlite3_malloc( sizeof(vectorVtab) ); if( pVtab == NULL ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; + } + zDbSName = sqlite3DbStrDup(db, argv[1]); // argv[1] is the database schema name by spec (see https://www.sqlite.org/vtab.html#the_xcreate_method) + if( zDbSName == NULL ){ + sqlite3_free(pVtab); + return SQLITE_NOMEM_BKPT; } memset(pVtab, 0, sizeof(*pVtab)); pVtab->db = db; + pVtab->zDbSName = zDbSName; *ppVtab = (sqlite3_vtab*)pVtab; return SQLITE_OK; } static int vectorVtabDisconnect(sqlite3_vtab *pVtab){ + vectorVtab *pVTab = (vectorVtab*)pVtab; + sqlite3DbFree(pVTab->db, pVTab->zDbSName); sqlite3_free(pVtab); return SQLITE_OK; } @@ -212480,7 +212557,7 @@ static int vectorVtabFilter( pCur->rows.aIntValues = NULL; pCur->rows.ppValues = NULL; - if( vectorIndexSearch(pVTab->db, argc, argv, &pCur->rows, &pVTab->base.zErrMsg) != 0 ){ + if( vectorIndexSearch(pVTab->db, pVTab->zDbSName, argc, argv, &pCur->rows, &pVTab->base.zErrMsg) != 0 ){ return SQLITE_ERROR; } diff --git a/libsql-ffi/bundled/src/sqlite3.c b/libsql-ffi/bundled/src/sqlite3.c index 46033982df..d5a72ca3c3 100644 --- a/libsql-ffi/bundled/src/sqlite3.c +++ b/libsql-ffi/bundled/src/sqlite3.c @@ -19000,6 +19000,7 @@ struct KeyInfo { * vector indices as they operate with names rather than with page numbers */ char *zIndexName; /* Name of the index (might be NULL) */ + char *zDbSName; /* Name of the database schema (might be NULL) */ u32 nRef; /* Number of references to this KeyInfo object */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nKeyField; /* Number of key columns in the index */ @@ -84921,7 +84922,7 @@ typedef struct BlobSpot BlobSpot; */ struct DiskAnnIndex { sqlite3 *db; /* Database connection */ - char *zDb; /* Database name */ + char *zDbSName; /* Database name */ char *zName; /* Index name */ char *zShadow; /* Shadow table name */ int nFormatVersion; /* DiskAnn format version */ @@ -85112,10 +85113,10 @@ int vectorOutRowsPut(VectorOutRows *, int, int, const u64 *, sqlite3_value *); void vectorOutRowsGet(sqlite3_context *, const VectorOutRows *, int, int); void vectorOutRowsFree(sqlite3 *, VectorOutRows *); -int diskAnnCreateIndex(sqlite3 *, const char *, const VectorIdxKey *, VectorIdxParams *); -int diskAnnClearIndex(sqlite3 *, const char *); -int diskAnnDropIndex(sqlite3 *, const char *); -int diskAnnOpenIndex(sqlite3 *, const char *, const VectorIdxParams *, DiskAnnIndex **); +int diskAnnCreateIndex(sqlite3 *, const char *, const char *, const VectorIdxKey *, VectorIdxParams *); +int diskAnnClearIndex(sqlite3 *, const char *, const char *); +int diskAnnDropIndex(sqlite3 *, const char *, const char *); +int diskAnnOpenIndex(sqlite3 *, const char *, const char *, const VectorIdxParams *, DiskAnnIndex **); void diskAnnCloseIndex(DiskAnnIndex *); int diskAnnInsert(const DiskAnnIndex *, const VectorInRow *, char **); int diskAnnDelete(const DiskAnnIndex *, const VectorInRow *, char **); @@ -85129,14 +85130,14 @@ typedef struct VectorIdxCursor VectorIdxCursor; int vectorIdxParseColumnType(const char *, int *, int *, const char **); -int vectorIndexCreate(Parse*, Index*, const IdList*); -int vectorIndexClear(sqlite3 *, const char *); -int vectorIndexDrop(sqlite3 *, const char *); -int vectorIndexCursorInit(sqlite3 *, VectorIdxCursor **, const char *); +int vectorIndexCreate(Parse*, Index*, const char *, const IdList*); +int vectorIndexClear(sqlite3 *, const char *, const char *); +int vectorIndexDrop(sqlite3 *, const char *, const char *); +int vectorIndexCursorInit(sqlite3 *, const char *, const char *, VectorIdxCursor **); void vectorIndexCursorClose(sqlite3 *, VectorIdxCursor *); int vectorIndexInsert(VectorIdxCursor *, const UnpackedRecord *, char **); int vectorIndexDelete(VectorIdxCursor *, const UnpackedRecord *, char **); -int vectorIndexSearch(sqlite3 *, int, sqlite3_value **, VectorOutRows *, char **); +int vectorIndexSearch(sqlite3 *, const char *, int, sqlite3_value **, VectorOutRows *, char **); #if 0 } /* end of the 'extern "C"' block */ @@ -97586,13 +97587,14 @@ case OP_OpenVectorIdx: { }else if( pOp->p4type==P4_INT32 ){ nField = pOp->p4.i; } + assert( pKeyInfo->zDbSName != NULL ); if( pOp->p5 == OPFLAG_FORDELETE ){ - rc = vectorIndexClear(db, pKeyInfo->zIndexName); + rc = vectorIndexClear(db, pKeyInfo->zDbSName, pKeyInfo->zIndexName); if( rc ){ goto abort_due_to_error; } } - rc = vectorIndexCursorInit(db, &cursor, pKeyInfo->zIndexName); + rc = vectorIndexCursorInit(db, pKeyInfo->zDbSName, pKeyInfo->zIndexName, &cursor); if( rc ) { goto abort_due_to_error; } @@ -125112,6 +125114,7 @@ static void destroyTable(Parse *pParse, Table *pTab){ Pgno iTab = pTab->tnum; Pgno iDestroyed = 0; Index *pIdx; + int iDb; #ifndef SQLITE_OMIT_VECTOR /* @@ -125125,9 +125128,12 @@ static void destroyTable(Parse *pParse, Table *pTab){ * 3. Delete index during the parsing stage (implemented variant) - it's hacky * and bit dirty but seems to me as pretty safe and easy way to delete index */ + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( IsVectorIndex(pIdx) ){ - vectorIndexDrop(pParse->db, pIdx->zName); + assert( 0 <= iDb && iDb < pParse->db->nDb ); + vectorIndexDrop(pParse->db, pParse->db->aDb[iDb].zDbSName, pIdx->zName); } } #endif @@ -126094,7 +126100,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( #ifndef SQLITE_OMIT_VECTOR - if( vectorIndexCreate(pParse, pIndex, pUsing) != SQLITE_OK ) { + if( vectorIndexCreate(pParse, pIndex, db->aDb[iDb].zDbSName, pUsing) != SQLITE_OK ) { goto exit_create_index; } idxType = pIndex->idxType; // vectorIndexCreate can update idxType to 4 (VECTOR INDEX) @@ -126451,6 +126457,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists "or PRIMARY KEY constraint cannot be dropped", 0); goto exit_drop_index; } + iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_VECTOR /* * There are several places to delete vector index: @@ -126464,10 +126471,9 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists * and bit dirty but seems to me as pretty safe and easy way to delete index */ if( IsVectorIndex(pIndex) ){ - vectorIndexDrop(pParse->db, pIndex->zName); + vectorIndexDrop(pParse->db, pParse->db->aDb[iDb].zDbSName, pIndex->zName); } #endif - iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_INDEX; @@ -127409,7 +127415,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ ** when it has finished using it. */ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ - int i; + int i, iDb; int nCol = pIdx->nColumn; int nKey = pIdx->nKeyCol; KeyInfo *pKey; @@ -127420,8 +127426,12 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); } if( pKey ){ + iDb = sqlite3SchemaToIndex(pParse->db, pIdx->pSchema); assert( sqlite3KeyInfoIsWriteable(pKey) ); pKey->zIndexName = sqlite3DbStrDup(pParse->db, pIdx->zName); + if( 0 <= iDb && iDb < pParse->db->nDb ){ + pKey->zDbSName = sqlite3DbStrDup(pParse->db, pParse->db->aDb[iDb].zDbSName); + } for(i=0; iazColl[i]; pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 : @@ -144878,6 +144888,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ p->db = db; p->nRef = 1; p->zIndexName = NULL; + p->zDbSName = NULL; memset(&p[1], 0, nExtra); }else{ return (KeyInfo*)sqlite3OomFault(db); @@ -144897,6 +144908,9 @@ SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){ if( p->zIndexName != NULL ){ sqlite3DbFree(p->db, p->zIndexName); } + if( p->zDbSName != NULL ){ + sqlite3DbFree(p->db, p->zDbSName); + } sqlite3DbNNFreeNN(p->db, p); } } @@ -209542,7 +209556,7 @@ int blobSpotCreate(const DiskAnnIndex *pIndex, BlobSpot **ppBlobSpot, u64 nRowid } // open blob in the end so we don't need to close it in error case - rc = sqlite3_blob_open(pIndex->db, pIndex->zDb, pIndex->zShadow, "data", nRowid, isWritable, &pBlobSpot->pBlob); + rc = sqlite3_blob_open(pIndex->db, pIndex->zDbSName, pIndex->zShadow, "data", nRowid, isWritable, &pBlobSpot->pBlob); rc = blobSpotConvertRc(pIndex, rc); if( rc != SQLITE_OK ){ goto out; @@ -209589,7 +209603,7 @@ int blobSpotReload(const DiskAnnIndex *pIndex, BlobSpot *pBlobSpot, u64 nRowid, pBlobSpot->isAborted = 0; pBlobSpot->nRowid = nRowid; - rc = sqlite3_blob_open(pIndex->db, pIndex->zDb, pIndex->zShadow, "data", nRowid, pBlobSpot->isWritable, &pBlobSpot->pBlob); + rc = sqlite3_blob_open(pIndex->db, pIndex->zDbSName, pIndex->zShadow, "data", nRowid, pBlobSpot->isWritable, &pBlobSpot->pBlob); rc = blobSpotConvertRc(pIndex, rc); if( rc != SQLITE_OK ){ goto abort; @@ -209786,6 +209800,7 @@ void nodeBinDebug(const DiskAnnIndex *pIndex, const BlobSpot *pBlobSpot) { int diskAnnCreateIndex( sqlite3 *db, + const char *zDbSName, const char *zIdxName, const VectorIdxKey *pKey, VectorIdxParams *pParams @@ -209830,7 +209845,8 @@ int diskAnnCreateIndex( } zSql = sqlite3MPrintf( db, - "CREATE TABLE IF NOT EXISTS %s_shadow (%s, data BLOB, PRIMARY KEY (%s))", + "CREATE TABLE IF NOT EXISTS \"%w\".%s_shadow (%s, data BLOB, PRIMARY KEY (%s))", + zDbSName, zIdxName, columnSqlDefs, columnSqlNames @@ -209840,15 +209856,15 @@ int diskAnnCreateIndex( return rc; } -int diskAnnClearIndex(sqlite3 *db, const char *zIdxName) { - char *zSql = sqlite3MPrintf(db, "DELETE FROM %s_shadow", zIdxName); +int diskAnnClearIndex(sqlite3 *db, const char *zDbSName, const char *zIdxName) { + char *zSql = sqlite3MPrintf(db, "DELETE FROM \"%w\".%s_shadow", zDbSName, zIdxName); int rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3DbFree(db, zSql); return rc; } -int diskAnnDropIndex(sqlite3 *db, const char *zIdxName){ - char *zSql = sqlite3MPrintf(db, "DROP TABLE %s_shadow", zIdxName); +int diskAnnDropIndex(sqlite3 *db, const char *zDbSName, const char *zIdxName){ + char *zSql = sqlite3MPrintf(db, "DROP TABLE \"%w\".%s_shadow", zDbSName, zIdxName); int rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3DbFree(db, zSql); return rc; @@ -209866,8 +209882,8 @@ static int diskAnnSelectRandomShadowRow(const DiskAnnIndex *pIndex, u64 *pRowid) zSql = sqlite3MPrintf( pIndex->db, - "SELECT rowid FROM %s LIMIT 1 OFFSET ABS(RANDOM()) %% MAX((SELECT COUNT(*) FROM %s), 1)", - pIndex->zShadow, pIndex->zShadow + "SELECT rowid FROM \"%w\".%s LIMIT 1 OFFSET ABS(RANDOM()) %% MAX((SELECT COUNT(*) FROM %s), 1)", + pIndex->zDbSName, pIndex->zShadow, pIndex->zShadow ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; @@ -209917,7 +209933,11 @@ static int diskAnnGetShadowRowid(const DiskAnnIndex *pIndex, const VectorInRow * rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "SELECT rowid FROM %s WHERE (%s) = (%s)", pIndex->zShadow, columnSqlNames, columnSqlPlaceholders); + zSql = sqlite3MPrintf( + pIndex->db, + "SELECT rowid FROM \"%w\".%s WHERE (%s) = (%s)", + pIndex->zDbSName, pIndex->zShadow, columnSqlNames, columnSqlPlaceholders + ); if( zSql == NULL ){ rc = SQLITE_NOMEM; goto out; @@ -209966,7 +209986,11 @@ static int diskAnnGetShadowRowKeys(const DiskAnnIndex *pIndex, u64 nRowid, const rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "SELECT %s FROM %s WHERE rowid = ?", columnSqlNames, pIndex->zShadow); + zSql = sqlite3MPrintf( + pIndex->db, + "SELECT %s FROM \"%w\".%s WHERE rowid = ?", + columnSqlNames, pIndex->zDbSName, pIndex->zShadow + ); if( zSql == NULL ){ rc = SQLITE_NOMEM; goto out; @@ -210016,7 +210040,11 @@ static int diskAnnInsertShadowRow(const DiskAnnIndex *pIndex, const VectorInRow rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "INSERT INTO %s VALUES (%s, ?) RETURNING rowid", pIndex->zShadow, columnSqlPlaceholders); + zSql = sqlite3MPrintf( + pIndex->db, + "INSERT INTO \"%w\".%s VALUES (%s, ?) RETURNING rowid", + pIndex->zDbSName, pIndex->zShadow, columnSqlPlaceholders + ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; goto out; @@ -210063,7 +210091,11 @@ static int diskAnnInsertShadowRow(const DiskAnnIndex *pIndex, const VectorInRow static int diskAnnDeleteShadowRow(const DiskAnnIndex *pIndex, i64 nRowid){ int rc; sqlite3_stmt *pStmt = NULL; - char *zSql = sqlite3MPrintf(pIndex->db, "DELETE FROM %s WHERE rowid = ?", pIndex->zShadow); + char *zSql = sqlite3MPrintf( + pIndex->db, + "DELETE FROM \"%w\".%s WHERE rowid = ?", + pIndex->zDbSName, pIndex->zShadow + ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; goto out; @@ -210586,9 +210618,6 @@ int diskAnnSearch( } rc = SQLITE_OK; out: - if( rc != SQLITE_OK ){ - vectorOutRowsFree(pIndex->db, pRows); - } diskAnnSearchCtxDeinit(&ctx); return rc; } @@ -210782,6 +210811,7 @@ int diskAnnDelete( // open index with zIdxName and pParams serialized binary parameters and set result to the ppIndex int diskAnnOpenIndex( sqlite3 *db, /* Database connection */ + const char *zDbSName, /* Database schema name */ const char *zIdxName, /* Index name */ const VectorIdxParams *pParams, /* Index parameters */ DiskAnnIndex **ppIndex /* OUT: Index */ @@ -210792,9 +210822,13 @@ int diskAnnOpenIndex( return SQLITE_NOMEM; } pIndex->db = db; - pIndex->zDb = sqlite3DbStrDup(db, db->aDb[0].zDbSName); + pIndex->zDbSName = sqlite3DbStrDup(db, zDbSName); pIndex->zName = sqlite3DbStrDup(db, zIdxName); pIndex->zShadow = sqlite3MPrintf(db, "%s_shadow", zIdxName); + if( pIndex->zShadow == NULL ){ + diskAnnCloseIndex(pIndex); + return SQLITE_NOMEM_BKPT; + } pIndex->nFormatVersion = vectorIdxParamsGetU64(pParams, VECTOR_FORMAT_PARAM_ID); pIndex->nDistanceFunc = vectorIdxParamsGetU64(pParams, VECTOR_METRIC_TYPE_PARAM_ID); pIndex->nBlockSize = vectorIdxParamsGetU64(pParams, VECTOR_BLOCK_SIZE_PARAM_ID) << DISKANN_BLOCK_SIZE_SHIFT; @@ -210803,14 +210837,13 @@ int diskAnnOpenIndex( pIndex->pruningAlpha = vectorIdxParamsGetF64(pParams, VECTOR_PRUNING_ALPHA_PARAM_ID); pIndex->insertL = vectorIdxParamsGetU64(pParams, VECTOR_INSERT_L_PARAM_ID); pIndex->searchL = vectorIdxParamsGetU64(pParams, VECTOR_SEARCH_L_PARAM_ID); - if( pIndex->zShadow == NULL || - pIndex->nDistanceFunc == 0 || + if( pIndex->nDistanceFunc == 0 || pIndex->nBlockSize == 0 || pIndex->nNodeVectorType == 0 || pIndex->nVectorDims == 0 ){ diskAnnCloseIndex(pIndex); - return SQLITE_NOMEM; + return SQLITE_ERROR; } if( pIndex->pruningAlpha == 0 ){ pIndex->pruningAlpha = VECTOR_PRUNING_ALPHA_DEFAULT; @@ -210831,8 +210864,8 @@ int diskAnnOpenIndex( } void diskAnnCloseIndex(DiskAnnIndex *pIndex){ - if( pIndex->zDb ){ - sqlite3DbFree(pIndex->db, pIndex->zDb); + if( pIndex->zDbSName ){ + sqlite3DbFree(pIndex->db, pIndex->zDbSName); } if( pIndex->zName ){ sqlite3DbFree(pIndex->db, pIndex->zName); @@ -211921,15 +211954,34 @@ int vectorIdxParseColumnType(const char *zType, int *pType, int *pDims, const ch return -1; } -int initVectorIndexMetaTable(sqlite3* db) { - static const char *zSql = "CREATE TABLE IF NOT EXISTS " VECTOR_INDEX_GLOBAL_META_TABLE " ( name TEXT PRIMARY KEY, metadata BLOB ) WITHOUT ROWID;"; - return sqlite3_exec(db, zSql, 0, 0, 0); +int initVectorIndexMetaTable(sqlite3* db, const char *zDbSName) { + int rc; + static const char *zSqlTemplate = "CREATE TABLE IF NOT EXISTS \"%w\"." VECTOR_INDEX_GLOBAL_META_TABLE " ( name TEXT PRIMARY KEY, metadata BLOB ) WITHOUT ROWID;"; + char* zSql; + + assert( zDbSName != NULL ); + + zSql = sqlite3_mprintf(zSqlTemplate, zDbSName); + if( zSql == NULL ){ + return SQLITE_NOMEM_BKPT; + } + rc = sqlite3_exec(db, zSql, 0, 0, 0); + sqlite3_free(zSql); + return rc; } -int insertIndexParameters(sqlite3* db, const char *zName, const VectorIdxParams *pParameters) { - static const char *zSql = "INSERT INTO " VECTOR_INDEX_GLOBAL_META_TABLE " VALUES (?, ?)"; - sqlite3_stmt* pStatement = NULL; +int insertIndexParameters(sqlite3* db, const char *zDbSName, const char *zName, const VectorIdxParams *pParameters) { int rc = SQLITE_ERROR; + static const char *zSqlTemplate = "INSERT INTO \"%w\"." VECTOR_INDEX_GLOBAL_META_TABLE " VALUES (?, ?)"; + sqlite3_stmt* pStatement = NULL; + char *zSql; + + assert( zDbSName != NULL ); + + zSql = sqlite3_mprintf(zSqlTemplate, zDbSName); + if( zSql == NULL ){ + return SQLITE_NOMEM_BKPT; + } rc = sqlite3_prepare_v2(db, zSql, -1, &pStatement, 0); if( rc != SQLITE_OK ){ @@ -211950,6 +212002,9 @@ int insertIndexParameters(sqlite3* db, const char *zName, const VectorIdxParams rc = SQLITE_OK; } clear_and_exit: + if( zSql != NULL ){ + sqlite3_free(zSql); + } if( pStatement != NULL ){ sqlite3_finalize(pStatement); } @@ -212043,24 +212098,31 @@ int vectorIndexGetParameters( } -int vectorIndexDrop(sqlite3 *db, const char *zIdxName) { +int vectorIndexDrop(sqlite3 *db, const char *zDbSName, const char *zIdxName) { // we want to try delete all traces of index on every attempt // this is done to prevent unrecoverable situations where index were dropped but index parameters deletion failed and second attempt will fail on first step - int rcIdx = diskAnnDropIndex(db, zIdxName); - int rcParams = removeIndexParameters(db, zIdxName); + int rcIdx, rcParams; + + assert( zDbSName != NULL ); + + rcIdx = diskAnnDropIndex(db, zDbSName, zIdxName); + rcParams = removeIndexParameters(db, zIdxName); return rcIdx != SQLITE_OK ? rcIdx : rcParams; } -int vectorIndexClear(sqlite3 *db, const char *zIdxName) { - return diskAnnClearIndex(db, zIdxName); +int vectorIndexClear(sqlite3 *db, const char *zDbSName, const char *zIdxName) { + assert( zDbSName != NULL ); + return diskAnnClearIndex(db, zDbSName, zIdxName); } -int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { +int vectorIndexCreate(Parse *pParse, Index *pIdx, const char *zDbSName, const IdList *pUsing) { int i, rc = SQLITE_OK; int dims, type; int hasLibsqlVectorIdxFn = 0, hasCollation = 0; const char *pzErrMsg; + assert( zDbSName != NULL ); + sqlite3 *db = pParse->db; Table *pTable = pIdx->pTable; struct ExprList_item *pListItem; @@ -212147,21 +212209,12 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { return SQLITE_ERROR; } - if( vectorIdxKeyGet(pTable, &idxKey, &pzErrMsg) != 0 ){ - sqlite3ErrorMsg(pParse, "failed to detect underlying table key: %s", pzErrMsg); - return SQLITE_ERROR; - } - if( idxKey.nKeyColumns != 1 ){ - sqlite3ErrorMsg(pParse, "vector index for tables without ROWID and composite primary key are not supported"); - return SQLITE_ERROR; - } - // schema is locked while db is initializing and we need to just proceed here if( db->init.busy == 1 ){ goto succeed; } - rc = initVectorIndexMetaTable(db); + rc = initVectorIndexMetaTable(db, zDbSName); if( rc != SQLITE_OK ){ return rc; } @@ -212169,12 +212222,20 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { if( rc != SQLITE_OK ){ return rc; } - rc = diskAnnCreateIndex(db, pIdx->zName, &idxKey, &idxParams); + if( vectorIdxKeyGet(pTable, &idxKey, &pzErrMsg) != 0 ){ + sqlite3ErrorMsg(pParse, "failed to detect underlying table key: %s", pzErrMsg); + return SQLITE_ERROR; + } + if( idxKey.nKeyColumns != 1 ){ + sqlite3ErrorMsg(pParse, "vector index for tables without ROWID and composite primary key are not supported"); + return SQLITE_ERROR; + } + rc = diskAnnCreateIndex(db, zDbSName, pIdx->zName, &idxKey, &idxParams); if( rc != SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to initialize diskann vector index"); return rc; } - rc = insertIndexParameters(db, pIdx->zName, &idxParams); + rc = insertIndexParameters(db, zDbSName, pIdx->zName, &idxParams); if( rc != SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to update global metadata table"); return rc; @@ -212186,7 +212247,7 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { return SQLITE_OK; } -int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows *pRows, char **pzErrMsg) { +int vectorIndexSearch(sqlite3 *db, const char* zDbSName, int argc, sqlite3_value **argv, VectorOutRows *pRows, char **pzErrMsg) { int type, dims, k, rc; const char *zIdxName; const char *zErrMsg; @@ -212197,6 +212258,8 @@ int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows VectorIdxParams idxParams; vectorIdxParamsInit(&idxParams, NULL, 0); + assert( zDbSName != NULL ); + if( argc != 3 ){ *pzErrMsg = sqlite3_mprintf("vector search must have exactly 3 parameters"); rc = SQLITE_ERROR; @@ -212242,22 +212305,22 @@ int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows rc = SQLITE_ERROR; goto out; } - pIndex = sqlite3FindIndex(db, zIdxName, db->aDb[0].zDbSName); + pIndex = sqlite3FindIndex(db, zIdxName, zDbSName); if( pIndex == NULL ){ *pzErrMsg = sqlite3_mprintf("vector index not found"); rc = SQLITE_ERROR; goto out; } + rc = diskAnnOpenIndex(db, zDbSName, zIdxName, &idxParams, &pDiskAnn); + if( rc != SQLITE_OK ){ + *pzErrMsg = sqlite3_mprintf("failed to open diskann index"); + goto out; + } if( vectorIdxKeyGet(pIndex->pTable, &pKey, &zErrMsg) != 0 ){ *pzErrMsg = sqlite3_mprintf("failed to extract table key: %s", zErrMsg); rc = SQLITE_ERROR; goto out; } - rc = diskAnnOpenIndex(db, zIdxName, &idxParams, &pDiskAnn); - if( rc != SQLITE_OK ){ - *pzErrMsg = sqlite3_mprintf("failed to open diskann index"); - goto out; - } rc = diskAnnSearch(pDiskAnn, pVector, k, &pKey, pRows, pzErrMsg); out: if( pDiskAnn != NULL ){ @@ -212303,14 +212366,17 @@ int vectorIndexDelete( int vectorIndexCursorInit( sqlite3 *db, - VectorIdxCursor **ppCursor, - const char *zIndexName + const char *zDbSName, + const char *zIndexName, + VectorIdxCursor **ppCursor ){ int rc; VectorIdxCursor* pCursor; VectorIdxParams params; vectorIdxParamsInit(¶ms, NULL, 0); + assert( zDbSName != NULL ); + if( vectorIndexGetParameters(db, zIndexName, ¶ms) != 0 ){ return SQLITE_ERROR; } @@ -212318,7 +212384,7 @@ int vectorIndexCursorInit( if( pCursor == 0 ){ return SQLITE_NOMEM_BKPT; } - rc = diskAnnOpenIndex(db, zIndexName, ¶ms, &pCursor->pIndex); + rc = diskAnnOpenIndex(db, zDbSName, zIndexName, ¶ms, &pCursor->pIndex); if( rc != SQLITE_OK ){ sqlite3DbFree(db, pCursor); return rc; @@ -212370,8 +212436,9 @@ void vectorIndexCursorClose(sqlite3 *db, VectorIdxCursor *pCursor){ typedef struct vectorVtab vectorVtab; struct vectorVtab { - sqlite3_vtab base; /* Base class - must be first */ - sqlite3 *db; /* Database connection */ + sqlite3_vtab base; /* Base class - must be first */ + sqlite3 *db; /* Database connection */ + char* zDbSName; /* Database schema name */ }; typedef struct vectorVtab_cursor vectorVtab_cursor; @@ -212394,26 +212461,36 @@ static int vectorVtabConnect( sqlite3_vtab **ppVtab, char **pzErr ){ - vectorVtab *pVtab; + char *zDbSName = NULL; + vectorVtab *pVtab = NULL; int rc; /* + * name of the database ignored by SQLite - so we don't need to provide any schema prefix here * hidden column are parameters of table-valued function (see https://www.sqlite.org/vtab.html#table_valued_functions) */ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(idx hidden, vector hidden, k hidden, id);"); if( rc != SQLITE_OK ){ return rc; } - pVtab = sqlite3_malloc( sizeof(*pVtab) ); + pVtab = sqlite3_malloc( sizeof(vectorVtab) ); if( pVtab == NULL ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; + } + zDbSName = sqlite3DbStrDup(db, argv[1]); // argv[1] is the database schema name by spec (see https://www.sqlite.org/vtab.html#the_xcreate_method) + if( zDbSName == NULL ){ + sqlite3_free(pVtab); + return SQLITE_NOMEM_BKPT; } memset(pVtab, 0, sizeof(*pVtab)); pVtab->db = db; + pVtab->zDbSName = zDbSName; *ppVtab = (sqlite3_vtab*)pVtab; return SQLITE_OK; } static int vectorVtabDisconnect(sqlite3_vtab *pVtab){ + vectorVtab *pVTab = (vectorVtab*)pVtab; + sqlite3DbFree(pVTab->db, pVTab->zDbSName); sqlite3_free(pVtab); return SQLITE_OK; } @@ -212480,7 +212557,7 @@ static int vectorVtabFilter( pCur->rows.aIntValues = NULL; pCur->rows.ppValues = NULL; - if( vectorIndexSearch(pVTab->db, argc, argv, &pCur->rows, &pVTab->base.zErrMsg) != 0 ){ + if( vectorIndexSearch(pVTab->db, pVTab->zDbSName, argc, argv, &pCur->rows, &pVTab->base.zErrMsg) != 0 ){ return SQLITE_ERROR; } diff --git a/libsql-sqlite3/src/build.c b/libsql-sqlite3/src/build.c index be34979926..80a90663ce 100644 --- a/libsql-sqlite3/src/build.c +++ b/libsql-sqlite3/src/build.c @@ -3323,6 +3323,7 @@ static void destroyTable(Parse *pParse, Table *pTab){ Pgno iTab = pTab->tnum; Pgno iDestroyed = 0; Index *pIdx; + int iDb; #ifndef SQLITE_OMIT_VECTOR /* @@ -3336,9 +3337,12 @@ static void destroyTable(Parse *pParse, Table *pTab){ * 3. Delete index during the parsing stage (implemented variant) - it's hacky * and bit dirty but seems to me as pretty safe and easy way to delete index */ + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( IsVectorIndex(pIdx) ){ - vectorIndexDrop(pParse->db, pIdx->zName); + assert( 0 <= iDb && iDb < pParse->db->nDb ); + vectorIndexDrop(pParse->db, pParse->db->aDb[iDb].zDbSName, pIdx->zName); } } #endif @@ -4305,7 +4309,7 @@ void sqlite3CreateIndex( #ifndef SQLITE_OMIT_VECTOR - if( vectorIndexCreate(pParse, pIndex, pUsing) != SQLITE_OK ) { + if( vectorIndexCreate(pParse, pIndex, db->aDb[iDb].zDbSName, pUsing) != SQLITE_OK ) { goto exit_create_index; } idxType = pIndex->idxType; // vectorIndexCreate can update idxType to 4 (VECTOR INDEX) @@ -4662,6 +4666,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ "or PRIMARY KEY constraint cannot be dropped", 0); goto exit_drop_index; } + iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_VECTOR /* * There are several places to delete vector index: @@ -4675,10 +4680,9 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ * and bit dirty but seems to me as pretty safe and easy way to delete index */ if( IsVectorIndex(pIndex) ){ - vectorIndexDrop(pParse->db, pIndex->zName); + vectorIndexDrop(pParse->db, pParse->db->aDb[iDb].zDbSName, pIndex->zName); } #endif - iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_INDEX; @@ -5620,7 +5624,7 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ ** when it has finished using it. */ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ - int i; + int i, iDb; int nCol = pIdx->nColumn; int nKey = pIdx->nKeyCol; KeyInfo *pKey; @@ -5631,8 +5635,12 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); } if( pKey ){ + iDb = sqlite3SchemaToIndex(pParse->db, pIdx->pSchema); assert( sqlite3KeyInfoIsWriteable(pKey) ); pKey->zIndexName = sqlite3DbStrDup(pParse->db, pIdx->zName); + if( 0 <= iDb && iDb < pParse->db->nDb ){ + pKey->zDbSName = sqlite3DbStrDup(pParse->db, pParse->db->aDb[iDb].zDbSName); + } for(i=0; iazColl[i]; pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 : diff --git a/libsql-sqlite3/src/select.c b/libsql-sqlite3/src/select.c index 7dbb3fb3e3..1ff4165bf2 100644 --- a/libsql-sqlite3/src/select.c +++ b/libsql-sqlite3/src/select.c @@ -1513,6 +1513,7 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ p->db = db; p->nRef = 1; p->zIndexName = NULL; + p->zDbSName = NULL; memset(&p[1], 0, nExtra); }else{ return (KeyInfo*)sqlite3OomFault(db); @@ -1532,6 +1533,9 @@ void sqlite3KeyInfoUnref(KeyInfo *p){ if( p->zIndexName != NULL ){ sqlite3DbFree(p->db, p->zIndexName); } + if( p->zDbSName != NULL ){ + sqlite3DbFree(p->db, p->zDbSName); + } sqlite3DbNNFreeNN(p->db, p); } } diff --git a/libsql-sqlite3/src/sqliteInt.h b/libsql-sqlite3/src/sqliteInt.h index f1cb287b5b..40f9cc6e7a 100644 --- a/libsql-sqlite3/src/sqliteInt.h +++ b/libsql-sqlite3/src/sqliteInt.h @@ -2641,6 +2641,7 @@ struct KeyInfo { * vector indices as they operate with names rather than with page numbers */ char *zIndexName; /* Name of the index (might be NULL) */ + char *zDbSName; /* Name of the database schema (might be NULL) */ u32 nRef; /* Number of references to this KeyInfo object */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nKeyField; /* Number of key columns in the index */ diff --git a/libsql-sqlite3/src/vdbe.c b/libsql-sqlite3/src/vdbe.c index 51f813650c..e2884ad5db 100644 --- a/libsql-sqlite3/src/vdbe.c +++ b/libsql-sqlite3/src/vdbe.c @@ -4229,13 +4229,14 @@ case OP_OpenVectorIdx: { }else if( pOp->p4type==P4_INT32 ){ nField = pOp->p4.i; } + assert( pKeyInfo->zDbSName != NULL ); if( pOp->p5 == OPFLAG_FORDELETE ){ - rc = vectorIndexClear(db, pKeyInfo->zIndexName); + rc = vectorIndexClear(db, pKeyInfo->zDbSName, pKeyInfo->zIndexName); if( rc ){ goto abort_due_to_error; } } - rc = vectorIndexCursorInit(db, &cursor, pKeyInfo->zIndexName); + rc = vectorIndexCursorInit(db, pKeyInfo->zDbSName, pKeyInfo->zIndexName, &cursor); if( rc ) { goto abort_due_to_error; } diff --git a/libsql-sqlite3/src/vectorIndex.c b/libsql-sqlite3/src/vectorIndex.c index 86b8c62dd2..2fc7657e84 100644 --- a/libsql-sqlite3/src/vectorIndex.c +++ b/libsql-sqlite3/src/vectorIndex.c @@ -212,7 +212,7 @@ int vectorInRowAlloc(sqlite3 *db, const UnpackedRecord *pRecord, VectorInRow *pV if( pVectorInRow->nKeys <= 0 ){ rc = SQLITE_ERROR; - goto out; + goto out; } if( sqlite3_value_type(pVectorValue)==SQLITE_NULL ){ @@ -233,7 +233,7 @@ int vectorInRowAlloc(sqlite3 *db, const UnpackedRecord *pRecord, VectorInRow *pV if( sqlite3_value_type(pVectorValue) == SQLITE_BLOB ){ vectorInitFromBlob(pVectorInRow->pVector, sqlite3_value_blob(pVectorValue), sqlite3_value_bytes(pVectorValue)); - } else if( sqlite3_value_type(pVectorValue) == SQLITE_TEXT ){ + } else if( sqlite3_value_type(pVectorValue) == SQLITE_TEXT ){ // users can put strings (e.g. '[1,2,3]') in the table and we should process them correctly if( vectorParse(pVectorValue, pVectorInRow->pVector, pzErrMsg) != 0 ){ rc = SQLITE_ERROR; @@ -321,10 +321,10 @@ void vectorOutRowsGet(sqlite3_context *context, const VectorOutRows *pRows, int void vectorOutRowsFree(sqlite3 *db, VectorOutRows *pRows) { int i; - + // both aIntValues and ppValues can be null if processing failing in the middle and we didn't created VectorOutRows assert( pRows->aIntValues == NULL || pRows->ppValues == NULL ); - + if( pRows->aIntValues != NULL ){ sqlite3DbFree(db, pRows->aIntValues); }else if( pRows->ppValues != NULL ){ @@ -337,8 +337,8 @@ void vectorOutRowsFree(sqlite3 *db, VectorOutRows *pRows) { } } -/* - * Internal type to represent VECTOR_COLUMN_TYPES array +/* + * Internal type to represent VECTOR_COLUMN_TYPES array * We support both FLOATNN and FNN_BLOB type names for the following reasons: * 1. FLOATNN is easy to type for humans and generally OK to use for column type names * 2. FNN_BLOB is aligned with SQLite affinity rules and can be used in cases where compatibility with type affinity rules is important @@ -349,15 +349,15 @@ struct VectorColumnType { int nBits; }; -static struct VectorColumnType VECTOR_COLUMN_TYPES[] = { - { "FLOAT32", 32 }, - { "FLOAT64", 64 }, - { "F32_BLOB", 32 }, - { "F64_BLOB", 64 } +static struct VectorColumnType VECTOR_COLUMN_TYPES[] = { + { "FLOAT32", 32 }, + { "FLOAT64", 64 }, + { "F32_BLOB", 32 }, + { "F64_BLOB", 64 } }; /* - * Internal type to represent VECTOR_PARAM_NAMES array with recognized parameters for index creation + * Internal type to represent VECTOR_PARAM_NAMES array with recognized parameters for index creation * For example, libsql_vector_idx(embedding, 'type=diskann', 'metric=cosine') */ struct VectorParamName { @@ -368,7 +368,7 @@ struct VectorParamName { u64 value; }; -static struct VectorParamName VECTOR_PARAM_NAMES[] = { +static struct VectorParamName VECTOR_PARAM_NAMES[] = { { "type", VECTOR_INDEX_TYPE_PARAM_ID, 0, "diskann", VECTOR_INDEX_TYPE_DISKANN }, { "metric", VECTOR_METRIC_TYPE_PARAM_ID, 0, "cosine", VECTOR_METRIC_TYPE_COS }, { "metric", VECTOR_METRIC_TYPE_PARAM_ID, 0, "l2", VECTOR_METRIC_TYPE_L2 }, @@ -550,15 +550,34 @@ int vectorIdxParseColumnType(const char *zType, int *pType, int *pDims, const ch return -1; } -int initVectorIndexMetaTable(sqlite3* db) { - static const char *zSql = "CREATE TABLE IF NOT EXISTS " VECTOR_INDEX_GLOBAL_META_TABLE " ( name TEXT PRIMARY KEY, metadata BLOB ) WITHOUT ROWID;"; - return sqlite3_exec(db, zSql, 0, 0, 0); +int initVectorIndexMetaTable(sqlite3* db, const char *zDbSName) { + int rc; + static const char *zSqlTemplate = "CREATE TABLE IF NOT EXISTS \"%w\"." VECTOR_INDEX_GLOBAL_META_TABLE " ( name TEXT PRIMARY KEY, metadata BLOB ) WITHOUT ROWID;"; + char* zSql; + + assert( zDbSName != NULL ); + + zSql = sqlite3_mprintf(zSqlTemplate, zDbSName); + if( zSql == NULL ){ + return SQLITE_NOMEM_BKPT; + } + rc = sqlite3_exec(db, zSql, 0, 0, 0); + sqlite3_free(zSql); + return rc; } -int insertIndexParameters(sqlite3* db, const char *zName, const VectorIdxParams *pParameters) { - static const char *zSql = "INSERT INTO " VECTOR_INDEX_GLOBAL_META_TABLE " VALUES (?, ?)"; - sqlite3_stmt* pStatement = NULL; +int insertIndexParameters(sqlite3* db, const char *zDbSName, const char *zName, const VectorIdxParams *pParameters) { int rc = SQLITE_ERROR; + static const char *zSqlTemplate = "INSERT INTO \"%w\"." VECTOR_INDEX_GLOBAL_META_TABLE " VALUES (?, ?)"; + sqlite3_stmt* pStatement = NULL; + char *zSql; + + assert( zDbSName != NULL ); + + zSql = sqlite3_mprintf(zSqlTemplate, zDbSName); + if( zSql == NULL ){ + return SQLITE_NOMEM_BKPT; + } rc = sqlite3_prepare_v2(db, zSql, -1, &pStatement, 0); if( rc != SQLITE_OK ){ @@ -579,6 +598,9 @@ int insertIndexParameters(sqlite3* db, const char *zName, const VectorIdxParams rc = SQLITE_OK; } clear_and_exit: + if( zSql != NULL ){ + sqlite3_free(zSql); + } if( pStatement != NULL ){ sqlite3_finalize(pStatement); } @@ -672,24 +694,31 @@ int vectorIndexGetParameters( } -int vectorIndexDrop(sqlite3 *db, const char *zIdxName) { +int vectorIndexDrop(sqlite3 *db, const char *zDbSName, const char *zIdxName) { // we want to try delete all traces of index on every attempt // this is done to prevent unrecoverable situations where index were dropped but index parameters deletion failed and second attempt will fail on first step - int rcIdx = diskAnnDropIndex(db, zIdxName); - int rcParams = removeIndexParameters(db, zIdxName); + int rcIdx, rcParams; + + assert( zDbSName != NULL ); + + rcIdx = diskAnnDropIndex(db, zDbSName, zIdxName); + rcParams = removeIndexParameters(db, zIdxName); return rcIdx != SQLITE_OK ? rcIdx : rcParams; } -int vectorIndexClear(sqlite3 *db, const char *zIdxName) { - return diskAnnClearIndex(db, zIdxName); +int vectorIndexClear(sqlite3 *db, const char *zDbSName, const char *zIdxName) { + assert( zDbSName != NULL ); + return diskAnnClearIndex(db, zDbSName, zIdxName); } -int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { +int vectorIndexCreate(Parse *pParse, Index *pIdx, const char *zDbSName, const IdList *pUsing) { int i, rc = SQLITE_OK; int dims, type; int hasLibsqlVectorIdxFn = 0, hasCollation = 0; const char *pzErrMsg; + assert( zDbSName != NULL ); + sqlite3 *db = pParse->db; Table *pTable = pIdx->pTable; struct ExprList_item *pListItem; @@ -776,21 +805,12 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { return SQLITE_ERROR; } - if( vectorIdxKeyGet(pTable, &idxKey, &pzErrMsg) != 0 ){ - sqlite3ErrorMsg(pParse, "failed to detect underlying table key: %s", pzErrMsg); - return SQLITE_ERROR; - } - if( idxKey.nKeyColumns != 1 ){ - sqlite3ErrorMsg(pParse, "vector index for tables without ROWID and composite primary key are not supported"); - return SQLITE_ERROR; - } - // schema is locked while db is initializing and we need to just proceed here if( db->init.busy == 1 ){ goto succeed; } - rc = initVectorIndexMetaTable(db); + rc = initVectorIndexMetaTable(db, zDbSName); if( rc != SQLITE_OK ){ return rc; } @@ -798,12 +818,20 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { if( rc != SQLITE_OK ){ return rc; } - rc = diskAnnCreateIndex(db, pIdx->zName, &idxKey, &idxParams); + if( vectorIdxKeyGet(pTable, &idxKey, &pzErrMsg) != 0 ){ + sqlite3ErrorMsg(pParse, "failed to detect underlying table key: %s", pzErrMsg); + return SQLITE_ERROR; + } + if( idxKey.nKeyColumns != 1 ){ + sqlite3ErrorMsg(pParse, "vector index for tables without ROWID and composite primary key are not supported"); + return SQLITE_ERROR; + } + rc = diskAnnCreateIndex(db, zDbSName, pIdx->zName, &idxKey, &idxParams); if( rc != SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to initialize diskann vector index"); return rc; } - rc = insertIndexParameters(db, pIdx->zName, &idxParams); + rc = insertIndexParameters(db, zDbSName, pIdx->zName, &idxParams); if( rc != SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to update global metadata table"); return rc; @@ -815,7 +843,7 @@ int vectorIndexCreate(Parse *pParse, Index *pIdx, const IdList *pUsing) { return SQLITE_OK; } -int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows *pRows, char **pzErrMsg) { +int vectorIndexSearch(sqlite3 *db, const char* zDbSName, int argc, sqlite3_value **argv, VectorOutRows *pRows, char **pzErrMsg) { int type, dims, k, rc; const char *zIdxName; const char *zErrMsg; @@ -826,6 +854,8 @@ int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows VectorIdxParams idxParams; vectorIdxParamsInit(&idxParams, NULL, 0); + assert( zDbSName != NULL ); + if( argc != 3 ){ *pzErrMsg = sqlite3_mprintf("vector search must have exactly 3 parameters"); rc = SQLITE_ERROR; @@ -871,22 +901,22 @@ int vectorIndexSearch(sqlite3 *db, int argc, sqlite3_value **argv, VectorOutRows rc = SQLITE_ERROR; goto out; } - pIndex = sqlite3FindIndex(db, zIdxName, db->aDb[0].zDbSName); + pIndex = sqlite3FindIndex(db, zIdxName, zDbSName); if( pIndex == NULL ){ *pzErrMsg = sqlite3_mprintf("vector index not found"); rc = SQLITE_ERROR; goto out; } + rc = diskAnnOpenIndex(db, zDbSName, zIdxName, &idxParams, &pDiskAnn); + if( rc != SQLITE_OK ){ + *pzErrMsg = sqlite3_mprintf("failed to open diskann index"); + goto out; + } if( vectorIdxKeyGet(pIndex->pTable, &pKey, &zErrMsg) != 0 ){ *pzErrMsg = sqlite3_mprintf("failed to extract table key: %s", zErrMsg); rc = SQLITE_ERROR; goto out; } - rc = diskAnnOpenIndex(db, zIdxName, &idxParams, &pDiskAnn); - if( rc != SQLITE_OK ){ - *pzErrMsg = sqlite3_mprintf("failed to open diskann index"); - goto out; - } rc = diskAnnSearch(pDiskAnn, pVector, k, &pKey, pRows, pzErrMsg); out: if( pDiskAnn != NULL ){ @@ -932,14 +962,17 @@ int vectorIndexDelete( int vectorIndexCursorInit( sqlite3 *db, - VectorIdxCursor **ppCursor, - const char *zIndexName + const char *zDbSName, + const char *zIndexName, + VectorIdxCursor **ppCursor ){ int rc; VectorIdxCursor* pCursor; VectorIdxParams params; vectorIdxParamsInit(¶ms, NULL, 0); + assert( zDbSName != NULL ); + if( vectorIndexGetParameters(db, zIndexName, ¶ms) != 0 ){ return SQLITE_ERROR; } @@ -947,7 +980,7 @@ int vectorIndexCursorInit( if( pCursor == 0 ){ return SQLITE_NOMEM_BKPT; } - rc = diskAnnOpenIndex(db, zIndexName, ¶ms, &pCursor->pIndex); + rc = diskAnnOpenIndex(db, zDbSName, zIndexName, ¶ms, &pCursor->pIndex); if( rc != SQLITE_OK ){ sqlite3DbFree(db, pCursor); return rc; diff --git a/libsql-sqlite3/src/vectorIndexInt.h b/libsql-sqlite3/src/vectorIndexInt.h index b50f6bc47d..34b1a8ab24 100644 --- a/libsql-sqlite3/src/vectorIndexInt.h +++ b/libsql-sqlite3/src/vectorIndexInt.h @@ -16,7 +16,7 @@ typedef struct BlobSpot BlobSpot; */ struct DiskAnnIndex { sqlite3 *db; /* Database connection */ - char *zDb; /* Database name */ + char *zDbSName; /* Database name */ char *zName; /* Index name */ char *zShadow; /* Shadow table name */ int nFormatVersion; /* DiskAnn format version */ @@ -207,10 +207,10 @@ int vectorOutRowsPut(VectorOutRows *, int, int, const u64 *, sqlite3_value *); void vectorOutRowsGet(sqlite3_context *, const VectorOutRows *, int, int); void vectorOutRowsFree(sqlite3 *, VectorOutRows *); -int diskAnnCreateIndex(sqlite3 *, const char *, const VectorIdxKey *, VectorIdxParams *); -int diskAnnClearIndex(sqlite3 *, const char *); -int diskAnnDropIndex(sqlite3 *, const char *); -int diskAnnOpenIndex(sqlite3 *, const char *, const VectorIdxParams *, DiskAnnIndex **); +int diskAnnCreateIndex(sqlite3 *, const char *, const char *, const VectorIdxKey *, VectorIdxParams *); +int diskAnnClearIndex(sqlite3 *, const char *, const char *); +int diskAnnDropIndex(sqlite3 *, const char *, const char *); +int diskAnnOpenIndex(sqlite3 *, const char *, const char *, const VectorIdxParams *, DiskAnnIndex **); void diskAnnCloseIndex(DiskAnnIndex *); int diskAnnInsert(const DiskAnnIndex *, const VectorInRow *, char **); int diskAnnDelete(const DiskAnnIndex *, const VectorInRow *, char **); @@ -224,14 +224,14 @@ typedef struct VectorIdxCursor VectorIdxCursor; int vectorIdxParseColumnType(const char *, int *, int *, const char **); -int vectorIndexCreate(Parse*, Index*, const IdList*); -int vectorIndexClear(sqlite3 *, const char *); -int vectorIndexDrop(sqlite3 *, const char *); -int vectorIndexCursorInit(sqlite3 *, VectorIdxCursor **, const char *); +int vectorIndexCreate(Parse*, Index*, const char *, const IdList*); +int vectorIndexClear(sqlite3 *, const char *, const char *); +int vectorIndexDrop(sqlite3 *, const char *, const char *); +int vectorIndexCursorInit(sqlite3 *, const char *, const char *, VectorIdxCursor **); void vectorIndexCursorClose(sqlite3 *, VectorIdxCursor *); int vectorIndexInsert(VectorIdxCursor *, const UnpackedRecord *, char **); int vectorIndexDelete(VectorIdxCursor *, const UnpackedRecord *, char **); -int vectorIndexSearch(sqlite3 *, int, sqlite3_value **, VectorOutRows *, char **); +int vectorIndexSearch(sqlite3 *, const char *, int, sqlite3_value **, VectorOutRows *, char **); #ifdef __cplusplus } /* end of the 'extern "C"' block */ diff --git a/libsql-sqlite3/src/vectordiskann.c b/libsql-sqlite3/src/vectordiskann.c index 9b29f422a2..1401961a93 100644 --- a/libsql-sqlite3/src/vectordiskann.c +++ b/libsql-sqlite3/src/vectordiskann.c @@ -169,7 +169,7 @@ int blobSpotCreate(const DiskAnnIndex *pIndex, BlobSpot **ppBlobSpot, u64 nRowid } // open blob in the end so we don't need to close it in error case - rc = sqlite3_blob_open(pIndex->db, pIndex->zDb, pIndex->zShadow, "data", nRowid, isWritable, &pBlobSpot->pBlob); + rc = sqlite3_blob_open(pIndex->db, pIndex->zDbSName, pIndex->zShadow, "data", nRowid, isWritable, &pBlobSpot->pBlob); rc = blobSpotConvertRc(pIndex, rc); if( rc != SQLITE_OK ){ goto out; @@ -205,7 +205,7 @@ int blobSpotReload(const DiskAnnIndex *pIndex, BlobSpot *pBlobSpot, u64 nRowid, return SQLITE_OK; } - // if last blob open/reopen operation aborted - we need to close current blob and open new one + // if last blob open/reopen operation aborted - we need to close current blob and open new one // (as all operations over aborted blob will return SQLITE_ABORT error) if( pBlobSpot->isAborted ){ if( pBlobSpot->pBlob != NULL ){ @@ -216,7 +216,7 @@ int blobSpotReload(const DiskAnnIndex *pIndex, BlobSpot *pBlobSpot, u64 nRowid, pBlobSpot->isAborted = 0; pBlobSpot->nRowid = nRowid; - rc = sqlite3_blob_open(pIndex->db, pIndex->zDb, pIndex->zShadow, "data", nRowid, pBlobSpot->isWritable, &pBlobSpot->pBlob); + rc = sqlite3_blob_open(pIndex->db, pIndex->zDbSName, pIndex->zShadow, "data", nRowid, pBlobSpot->isWritable, &pBlobSpot->pBlob); rc = blobSpotConvertRc(pIndex, rc); if( rc != SQLITE_OK ){ goto abort; @@ -413,6 +413,7 @@ void nodeBinDebug(const DiskAnnIndex *pIndex, const BlobSpot *pBlobSpot) { int diskAnnCreateIndex( sqlite3 *db, + const char *zDbSName, const char *zIdxName, const VectorIdxKey *pKey, VectorIdxParams *pParams @@ -457,7 +458,8 @@ int diskAnnCreateIndex( } zSql = sqlite3MPrintf( db, - "CREATE TABLE IF NOT EXISTS %s_shadow (%s, data BLOB, PRIMARY KEY (%s))", + "CREATE TABLE IF NOT EXISTS \"%w\".%s_shadow (%s, data BLOB, PRIMARY KEY (%s))", + zDbSName, zIdxName, columnSqlDefs, columnSqlNames @@ -467,15 +469,15 @@ int diskAnnCreateIndex( return rc; } -int diskAnnClearIndex(sqlite3 *db, const char *zIdxName) { - char *zSql = sqlite3MPrintf(db, "DELETE FROM %s_shadow", zIdxName); +int diskAnnClearIndex(sqlite3 *db, const char *zDbSName, const char *zIdxName) { + char *zSql = sqlite3MPrintf(db, "DELETE FROM \"%w\".%s_shadow", zDbSName, zIdxName); int rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3DbFree(db, zSql); return rc; } -int diskAnnDropIndex(sqlite3 *db, const char *zIdxName){ - char *zSql = sqlite3MPrintf(db, "DROP TABLE %s_shadow", zIdxName); +int diskAnnDropIndex(sqlite3 *db, const char *zDbSName, const char *zIdxName){ + char *zSql = sqlite3MPrintf(db, "DROP TABLE \"%w\".%s_shadow", zDbSName, zIdxName); int rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3DbFree(db, zSql); return rc; @@ -492,9 +494,9 @@ static int diskAnnSelectRandomShadowRow(const DiskAnnIndex *pIndex, u64 *pRowid) char *zSql = NULL; zSql = sqlite3MPrintf( - pIndex->db, - "SELECT rowid FROM %s LIMIT 1 OFFSET ABS(RANDOM()) %% MAX((SELECT COUNT(*) FROM %s), 1)", - pIndex->zShadow, pIndex->zShadow + pIndex->db, + "SELECT rowid FROM \"%w\".%s LIMIT 1 OFFSET ABS(RANDOM()) %% MAX((SELECT COUNT(*) FROM %s), 1)", + pIndex->zDbSName, pIndex->zShadow, pIndex->zShadow ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; @@ -544,7 +546,11 @@ static int diskAnnGetShadowRowid(const DiskAnnIndex *pIndex, const VectorInRow * rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "SELECT rowid FROM %s WHERE (%s) = (%s)", pIndex->zShadow, columnSqlNames, columnSqlPlaceholders); + zSql = sqlite3MPrintf( + pIndex->db, + "SELECT rowid FROM \"%w\".%s WHERE (%s) = (%s)", + pIndex->zDbSName, pIndex->zShadow, columnSqlNames, columnSqlPlaceholders + ); if( zSql == NULL ){ rc = SQLITE_NOMEM; goto out; @@ -563,7 +569,7 @@ static int diskAnnGetShadowRowid(const DiskAnnIndex *pIndex, const VectorInRow * if( rc != SQLITE_ROW ){ goto out; } - + assert( sqlite3_column_type(pStmt, 0) == SQLITE_INTEGER ); *pRowid = sqlite3_column_int64(pStmt, 0); @@ -593,7 +599,11 @@ static int diskAnnGetShadowRowKeys(const DiskAnnIndex *pIndex, u64 nRowid, const rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "SELECT %s FROM %s WHERE rowid = ?", columnSqlNames, pIndex->zShadow); + zSql = sqlite3MPrintf( + pIndex->db, + "SELECT %s FROM \"%w\".%s WHERE rowid = ?", + columnSqlNames, pIndex->zDbSName, pIndex->zShadow + ); if( zSql == NULL ){ rc = SQLITE_NOMEM; goto out; @@ -643,7 +653,11 @@ static int diskAnnInsertShadowRow(const DiskAnnIndex *pIndex, const VectorInRow rc = SQLITE_ERROR; goto out; } - zSql = sqlite3MPrintf(pIndex->db, "INSERT INTO %s VALUES (%s, ?) RETURNING rowid", pIndex->zShadow, columnSqlPlaceholders); + zSql = sqlite3MPrintf( + pIndex->db, + "INSERT INTO \"%w\".%s VALUES (%s, ?) RETURNING rowid", + pIndex->zDbSName, pIndex->zShadow, columnSqlPlaceholders + ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; goto out; @@ -690,7 +704,11 @@ static int diskAnnInsertShadowRow(const DiskAnnIndex *pIndex, const VectorInRow static int diskAnnDeleteShadowRow(const DiskAnnIndex *pIndex, i64 nRowid){ int rc; sqlite3_stmt *pStmt = NULL; - char *zSql = sqlite3MPrintf(pIndex->db, "DELETE FROM %s WHERE rowid = ?", pIndex->zShadow); + char *zSql = sqlite3MPrintf( + pIndex->db, + "DELETE FROM \"%w\".%s WHERE rowid = ?", + pIndex->zDbSName, pIndex->zShadow + ); if( zSql == NULL ){ rc = SQLITE_NOMEM_BKPT; goto out; @@ -939,7 +957,7 @@ static int diskAnnReplaceEdgeIdx(const DiskAnnIndex *pIndex, BlobSpot *pNodeBlob for(i = nEdges - 1; i >= 0; i--){ u64 edgeRowid; float edgeToNew, nodeToEdge; - + nodeBinEdge(pIndex, pNodeBlob, i, &edgeRowid, &edgeVector); if( edgeRowid == newRowid ){ // deletes can leave "zombie" edges in the graph and we must override them and not store duplicate edges in the node @@ -1213,9 +1231,6 @@ int diskAnnSearch( } rc = SQLITE_OK; out: - if( rc != SQLITE_OK ){ - vectorOutRowsFree(pIndex->db, pRows); - } diskAnnSearchCtxDeinit(&ctx); return rc; } @@ -1409,6 +1424,7 @@ int diskAnnDelete( // open index with zIdxName and pParams serialized binary parameters and set result to the ppIndex int diskAnnOpenIndex( sqlite3 *db, /* Database connection */ + const char *zDbSName, /* Database schema name */ const char *zIdxName, /* Index name */ const VectorIdxParams *pParams, /* Index parameters */ DiskAnnIndex **ppIndex /* OUT: Index */ @@ -1419,9 +1435,13 @@ int diskAnnOpenIndex( return SQLITE_NOMEM; } pIndex->db = db; - pIndex->zDb = sqlite3DbStrDup(db, db->aDb[0].zDbSName); + pIndex->zDbSName = sqlite3DbStrDup(db, zDbSName); pIndex->zName = sqlite3DbStrDup(db, zIdxName); pIndex->zShadow = sqlite3MPrintf(db, "%s_shadow", zIdxName); + if( pIndex->zShadow == NULL ){ + diskAnnCloseIndex(pIndex); + return SQLITE_NOMEM_BKPT; + } pIndex->nFormatVersion = vectorIdxParamsGetU64(pParams, VECTOR_FORMAT_PARAM_ID); pIndex->nDistanceFunc = vectorIdxParamsGetU64(pParams, VECTOR_METRIC_TYPE_PARAM_ID); pIndex->nBlockSize = vectorIdxParamsGetU64(pParams, VECTOR_BLOCK_SIZE_PARAM_ID) << DISKANN_BLOCK_SIZE_SHIFT; @@ -1430,14 +1450,13 @@ int diskAnnOpenIndex( pIndex->pruningAlpha = vectorIdxParamsGetF64(pParams, VECTOR_PRUNING_ALPHA_PARAM_ID); pIndex->insertL = vectorIdxParamsGetU64(pParams, VECTOR_INSERT_L_PARAM_ID); pIndex->searchL = vectorIdxParamsGetU64(pParams, VECTOR_SEARCH_L_PARAM_ID); - if( pIndex->zShadow == NULL || - pIndex->nDistanceFunc == 0 || - pIndex->nBlockSize == 0 || - pIndex->nNodeVectorType == 0 || - pIndex->nVectorDims == 0 + if( pIndex->nDistanceFunc == 0 || + pIndex->nBlockSize == 0 || + pIndex->nNodeVectorType == 0 || + pIndex->nVectorDims == 0 ){ diskAnnCloseIndex(pIndex); - return SQLITE_NOMEM; + return SQLITE_ERROR; } if( pIndex->pruningAlpha == 0 ){ pIndex->pruningAlpha = VECTOR_PRUNING_ALPHA_DEFAULT; @@ -1458,8 +1477,8 @@ int diskAnnOpenIndex( } void diskAnnCloseIndex(DiskAnnIndex *pIndex){ - if( pIndex->zDb ){ - sqlite3DbFree(pIndex->db, pIndex->zDb); + if( pIndex->zDbSName ){ + sqlite3DbFree(pIndex->db, pIndex->zDbSName); } if( pIndex->zName ){ sqlite3DbFree(pIndex->db, pIndex->zName); diff --git a/libsql-sqlite3/src/vectorvtab.c b/libsql-sqlite3/src/vectorvtab.c index 7f5061f28e..12a3517ac0 100644 --- a/libsql-sqlite3/src/vectorvtab.c +++ b/libsql-sqlite3/src/vectorvtab.c @@ -31,8 +31,9 @@ typedef struct vectorVtab vectorVtab; struct vectorVtab { - sqlite3_vtab base; /* Base class - must be first */ - sqlite3 *db; /* Database connection */ + sqlite3_vtab base; /* Base class - must be first */ + sqlite3 *db; /* Database connection */ + char* zDbSName; /* Database schema name */ }; typedef struct vectorVtab_cursor vectorVtab_cursor; @@ -55,26 +56,36 @@ static int vectorVtabConnect( sqlite3_vtab **ppVtab, char **pzErr ){ - vectorVtab *pVtab; + char *zDbSName = NULL; + vectorVtab *pVtab = NULL; int rc; - /* + /* + * name of the database ignored by SQLite - so we don't need to provide any schema prefix here * hidden column are parameters of table-valued function (see https://www.sqlite.org/vtab.html#table_valued_functions) */ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(idx hidden, vector hidden, k hidden, id);"); if( rc != SQLITE_OK ){ return rc; } - pVtab = sqlite3_malloc( sizeof(*pVtab) ); + pVtab = sqlite3_malloc( sizeof(vectorVtab) ); if( pVtab == NULL ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; + } + zDbSName = sqlite3DbStrDup(db, argv[1]); // argv[1] is the database schema name by spec (see https://www.sqlite.org/vtab.html#the_xcreate_method) + if( zDbSName == NULL ){ + sqlite3_free(pVtab); + return SQLITE_NOMEM_BKPT; } memset(pVtab, 0, sizeof(*pVtab)); pVtab->db = db; + pVtab->zDbSName = zDbSName; *ppVtab = (sqlite3_vtab*)pVtab; return SQLITE_OK; } static int vectorVtabDisconnect(sqlite3_vtab *pVtab){ + vectorVtab *pVTab = (vectorVtab*)pVtab; + sqlite3DbFree(pVTab->db, pVTab->zDbSName); sqlite3_free(pVtab); return SQLITE_OK; } @@ -132,7 +143,7 @@ static int vectorVtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ } static int vectorVtabFilter( - sqlite3_vtab_cursor *pVtabCursor, + sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ @@ -141,7 +152,7 @@ static int vectorVtabFilter( pCur->rows.aIntValues = NULL; pCur->rows.ppValues = NULL; - if( vectorIndexSearch(pVTab->db, argc, argv, &pCur->rows, &pVTab->base.zErrMsg) != 0 ){ + if( vectorIndexSearch(pVTab->db, pVTab->zDbSName, argc, argv, &pCur->rows, &pVTab->base.zErrMsg) != 0 ){ return SQLITE_ERROR; } diff --git a/libsql-sqlite3/test/libsql_vector_index.test b/libsql-sqlite3/test/libsql_vector_index.test index ec8350a585..f46e2a0799 100644 --- a/libsql-sqlite3/test/libsql_vector_index.test +++ b/libsql-sqlite3/test/libsql_vector_index.test @@ -210,6 +210,24 @@ do_execsql_test vector-index-dont-affect-sql { SELECT rowid FROM t_vector_other_sql WHERE emb = vector('[7,8]'); } {4 1 1 2 3 4} +do_execsql_test vector-attach { + CREATE TABLE t_attach ( emb FLOAT32(2) ); + INSERT INTO t_attach VALUES (vector('[1,2]')), (vector('[3,4]')); + CREATE INDEX t_attach_idx ON t_attach(libsql_vector_idx(emb)); + ATTACH ':memory:' as "q u o t e "" h e r e "; + CREATE TABLE "q u o t e "" h e r e ".t_attach ( emb FLOAT32(2) ); + INSERT INTO "q u o t e "" h e r e ".t_attach VALUES (vector('[5,6]')), (vector('[7,8]')); + CREATE INDEX "q u o t e "" h e r e ".t_attach_idx ON t_attach(libsql_vector_idx(emb)); + SELECT rowid FROM vector_top_k('t_attach_idx', vector('[5,6]'), 4); +} {2 1} + +do_execsql_test vector-vacuum { + CREATE TABLE t_vacuum ( emb FLOAT32(2) ); + INSERT INTO t_vacuum VALUES (vector('[1,2]')), (vector('[3,4]')); + CREATE INDEX t_vacuum_idx ON t_vacuum(libsql_vector_idx(emb)); + VACUUM INTO ':memory:'; +} {} + proc error_messages {sql} { set ret "" catch {