-C Fix\sa\sbug\swhere\saccessPayload()\swas\scalling\sPagerWrite()\son\sthe\swrong\spage\shandle.\sTicket\s#2332.\s(CVS\s3906)
-D 2007-05-03T13:11:32
+C Test\scases\sand\sminor\sbugfixes\sfor\sincremental\sblob\sAPIs.\s(CVS\s3907)
+D 2007-05-03T16:31:26
F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
F src/select.c 3c8f3bc7fd823abb8af30ec89ba6bcc515923fa1
F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
F src/shell.c 3ae4654560e91220a95738a73d135d91d937cda1
-F src/sqlite.h.in 1e053c58fd4df28c38ffdca2443b16d5f76f6f1e
+F src/sqlite.h.in a666300976897eced975b448f722a722b362c6b1
F src/sqlite3ext.h 7d0d363ea7327e817ef0dfe1b7eee1f171b72890
F src/sqliteInt.h 0b14d0eae083aafca0562d2261a404e5e5abc5f0
F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06
-F src/tclsqlite.c 23082fa8affdf3ae73937ca0755754fc562674bc
+F src/tclsqlite.c dde509871614d17f8ab5f3b4bc496b0af07280c7
F src/test1.c 29a39fdde51f4612082ecf3f5af54dac93766f87
F src/test2.c 24458b17ab2f3c90cbc1c8446bd7ffe69be62f88
F src/test3.c 946ea9d1a8c928656e3c70f0a2fcb8e733a15e86
F src/vdbeInt.h cb02cbbceddf3b40d49012e9f41576f17bcbec97
F src/vdbeapi.c 37d793559390bec8a00c556f651f21b5f9e589af
F src/vdbeaux.c 8c7f22e22d1ea578971f5a3fcd3a56a6882ced64
-F src/vdbeblob.c 0e070ded61b5db6ac55085d542bda5c2ee9e1a5f
+F src/vdbeblob.c 74fe0c7fc149a80715be7e3a33ed0e545d5e33e1
F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
F src/vdbemem.c ba98f8572ec4609846b368fa7580db178022f1bb
F src/vtab.c 89a0d5f39c1beba65a77fdb4d507b831fc5e6baf
F test/func.test 6727c7729472ae52b5acd86e802f89aa350ba50f
F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
-F test/incrblob.test cca13ec6c3a163c86821dd49a81bb5b519850e79
+F test/incrblob.test 9f6f5c23716d6c9386d1011cff732399900750df
F test/incrvacuum.test 2173bc075c7b3b96ccf228d737dd4f5c29500dc4
F test/incrvacuum_ioerr.test 0ebc382bcc2036ec58cf49cc5ffada45f75d907b
F test/index.test e65df12bed94b2903ee89987115e1578687e9266
F test/tkt2213.test 8cf7c446e1fcd0627fffe7fc19046eb24ac7333b
F test/tkt2251.test 3f0549213386ed911715665a908ff2bb7a871002
F test/tkt2285.test c618085f0c13ec3347e607f83c34ada0721b4bfa
-F test/tkt2332.test a7d678dc146ca7d2dae7bcc19f4659a389225b36
+F test/tkt2332.test 1623a64e0dfd5cf6d02e095d49ed3af1010da7c9
F test/trace.test 75ffc1b992c780d054748a656e3e7fd674f18567
F test/trans.test 3fe1b9e03b523482eee2b869858c5c1eca7b218b
F test/trigger1.test b361161cf20614024cc1e52ea0bdec250776b2ae
F test/trigger8.test 3a09275aa2214fdff56f731b1e775d8dfee4408a
F test/types.test 98e7a631bddf0806204358b452b02d0e319318a6
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
-F test/types3.test ea0ddf793ad5cd17c3b029dd8f48473059f873b6
+F test/types3.test c08b2b960064be30f5237cd2cf4680f32ba190be
F test/unique.test 0253c4227a5dc533e312202ce21ecfad18058d18
F test/update.test 7669ca789d62c258b678e8aa7a22a57eac10f2cf
F test/utf16.test 20e2d9ba0d57e952a18b1ac8deab9ad49e082893
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 678d672b73cc7b7f563c15daee3831cb5bbd890e
-R 3fe50c067312d08e6c011ae37b9a3ab6
+P cf9eeba7be64ad29cddd320832db10c799fb6e8e
+R 8a6f6ed85fe488d5b2d00df7c522a2c5
U danielk1977
-Z 99cbb46fec6c9867e749c0e87c86be78
+Z 6a4151dc8ab67d985ea9dda4365f5921
-cf9eeba7be64ad29cddd320832db10c799fb6e8e
\ No newline at end of file
+e12c522383bd40af375a52d2e68612c4dc7fd4db
\ No newline at end of file
** This header file defines the interface that the SQLite library
** presents to client programs.
**
-** @(#) $Id: sqlite.h.in,v 1.203 2007/05/02 01:34:31 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.204 2007/05/03 16:31:26 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
****** EXPERIMENTAL - subject to change without notice **************
*/
+/*
+** An instance of the following opaque structure is used to
+** represent an open blob handle.
+*/
typedef struct sqlite3_blob sqlite3_blob;
+/*
+** Open a handle to the blob located in row iRow,, column zColumn,
+** table zTable in database zDb. i.e. the same blob that would
+** be selected by:
+**
+** "SELECT zColumn FROM zDb.zTable WHERE rowid = iRow;
+**
+** If the flags parameter is non-zero, the blob is opened for
+** read and write access. If it is zero, the blob is opened for read
+** access.
+**
+** On success, SQLITE_OK is returned and the new blob-handle is
+** written to *ppBlob. Otherwise an error code is returned and
+** any value written to *ppBlob should not be used by the caller.
+** This function sets the database-handle error code and message
+** accessible via sqlite3_errcode() and sqlite3_errmsg().
+*/
int sqlite3_blob_open(
sqlite3*,
const char *zDb,
sqlite3_blob **ppBlob
);
+/*
+** Close an open blob handle.
+*/
int sqlite3_blob_close(sqlite3_blob *);
+/*
+** Return the size in bytes of the blob accessible via the open
+** blob-handle passed as an argument.
+*/
+int sqlite3_blob_bytes(sqlite3_blob *);
+
+/*
+** This function is used to read data from an open blob-handle into
+** a caller supplied buffer. n bytes of data are copied into buffer
+** z from the open blob, starting at offset iOffset.
+**
+** On success, SQLITE_OK is returned. Otherwise, an SQLite error
+** code.
+*/
int sqlite3_blob_read(sqlite3_blob *, void *z, int n, int iOffset);
+
+/*
+** This function is used to write data from an open blob-handle into
+** a user supplied buffer. n bytes of data are copied from the buffer
+** pointed to by z into the open blob, starting at offset iOffset.
+**
+** If the blob-handle passed as the first argument was not opened for
+** writing (the flags parameter to sqlite3_blob_open was zero), this
+** function returns SQLITE_READONLY.
+**
+** This function may only modify the contents of the blob, it is
+** not possible to increase the size of a blob using this API. If
+** offset iOffset is less than n bytes from the end of the blob,
+** SQLITE_ERROR is returned and no data is written.
+**
+** On success, SQLITE_OK is returned. Otherwise, an SQLite error
+** code. If an error occurs, this function sets the
+*/
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
** 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.181 2007/05/02 13:16:31 danielk1977 Exp $
+** $Id: tclsqlite.c,v 1.182 2007/05/03 16:31:26 danielk1977 Exp $
*/
#include "tcl.h"
#include <errno.h>
const char *zDb,
const char *zTable,
const char *zColumn,
- sqlite_int64 iRow
+ sqlite_int64 iRow,
+ int isReadonly
){
IncrblobChannel *p;
+ sqlite3 *db = pDb->db;
sqlite3_blob *pBlob;
int rc;
- int flags = TCL_READABLE|TCL_WRITABLE;
+ int flags = TCL_READABLE|(isReadonly ? 0 : 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);
+ rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, &pBlob);
if( rc!=SQLITE_OK ){
Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
return TCL_ERROR;
}
/*
- ** $db incrblob ?DB? TABLE COLUMN ROWID
+ ** $db incrblob ?-readonly? ?DB? TABLE COLUMN ROWID
*/
case DB_INCRBLOB: {
+ int isReadonly = 0;
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");
+ /* Check for the -readonly option */
+ if( objc>3 && strcmp(Tcl_GetString(objv[2]), "-readonly")==0 ){
+ isReadonly = 1;
+ }
+
+ if( objc!=(5+isReadonly) && objc!=(6+isReadonly) ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? ?DB? TABLE COLUMN ROWID");
return TCL_ERROR;
}
- if( objc==6 ){
+ if( objc==(6+isReadonly) ){
zDb = Tcl_GetString(objv[2]);
}
zTable = Tcl_GetString(objv[objc-3]);
rc = Tcl_GetWideIntFromObj(interp, objv[objc-1], &iRow);
if( rc==TCL_OK ){
- rc = createIncrblobChannel(interp, pDb, zDb, zTable, zColumn, iRow);
+ rc = createIncrblobChannel(
+ interp, pDb, zDb, zTable, zColumn, iRow, isReadonly
+ );
}
break;
}
**
*************************************************************************
**
-** $Id: vdbeblob.c,v 1.3 2007/05/03 11:43:33 danielk1977 Exp $
+** $Id: vdbeblob.c,v 1.4 2007/05/03 16:31:26 danielk1977 Exp $
*/
#include "sqliteInt.h"
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 */
};
Vdbe *v = 0;
+ int rc = SQLITE_OK;
+ char zErr[128] = {0};
do {
Parse sParse;
pTab = sqlite3LocateTable(&sParse, zTable, zDb);
if( !pTab ){
- sqlite3Error(db, sParse.rc, "%s", sParse.zErrMsg);
+ if( sParse.zErrMsg ){
+ sqlite3_snprintf(sizeof(zErr), zErr, "%s", sParse.zErrMsg);
+ zErr[sizeof(zErr)-1] = '\0';
+ }
sqliteFree(sParse.zErrMsg);
- rc = sParse.rc;
+ rc = SQLITE_ERROR;
sqlite3SafetyOff(db);
goto blob_open_out;
}
}
}
if( iCol==pTab->nCol ){
- sqlite3Error(db, SQLITE_ERROR, "no such column: %s", zColumn);
- sqliteFree(sParse.zErrMsg);
+ sprintf(zErr, "no such column: \"%s\"", zColumn);
rc = SQLITE_ERROR;
sqlite3SafetyOff(db);
goto blob_open_out;
if( rc!=SQLITE_ROW ){
nAttempt++;
rc = sqlite3_finalize((sqlite3_stmt *)v);
+ sprintf(zErr, "no such rowid: %lld", iRow);
v = 0;
}
} while( nAttempt<5 && rc==SQLITE_SCHEMA );
pBlob->nByte = sqlite3VdbeSerialTypeLen(type);
*ppBlob = (sqlite3_blob *)pBlob;
rc = SQLITE_OK;
- }else{
- if( rc==SQLITE_DONE ){
- rc = SQLITE_ERROR;
- }
+ }else if( rc==SQLITE_OK ){
+ rc = SQLITE_ERROR;
}
blob_open_out:
if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
sqlite3_finalize((sqlite3_stmt *)v);
}
- sqlite3Error(db, rc, "");
+ sqlite3Error(db, rc, zErr);
return sqlite3ApiExit(db, rc);
}
#
#***********************************************************************
#
-# $Id: incrblob.test,v 1.4 2007/05/03 13:11:32 danielk1977 Exp $
+# $Id: incrblob.test,v 1.5 2007/05/03 16:31:26 danielk1977 Exp $
#
set testdir [file dirname $argv0]
#------------------------------------------------------------------------
-# incrblob-2.*: Test that the following operations use ptrmap pages:
+# incrblob-2.*:
#
-# * Reading near the end of a blob,
-# * Writing near the end of a blob (TODO),
-# * SELECT a column value that is located on an overflow page (TODO).
+# Test that the following operations use ptrmap pages to reduce
+# unnecessary reads:
#
+# * Reading near the end of a blob,
+# * Writing near the end of a blob, and
+# * SELECT a column value that is located on an overflow page.
#
proc nRead {db} {
set bt [btree_from_db $db]
array set stats [btree_pager_stats $bt]
return $stats(read)
}
+proc nWrite {db} {
+ set bt [btree_from_db $db]
+ array set stats [btree_pager_stats $bt]
+ return $stats(write)
+}
foreach AutoVacuumMode [list 0 1] {
set ::str [string repeat abcdefghij 2900]
execsql {
BEGIN;
- CREATE TABLE blobs(k PRIMARY KEY, v BLOB);
+ CREATE TABLE blobs(k PRIMARY KEY, v BLOB, i INTEGER);
DELETE FROM blobs;
- INSERT INTO blobs VALUES('one', $::str || randstr(500,500));
+ INSERT INTO blobs VALUES('one', $::str || randstr(500,500), 45);
COMMIT;
}
expr [file size test.db]/1024
nRead db
} [expr $AutoVacuumMode ? 4 : 30]
- do_test incrblob-2.$AutoVacuumMode.3 {
+ do_test incrblob-2.$AutoVacuumMode.4 {
string range [db one {SELECT v FROM blobs}] end-19 end
} $::fragment
+
+ do_test incrblob-2.$AutoVacuumMode.5 {
+ # Open and close the db to make sure the page cache is empty.
+ db close
+ sqlite3 db test.db
+
+ # Write the second-to-last 20 bytes of the blob via a blob handle.
+ #
+ set ::blob [db incrblob blobs v 1]
+ seek $::blob -40 end
+ puts -nonewline $::blob "1234567890abcdefghij"
+ flush $::blob
+
+ # If the database is not in auto-vacuum mode, the whole of
+ # the overflow-chain must be scanned. In auto-vacuum mode,
+ # sqlite uses the ptrmap pages to avoid reading the other pages.
+ #
+ nRead db
+ } [expr $AutoVacuumMode ? 4 : 30]
+
+ # Pages 1 (the write-counter) and 32 (the blob data) were written.
+ do_test incrblob-2.$AutoVacuumMode.6 {
+ close $::blob
+ nWrite db
+ } 2
+
+ do_test incrblob-2.$AutoVacuumMode.7 {
+ string range [db one {SELECT v FROM blobs}] end-39 end-20
+ } "1234567890abcdefghij"
+
+ do_test incrblob-2.$AutoVacuumMode.8 {
+ # Open and close the db to make sure the page cache is empty.
+ db close
+ sqlite3 db test.db
+
+ execsql { SELECT i FROM blobs }
+ } {45}
+
+ do_test incrblob-2.$AutoVacuumMode.9 {
+ nRead db
+ } [expr $AutoVacuumMode ? 4 : 30]
}
+#------------------------------------------------------------------------
+# incrblob-3.*:
+#
+# Test the outcome of trying to write to a read-only blob handle.
+#
+# TODO: The following test only tests the tcl interface, not the
+# underlying sqlite3 interface. Need to find some other method
+# to call sqlite3_blob_write() on a readonly handle...
+#
+do_test incrblob-3.1 {
+ set ::blob [db incrblob -readonly blobs v 1]
+ seek $::blob -40 end
+ read $::blob 20
+} "1234567890abcdefghij"
+do_test incrblob-3.2 {
+ seek $::blob 0
+ set rc [catch {
+ puts -nonewline $::blob "helloworld"
+ } msg]
+ list $rc $msg
+} "1 {channel \"$::blob\" wasn't opened for writing}"
+
+#------------------------------------------------------------------------
+# incrblob-4.*:
+#
+# Try a couple of error conditions:
+#
+# 4.1 - Attempt to open a row that does not exist.
+# 4.2 - Attempt to open a column that does not exist.
+# 4.3 - Attempt to open a table that does not exist.
+# 4.4 - Attempt to open a database that does not exist.
+#
+do_test incrblob-4.1 {
+ set rc [catch {
+ set ::blob [db incrblob blobs v 2]
+ } msg ]
+ list $rc $msg
+} {1 {no such rowid: 2}}
+
+do_test incrblob-4.2 {
+ set rc [catch {
+ set ::blob [db incrblob blobs blue 1]
+ } msg ]
+ list $rc $msg
+} {1 {no such column: "blue"}}
+
+do_test incrblob-4.3 {
+ set rc [catch {
+ set ::blob [db incrblob nosuchtable blue 1]
+ } msg ]
+ list $rc $msg
+} {1 {no such table: main.nosuchtable}}
+
+do_test incrblob-4.4 {
+ set rc [catch {
+ set ::blob [db incrblob nosuchdb blobs v 1]
+ } msg ]
+ list $rc $msg
+} {1 {no such table: nosuchdb.blobs}}
+
finish_test
+
#
#***********************************************************************
#
-# $Id: tkt2332.test,v 1.1 2007/05/03 13:11:32 danielk1977 Exp $
+# $Id: tkt2332.test,v 1.2 2007/05/03 16:31:26 danielk1977 Exp $
#
set testdir [file dirname $argv0]
incr ::iKey
}
+# Free memory:
+unset ::blobstr
+
finish_test
# of this file is testing the interaction of SQLite manifest types
# with Tcl dual-representations.
#
-# $Id: types3.test,v 1.5 2006/01/17 09:35:03 danielk1977 Exp $
+# $Id: types3.test,v 1.6 2007/05/03 16:31:26 danielk1977 Exp $
#
set testdir [file dirname $argv0]
# A variable with an integer representation comes in as INTEGER
do_test types3-1.2 {
- set V [expr {1+2}]
+ set V [expr {int(1+2)}]
concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
} {int integer}
do_test types3-1.3 {