Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -172,11 +172,11 @@ fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \ fts3_tokenize_vtab.lo \ fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ fts5.lo \ func.lo global.lo hash.lo \ - icu.lo insert.lo json1.lo legacy.lo loadext.lo \ + icu.lo insert.lo json1.lo kvapi.o legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ @@ -224,10 +224,11 @@ $(TOP)/src/global.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ + $(TOP)/src/kvapi.c \ $(TOP)/src/legacy.c \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ $(TOP)/src/mem0.c \ @@ -765,10 +766,13 @@ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/hash.c insert.lo: $(TOP)/src/insert.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/insert.c +kvapi.lo: $(TOP)/src/kvapi.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/kvapi.c + legacy.lo: $(TOP)/src/legacy.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/legacy.c loadext.lo: $(TOP)/src/loadext.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/loadext.c Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -1079,11 +1079,11 @@ fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo \ fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \ fts3_tokenize_vtab.lo fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ fts5.lo \ func.lo global.lo hash.lo \ - icu.lo insert.lo legacy.lo loadext.lo \ + icu.lo insert.lo kvapi.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ pager.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ @@ -1144,10 +1144,11 @@ $(TOP)\src\fkey.c \ $(TOP)\src\func.c \ $(TOP)\src\global.c \ $(TOP)\src\hash.c \ $(TOP)\src\insert.c \ + $(TOP)\src\kvapi.c \ $(TOP)\src\legacy.c \ $(TOP)\src\loadext.c \ $(TOP)\src\main.c \ $(TOP)\src\malloc.c \ $(TOP)\src\mem0.c \ @@ -1739,10 +1740,13 @@ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\hash.c insert.lo: $(TOP)\src\insert.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\insert.c +kvapi.lo: $(TOP)\src\kvapi.c $(HDR) + $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\kvapi.c + legacy.lo: $(TOP)\src\legacy.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\legacy.c loadext.lo: $(TOP)\src\loadext.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\loadext.c Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -60,11 +60,11 @@ fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o \ fts3_tokenize_vtab.o \ fts3_unicode.o fts3_unicode2.o \ fts3_write.o fts5.o func.o global.o hash.o \ - icu.o insert.o json1.o legacy.o loadext.o \ + icu.o insert.o json1.o kvapi.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ @@ -104,10 +104,11 @@ $(TOP)/src/global.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ + $(TOP)/src/kvapi.c \ $(TOP)/src/legacy.c \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ $(TOP)/src/mem0.c \ ADDED src/kvapi.c Index: src/kvapi.c ================================================================== --- /dev/null +++ src/kvapi.c @@ -0,0 +1,129 @@ +/* +** 2017-01-18 +** +** 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 code used to implement key/value access interface. +*/ + +#include "sqliteInt.h" + +#ifndef SQLITE_OMIT_KEYVALUE_ACCESSOR + +/* +** An sqlite3_kv object is an accessor for key/value pairs. +** +** This is an opaque object. The public interface sees pointers to this +** object, but not the internals. So the internal composition of this +** object is free to change from one release to the next without breaking +** compatibility. +*/ +struct sqlite3_kv { + sqlite3 *db; /* The database holding the table to be accessed */ + u32 iRoot; /* Root page of the table */ + int iGen; /* Schema generation number */ + int iCookie; /* Schema cookie number from the database file */ + Schema *pSchema; /* Schema holding the table */ + sqlite3_int64 iRowid; /* Current rowid */ +}; + +/* +** Create a new sqlite3_kv object open on zDb.zTable and return +** a pointer to that object. +*/ +int sqlite3_kv_open( + sqlite3 *db, /* The database connection */ + const char *zDb, /* Schema containing zTable. NULL for "main" */ + const char *zTable, /* Name of table the key/value table */ + unsigned int flags, /* Must be zero. Reserved for future expansion. */ + sqlite3_kv **ppKvOut /* Store the new sqlite3_kv object here */ +){ + sqlite3_kv *pKv; + Table *pTab; + int rc = SQLITE_ERROR; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( ppKvOut==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + *ppKvOut = 0; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + sqlite3_mutex_enter(db->mutex); + sqlite3BtreeEnterAll(db); + + pTab = sqlite3FindTable(db, zTable, zDb); + if( pTab==0 ){ + goto kv_open_done; + } + if( !((pTab->nCol==1 && pTab->iPKey<0) + || (pTab->nCol==2 && pTab->iPKey==0)) + ){ + /* Must be an single-column table without an INTEGER PRIMARY KEY, + ** or a two-column table where the first column is the INTEGER PRIMARY KEY + */ + goto kv_open_done; + } + if( pTab->pIndex!=0 || pTab->pFKey!=0 || pTab->pCheck!=0 ){ + /* Do not allow secondary indexes, foreign keys, or CHECK constraints */ + goto kv_open_done; + } + if( pTab->tabFlags & (TF_Autoincrement|TF_Virtual|TF_WithoutRowid) ){ + /* Must not have autoincrement. Must not be a virtual table or a + ** without rowid table */ + goto kv_open_done; + } + *ppKvOut = pKv = sqlite3_malloc(sizeof(*pKv)); + if( pKv==0 ){ + rc = SQLITE_NOMEM; + goto kv_open_done; + } + pKv->db = db; + pKv->iGen = pTab->pSchema->iGeneration; + pKv->iCookie = pTab->pSchema->schema_cookie; + pKv->pSchema = pTab->pSchema; + pKv->iRoot = pTab->tnum; + rc = SQLITE_OK; + +kv_open_done: + sqlite3BtreeLeaveAll(db); + sqlite3_mutex_leave(db->mutex); + return rc; +} + +/* +** Free the key/value accessor at pKv +*/ +int sqlite3_kv_close(sqlite3_kv *pKv){ + sqlite3_free(pKv); + return SQLITE_OK; +} + +int sqlite3_kv_seek(sqlite3_kv *pKv, sqlite3_int64 rowid){ + return SQLITE_MISUSE; +} +int sqlite3_kv_reset(sqlite3_kv *pKv){ + return SQLITE_MISUSE; +} +int sqlite3_kv_bytes(sqlite3_kv *pKv){ + return -1; +} +int sqlite3_kv_read(sqlite3_kv *pKv, void *pBuf, int amt, int offset){ + return SQLITE_MISUSE; +} +int sqlite3_kv_insert(sqlite3_kv *pKv, sqlite3_int64 rid, int sz, void *pBuf){ + return SQLITE_MISUSE; +} + +#endif /* #ifndef SQLITE_OMIT_KEYVALU_ACCESSOR */ Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -495,10 +495,12 @@ #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) +#define SQLITE_KV_BEFORE (SQLITE_NOTFOUND | (1<<8)) +#define SQLITE_KV_AFTER (SQLITE_NOTFOUND | (2<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) @@ -8446,10 +8448,120 @@ ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); + +/* +** CAPI3REF: Key/Value Access +** EXPERIMENTAL +** +** The sqlite3_kv object is used to access database tables using +** the key/value interface. +*/ +typedef struct sqlite3_kv sqlite3_kv; + + +/* +** CAPI3REF: Open a key/value accessor object +** CONSTRUCTOR: sqlite3_kv +** EXPERIMENTAL +** +** This interface attempts create a new [sqlite3_kv] object and +** return a pointer to that object in the 5th parameter. +** SQLITE_OK is returned on success or an error code if something +** goes wrong. +*/ +int sqlite3_kv_open( + sqlite3 *db, + const char *zDb, + const char *zTable, + unsigned int flags, /* Must be zero. Reserved for future expansion. */ + sqlite3_kv **ppKvOut +); + +/* +** CAPI3REF: Close a key/value accessor object +** DESTRUCTOR: sqlite3_kv +** EXPERIMENTAL +** +** The sqlite3_kv_close(P) interface closes a key/value accessor P. +** The argument to sqlite3_kv_close(P) may be NULL, resulting in a harmless +** no-op. +*/ +int sqlite3_kv_close(sqlite3_kv*); + +/* +** CAPI3REF: Reposition a key/value accessor object +** METHOD: sqlite3_kv +** EXPERIMENTAL +** +** The sqlite3_kv_seek(P,I) interface attempts to reposition +** the key/value accessor object P so that it is pointing to a table +** entry with rowid I. The sqlite3_kv_seek(P,I) interface returns +** SQLITE_OK on success or an error code if it is unable to complete +** the operation. If no record exists with rowid I, then sqlite3_kv_seek(P,I) +** attempts to position itself at another nearby record and returns +** either SQLITE_KV_BEFORE or SQLITE_KV_AFTER depending on whether the +** record the accessor is left pointing to is less than or greater than I, +** respectively. If the table is empty, sqlite3_kv_seek(P,I) returns +** SQLITE_EMPTY. +** +** A successful sqlite3_kv_seek(P,I) call leave the key/value accessor +** P holding a read transaction open on the database. Use sqlite3_kv_reset(P) +** or sqlite3_kv_close(P) to release this transaction. +*/ +int sqlite3_kv_seek(sqlite3_kv*, sqlite3_int64 rowid); + +/* +** CAPI3REF: Reset a key/value accessor object +** METHOD: sqlite3_kv +** EXPERIMENTAL +** +** The sqlite3_kv_reset(P) interface restores the key/value accessor P +** back to its original state, releasing any transactions held. +*/ +int sqlite3_kv_reset(sqlite3_kv*); + +/* +** CAPI3REF: Find the size of the value for a key/value pair +** METHOD: sqlite3_kv +** EXPERIMENTAL +** +** The sqlite3_kv_bytes(P) interface returns the size of the value +** in the key/value pair that the key/value accessor object P is currently +** pointing to. The sqlite3_kv_bytes(P) interface returns a negative +** number if the key/value accessor object P is not currently pointing +** to a valid entry in the table. +*/ +int sqlite3_kv_bytes(sqlite3_kv*); + +/* +** CAPI3REF: Read the value for a key/value pair +** METHOD: sqlite3_kv +** EXPERIMENTAL +** +** The sqlite3_kv_read(P,B,A,O) interface attempts to read A bytes of +** content beginning at offset O from the value that the key/value accessor P +** is currently pointing at, storing the results in buffer B. +** The caller must ensure that the buffer B is at least A bytes in length. +*/ +int sqlite3_kv_read(sqlite3_kv*, void *pBuf, int amt, int offset); + +/* +** CAPI3REF: Insert a new key/value pair +** METHOD: sqlite3_kv +** EXPERIMENTAL +** +** The sqlite3_kv_insert(P,I,N,B) interface inserts a key/value pair +** having key I and a value that is an N-byte BLOB pointed to by B. +** In a call to sqlite3_kv_insert(P,I,N,B), if a previous row with key I +** already exists in the table, then that previous row is replaced by +** the new row. +*/ +int sqlite3_kv_insert(sqlite3_kv*, sqlite3_int64 rowid, int size, void *pBuf); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT Index: tool/mksqlite3c-noext.tcl ================================================================== --- tool/mksqlite3c-noext.tcl +++ tool/mksqlite3c-noext.tcl @@ -336,10 +336,11 @@ callback.c delete.c func.c fkey.c insert.c + kvapi.c legacy.c loadext.c pragma.c prepare.c select.c Index: tool/mksqlite3c.tcl ================================================================== --- tool/mksqlite3c.tcl +++ tool/mksqlite3c.tcl @@ -344,10 +344,11 @@ callback.c delete.c func.c fkey.c insert.c + kvapi.c legacy.c loadext.c pragma.c prepare.c select.c