/ Check-in [e12c5223]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Test cases and minor bugfixes for incremental blob APIs. (CVS 3907)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:e12c522383bd40af375a52d2e68612c4dc7fd4db
User & Date: danielk1977 2007-05-03 16:31:26
Context
2007-05-03
16:55
Get the amalgamation builder working with incremental I/O. (CVS 3908) check-in: 92b53601 user: drh tags: trunk
16:31
Test cases and minor bugfixes for incremental blob APIs. (CVS 3907) check-in: e12c5223 user: danielk1977 tags: trunk
13:11
Fix a bug where accessPayload() was calling PagerWrite() on the wrong page handle. Ticket #2332. (CVS 3906) check-in: cf9eeba7 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/sqlite.h.in.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1869
1870
1871
1872
1873
1874
1875




1876
1877

















1878
1879
1880
1881
1882
1883
1884
1885
1886
1887



1888
1889














1890


















1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** 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 $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
**
** When the virtual-table mechanism stablizes, we will declare the
** interface fixed, support it indefinitely, and remove this comment.
**
****** 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.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif







|







 







>
>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<













8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947

1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.204 2007/05/03 16:31:26 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
**
** When the virtual-table mechanism stablizes, we will declare the
** interface fixed, support it indefinitely, and remove this comment.
**
****** 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,
  const char *zTable,
  const char *zColumn,
  sqlite_int64 iRow,
  int flags,
  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);


/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif

Changes to src/tclsqlite.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
295
296
297
298
299
300
301
302

303
304

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
....
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854

1855
1856
1857
1858
1859
1860





1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872

1873

1874
1875
1876
1877
1878
1879
1880
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** 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 $
*/
#include "tcl.h"
#include <errno.h>

/*
** Some additional include files are needed if this file is not
** appended to the amalgamation.
................................................................................
*/
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;
  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;
................................................................................
      /* Must flush any cached statements */
      flushStmtCache( pDb );
    }
    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
  **







|







 







|
>


>


|





|







 







|


>





|
>
>
>
>
>
|



|







>
|
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
....
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** 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.182 2007/05/03 16:31:26 danielk1977 Exp $
*/
#include "tcl.h"
#include <errno.h>

/*
** Some additional include files are needed if this file is not
** appended to the amalgamation.
................................................................................
*/
static int createIncrblobChannel(
  Tcl_Interp *interp, 
  SqliteDb *pDb, 
  const char *zDb,
  const char *zTable, 
  const char *zColumn, 
  sqlite_int64 iRow,
  int isReadonly
){
  IncrblobChannel *p;
  sqlite3 *db = pDb->db;
  sqlite3_blob *pBlob;
  int rc;
  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(db, zDb, zTable, zColumn, iRow, !isReadonly, &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;
................................................................................
      /* Must flush any cached statements */
      flushStmtCache( pDb );
    }
    break;
  }

  /*
  **     $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;

    /* 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+isReadonly) ){
      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, isReadonly
      );
    }
    break;
  }

  /*
  **     $db interrupt
  **

Changes to src/vdbeblob.c.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
..
79
80
81
82
83
84
85


86
87
88
89
90
91
92
..
94
95
96
97
98
99
100
101



102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
158
159
160
161
162
163
164

165
166
167
168
169
170
171
...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
**
**    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.3 2007/05/03 11:43:33 danielk1977 Exp $
*/

#include "sqliteInt.h"
#include "vdbeInt.h"

#ifndef SQLITE_OMIT_INCRBLOB

................................................................................
  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,
................................................................................
    {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;
................................................................................
    rc = sqlite3SafetyOn(db);
    if( rc!=SQLITE_OK ){
      return rc;
    }

    pTab = sqlite3LocateTable(&sParse, zTable, zDb);
    if( !pTab ){
      sqlite3Error(db, sParse.rc, "%s", sParse.zErrMsg);



      sqliteFree(sParse.zErrMsg);
      rc = sParse.rc;

      sqlite3SafetyOff(db);
      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;
      sqlite3SafetyOff(db);
      goto blob_open_out;
    }

    v = sqlite3VdbeCreate(db);
    if( v ){
................................................................................
    }

    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
................................................................................
    pBlob->pCsr =  v->apCsr[0]->pCursor;
    sqlite3BtreeCacheOverflow(pBlob->pCsr);
    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){







|







 







<







 







>
>







 







|
>
>
>

<
>











|
<







 







>







 







<
|
|
<






|







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
39
40
41
42
43
44
45

46
47
48
49
50
51
52
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
..
95
96
97
98
99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
...
191
192
193
194
195
196
197

198
199

200
201
202
203
204
205
206
207
208
209
210
211
212
213
**
**    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.4 2007/05/03 16:31:26 danielk1977 Exp $
*/

#include "sqliteInt.h"
#include "vdbeInt.h"

#ifndef SQLITE_OMIT_INCRBLOB

................................................................................
  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 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,
................................................................................
    {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;
  int rc = SQLITE_OK;
  char zErr[128] = {0};

  do {
    Parse sParse;
    Table *pTab;

    memset(&sParse, 0, sizeof(Parse));
    sParse.db = db;
................................................................................
    rc = sqlite3SafetyOn(db);
    if( rc!=SQLITE_OK ){
      return rc;
    }

    pTab = sqlite3LocateTable(&sParse, zTable, zDb);
    if( !pTab ){
      if( sParse.zErrMsg ){
        sqlite3_snprintf(sizeof(zErr), zErr, "%s", sParse.zErrMsg);
        zErr[sizeof(zErr)-1] = '\0';
      }
      sqliteFree(sParse.zErrMsg);

      rc = SQLITE_ERROR;
      sqlite3SafetyOff(db);
      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 ){
      sprintf(zErr, "no such column: \"%s\"", zColumn);

      rc = SQLITE_ERROR;
      sqlite3SafetyOff(db);
      goto blob_open_out;
    }

    v = sqlite3VdbeCreate(db);
    if( v ){
................................................................................
    }

    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);
      sprintf(zErr, "no such rowid: %lld", iRow);
      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
................................................................................
    pBlob->pCsr =  v->apCsr[0]->pCursor;
    sqlite3BtreeCacheOverflow(pBlob->pCsr);
    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_OK ){
    rc = SQLITE_ERROR;

  }

blob_open_out:
  if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
    sqlite3_finalize((sqlite3_stmt *)v);
  }
  sqlite3Error(db, rc, zErr);
  return sqlite3ApiExit(db, rc);
}

/*
** Close a blob handle.
*/
int sqlite3_blob_close(sqlite3_blob *pBlob){

Changes to test/incrblob.test.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
75
76
77
78
79
80
81


82

83
84
85
86
87
88
89
90
91
92
93





94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
132
133
134
135
136
137
138
139
140
141
142




143

































































































144

#
#    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.4 2007/05/03 13:11:32 danielk1977 Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test incrblob-1.1 {
  execsql {
................................................................................
} {....123456}
do_test incrblob-1.3.10 {
  close $::blob
} {}


#------------------------------------------------------------------------


# incrblob-2.*: Test that the following operations use ptrmap pages:

#
#     * 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).
#
#
proc nRead {db} {
  set bt [btree_from_db $db]
  array set stats [btree_pager_stats $bt]
  return $stats(read)
}






foreach AutoVacuumMode [list 0 1] {

  db close
  file delete -force test.db test.db-journal

  sqlite3 db test.db
  execsql "PRAGMA auto_vacuum = $AutoVacuumMode"

  do_test incrblob-2.$AutoVacuumMode.1 {
    set ::str [string repeat abcdefghij 2900]
    execsql {
      BEGIN;
      CREATE TABLE blobs(k PRIMARY KEY, v BLOB);
      DELETE FROM blobs;
      INSERT INTO blobs VALUES('one', $::str || randstr(500,500));
      COMMIT;
    }
    expr [file size test.db]/1024
  } [expr 31 + $AutoVacuumMode]

  do_test incrblob-2.$AutoVacuumMode.2 {
    execsql {
................................................................................
    # 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]

  do_test incrblob-2.$AutoVacuumMode.3 {
    string range [db one {SELECT v FROM blobs}] end-19 end
  } $::fragment
}






































































































finish_test








|







 







>
>
|
>


|
|
<






>
>
>
>
>













|

|







 







|


|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#
#    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.5 2007/05/03 16:31:26 danielk1977 Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test incrblob-1.1 {
  execsql {
................................................................................
} {....123456}
do_test incrblob-1.3.10 {
  close $::blob
} {}


#------------------------------------------------------------------------
# incrblob-2.*: 
#
# 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] {

  db close
  file delete -force test.db test.db-journal

  sqlite3 db test.db
  execsql "PRAGMA auto_vacuum = $AutoVacuumMode"

  do_test incrblob-2.$AutoVacuumMode.1 {
    set ::str [string repeat abcdefghij 2900]
    execsql {
      BEGIN;
      CREATE TABLE blobs(k PRIMARY KEY, v BLOB, i INTEGER);
      DELETE FROM blobs;
      INSERT INTO blobs VALUES('one', $::str || randstr(500,500), 45);
      COMMIT;
    }
    expr [file size test.db]/1024
  } [expr 31 + $AutoVacuumMode]

  do_test incrblob-2.$AutoVacuumMode.2 {
    execsql {
................................................................................
    # 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]

  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

Changes to test/tkt2332.test.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
52
53
54
55
56
57
58



59
#
#    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: tkt2332.test,v 1.1 2007/05/03 13:11:32 danielk1977 Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test tkt2332.1 {
  execsql {
................................................................................
  do_test tkt2332.$Len.5 {
    lindex [execsql {SELECT v FROM blobs WHERE k = $::iKey}] 0
  } $::blobstr

  incr ::iKey
}




finish_test







|







 







>
>
>

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
52
53
54
55
56
57
58
59
60
61
62
#
#    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: tkt2332.test,v 1.2 2007/05/03 16:31:26 danielk1977 Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test tkt2332.1 {
  execsql {
................................................................................
  do_test tkt2332.$Len.5 {
    lindex [execsql {SELECT v FROM blobs WHERE k = $::iKey}] 0
  } $::blobstr

  incr ::iKey
}

# Free memory:
unset ::blobstr

finish_test

Changes to test/types3.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus
# 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 $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# A variable with only a string representation comes in as TEXT
do_test types3-1.1 {
................................................................................
  set V {}
  append V {}
  concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
} {string text}

# A variable with an integer representation comes in as INTEGER
do_test types3-1.2 {
  set V [expr {1+2}]
  concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
} {int integer}
do_test types3-1.3 {
  set V [expr {1+123456789012345}]
  concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
} {wideInt integer}








|







 







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus
# of this file is testing the interaction of SQLite manifest types
# with Tcl dual-representations.
#
# $Id: types3.test,v 1.6 2007/05/03 16:31:26 danielk1977 Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# A variable with only a string representation comes in as TEXT
do_test types3-1.1 {
................................................................................
  set V {}
  append V {}
  concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
} {string text}

# A variable with an integer representation comes in as INTEGER
do_test types3-1.2 {
  set V [expr {int(1+2)}]
  concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
} {int integer}
do_test types3-1.3 {
  set V [expr {1+123456789012345}]
  concat [tcl_variable_type V] [execsql {SELECT typeof(:V)}]
} {wideInt integer}