First approximation of incremental blob IO API. (CVS 3892)

FossilOrigin-Name: c444836e7b690c16dd6acff571c613a23beb42dc
This commit is contained in:
danielk1977 2007-05-01 17:49:49 +00:00
parent cc3e986643
commit b4e9af9ff9
10 changed files with 590 additions and 20 deletions

View File

@ -62,7 +62,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o btree.o build.o \
pager.o parse.o pragma.o prepare.o printf.o random.o \
select.o table.o tclsqlite.o tokenize.o trigger.o \
update.o util.o vacuum.o \
vdbe.o vdbeapi.o vdbeaux.o vdbefifo.o vdbemem.o \
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \
where.o utf.o legacy.o vtab.o
# All of the source code files.
@ -115,6 +115,7 @@ SRC = \
$(TOP)/src/vdbe.h \
$(TOP)/src/vdbeapi.c \
$(TOP)/src/vdbeaux.c \
$(TOP)/src/vdbeblob.c \
$(TOP)/src/vdbefifo.c \
$(TOP)/src/vdbemem.c \
$(TOP)/src/vdbeInt.h \
@ -410,6 +411,9 @@ vdbeapi.o: $(TOP)/src/vdbeapi.c $(VDBEHDR) $(HDR)
vdbeaux.o: $(TOP)/src/vdbeaux.c $(VDBEHDR) $(HDR)
$(TCCX) -c $(TOP)/src/vdbeaux.c
vdbeblob.o: $(TOP)/src/vdbeblob.c $(VDBEHDR) $(HDR)
$(TCCX) -c $(TOP)/src/vdbeblob.c
vdbefifo.o: $(TOP)/src/vdbefifo.c $(VDBEHDR) $(HDR)
$(TCCX) -c $(TOP)/src/vdbefifo.c

View File

@ -1,5 +1,5 @@
C Modify\sloadSegmentLeavesInt()\sto\scorrectly\shandle\sprefix\ssearching.\nThe\snew\sfunction\sdocListUnion()\sis\sused\sto\saccumulate\sa\sunion\sof\sthe\nhits\sfor\sthe\smatching\sterms,\swhich\swill\sbe\smerged\sacross\ssegments\nusing\sdocListMerge().\s(CVS\s3891)
D 2007-05-01T17:14:59
C First\sapproximation\sof\sincremental\sblob\sIO\sAPI.\s(CVS\s3892)
D 2007-05-01T17:49:49
F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@ -44,7 +44,7 @@ F ext/fts2/fts2_tokenizer1.c 5c979fe8815f95396beb22b627571da895a025af
F ext/fts2/mkfts2amal.tcl 2a9ec76b0760fe7f3669dca5bc0d60728bc1c977
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F ltmain.sh 56abb507100ed2d4261f6dd1653dec3cf4066387
F main.mk dd14eeac33ac5e6c062f43e79876948f1d64908b
F main.mk 2bc462dba2a8332a8a831f5ddd718b4ad7aac6c8
F mkdll.sh ed62756baf44babf562a7843588790c02fee2106
F mkopcodec.awk bd46ad001c98dfbab07b1713cb8e692fa0e5415d
F mkopcodeh.awk cde995d269aa06c94adbf6455bea0acedb913fa5
@ -59,8 +59,8 @@ F src/alter.c 2c79ec40f65e33deaf90ca493422c74586e481a3
F src/analyze.c 4bbf5ddf9680587c6d4917e02e378b6037be3651
F src/attach.c a16ada4a4654a0d126b8223ec9494ebb81bc5c3c
F src/auth.c 902f4722661c796b97f007d9606bd7529c02597f
F src/btree.c abdbd0261d2ab4fed65332cc93f959906c185d22
F src/btree.h 4c0b5855cef3e4e6627358aa69541d21a2015947
F src/btree.c 80814622b3e3d6b71e53a9286e1a8a2ea486da11
F src/btree.h 9a219f01b732c8be4b3ccd8143881204da63e80f
F src/build.c 02e01ec7907c7d947ab3041fda0e81eaed05db42
F src/callback.c 6414ed32d55859d0f65067aa5b88d2da27b3af9e
F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675
@ -97,11 +97,11 @@ F src/random.c 6119474a6f6917f708c1dee25b9a8e519a620e88
F src/select.c b914abca0ba28893e7fb7c7fb97a05e240e2ce8b
F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
F src/shell.c 3ae4654560e91220a95738a73d135d91d937cda1
F src/sqlite.h.in e429f66f9245c7f8675db24b230c950b8672ad1c
F src/sqlite.h.in 2dd7d439a1b991043388c7c37eb7db957d7276ff
F src/sqlite3ext.h 7d0d363ea7327e817ef0dfe1b7eee1f171b72890
F src/sqliteInt.h 0b14d0eae083aafca0562d2261a404e5e5abc5f0
F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06
F src/tclsqlite.c ec69eb9ad56d03fbf7570ca1ca5ea947d1ec4b6f
F src/tclsqlite.c 82f7be1e8015ef224e2a9410a8f98dd6f61d64e9
F src/test1.c f1271d41719d05348e6dc39722260e17b8d7ddc1
F src/test2.c 24458b17ab2f3c90cbc1c8446bd7ffe69be62f88
F src/test3.c 65f92247cf8592854e9bf5115b3fb711f8b33280
@ -130,6 +130,7 @@ F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691
F src/vdbeInt.h 4b19fd8febad3fd14c4c97adaefc06754d323132
F src/vdbeapi.c 37fc2818bec64b361af73f3935699107bab0e625
F src/vdbeaux.c ef59545f53f90394283f2fd003375d3ebbf0bd6e
F src/vdbeblob.c 6d3128c71d5a6b8db627ea3052ed5aaaaf26e672
F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
F src/vdbemem.c 981a113405bd9b80aeb71fe246a2f01708e8a8f7
F src/vtab.c 89a0d5f39c1beba65a77fdb4d507b831fc5e6baf
@ -238,6 +239,7 @@ F test/fts2m.test 4b30142ead6f3ed076e880a2a464064c5ad58c51
F test/func.test 8a3bc8e8365dc0053c826923c0f738645f50f2f5
F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
F test/incrblob.test 86708ae039f564535e4200ac0e61dab5a6d6f18e
F test/incrvacuum.test a4c9022d7b26b10495616cc5a255f11afb683be8
F test/incrvacuum_ioerr.test 0ebc382bcc2036ec58cf49cc5ffada45f75d907b
F test/index.test e65df12bed94b2903ee89987115e1578687e9266
@ -329,7 +331,7 @@ F test/subselect.test 974e87f8fc91c5f00dd565316d396a5a6c3106c4
F test/sync.test d05397b8f89f423dd6dba528692019ab036bc1c3
F test/table.test feea6a3eb08cf166f570255eea5447e42ef82498
F test/tableapi.test 036575a98dcce7c92e9f39056839bbad8a715412
F test/tclsqlite.test 51334389283c74bcbe28645a73159b17e239e9f3
F test/tclsqlite.test 726c301d35a2c1f4181fb772a607f785dd9e284e
F test/temptable.test c36f3e5a94507abb64f7ba23deeb4e1a8a8c3821
F test/tester.tcl dcebe3c5bf15f3b4ba015b4b2237030c1e384941
F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
@ -466,7 +468,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
P e5e6af55ccc5c1a8a9206b42f1dd7bf547cb97ca
R 2c25fe5630cee9d287b5318018624ff2
U shess
Z bcafc47e1cdd98b2861c3ec5b23e7e20
P 72c796307338c2751a91c30f6fb16989afbf3816
R 30bd64cea77836fbaf98d3678ccdaa09
U danielk1977
Z 5cdc2c295a9fcd37c90d369f1b123096

View File

@ -1 +1 @@
72c796307338c2751a91c30f6fb16989afbf3816
c444836e7b690c16dd6acff571c613a23beb42dc

View File

@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.362 2007/04/30 16:55:01 danielk1977 Exp $
** $Id: btree.c,v 1.363 2007/05/01 17:49:49 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@ -6810,6 +6810,61 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
}
#endif
#ifndef SQLITE_OMIT_INCRBLOB
/*
** Argument pCsr must be a cursor opened for writing on an
** INTKEY table currently pointing at a valid table entry.
** This function modifies the data stored as part of that entry.
** Only the data content may only be modified, it is not possible
** to change the length of the data stored.
*/
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, const void *z){
/* TODO: The following is only a stop-gap implementation. It needs
** to be made efficient using the optimistic overflow page trick.
** Similar changes need to be made to sqlite3BtreeData().
*/
i64 iKey;
int rc;
int nCopy;
u32 nData;
char *zData;
rc = sqlite3BtreeKeySize(pCsr, &iKey);
if( rc!=SQLITE_OK ){
return rc;
}
rc = sqlite3BtreeDataSize(pCsr, &nData);
if( rc!=SQLITE_OK ){
return rc;
}
zData = sqliteMalloc(nData);
if( !zData ){
return SQLITE_NOMEM;
}
rc = sqlite3BtreeData(pCsr, 0, nData, (void *)zData);
if( rc!=SQLITE_OK ){
sqliteFree(zData);
return rc;
}
nCopy = amt;
if( nCopy>(nData-offset) ){
nCopy = nData-offset;
}
if( nCopy>0 ){
memcpy(&zData[offset], z, amt);
rc = sqlite3BtreeInsert(pCsr, 0, iKey, zData, nData, 0);
}
sqliteFree(zData);
return rc;
}
#endif
/*
** The following debugging interface has to be in this file (rather
** than in, for example, test1.c) so that it can get access to

View File

@ -13,7 +13,7 @@
** subsystem. See comments in the source code for a detailed description
** of what each interface routine does.
**
** @(#) $Id: btree.h,v 1.75 2007/04/26 14:42:36 danielk1977 Exp $
** @(#) $Id: btree.h,v 1.76 2007/05/01 17:49:49 danielk1977 Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_
@ -141,6 +141,7 @@ int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
struct Pager *sqlite3BtreePager(Btree*);
int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, const void*);
#ifdef SQLITE_TEST
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);

View File

@ -12,7 +12,7 @@
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.201 2007/03/30 20:43:42 drh Exp $
** @(#) $Id: sqlite.h.in,v 1.202 2007/05/01 17:49:49 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@ -1871,6 +1871,24 @@ int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
****** EXPERIMENTAL - subject to change without notice **************
*/
typedef struct sqlite3_blob sqlite3_blob;
int sqlite3_blob_open(
sqlite3*,
const char *zDb,
const char *zTable,
const char *zColumn,
sqlite_int64 iRow,
int flags,
sqlite3_blob **ppBlob
);
int sqlite3_blob_close(sqlite3_blob *);
int sqlite3_blob_read(sqlite3_blob *, void *z, int n, int iOffset);
int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
int sqlite3_blob_bytes(sqlite3_blob *);
/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.

View File

@ -12,9 +12,10 @@
** A TCL Interface to SQLite. Append this file to sqlite3.c and
** compile the whole thing to build a TCL-enabled version of SQLite.
**
** $Id: tclsqlite.c,v 1.179 2007/04/06 15:02:14 drh Exp $
** $Id: tclsqlite.c,v 1.180 2007/05/01 17:49:49 danielk1977 Exp $
*/
#include "tcl.h"
#include <errno.h>
/*
** Some additional include files are needed if this file is not
@ -115,6 +116,178 @@ struct SqliteDb {
int nStmt; /* Number of statements in stmtList */
};
typedef struct IncrblobChannel IncrblobChannel;
struct IncrblobChannel {
sqlite3_blob *pBlob;
int iSeek; /* Current seek offset */
};
/*
** Close an incremental blob channel.
*/
static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){
IncrblobChannel *p = (IncrblobChannel *)instanceData;
sqlite3_blob_close(p->pBlob);
Tcl_Free((char *)p);
return TCL_OK;
}
/*
** Read data from an incremental blob channel.
*/
static int incrblobInput(
ClientData instanceData,
char *buf,
int bufSize,
int *errorCodePtr
){
IncrblobChannel *p = (IncrblobChannel *)instanceData;
int nRead = bufSize; /* Number of bytes to read */
int nBlob; /* Total size of the blob */
int rc; /* sqlite error code */
nBlob = sqlite3_blob_bytes(p->pBlob);
if( (p->iSeek+nRead)>nBlob ){
nRead = nBlob-p->iSeek;
}
if( nRead<=0 ){
return 0;
}
rc = sqlite3_blob_read(p->pBlob, (void *)buf, nRead, p->iSeek);
if( rc!=SQLITE_OK ){
*errorCodePtr = rc;
return -1;
}
p->iSeek += nRead;
return nRead;
}
static int incrblobOutput(
ClientData instanceData,
CONST char *buf,
int toWrite,
int *errorCodePtr
){
IncrblobChannel *p = (IncrblobChannel *)instanceData;
int nWrite = toWrite; /* Number of bytes to write */
int nBlob; /* Total size of the blob */
int rc; /* sqlite error code */
nBlob = sqlite3_blob_bytes(p->pBlob);
if( (p->iSeek+nWrite)>nBlob ){
*errorCodePtr = EINVAL;
return -1;
}
if( nWrite<=0 ){
return 0;
}
rc = sqlite3_blob_write(p->pBlob, (void *)buf, nWrite, p->iSeek);
if( rc!=SQLITE_OK ){
*errorCodePtr = EIO;
return -1;
}
p->iSeek += nWrite;
return nWrite;
}
/*
** Seek an incremental blob channel.
*/
static int incrblobSeek(
ClientData instanceData,
long offset,
int seekMode,
int *errorCodePtr
){
IncrblobChannel *p = (IncrblobChannel *)instanceData;
switch( seekMode ){
case SEEK_SET:
p->iSeek = offset;
break;
case SEEK_CUR:
p->iSeek += offset;
break;
case SEEK_END:
p->iSeek = sqlite3_blob_bytes(p->pBlob) + offset;
break;
default: assert(!"Bad seekMode");
}
return p->iSeek;
}
static void incrblobWatch(ClientData instanceData, int mode){
/* NO-OP */
}
static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){
return TCL_ERROR;
}
static Tcl_ChannelType IncrblobChannelType = {
"incrblob", /* typeName */
TCL_CHANNEL_VERSION_2, /* version */
incrblobClose, /* closeProc */
incrblobInput, /* inputProc */
incrblobOutput, /* outputProc */
incrblobSeek, /* seekProc */
0, /* setOptionProc */
0, /* getOptionProc */
incrblobWatch, /* watchProc (this is a no-op) */
incrblobHandle, /* getHandleProc (always returns error) */
0, /* close2Proc */
0, /* blockModeProc */
0, /* flushProc */
0, /* handlerProc */
0, /* wideSeekProc */
0, /* threadActionProc */
};
/*
** Create a new incrblob channel.
*/
static int createIncrblobChannel(
Tcl_Interp *interp,
SqliteDb *pDb,
const char *zDb,
const char *zTable,
const char *zColumn,
sqlite_int64 iRow
){
IncrblobChannel *p;
sqlite3_blob *pBlob;
int rc;
Tcl_Channel channel;
int flags = TCL_READABLE|TCL_WRITABLE;
/* This variable is used to name the channels: "incrblob_[incr count]" */
static int count = 0;
char zChannel[64];
rc = sqlite3_blob_open(pDb->db, zDb, zTable, zColumn, iRow, 1, &pBlob);
if( rc!=SQLITE_OK ){
Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
return TCL_ERROR;
}
p = (IncrblobChannel *)Tcl_Alloc(sizeof(IncrblobChannel));
p->iSeek = 0;
p->pBlob = pBlob;
sprintf(zChannel, "incrblob_%d", ++count);
channel = Tcl_CreateChannel(&IncrblobChannelType, zChannel, p, flags);
Tcl_RegisterChannel(interp, channel);
Tcl_SetResult(interp, (char *)Tcl_GetChannelName(channel), TCL_VOLATILE);
return TCL_OK;
}
/*
** Look at the script prefix in pCmd. We will be executing this script
** after first appending one or more arguments. This routine analyzes
@ -676,6 +849,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
"collation_needed", "commit_hook", "complete",
"copy", "enable_load_extension","errorcode",
"eval", "exists", "function",
"incrblob",
"interrupt", "last_insert_rowid", "nullvalue",
"onecolumn", "profile", "progress",
"rekey", "rollback_hook", "timeout",
@ -688,6 +862,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
DB_COLLATION_NEEDED, DB_COMMIT_HOOK, DB_COMPLETE,
DB_COPY, DB_ENABLE_LOAD_EXTENSION,DB_ERRORCODE,
DB_EVAL, DB_EXISTS, DB_FUNCTION,
DB_INCRBLOB,
DB_INTERRUPT, DB_LAST_INSERT_ROWID,DB_NULLVALUE,
DB_ONECOLUMN, DB_PROFILE, DB_PROGRESS,
DB_REKEY, DB_ROLLBACK_HOOK, DB_TIMEOUT,
@ -1623,6 +1798,33 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
break;
}
/*
** $db incrblob ?DB? TABLE COLUMN ROWID
*/
case DB_INCRBLOB: {
const char *zDb = "main";
const char *zTable;
const char *zColumn;
sqlite_int64 iRow;
if( objc!=5 && objc!=6 ){
Tcl_WrongNumArgs(interp, 2, objv, "?DB? TABLE ROWID");
return TCL_ERROR;
}
if( objc==6 ){
zDb = Tcl_GetString(objv[2]);
}
zTable = Tcl_GetString(objv[objc-3]);
zColumn = Tcl_GetString(objv[objc-2]);
rc = Tcl_GetWideIntFromObj(interp, objv[objc-1], &iRow);
if( rc==TCL_OK ){
rc = createIncrblobChannel(interp, pDb, zDb, zTable, zColumn, iRow);
}
break;
}
/*
** $db interrupt
**

235
src/vdbeblob.c Normal file
View File

@ -0,0 +1,235 @@
/*
** 2007 May 1
**
** 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.
**
*************************************************************************
**
** $Id: vdbeblob.c,v 1.1 2007/05/01 17:49:49 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
#ifndef SQLITE_OMIT_INCRBLOB
/*
** Valid sqlite3_blob* handles point to Incrblob structures.
*/
typedef struct Incrblob Incrblob;
struct Incrblob {
int flags; /* Copy of "flags" passed to sqlite3_blob_open() */
int nByte; /* Size of open blob, in bytes */
int iOffset; /* Byte offset of blob in cursor data */
BtCursor *pCsr; /* Cursor pointing at blob row */
sqlite3_stmt *pStmt; /* Statement holding cursor open */
};
/*
** Open a blob handle.
*/
int sqlite3_blob_open(
sqlite3* db,
const char *zDb,
const char *zTable,
const char *zColumn,
sqlite_int64 iRow,
int flags, /* True -> read/write access, false -> read-only */
sqlite3_blob **ppBlob
){
int rc = SQLITE_OK;
int nAttempt = 0;
int iCol; /* Index of zColumn in row-record */
/* This VDBE program seeks a btree cursor to the identified
** db/table/row entry. The reason for using a vdbe program instead
** of writing code to use the b-tree layer directly is that the
** vdbe program will take advantage of the various transaction,
** locking and error handling infrastructure built into the vdbe.
**
** After seeking the cursor, the vdbe executes an OP_Callback.
** Code external to the Vdbe then "borrows" the b-tree cursor and
** uses it to implement the blob_read(), blob_write() and
** blob_bytes() functions.
**
** The sqlite3_blob_close() function finalizes the vdbe program,
** which closes the b-tree cursor and (possibly) commits the
** transaction.
*/
static const VdbeOpList openBlob[] = {
{OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
{OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */
{OP_Integer, 0, 0, 0}, /* 2: Database number */
/* One of the following two instructions is replaced by an
** OP_Noop before exection.
*/
{OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
{OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
{OP_SetNumColumns, 0, 0, 0}, /* 5: Num cols for cursor */
{OP_Variable, 1, 0, 0}, /* 6: Push the rowid to the stack */
{OP_NotExists, 0, 10, 0}, /* 7: Seek the cursor */
{OP_Column, 0, 0, 0}, /* 8 */
{OP_Callback, 0, 0, 0}, /* 9 */
{OP_Close, 0, 0, 0}, /* 10 */
{OP_Halt, 0, 0, 0}, /* 11 */
};
Vdbe *v = 0;
do {
Parse sParse;
Table *pTab;
memset(&sParse, 0, sizeof(Parse));
sParse.db = db;
pTab = sqlite3LocateTable(&sParse, zTable, zDb);
if( !pTab ){
sqlite3Error(db, sParse.rc, "%s", sParse.zErrMsg);
sqliteFree(sParse.zErrMsg);
rc = sParse.rc;
goto blob_open_out;
}
/* Now search pTab for the exact column. */
for(iCol=0; iCol < pTab->nCol; iCol++) {
if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
break;
}
}
if( iCol==pTab->nCol ){
sqlite3Error(db, SQLITE_ERROR, "no such column: %s", zColumn);
sqliteFree(sParse.zErrMsg);
rc = SQLITE_ERROR;
goto blob_open_out;
}
v = sqlite3VdbeCreate(db);
if( v ){
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
/* Configure the OP_Transaction */
sqlite3VdbeChangeP1(v, 0, iDb);
sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0));
/* Configure the OP_VerifyCookie */
sqlite3VdbeChangeP1(v, 1, iDb);
sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
/* Configure the db number pushed onto the stack */
sqlite3VdbeChangeP1(v, 2, iDb);
/* Remove either the OP_OpenWrite or OpenRead. Set the P2 parameter
** of the other to pTab->tnum.
*/
sqlite3VdbeChangeToNoop(v, (flags ? 3 : 4), 1);
sqlite3VdbeChangeP2(v, (flags ? 4 : 3), pTab->tnum);
/* Configure the OP_SetNumColumns. Configure the cursor to
** think that the table has one more column than it really
** does. An OP_Column to retrieve this imaginary column will
** always return an SQL NULL. This is useful because it means
** we can invoke OP_Column to fill in the vdbe cursors type
** and offset cache without causing any IO.
*/
sqlite3VdbeChangeP2(v, 5, pTab->nCol+1);
sqlite3VdbeMakeReady(v, 1, 0, 1, 0);
}
sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow);
rc = sqlite3_step((sqlite3_stmt *)v);
if( rc!=SQLITE_ROW ){
nAttempt++;
rc = sqlite3_finalize((sqlite3_stmt *)v);
v = 0;
}
} while( nAttempt<5 && rc==SQLITE_SCHEMA );
if( rc==SQLITE_ROW ){
/* The row-record has been opened successfully. Check that the
** column in question contains text or a blob. If it contains
** text, it is up to the caller to get the encoding right.
*/
Incrblob *pBlob;
u32 type = v->apCsr[0]->aType[iCol];
if( type<12 ){
rc = SQLITE_ERROR;
goto blob_open_out;
}
pBlob = (Incrblob *)sqliteMalloc(sizeof(Incrblob));
if( sqlite3MallocFailed() ){
sqliteFree(pBlob);
goto blob_open_out;
}
pBlob->flags = flags;
pBlob->pCsr = v->apCsr[0]->pCursor;
pBlob->pStmt = (sqlite3_stmt *)v;
pBlob->iOffset = v->apCsr[0]->aOffset[iCol];
pBlob->nByte = sqlite3VdbeSerialTypeLen(type);
*ppBlob = (sqlite3_blob *)pBlob;
rc = SQLITE_OK;
}else{
if( rc==SQLITE_DONE ){
rc = SQLITE_ERROR;
}
}
blob_open_out:
if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
sqlite3_finalize((sqlite3_stmt *)v);
}
sqlite3Error(db, rc, "");
return sqlite3ApiExit(db, rc);
}
/*
** Close a blob handle.
*/
int sqlite3_blob_close(sqlite3_blob *pBlob){
Incrblob *p = (Incrblob *)pBlob;
sqlite3_finalize(p->pStmt);
sqliteFree(p);
return SQLITE_OK;
}
/*
** Read data from a blob handle.
*/
int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){
Incrblob *p = (Incrblob *)pBlob;
if( (iOffset+n)>p->nByte ){
return SQLITE_ERROR;
}
return sqlite3BtreeData(p->pCsr, iOffset+p->iOffset, n, z);
}
/*
** Write data to a blob handle.
*/
int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
Incrblob *p = (Incrblob *)pBlob;
if( (iOffset+n)>p->nByte ){
return SQLITE_ERROR;
}
return sqlite3BtreePutData(p->pCsr, iOffset+p->iOffset, n, z);
}
/*
** Query a blob handle for the size of the data.
*/
int sqlite3_blob_bytes(sqlite3_blob *pBlob){
Incrblob *p = (Incrblob *)pBlob;
return p->nByte;
}
#endif /* #ifndef SQLITE_OMIT_INCRBLOB */

53
test/incrblob.test Normal file
View File

@ -0,0 +1,53 @@
# 2007 May 1
#
# 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.
#
#***********************************************************************
#
# $Id: incrblob.test,v 1.1 2007/05/01 17:49:49 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_test incrblob-1.1 {
execsql {
CREATE TABLE blobs(k PRIMARY KEY, v BLOB);
INSERT INTO blobs VALUES('one', X'0102030405060708090A');
INSERT INTO blobs VALUES('two', X'0A090807060504030201');
}
} {}
do_test incrblob-1.2.1 {
set ::blob [db incrblob blobs v 1]
} {incrblob_1}
do_test incrblob-1.2.2 {
binary scan [read $::blob] c* data
set data
} {1 2 3 4 5 6 7 8 9 10}
do_test incrblob-1.2.3 {
seek $::blob 0
puts -nonewline $::blob "1234567890"
flush $::blob
} {}
do_test incrblob-1.2.4 {
seek $::blob 0
binary scan [read $::blob] c* data
set data
} {49 50 51 52 53 54 55 56 57 48}
do_test incrblob-1.2.5 {
close $::blob
} {}
do_test incrblob-1.2.6 {
execsql {
SELECT v FROM blobs WHERE rowid = 1;
}
} {1234567890}
finish_test

View File

@ -15,7 +15,7 @@
# interface is pretty well tested. This file contains some addition
# tests for fringe issues that the main test suite does not cover.
#
# $Id: tclsqlite.test,v 1.56 2006/09/01 15:49:06 drh Exp $
# $Id: tclsqlite.test,v 1.57 2007/05/01 17:49:49 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -34,7 +34,7 @@ do_test tcl-1.1 {
do_test tcl-1.2 {
set v [catch {db bogus} msg]
lappend v $msg
} {1 {bad option "bogus": must be authorizer, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, rollback_hook, timeout, total_changes, trace, transaction, update_hook, or version}}
} {1 {bad option "bogus": must be authorizer, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, rollback_hook, timeout, total_changes, trace, transaction, update_hook, or version}}
do_test tcl-1.3 {
execsql {CREATE TABLE t1(a int, b int)}
execsql {INSERT INTO t1 VALUES(10,20)}