Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Use the pointer-map pages to make the incremental blob API more efficient. (CVS 3896) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
93a3bf71d576096f4b5a3db256ca6f9b |
User & Date: | danielk1977 2007-05-02 13:16:30.000 |
Context
2007-05-02
| ||
13:30 | Add support for zero-blobs to the OP_MakeRecord opcode. First test cases of zeroblob functionality. (CVS 3897) (check-in: e6d560ddee user: drh tags: trunk) | |
13:16 | Use the pointer-map pages to make the incremental blob API more efficient. (CVS 3896) (check-in: 93a3bf71d5 user: danielk1977 tags: trunk) | |
02:08 | Fix some compiler warnings. Add the (untested) zeroblob() SQL function. (CVS 3895) (check-in: 6f4f8ba7ec user: drh tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2004 April 6 ** ** 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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2004 April 6 ** ** 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: btree.c,v 1.365 2007/05/02 13:16:30 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: ** "Sorting And Searching", pages 473-480. Addison-Wesley ** Publishing Company, Reading, Massachusetts. |
︙ | ︙ | |||
3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 | }else{ getCellInfo(pCur); *pSize = pCur->info.nData; } } return rc; } /* ** Read payload information from the entry that the pCur cursor is ** pointing to. Begin reading the payload at "offset" and read ** a total of "amt" bytes. Put the result in zBuf. ** ** This routine does not make a distinction between key and data. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 | }else{ getCellInfo(pCur); *pSize = pCur->info.nData; } } return rc; } /* ** Given the page number of an overflow page in the database (parameter ** ovfl), this function finds the page number of the next page in the ** linked list of overflow pages. If possible, it uses the auto-vacuum ** pointer-map data instead of reading the content of page ovfl to do so. ** ** If an error occurs an SQLite error code is returned. Otherwise: ** ** Unless pPgnoNext is NULL, the page number of the next overflow ** page in the linked list is written to *pPgnoNext. If page ovfl ** is the last page in it's linked list, *pPgnoNext is set to zero. ** ** If ppPage is not NULL, *ppPage is set to the MemPage* handle ** for page ovfl. The underlying pager page may have been requested ** with the noContent flag set, so the page data accessable via ** this handle may not be trusted. */ static int getOverflowPage( BtShared *pBt, Pgno ovfl, /* Overflow page */ MemPage **ppPage, /* OUT: MemPage handle */ Pgno *pPgnoNext /* OUT: Next overflow page number */ ){ Pgno next = 0; int rc; /* One of these must not be NULL. Otherwise, why call this function? */ assert(ppPage || pPgnoNext); /* If pPgnoNext is NULL, then this function is being called to obtain ** a MemPage* reference only. No page-data is required in this case. */ if( !pPgnoNext ){ return getPage(pBt, ovfl, ppPage, 1); } #ifndef SQLITE_OMIT_AUTOVACUUM /* Try to find the next page in the overflow list using the ** autovacuum pointer-map pages. Guess that the next page in ** the overflow list is page number (ovfl+1). If that guess turns ** out to be wrong, fall back to loading the data of page ** number ovfl to determine the next page number. */ if( pBt->autoVacuum ){ Pgno pgno; Pgno iGuess = ovfl+1; u8 eType; while( PTRMAP_ISPAGE(pBt, iGuess) || iGuess==PENDING_BYTE_PAGE(pBt) ){ iGuess++; } if( iGuess<sqlite3PagerPagecount(pBt->pPager) ){ rc = ptrmapGet(pBt, iGuess, &eType, &pgno); if( rc!=SQLITE_OK ){ return rc; } if( eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ next = iGuess; } } } #endif if( next==0 || ppPage ){ MemPage *pPage = 0; rc = getPage(pBt, ovfl, &pPage, next!=0); assert(rc==SQLITE_OK || pPage==0); if( next==0 && rc==SQLITE_OK ){ next = get4byte(pPage->aData); } if( ppPage ){ *ppPage = pPage; }else{ releasePage(pPage); } } *pPgnoNext = next; return rc; } /* ** Read payload information from the entry that the pCur cursor is ** pointing to. Begin reading the payload at "offset" and read ** a total of "amt" bytes. Put the result in zBuf. ** ** This routine does not make a distinction between key and data. |
︙ | ︙ | |||
3082 3083 3084 3085 3086 3087 3088 | }else{ offset -= pCur->info.nLocal; } ovflSize = pBt->usableSize - 4; if( amt>0 ){ nextPage = get4byte(&aPayload[pCur->info.nLocal]); while( amt>0 && nextPage ){ | > > > > > > > > > > > > > > | > | | | | | | < < | < < | 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 | }else{ offset -= pCur->info.nLocal; } ovflSize = pBt->usableSize - 4; if( amt>0 ){ nextPage = get4byte(&aPayload[pCur->info.nLocal]); while( amt>0 && nextPage ){ if( offset>=ovflSize ){ /* The only reason to read this page is to obtain the page ** number for the next page in the overflow chain. So try ** the getOverflowPage() shortcut. */ rc = getOverflowPage(pBt, nextPage, 0, &nextPage); if( rc!=SQLITE_OK ){ return rc; } offset -= ovflSize; }else{ /* Need to read this page properly, to obtain data to copy into ** the caller's buffer. */ DbPage *pDbPage; int a = amt; rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage); if( rc!=0 ){ return rc; } aPayload = sqlite3PagerGetData(pDbPage); nextPage = get4byte(aPayload); if( a + offset > ovflSize ){ a = ovflSize - offset; } memcpy(pBuf, &aPayload[offset+4], a); offset = 0; amt -= a; pBuf += a; sqlite3PagerUnref(pDbPage); } } } if( amt>0 ){ return SQLITE_CORRUPT_BKPT; } return SQLITE_OK; |
︙ | ︙ | |||
4042 4043 4044 4045 4046 4047 4048 | TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); } releasePage(pTrunk); } return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 | TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); } releasePage(pTrunk); } return rc; } /* ** Free any overflow pages associated with the given Cell. */ static int clearCell(MemPage *pPage, unsigned char *pCell){ BtShared *pBt = pPage->pBt; CellInfo info; Pgno ovflPgno; |
︙ | ︙ | |||
6821 6822 6823 6824 6825 6826 6827 | ** 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){ | | < < < < > > > | > > | > | > > > > > > > > > | > | | > > > > > | > > > | > | | > > > > > > | | | | > > > > > | > > > > > > > > > | > > > > > > > | > > | | > > > > | | > > | | | < | > | > > > | | | | | 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 | ** 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){ BtShared *pBt = pCsr->pBtree->pBt; int rc; u32 iRem = amt; /* Remaining bytes to write */ u8 *zRem = (u8 *)z; /* Pointer to data not yet written */ u32 iOffset = offset; /* Offset from traversal point to start of write */ Pgno iOvfl; /* Page number for next overflow page */ int ovflSize; /* Bytes of data per overflow page. */ CellInfo *pInfo; /* Check some preconditions: ** (a) a write-transaction is open, ** (b) the cursor is open for writing, ** (c) there is no read-lock on the table being modified and ** (d) the cursor points at a valid row of an intKey table. */ if( pBt->inTransaction!=TRANS_WRITE ){ /* Must start a transaction before doing an insert */ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } assert( !pBt->readOnly ); if( !pCsr->wrFlag ){ return SQLITE_PERM; /* Cursor not open for writing */ } if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr) ){ return SQLITE_LOCKED; /* The table pCur points to has a read lock */ } if( pCsr->eState==CURSOR_INVALID || !pCsr->pPage->intKey ){ return SQLITE_ERROR; } /* Parse the cell-info. Check that the cell-data area is large ** enough for the proposed write operation. */ getCellInfo(pCsr); pInfo = &pCsr->info; if( pInfo->nData<(offset+amt) ){ return SQLITE_ERROR; } if( pInfo->nLocal>iOffset ){ /* In this case data must be written to the b-tree page. */ int iWrite = pInfo->nLocal - offset; if( iWrite>iRem ){ iWrite = iRem; } rc = sqlite3PagerWrite(pCsr->pPage->pDbPage); if( rc!=SQLITE_OK ){ return rc; } memcpy(&pInfo->pCell[iOffset+pInfo->nHeader], zRem, iWrite); zRem += iWrite; iRem -= iWrite; } iOffset = ((iOffset<pInfo->nLocal)?0:(iOffset-pInfo->nLocal)); ovflSize = pBt->usableSize - 4; assert(pInfo->iOverflow>0 || iRem==0); iOvfl = get4byte(&pInfo->pCell[pInfo->iOverflow]); while( iRem>0 ){ if( iOffset>ovflSize ){ /* The only reason to read this page is to obtain the page ** number for the next page in the overflow chain. So try ** the getOverflowPage() shortcut. */ rc = getOverflowPage(pBt, iOvfl, 0, &iOvfl); if( rc!=SQLITE_OK ){ return rc; } iOffset -= ovflSize; }else{ int iWrite = ovflSize - iOffset; DbPage *pOvfl; /* The overflow page. */ u8 *aData; /* Page data */ rc = sqlite3PagerGet(pBt->pPager, iOvfl, &pOvfl); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pOvfl); if( rc!=SQLITE_OK ){ sqlite3PagerUnref(pOvfl); return rc; } aData = sqlite3PagerGetData(pOvfl); iOvfl = get4byte(aData); if( iWrite>iRem ){ iWrite = iRem; } memcpy(&aData[iOffset+4], zRem, iWrite); sqlite3PagerUnref(pOvfl); zRem += iWrite; iRem -= iWrite; iOffset = ((iOffset<ovflSize)?0:(iOffset-ovflSize)); } } return SQLITE_OK; } #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 ** the definition of BtShared. |
︙ | ︙ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** 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. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** 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. |
︙ | ︙ | |||
85 86 87 88 89 90 91 92 93 94 95 96 97 98 | SqlPreparedStmt *pNext; /* Next in linked list */ SqlPreparedStmt *pPrev; /* Previous on the list */ sqlite3_stmt *pStmt; /* The prepared statement */ int nSql; /* chars in zSql[] */ char zSql[1]; /* Text of the SQL statement */ }; /* ** There is one instance of this structure for each SQLite database ** that has been opened by the SQLite TCL interface. */ typedef struct SqliteDb SqliteDb; struct SqliteDb { sqlite3 *db; /* The "real" database structure. MUST BE FIRST */ | > > | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | SqlPreparedStmt *pNext; /* Next in linked list */ SqlPreparedStmt *pPrev; /* Previous on the list */ sqlite3_stmt *pStmt; /* The prepared statement */ int nSql; /* chars in zSql[] */ char zSql[1]; /* Text of the SQL statement */ }; typedef struct IncrblobChannel IncrblobChannel; /* ** There is one instance of this structure for each SQLite database ** that has been opened by the SQLite TCL interface. */ typedef struct SqliteDb SqliteDb; struct SqliteDb { sqlite3 *db; /* The "real" database structure. MUST BE FIRST */ |
︙ | ︙ | |||
110 111 112 113 114 115 116 117 118 | SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ }; | > < > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 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 | SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ }; struct IncrblobChannel { SqliteDb *pDb; /* Associated database connection */ sqlite3_blob *pBlob; /* sqlite3 blob handle */ int iSeek; /* Current seek offset */ Tcl_Channel channel; /* Channel identifier */ IncrblobChannel *pNext; /* Linked list of all open incrblob channels */ IncrblobChannel *pPrev; /* Linked list of all open incrblob channels */ }; /* ** Close all incrblob channels opened using database connection pDb. ** This is called when shutting down the database connection. */ static void closeIncrblobChannels(SqliteDb *pDb){ IncrblobChannel *p; IncrblobChannel *pNext; for(p=pDb->pIncrblob; p; p=pNext){ pNext = p->pNext; /* Note: Calling unregister here call Tcl_Close on the incrblob channel, ** which deletes the IncrblobChannel structure at *p. So do not ** call Tcl_Free() here. */ Tcl_UnregisterChannel(pDb->interp, p->channel); } } /* ** Close an incremental blob channel. */ static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){ IncrblobChannel *p = (IncrblobChannel *)instanceData; sqlite3_blob_close(p->pBlob); /* Remove the channel from the SqliteDb.pIncrblob list. */ if( p->pNext ){ p->pNext->pPrev = p->pPrev; } if( p->pPrev ){ p->pPrev->pNext = p->pNext; } if( p->pDb->pIncrblob==p ){ p->pDb->pIncrblob = p->pNext; } Tcl_Free((char *)p); return TCL_OK; } /* ** Read data from an incremental blob channel. */ |
︙ | ︙ | |||
160 161 162 163 164 165 166 167 168 169 170 171 172 173 | return -1; } p->iSeek += nRead; return nRead; } static int incrblobOutput( ClientData instanceData, CONST char *buf, int toWrite, int *errorCodePtr ){ IncrblobChannel *p = (IncrblobChannel *)instanceData; | > > > | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | return -1; } p->iSeek += nRead; return nRead; } /* ** Write data to an incremental blob channel. */ static int incrblobOutput( ClientData instanceData, CONST char *buf, int toWrite, int *errorCodePtr ){ IncrblobChannel *p = (IncrblobChannel *)instanceData; |
︙ | ︙ | |||
259 260 261 262 263 264 265 | const char *zTable, const char *zColumn, sqlite_int64 iRow ){ IncrblobChannel *p; sqlite3_blob *pBlob; int rc; | < | | > > > > > > > > > | | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | 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; p->pBlob = pBlob; sprintf(zChannel, "incrblob_%d", ++count); p->channel = Tcl_CreateChannel(&IncrblobChannelType, zChannel, p, flags); Tcl_RegisterChannel(interp, p->channel); /* Link the new channel into the SqliteDb.pIncrblob list. */ p->pNext = pDb->pIncrblob; p->pPrev = 0; if( p->pNext ){ p->pNext->pPrev = p; } pDb->pIncrblob = p; p->pDb = pDb; Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->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 ** the script to see if it is safe to use Tcl_EvalObjv() on the script |
︙ | ︙ | |||
359 360 361 362 363 364 365 366 367 368 369 370 371 372 | /* ** TCL calls this procedure when an sqlite3 database command is ** deleted. */ static void DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; flushStmtCache(pDb); sqlite3_close(pDb->db); while( pDb->pFunc ){ SqlFunc *pFunc = pDb->pFunc; pDb->pFunc = pFunc->pNext; Tcl_DecrRefCount(pFunc->pScript); Tcl_Free((char*)pFunc); } | > | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | /* ** TCL calls this procedure when an sqlite3 database command is ** deleted. */ static void DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; flushStmtCache(pDb); closeIncrblobChannels(pDb); sqlite3_close(pDb->db); while( pDb->pFunc ){ SqlFunc *pFunc = pDb->pFunc; pDb->pFunc = pFunc->pNext; Tcl_DecrRefCount(pFunc->pScript); Tcl_Free((char*)pFunc); } |
︙ | ︙ |
Changes to test/incrblob.test.
1 2 3 4 5 6 7 8 9 10 11 | # 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. # #*********************************************************************** # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # 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.2 2007/05/02 13:16:31 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); |
︙ | ︙ | |||
44 45 46 47 48 49 50 | } {} do_test incrblob-1.2.6 { execsql { SELECT v FROM blobs WHERE rowid = 1; } } {1234567890} | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | } {} do_test incrblob-1.2.6 { execsql { SELECT v FROM blobs WHERE rowid = 1; } } {1234567890} #-------------------------------------------------------------------- # Test cases incrblob-2.X check that it is possible to read and write # regions of a blob that lie on overflow pages. do_test incrblob-2.1 { set ::str "[string repeat . 10000]" execsql { INSERT INTO blobs(rowid, k, v) VALUES(3, 'three', $::str); } } {} do_test incrblob-2.2 { set ::blob [db incrblob blobs v 3] seek $::blob 8500 read $::blob 10 } {..........} do_test incrblob-2.3 { seek $::blob 8500 puts -nonewline $::blob 1234567890 } {} do_test incrblob-2.4 { seek $::blob 8496 read $::blob 10 } {....123456} do_test incrblob-2.10 { close $::blob } {} finish_test |