/ Check-in [c8a7b725]
Login

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

Overview
Comment:Better detection and reporting of errors when initializing from the sqlite_master table. (CVS 688)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c8a7b725b7cec792d2148455a4cecbce9dfebe80
User & Date: drh 2002-07-19 17:46:39
Context
2002-07-19
18:13
Add static ident strings (such as picked up by the RCS "ident" command) containing the library version number. (CVS 689) check-in: 712ee391 user: drh tags: trunk
17:46
Better detection and reporting of errors when initializing from the sqlite_master table. (CVS 688) check-in: c8a7b725 user: drh tags: trunk
2002-07-18
11:10
Version 2.6.0 Release 2 (CVS 687) check-in: cc4f824b user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

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
..
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
..
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
...
138
139
140
141
142
143
144

145
146
147
148
149
150
151
...
197
198
199
200
201
202
203


204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
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
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.88 2002/07/18 01:27:18 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>











/*
** This is the callback routine for the code that initializes the
** database.  See sqliteInit() below for additional information.
**
** Each callback contains the following information:
**
................................................................................
**     argv[0] = "file-format" or "schema-cookie" or "table" or "index"
**     argv[1] = table or index name or meta statement type.
**     argv[2] = root page number for table or index.  NULL for meta.
**     argv[3] = SQL create statement for the table or index
**     argv[4] = "1" for temporary files, "0" for main database
**
*/
int sqliteInitCallback(void *pDb, int argc, char **argv, char **azColName){
  sqlite *db = (sqlite*)pDb;

  Parse sParse;
  int nErr = 0;

  /* TODO: Do some validity checks on all fields.  In particular,
  ** make sure fields do not contain NULLs. Otherwise we might core
  ** when attempting to initialize from a corrupt database file. */

................................................................................
      if( argv[3] && argv[3][0] ){
        /* Call the parser to process a CREATE TABLE, INDEX or VIEW.
        ** But because sParse.initFlag is set to 1, no VDBE code is generated
        ** or executed.  All the parser does is build the internal data
        ** structures that describe the table, index, or view.
        */
        memset(&sParse, 0, sizeof(sParse));
        sParse.db = db;
        sParse.initFlag = 1;
        sParse.isTemp = argv[4][0] - '0';
        sParse.newTnum = atoi(argv[2]);
        sqliteRunParser(&sParse, argv[3], 0);
      }else{
        /* If the SQL column is blank it means this is an index that
        ** was created to be the PRIMARY KEY or to fulfill a UNIQUE
        ** constraint for a CREATE TABLE.  The index should have already
        ** been created when we processed the CREATE TABLE.  All we have
        ** to do here is record the root page number for that index.
        */
        Index *pIndex = sqliteFindIndex(db, argv[1]);
        if( pIndex==0 || pIndex->tnum!=0 ){
          /* This can occur if there exists an index on a TEMP table which
          ** has the same name as another index on a permanent index.  Since
          ** the permanent table is hidden by the TEMP table, we can also
          ** safely ignore the index on the permanent table.
          */
          /* Do Nothing */;
................................................................................
** name of the table to be reconstructed is passed in as argv[0].
**
** This routine is used to automatically upgrade a database from
** format version 1 or 2 to version 3.  The correct operation of
** this routine relys on the fact that no indices are used when
** copying a table out to a temporary file.
*/
static int 
upgrade_3_callback(void *pDb, int argc, char **argv, char **NotUsed){
  sqlite *db = (sqlite*)pDb;
  int rc;
  Table *pTab;
  Trigger *pTrig;


  pTab = sqliteFindTable(db, argv[0]);
  if( pTab ){
    pTrig = pTab->pTrigger;
    pTab->pTrigger = 0;  /* Disable all triggers before rebuilding the table */
  }
  rc = sqlite_exec_printf(db,
    "CREATE TEMP TABLE sqlite_x AS SELECT * FROM '%q'; "
    "DELETE FROM '%q'; "
    "INSERT INTO '%q' SELECT * FROM sqlite_x; "
    "DROP TABLE sqlite_x;",
    0, 0, 0, argv[0], argv[0], argv[0]);




  if( pTab ) pTab->pTrigger = pTrig;  /* Re-enable triggers */
  return rc!=SQLITE_OK;
}



/*
................................................................................
  int rc;
  BtCursor *curMain;
  int size;
  Table *pTab;
  char *azArg[6];
  int meta[SQLITE_N_BTREE_META];
  Parse sParse;


  /*
  ** The master database table has a structure like this
  */
  static char master_schema[] = 
     "CREATE TABLE sqlite_master(\n"
     "  type text,\n"
................................................................................
  */
  azArg[0] = "table";
  azArg[1] = MASTER_NAME;
  azArg[2] = "2";
  azArg[3] = master_schema;
  azArg[4] = "0";
  azArg[5] = 0;


  sqliteInitCallback(db, 5, azArg, 0);
  pTab = sqliteFindTable(db, MASTER_NAME);
  if( pTab ){
    pTab->readOnly = 1;
  }
  azArg[1] = TEMP_MASTER_NAME;
  azArg[3] = temp_master_schema;
  azArg[4] = "1";
  sqliteInitCallback(db, 5, azArg, 0);
  pTab = sqliteFindTable(db, TEMP_MASTER_NAME);
  if( pTab ){
    pTab->readOnly = 1;
  }

  /* Create a cursor to hold the database open
  */
................................................................................

  /* Read the schema information out of the schema tables
  */
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.pBe = db->pBe;
  sParse.xCallback = sqliteInitCallback;
  sParse.pArg = (void*)db;
  sParse.initFlag = 1;
  sqliteRunParser(&sParse,
      db->file_format>=2 ? init_script : older_init_script,
      pzErrMsg);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
................................................................................

  /* If the database is in formats 1 or 2, then upgrade it to
  ** version 3.  This will reconstruct all indices.  If the
  ** upgrade fails for any reason (ex: out of disk space, database
  ** is read only, interrupt receive, etc.) then refuse to open.
  */
  if( db->file_format<3 ){
    char *zErr;

    int meta[SQLITE_N_BTREE_META];



    db->file_format = 3;
    rc = sqlite_exec(db,
      "BEGIN; SELECT name FROM sqlite_master WHERE type='table';",
      upgrade_3_callback,
      db,
      &zErr);
    if( rc==SQLITE_OK ){
      sqliteBtreeGetMeta(db->pBe, meta);
      meta[2] = 3;
      sqliteBtreeUpdateMeta(db->pBe, meta);
      sqlite_exec(db, "COMMIT", 0, 0, 0);
    }
    if( rc!=SQLITE_OK ){
      sqliteSetString(pzErrMsg, 
        "unable to upgrade database to the version 2.6 format",
        zErr ? ": " : 0, zErr, 0);
      sqliteFree(zErr);
      sqliteStrRealloc(pzErrMsg);
      sqlite_close(db);
      return 0;
    }

  }

  /* Return a pointer to the newly opened database structure */
  return db;

no_mem_on_open:
  sqliteSetString(pzErrMsg, "out of memory", 0);







|




>
>
>
>
>
>
>
>
>
>







 







|
|
>







 







|



|







|







 







|
|
|



>

|




|




|
>
>
>
>







 







>







 







>
>
|







|







 







|







 







|
>


>
>




|











|




>







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
..
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
...
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
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
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
...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
...
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
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.89 2002/07/19 17:46:39 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
** from sqliteInit into the sqliteInitCallback.
*/
typedef struct {
  sqlite *db;         /* The database being initialized */
  char **pzErrMsg;    /* Error message stored here */
} InitData;


/*
** This is the callback routine for the code that initializes the
** database.  See sqliteInit() below for additional information.
**
** Each callback contains the following information:
**
................................................................................
**     argv[0] = "file-format" or "schema-cookie" or "table" or "index"
**     argv[1] = table or index name or meta statement type.
**     argv[2] = root page number for table or index.  NULL for meta.
**     argv[3] = SQL create statement for the table or index
**     argv[4] = "1" for temporary files, "0" for main database
**
*/
static
int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
  InitData *pData = (InitData*)pInit;
  Parse sParse;
  int nErr = 0;

  /* TODO: Do some validity checks on all fields.  In particular,
  ** make sure fields do not contain NULLs. Otherwise we might core
  ** when attempting to initialize from a corrupt database file. */

................................................................................
      if( argv[3] && argv[3][0] ){
        /* Call the parser to process a CREATE TABLE, INDEX or VIEW.
        ** But because sParse.initFlag is set to 1, no VDBE code is generated
        ** or executed.  All the parser does is build the internal data
        ** structures that describe the table, index, or view.
        */
        memset(&sParse, 0, sizeof(sParse));
        sParse.db = pData->db;
        sParse.initFlag = 1;
        sParse.isTemp = argv[4][0] - '0';
        sParse.newTnum = atoi(argv[2]);
        sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
      }else{
        /* If the SQL column is blank it means this is an index that
        ** was created to be the PRIMARY KEY or to fulfill a UNIQUE
        ** constraint for a CREATE TABLE.  The index should have already
        ** been created when we processed the CREATE TABLE.  All we have
        ** to do here is record the root page number for that index.
        */
        Index *pIndex = sqliteFindIndex(pData->db, argv[1]);
        if( pIndex==0 || pIndex->tnum!=0 ){
          /* This can occur if there exists an index on a TEMP table which
          ** has the same name as another index on a permanent index.  Since
          ** the permanent table is hidden by the TEMP table, we can also
          ** safely ignore the index on the permanent table.
          */
          /* Do Nothing */;
................................................................................
** name of the table to be reconstructed is passed in as argv[0].
**
** This routine is used to automatically upgrade a database from
** format version 1 or 2 to version 3.  The correct operation of
** this routine relys on the fact that no indices are used when
** copying a table out to a temporary file.
*/
static
int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){
  InitData *pData = (InitData*)pInit;
  int rc;
  Table *pTab;
  Trigger *pTrig;
  char *zErr;

  pTab = sqliteFindTable(pData->db, argv[0]);
  if( pTab ){
    pTrig = pTab->pTrigger;
    pTab->pTrigger = 0;  /* Disable all triggers before rebuilding the table */
  }
  rc = sqlite_exec_printf(pData->db,
    "CREATE TEMP TABLE sqlite_x AS SELECT * FROM '%q'; "
    "DELETE FROM '%q'; "
    "INSERT INTO '%q' SELECT * FROM sqlite_x; "
    "DROP TABLE sqlite_x;",
    0, 0, &zErr, argv[0], argv[0], argv[0]);
  if( zErr ){
    sqliteSetString(pData->pzErrMsg, zErr, 0);
    sqlite_freemem(zErr);
  }
  if( pTab ) pTab->pTrigger = pTrig;  /* Re-enable triggers */
  return rc!=SQLITE_OK;
}



/*
................................................................................
  int rc;
  BtCursor *curMain;
  int size;
  Table *pTab;
  char *azArg[6];
  int meta[SQLITE_N_BTREE_META];
  Parse sParse;
  InitData initData;

  /*
  ** The master database table has a structure like this
  */
  static char master_schema[] = 
     "CREATE TABLE sqlite_master(\n"
     "  type text,\n"
................................................................................
  */
  azArg[0] = "table";
  azArg[1] = MASTER_NAME;
  azArg[2] = "2";
  azArg[3] = master_schema;
  azArg[4] = "0";
  azArg[5] = 0;
  initData.db = db;
  initData.pzErrMsg = pzErrMsg;
  sqliteInitCallback(&initData, 5, azArg, 0);
  pTab = sqliteFindTable(db, MASTER_NAME);
  if( pTab ){
    pTab->readOnly = 1;
  }
  azArg[1] = TEMP_MASTER_NAME;
  azArg[3] = temp_master_schema;
  azArg[4] = "1";
  sqliteInitCallback(&initData, 5, azArg, 0);
  pTab = sqliteFindTable(db, TEMP_MASTER_NAME);
  if( pTab ){
    pTab->readOnly = 1;
  }

  /* Create a cursor to hold the database open
  */
................................................................................

  /* Read the schema information out of the schema tables
  */
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.pBe = db->pBe;
  sParse.xCallback = sqliteInitCallback;
  sParse.pArg = (void*)&initData;
  sParse.initFlag = 1;
  sqliteRunParser(&sParse,
      db->file_format>=2 ? init_script : older_init_script,
      pzErrMsg);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
................................................................................

  /* If the database is in formats 1 or 2, then upgrade it to
  ** version 3.  This will reconstruct all indices.  If the
  ** upgrade fails for any reason (ex: out of disk space, database
  ** is read only, interrupt receive, etc.) then refuse to open.
  */
  if( db->file_format<3 ){
    char *zErr = 0;
    InitData initData;
    int meta[SQLITE_N_BTREE_META];

    initData.db = db;
    initData.pzErrMsg = &zErr;
    db->file_format = 3;
    rc = sqlite_exec(db,
      "BEGIN; SELECT name FROM sqlite_master WHERE type='table';",
      upgrade_3_callback,
      &initData,
      &zErr);
    if( rc==SQLITE_OK ){
      sqliteBtreeGetMeta(db->pBe, meta);
      meta[2] = 3;
      sqliteBtreeUpdateMeta(db->pBe, meta);
      sqlite_exec(db, "COMMIT", 0, 0, 0);
    }
    if( rc!=SQLITE_OK ){
      sqliteSetString(pzErrMsg, 
        "unable to upgrade database to the version 2.6 format",
        zErr ? ": " : 0, zErr, 0);
      sqlite_freemem(zErr);
      sqliteStrRealloc(pzErrMsg);
      sqlite_close(db);
      return 0;
    }
    sqlite_freemem(zErr);
  }

  /* Return a pointer to the newly opened database structure */
  return db;

no_mem_on_open:
  sqliteSetString(pzErrMsg, "out of memory", 0);

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
945
946
947
948
949
950
951
952
**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.139 2002/07/18 00:34:12 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
#include <stdio.h>
................................................................................
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
TriggerStep *sqliteTriggerSelectStep(Select*);
TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int);
TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*);
void sqliteDeleteTrigger(Trigger*);
int sqliteJoinType(Parse*, Token*, Token*, Token*);
int sqliteInitCallback(void*,int,char**,char**);







|







 







<
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
945
946
947
948
949
950
951

**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.140 2002/07/19 17:46:39 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
#include <stdio.h>
................................................................................
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
TriggerStep *sqliteTriggerSelectStep(Select*);
TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int);
TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*);
void sqliteDeleteTrigger(Trigger*);
int sqliteJoinType(Parse*, Token*, Token*, Token*);

Changes to test/version.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
173
174
175
176
177
178
179
180
181
182
#    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 ability of the library to detect
# past or future file format version numbers and respond appropriately.
#
# $Id: version.test,v 1.2 2002/07/18 02:07:08 drh Exp $

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

# Current file format version
set VX 3

................................................................................
  eval btree_update_meta $::bt $m2
  btree_commit $::bt
  btree_close $::bt
  catch {file attributes test.db -permissions 0444}
  catch {file attributes test.db -readonly 1}
  set rc [catch {sqlite db test.db} msg]
  lappend rc $msg
} {1 {unable to upgrade database to the version 2.6 format}}

finish_test







|







 







|


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
173
174
175
176
177
178
179
180
181
182
#    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 ability of the library to detect
# past or future file format version numbers and respond appropriately.
#
# $Id: version.test,v 1.3 2002/07/19 17:46:41 drh Exp $

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

# Current file format version
set VX 3

................................................................................
  eval btree_update_meta $::bt $m2
  btree_commit $::bt
  btree_close $::bt
  catch {file attributes test.db -permissions 0444}
  catch {file attributes test.db -readonly 1}
  set rc [catch {sqlite db test.db} msg]
  lappend rc $msg
} {1 {unable to upgrade database to the version 2.6 format: attempt to write a readonly database}}

finish_test