SQLite

Check-in [2fe9f5101c]
Login

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

Overview
Comment:VACUUM returns SQLITE_INTERRUPT when interrupted. Ticket #593. (CVS 1228)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2fe9f5101cb0f743532912ece3d37f6c873e7025
User & Date: drh 2004-02-12 13:02:56.000
Context
2004-02-12
15:31
Always reload the schema after a rollback. Ticket #594. (CVS 1229) (check-in: 12c7a83f8e user: drh tags: trunk)
13:02
VACUUM returns SQLITE_INTERRUPT when interrupted. Ticket #593. (CVS 1228) (check-in: 2fe9f5101c user: drh tags: trunk)
2004-02-11
16:38
Only define _FILE_OFFSET_BITS if it is not already defined. Ticket #605. (CVS 1227) (check-in: 300c5543dc user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/vacuum.c.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** This file contains code used to implement the VACUUM command.
**
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
** $Id: vacuum.c,v 1.10 2004/02/11 09:46:33 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

/*
** A structure for holding a dynamic string - a string that can grow
** without bound. 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** This file contains code used to implement the VACUUM command.
**
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
** $Id: vacuum.c,v 1.11 2004/02/12 13:02:56 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

/*
** A structure for holding a dynamic string - a string that can grow
** without bound. 
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
/*
** This is the second stage callback.  Each invocation contains all the
** data for a single row of a single table in the original database.  This
** routine must write that information into the new database.
*/
static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
  vacuumStruct *p = (vacuumStruct*)pArg;
  int rc = 0;
  const char *zSep = "(";
  int i;

  if( argv==0 ) return 0;
  p->s2.nUsed = 0;
  appendText(&p->s2, "INSERT INTO ", -1);
  appendQuoted(&p->s2, p->zTable);
  appendText(&p->s2, " VALUES", -1);
  for(i=0; i<argc; i++){
    appendText(&p->s2, zSep, 1);
    zSep = ",";
    if( argv[i]==0 ){
      appendText(&p->s2, "NULL", 4);
    }else{
      appendQuoted(&p->s2, argv[i]);
    }
  }
  appendText(&p->s2,")", 1);
  rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
  return rc;
}

/*
** This is the first stage callback.  Each invocation contains three
** arguments where are taken from the SQLITE_MASTER table of the original
** database:  (1) the entry type, (2) the entry name, and (3) the SQL for
** the entry.  In all cases, execute the SQL of the third argument.







<


















|
|







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
/*
** This is the second stage callback.  Each invocation contains all the
** data for a single row of a single table in the original database.  This
** routine must write that information into the new database.
*/
static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
  vacuumStruct *p = (vacuumStruct*)pArg;

  const char *zSep = "(";
  int i;

  if( argv==0 ) return 0;
  p->s2.nUsed = 0;
  appendText(&p->s2, "INSERT INTO ", -1);
  appendQuoted(&p->s2, p->zTable);
  appendText(&p->s2, " VALUES", -1);
  for(i=0; i<argc; i++){
    appendText(&p->s2, zSep, 1);
    zSep = ",";
    if( argv[i]==0 ){
      appendText(&p->s2, "NULL", 4);
    }else{
      appendQuoted(&p->s2, argv[i]);
    }
  }
  appendText(&p->s2,")", 1);
  p->rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
  return p->rc;
}

/*
** This is the first stage callback.  Each invocation contains three
** arguments where are taken from the SQLITE_MASTER table of the original
** database:  (1) the entry type, (2) the entry name, and (3) the SQL for
** the entry.  In all cases, execute the SQL of the third argument.
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
    p->zTable = argv[1];
    rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
    if( zErrMsg ){
      sqliteSetString(p->pzErrMsg, zErrMsg, (char*)0);
      sqlite_freemem(zErrMsg);
    }
  }

  return rc;
}

/*
** This callback is used to transfer PRAGMA settings from one database
** to the other.  The value in argv[0] should be passed to a pragma
** identified by ((vacuumStruct*)pArg)->zPragma.
*/
static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
  vacuumStruct *p = (vacuumStruct*)pArg;
  int rc = 0;
  char zBuf[200];
  assert( argc==1 );
  if( argv==0 ) return 0;
  assert( argv[0]!=0 );
  assert( strlen(p->zPragma)<100 );
  assert( strlen(argv[0])<30 );
  sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
  rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
  return rc;
}

/*
** Generate a random name of 20 character in length.
*/
static void randomName(unsigned char *zBuf){
  static const unsigned char zChars[] =







>










<







|
|







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
    p->zTable = argv[1];
    rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
    if( zErrMsg ){
      sqliteSetString(p->pzErrMsg, zErrMsg, (char*)0);
      sqlite_freemem(zErrMsg);
    }
  }
  if( rc!=SQLITE_ABORT ) p->rc = rc;
  return rc;
}

/*
** This callback is used to transfer PRAGMA settings from one database
** to the other.  The value in argv[0] should be passed to a pragma
** identified by ((vacuumStruct*)pArg)->zPragma.
*/
static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
  vacuumStruct *p = (vacuumStruct*)pArg;

  char zBuf[200];
  assert( argc==1 );
  if( argv==0 ) return 0;
  assert( argv[0]!=0 );
  assert( strlen(p->zPragma)<100 );
  assert( strlen(argv[0])<30 );
  sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
  p->rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
  return p->rc;
}

/*
** Generate a random name of 20 character in length.
*/
static void randomName(unsigned char *zBuf){
  static const unsigned char zChars[] =
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
  
  dbNew = sqlite_open(zTemp, 0, &zErrMsg);
  if( dbNew==0 ){
    sqliteSetString(pzErrMsg, "unable to open a temporary database at ",
       zTemp, " - ", zErrMsg, (char*)0);
    goto end_of_vacuum;
  }
  if( execsql(pzErrMsg, db, "BEGIN") ) goto end_of_vacuum;
  if( execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN") ){
    goto end_of_vacuum;
  }
  
  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pzErrMsg = pzErrMsg;
  for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){







|
|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
  
  dbNew = sqlite_open(zTemp, 0, &zErrMsg);
  if( dbNew==0 ){
    sqliteSetString(pzErrMsg, "unable to open a temporary database at ",
       zTemp, " - ", zErrMsg, (char*)0);
    goto end_of_vacuum;
  }
  if( (rc = execsql(pzErrMsg, db, "BEGIN"))!=0 ) goto end_of_vacuum;
  if( (rc = execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN"))!=0 ){
    goto end_of_vacuum;
  }
  
  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pzErrMsg = pzErrMsg;
  for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
312
313
314
315
316
317
318

319
320
321
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);
  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);
  if( rc==SQLITE_ABORT ) rc = SQLITE_ERROR;

  return rc;
#endif
}







>
|


311
312
313
314
315
316
317
318
319
320
321
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);
  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);
  if( rc==SQLITE_ABORT ) rc = SQLITE_ERROR;
  if( sVac.rc!=SQLITE_OK ) rc = sVac.rc;
  return sVac.rc;
#endif
}
Changes to src/vdbe.c.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.259 2004/02/11 09:46:33 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.260 2004/02/12 13:02:56 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
505
506
507
508
509
510
511

512
513
514
515
516
517
518
  assert( p->explain==0 );
  if( sqlite_malloc_failed ) goto no_mem;
  pTos = p->pTos;
  if( p->popStack ){
    popStack(&pTos, p->popStack);
    p->popStack = 0;
  }

  for(pc=p->pc; rc==SQLITE_OK; pc++){
    assert( pc>=0 && pc<p->nOp );
    assert( pTos<=&p->aStack[pc] );
#ifdef VDBE_PROFILE
    origPc = pc;
    start = hwtime();
#endif







>







505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
  assert( p->explain==0 );
  if( sqlite_malloc_failed ) goto no_mem;
  pTos = p->pTos;
  if( p->popStack ){
    popStack(&pTos, p->popStack);
    p->popStack = 0;
  }
  CHECK_FOR_INTERRUPT;
  for(pc=p->pc; rc==SQLITE_OK; pc++){
    assert( pc>=0 && pc<p->nOp );
    assert( pTos<=&p->aStack[pc] );
#ifdef VDBE_PROFILE
    origPc = pc;
    start = hwtime();
#endif
Added test/interrupt.test.


























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
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
38
39
40
41
42
43
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
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
# 2004 Feb 8
#
# 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is the sqlite_interrupt() API.
#
# $Id: interrupt.test,v 1.1 2004/02/12 13:02:57 drh Exp $


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

# Compute a checksum on the entire database.
#
proc cksum {{db db}} {
  set txt [$db eval {SELECT name, type, sql FROM sqlite_master}]\n
  foreach tbl [$db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
    append txt [$db eval "SELECT * FROM $tbl"]\n
  }
  foreach prag {default_synchronous default_cache_size} {
    append txt $prag-[$db eval "PRAGMA $prag"]\n
  }
  set cksum [string length $txt]-[md5 $txt]
  # puts $cksum-[file size test.db]
  return $cksum
}

# This routine attempts to execute the sql in $sql.  It triggers an
# interrupt a progressively later and later points during the processing
# and checks to make sure SQLITE_INTERRUPT is returned.  Eventually,
# the routine completes successfully.
#
proc interrupt_test {testid sql result {initcnt 0}} {
  set orig_sum [cksum]
  set i $initcnt
  while 1 {
    incr i
    set ::sqlite_interrupt_count $i
    do_test $testid.$i.1 [format {
      set ::r [catchsql %s]
      set ::code [db errorcode]
      expr {$::code==0 || $::code==9}
    } [list $sql]] 1
    if {$::code==9} {
      do_test $testid.$i.2 {
        cksum
      } $orig_sum
    } else {
      do_test $testid.$i.99 {
        set ::r
      } [list 0 $result]
      break
    }
  }
  set ::sqlite_interrupt_count 0
}

do_test interrupt-1.1 {
  execsql {
    CREATE TABLE t1(a,b);
    SELECT name FROM sqlite_master;
  }
} {t1}
interrupt_test interrupt-1.2 {DROP TABLE t1} {}
do_test interrupt-1.3 {
  execsql {
    SELECT name FROM sqlite_master;
  }
} {}
integrity_check interrupt-1.4

do_test interrrupt-2.1 {
  execsql {
    BEGIN;
    CREATE TABLE t1(a,b);
    INSERT INTO t1 VALUES(1,randstr(300,400));
    INSERT INTO t1 SELECT a+1, randstr(300,400) FROM t1;
    INSERT INTO t1 SELECT a+2, a || '-' || b FROM t1;
    INSERT INTO t1 SELECT a+4, a || '-' || b FROM t1;
    INSERT INTO t1 SELECT a+8, a || '-' || b FROM t1;
    INSERT INTO t1 SELECT a+16, a || '-' || b FROM t1;
    INSERT INTO t1 SELECT a+32, a || '-' || b FROM t1;
    COMMIT;
    UPDATE t1 SET b=substr(b,-5,5);
    SELECT count(*) from t1;
  }
} 64
set origsize [file size test.db]
set cksum [db eval {SELECT md5sum(a || b) FROM t1}]
interrupt_test interrupt-2.2 {VACUUM} {} 100
do_test interrupt-2.3 {
  execsql {
    SELECT md5sum(a || b) FROM t1;
  }
} $cksum
do_test interrupt-2.4 {
  expr {$::origsize>[file size test.db]}
} 1
integrity_check interrupt-2.5


finish_test