SQLite

Check-in [64705410bd]
Login

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

Overview
Comment:Test some more incremental IO error cases. (CVS 3910)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 64705410bdf43b6283f7a7e59ce8c20d09cd46e4
User & Date: danielk1977 2007-05-03 18:14:10.000
Context
2007-05-03
20:06
fix from Gentoo for allowing TCLLIBDIR to be overridden in env (CVS 3911) (check-in: e54a49e264 user: vapier tags: trunk)
18:14
Test some more incremental IO error cases. (CVS 3910) (check-in: 64705410bd user: danielk1977 tags: trunk)
17:18
Improvements to I/O tracing output. Require -DSQLITE_ENABLE_IOTRACE when compiling shell.c in order to link in the I/O tracing capability. (CVS 3909) (check-in: 8a43e1676f user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/vdbeblob.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
** 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.4 2007/05/03 16:31:26 danielk1977 Exp $
*/

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

#ifndef SQLITE_OMIT_INCRBLOB













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
** 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.5 2007/05/03 18:14:10 danielk1977 Exp $
*/

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

#ifndef SQLITE_OMIT_INCRBLOB

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
      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 ){
      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







<














|




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

















|
|







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
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
      return rc;
    }

    pTab = sqlite3LocateTable(&sParse, zTable, zDb);
    if( !pTab ){
      if( sParse.zErrMsg ){
        sqlite3_snprintf(sizeof(zErr), zErr, "%s", sParse.zErrMsg);

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

    /* If the value is being opened for writing, check that the
    ** column is not indexed. It is against the rules to open an
    ** indexed column for writing.
    */
    if( flags ){
      Index *pIdx;
      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
        int j;
        for(j=0; j<pIdx->nColumn; j++){
          if( pIdx->aiColumn[j]==iCol ){
            strcpy(zErr, "cannot open indexed column for writing");
            rc = SQLITE_ERROR;
            sqlite3SafetyOff(db);
            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
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
    }

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







|













>
>
>

















>







>







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
    }

    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);
      sqlite3_snprintf(sizeof(zErr), zErr, sqlite3_errmsg(db));
      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 ){
      sqlite3_snprintf(sizeof(zErr), zErr, "cannot open value of type %s",
          type==0?"null": type==7?"real": "integer"
      );
      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;
    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 ){
    sqlite3_snprintf(sizeof(zErr), zErr, "no such rowid: %lld", iRow);
    rc = SQLITE_ERROR;
  }

blob_open_out:
  if( rc!=SQLITE_OK || sqlite3MallocFailed() ){
    sqlite3_finalize((sqlite3_stmt *)v);
  }
  zErr[sizeof(zErr)-1] = '\0';
  sqlite3Error(db, rc, zErr);
  return sqlite3ApiExit(db, rc);
}

/*
** Close a blob handle.
*/
Changes to test/incrblob.test.
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.5 2007/05/03 16:31:26 danielk1977 Exp $
#

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

do_test incrblob-1.1 {
  execsql {











|







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.6 2007/05/03 18:14:10 danielk1977 Exp $
#

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

do_test incrblob-1.1 {
  execsql {
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










































































































































  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





















































































































































>













>
>
>
>
>
>
>






<






<



|


<







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  read $::blob 20
} "1234567890abcdefghij"
do_test incrblob-3.2 {
  seek $::blob 0
  set rc [catch {
    puts -nonewline $::blob "helloworld"
  } msg]
  close $::blob
  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.
#
#     4.5 - Attempt to open an integer
#     4.6 - Attempt to open a real value
#     4.7 - Attempt to open an SQL null
#
#     4.8 - Attempt to open an indexed column for writing
#     4.9 - Attempt to open an indexed column for reading (this works)
#
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}}

do_test incrblob-4.5 {
  set rc [catch {
    set ::blob [db incrblob blobs i 1]
  } msg ] 
  list $rc $msg
} {1 {cannot open value of type integer}}
do_test incrblob-4.6 {
  execsql {
    INSERT INTO blobs(k, v, i) VALUES(123, 567.765, NULL);
  }
  set rc [catch {
    set ::blob [db incrblob blobs v 2]
  } msg ] 
  list $rc $msg
} {1 {cannot open value of type real}}
do_test incrblob-4.7 {
  set rc [catch {
    set ::blob [db incrblob blobs i 2]
  } msg ] 
  list $rc $msg
} {1 {cannot open value of type null}}

do_test incrblob-4.8 {
  execsql {
    INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world');
  }
  set rc [catch {
    set ::blob [db incrblob blobs k 3]
  } msg ] 
  list $rc $msg
} {1 {cannot open indexed column for writing}}

do_test incrblob-4.9.1 {
  set rc [catch {
    set ::blob [db incrblob -readonly blobs k 3]
  } msg]
} {0}
do_test incrblob-4.9.2 {
  binary scan [read $::blob] c* c
  close $::blob
  set c
} {1 2 3 4 5 6 7 8 9}

#------------------------------------------------------------------------
# incrblob-5.*: 
#
#     Test that opening a blob in an attached database works.
#
do_test incrblob-5.1 {
  file delete -force test2.db test2.db-journal
  set ::size [expr [file size [info script]]]
  execsql {
    ATTACH 'test2.db' AS aux;
    CREATE TABLE aux.files(name, text);
    INSERT INTO aux.files VALUES('this one', zeroblob($::size));
  }
  set fd  [db incrblob aux files text 1]
  set fd2 [open [info script]]
  puts -nonewline $fd [read $fd2]
  close $fd
  close $fd2
  set ::text [db one {select text from aux.files}]
  string length $::text
} [file size [info script]]
do_test incrblob-5.2 {
  set fd2 [open [info script]]
  set ::data [read $fd2]
  close $fd2
  set ::data
} $::text

# free memory
unset ::data
unset ::text

#------------------------------------------------------------------------
# incrblob-6.*: 
#
#     Test that opening a blob for write-access is impossible if
#     another connection has the database RESERVED lock.
#
#     Then test that blob writes that take place inside of a
#     transaction are not visible to external connections until
#     after the transaction is commited and the blob channel 
#     closed.
#
do_test incrblob-6.1 {
  sqlite3 db2 test.db
  execsql {
    BEGIN;
    INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection');
  } db2
} {}
do_test incrblob-6.2 {
  execsql {
    SELECT rowid FROM blobs
  }
} {1 2 3}
do_test incrblob-6.3 {
  set rc [catch {
    db incrblob blobs v 1
  } msg]
  list $rc $msg
} {1 {database is locked}}
do_test incrblob-6.4 {
  set rc [catch {
    db incrblob blobs v 3
  } msg]
  list $rc $msg
} {1 {database is locked}}
do_test incrblob-6.5 {
  set ::blob [db incrblob -readonly blobs v 3]
  read $::blob
} {hello}
do_test incrblob-6.6 {
  close $::blob
} {}

do_test incrblob-6.7 {
  set ::blob [db2 incrblob blobs i 4]
  gets $::blob
} {connection}
do_test incrblob-6.8 {
  tell $::blob
} {10}
breakpoint
do_test incrblob-6.9 {
  seek $::blob 0
  puts -nonewline $::blob "invocation"
  flush $::blob
} {}

# At this point rollback or commit should be illegal (because 
# there is an open blob channel).
do_test incrblob-6.10 {
  catchsql {
    ROLLBACK;
  } db2
} {1 {cannot rollback transaction - SQL statements in progress}}
do_test incrblob-6.11 {
  catchsql {
    COMMIT;
  } db2
} {1 {cannot commit transaction - SQL statements in progress}}

do_test incrblob-6.12 {
  execsql {
    SELECT * FROM blobs WHERE rowid = 4;
  }
} {}
do_test incrblob-6.13 {
  close $::blob
  execsql {
    COMMIT;
  } db2
} {}
do_test incrblob-6.14 {
  execsql {
    SELECT * FROM blobs WHERE rowid = 4;
  }
} {a different invocation}
db2 close

finish_test