summaryrefslogtreecommitdiffstats
path: root/tool/rollback-test.c
blob: 915d9d203dadf8dfbb36c987f66219edc5517791 (plain)
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
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
/*
** This program is used to generate and verify databases with hot journals.
** Use this program to generate a hot journal on one machine and verify
** that it rolls back correctly on another machine with a different
** architecture.
**
** Usage:
**
**     rollback-test new [-utf8] [-utf16le] [-utf16be] [-pagesize=N] DATABASE
**     rollback-test check DATABASE
**     rollback-test crash [-wal] [-rollback] DATABASE
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"

static void usage(char *argv0){
  fprintf(stderr,
    "Usage: %s new [-utf8] [-utf16le] [-utf16be] [-pagesize=N] DATABASE\n"
    "       %s check DATABASE\n"
    "       %s crash [-wal] DATABASE\n",
    argv0, argv0, argv0
  );
  exit(1);
}

static sqlite3 *openDb(const char *zFilename){
  int rc;
  sqlite3 *db;
  rc = sqlite3_open(zFilename, &db);
  if( rc ){
    fprintf(stderr, "Cannot open \"%s\": %s\n",
            zFilename, sqlite3_errmsg(db));
    sqlite3_close(db);
    exit(1);
  }
  return db;
}

static int nReply = 0;
static char zReply[1000];

static int execCallback(void *NotUsed, int nArg, char **azArg, char **azCol){
  int i, n;
  char *z;
  for(i=0; i<nArg; i++){
    z = azArg[i];
    if( z==0 ) z = "NULL";
    if( nReply>0 && nReply<sizeof(zReply)-1 ) zReply[nReply++] = ' ';
    n = strlen(z);
    if( nReply+n>=sizeof(zReply)-1 ) n = sizeof(zReply) - nReply - 1;
    memcpy(&zReply[nReply], z, n);
    nReply += n;
    zReply[nReply] = 0;
  }
  return 0;
}

static void runSql(sqlite3 *db, const char *zSql){
  char *zErr = 0;
  int rc;
  nReply = 0;
  rc = sqlite3_exec(db, zSql, execCallback, 0, &zErr);
  if( zErr ){
    fprintf(stderr, "SQL error: %s\n", zErr);
    exit(1);
  }
  if( rc ){
    fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
    exit(1);
  }
}

int main(int argc, char **argv){
  sqlite3 *db;
  int i;

  if( argc<3 ) usage(argv[0]);
  if( strcmp(argv[1], "new")==0 ){
    db = openDb(argv[argc-1]);
    for(i=2; i<argc-1; i++){
      if( strcmp(argv[i],"-utf8")==0 ){
        runSql(db, "PRAGMA encoding=UTF8");
      }else if( strcmp(argv[i], "-utf16le")==0 ){
        runSql(db, "PRAGMA encoding=UTF16LE");
      }else if( strcmp(argv[i], "-utf16be")==0 ){
        runSql(db, "PRAGMA encoding=UTF16BE");
      }else if( strncmp(argv[i], "-pagesize=", 10)==0 ){
        int szPg = atoi(&argv[i][10]);
        char zBuf[100];
        sprintf(zBuf, "PRAGMA pagesize=%d", szPg);
        runSql(db, zBuf);
      }else{
        fprintf(stderr, "unknown option %s\n", argv[i]);
        usage(argv[0]);
      }
    }
    runSql(db, 
       "BEGIN;"
       "CREATE TABLE t1(x INTEGER PRIMARY KEY, y);"
       "INSERT INTO t1(y) VALUES('abcdefghijklmnopqrstuvwxyz');"
       "INSERT INTO t1(y) VALUES('abcdefghijklmnopqrstuvwxyz');"
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 4 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 8 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 16 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 32 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 64 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 128 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 256 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 512 */
       "INSERT INTO t1(y) SELECT y FROM t1;" /* 1024 */
       "UPDATE t1 SET y=(y || x);"
       "CREATE INDEX t1y ON t1(y);"
       "COMMIT;"
    );
    sqlite3_close(db);
  }else if( strcmp(argv[1], "check")==0 ){
    db = openDb(argv[argc-1]);
    runSql(db, "PRAGMA integrity_check");
    if( strcmp(zReply, "ok")!=0 ){
      fprintf(stderr, "Integrity check: %s\n", zReply);
      exit(1);
    }
    runSql(db, 
      "SELECT count(*) FROM t1 WHERE y<>('abcdefghijklmnopqrstuvwxyz' || x)"
    );
    if( strcmp(zReply, "0")!=0 ){
      fprintf(stderr, "Wrong content\n");
      exit(1);
    }
    printf("Ok\n");
  }else if( strcmp(argv[1], "crash")==0 ){
    db = openDb(argv[argc-1]);
    for(i=2; i<argc-1; i++){
      if( strcmp(argv[i],"-wal")==0 ){
        runSql(db, "PRAGMA journal_mode=WAL");
      }else if( strcmp(argv[i], "-rollback")==0 ){
        runSql(db, "PRAGMA journal_mode=DELETE");
      }else{
        fprintf(stderr, "unknown option %s\n", argv[i]);
        usage(argv[0]);
      }
    }
    runSql(db,
      "PRAGMA cache_size=10;"
      "BEGIN;"
      "UPDATE t1 SET y=(y || -x)"
    );
    exit(0);
  }else{
    usage(argv[0]);
  }
  return 0;
}