SQLite

Check-in [69a493182f]
Login

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

Overview
Comment:Add a sqlite3_log() call on anonymous constraint failures. Fix the output of test cases having to do with improved reprepare reporting. Fix the VACUUM command to report more helpful error messages when things go wrong.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 69a493182fd77bec91598516ee42c11a6db1d039
User & Date: drh 2010-02-24 19:23:56.000
References
2010-02-25
02:32
Merge in all of the logging enhancements. This is a cherrypick merge of the following check-ins: [103321e37a], [a8076aede3], [6d910245ad], [7c4cca6d1a], [edea3bb740], [1a6d4bb130], [a8c984c1d6], [69a493182f], and [1168763d2c]. (check-in: 46f406b202 user: drh tags: branch-3.6.22)
Context
2010-02-24
19:36
Changes to compile time option diags to report values for some defines. Added test cases to TCL test suite (ctime.test). (check-in: dd480f62af user: shaneh tags: trunk)
19:23
Add a sqlite3_log() call on anonymous constraint failures. Fix the output of test cases having to do with improved reprepare reporting. Fix the VACUUM command to report more helpful error messages when things go wrong. (check-in: 69a493182f user: drh tags: trunk)
18:40
Fix an incorrect ALWAYS() macro in vdbeapi.c. Fix the output of a few test cases that changed due to better error propagation out of reprepare. (check-in: a8c984c1d6 user: drh tags: trunk)
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to src/vacuum.c.
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+



-
+






+




-
+






-
+







-
+

-
+




-
+







** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
*/
#include "sqliteInt.h"
#include "vdbeInt.h"

#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/*
** Finalize a prepared statement.  If there was an error, store the
** text of the error message in *pzErrMsg.  Return the result code.
*/
static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
  int rc;
  rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
  if( rc ){
    sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
  }
  return rc;
}

/*
** Execute zSql on database db. Return an error code.
*/
static int execSql(sqlite3 *db, const char *zSql){
static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
  sqlite3_stmt *pStmt;
  VVA_ONLY( int rc; )
  if( !zSql ){
    return SQLITE_NOMEM;
  }
  if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
    sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
    return sqlite3_errcode(db);
  }
  VVA_ONLY( rc = ) sqlite3_step(pStmt);
  assert( rc!=SQLITE_ROW );
  return sqlite3_finalize(pStmt);
  return vacuumFinalize(db, pStmt, pzErrMsg);
}

/*
** Execute zSql on database db. The statement returns exactly
** one column. Execute this as SQL on the same database.
*/
static int execExecSql(sqlite3 *db, const char *zSql){
static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
  sqlite3_stmt *pStmt;
  int rc;

  rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
  if( rc!=SQLITE_OK ) return rc;

  while( SQLITE_ROW==sqlite3_step(pStmt) ){
    rc = execSql(db, (char*)sqlite3_column_text(pStmt, 0));
    rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
    if( rc!=SQLITE_OK ){
      sqlite3_finalize(pStmt);
      vacuumFinalize(db, pStmt, pzErrMsg);
      return rc;
    }
  }

  return sqlite3_finalize(pStmt);
  return vacuumFinalize(db, pStmt, pzErrMsg);
}

/*
** The non-standard VACUUM command is used to clean up the database,
** collapse free space, etc.  It is modelled after the VACUUM command
** in PostgreSQL.
**
121
122
123
124
125
126
127
128

129
130
131
132
133
134
135
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149







-
+







  ** that actually made the VACUUM run slower.  Very little journalling
  ** actually occurs when doing a vacuum since the vacuum_db is initially
  ** empty.  Only the journal header is written.  Apparently it takes more
  ** time to parse and run the PRAGMA to turn journalling off than it does
  ** to write the journal header file.
  */
  zSql = "ATTACH '' AS vacuum_db;";
  rc = execSql(db, zSql);
  rc = execSql(db, pzErrMsg, zSql);
  if( rc!=SQLITE_OK ) goto end_of_vacuum;
  pDb = &db->aDb[db->nDb-1];
  assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 );
  pTemp = db->aDb[db->nDb-1].pBt;

  /* The call to execSql() to attach the temp database has left the file
  ** locked (as there was more than one active statement when the transaction
153
154
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
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
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
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







-
+










-
+





-
+





-
+



-
+








-
+





-





-
+




-
+












-
+







  if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0)
   || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
   || NEVER(db->mallocFailed)
  ){
    rc = SQLITE_NOMEM;
    goto end_of_vacuum;
  }
  rc = execSql(db, "PRAGMA vacuum_db.synchronous=OFF");
  rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
  if( rc!=SQLITE_OK ){
    goto end_of_vacuum;
  }

#ifndef SQLITE_OMIT_AUTOVACUUM
  sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac :
                                           sqlite3BtreeGetAutoVacuum(pMain));
#endif

  /* Begin a transaction */
  rc = execSql(db, "BEGIN EXCLUSIVE;");
  rc = execSql(db, pzErrMsg, "BEGIN EXCLUSIVE;");
  if( rc!=SQLITE_OK ) goto end_of_vacuum;

  /* Query the schema of the main database. Create a mirror schema
  ** in the temporary database.
  */
  rc = execExecSql(db, 
  rc = execExecSql(db, pzErrMsg,
      "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
      "  FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
      "   AND rootpage>0"
  );
  if( rc!=SQLITE_OK ) goto end_of_vacuum;
  rc = execExecSql(db, 
  rc = execExecSql(db, pzErrMsg,
      "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
      "  FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' ");
  if( rc!=SQLITE_OK ) goto end_of_vacuum;
  rc = execExecSql(db, 
  rc = execExecSql(db, pzErrMsg,
      "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
      "  FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
  if( rc!=SQLITE_OK ) goto end_of_vacuum;

  /* Loop through the tables in the main database. For each, do
  ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
  ** the contents to the temporary database.
  */
  rc = execExecSql(db, 
  rc = execExecSql(db, pzErrMsg,
      "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
      "|| ' SELECT * FROM main.' || quote(name) || ';'"
      "FROM main.sqlite_master "
      "WHERE type = 'table' AND name!='sqlite_sequence' "
      "  AND rootpage>0"

  );
  if( rc!=SQLITE_OK ) goto end_of_vacuum;

  /* Copy over the sequence table
  */
  rc = execExecSql(db, 
  rc = execExecSql(db, pzErrMsg,
      "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' "
      "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' "
  );
  if( rc!=SQLITE_OK ) goto end_of_vacuum;
  rc = execExecSql(db, 
  rc = execExecSql(db, pzErrMsg,
      "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
      "|| ' SELECT * FROM main.' || quote(name) || ';' "
      "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';"
  );
  if( rc!=SQLITE_OK ) goto end_of_vacuum;


  /* Copy the triggers, views, and virtual tables from the main database
  ** over to the temporary database.  None of these objects has any
  ** associated storage, so all we have to do is copy their entries
  ** from the SQLITE_MASTER table.
  */
  rc = execSql(db,
  rc = execSql(db, pzErrMsg,
      "INSERT INTO vacuum_db.sqlite_master "
      "  SELECT type, name, tbl_name, rootpage, sql"
      "    FROM main.sqlite_master"
      "   WHERE type='view' OR type='trigger'"
      "      OR (type='table' AND rootpage=0)"
  );
  if( rc ) goto end_of_vacuum;
Changes to src/vdbe.c.
846
847
848
849
850
851
852


853
854
855
856
857
858
859
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861







+
+







  p->rc = pOp->p1;
  p->errorAction = (u8)pOp->p2;
  p->pc = pc;
  if( pOp->p4.z ){
    assert( p->rc!=SQLITE_OK );
    sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
    sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z);
  }else if( p->rc ){
    sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql);
  }
  rc = sqlite3VdbeHalt(p);
  assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
  if( rc==SQLITE_BUSY ){
    p->rc = rc = SQLITE_BUSY;
  }else{
    assert( rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT );
Changes to test/analyze3.test.
477
478
479
480
481
482
483
484

485
486
487

488
489
490
491
492
493
494
477
478
479
480
481
482
483

484
485
486

487
488
489
490
491
492
493
494







-
+


-
+







  sqlite3_step $S
} {SQLITE_DONE}
do_test analyze3-4.1.2 {
  sqlite3_reset $S
  sqlite3_bind_text $S 2 "abc" 3
  execsql { DROP TABLE t1 }
  sqlite3_step $S
} {SQLITE_SCHEMA}
} {SQLITE_ERROR}
do_test analyze3-4.1.3 {
  sqlite3_finalize $S
} {SQLITE_SCHEMA}
} {SQLITE_ERROR}

# Check an authorization error.
#
do_test analyze3-4.2.1 {
  execsql {
    BEGIN;
    CREATE TABLE t1(a, b, c);
507
508
509
510
511
512
513
514

515
516
517

518
519
520
521
522
523
524
525
526
527
528
529

530
531
532

533
534
535
536
537
538
539
507
508
509
510
511
512
513

514
515
516

517
518
519
520
521
522
523
524
525
526
527
528

529
530
531

532
533
534
535
536
537
538
539







-
+


-
+











-
+


-
+







  if {[lindex $args 0] == "SQLITE_READ"} {return SQLITE_DENY}
  return SQLITE_OK
}
do_test analyze3-4.2.2 {
  sqlite3_reset $S
  sqlite3_bind_text $S 2 "abc" 3
  sqlite3_step $S
} {SQLITE_SCHEMA}
} {SQLITE_AUTH}
do_test analyze3-4.2.4 {
  sqlite3_finalize $S
} {SQLITE_SCHEMA}
} {SQLITE_AUTH}

# Check the effect of an authorization error that occurs in a re-prepare
# performed by sqlite3_step() is the same as one that occurs within
# sqlite3Reprepare().
#
do_test analyze3-4.3.1 {
  db auth {}
  set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
  execsql { CREATE TABLE t2(d, e, f) }
  db auth auth
  sqlite3_step $S
} {SQLITE_SCHEMA}
} {SQLITE_AUTH}
do_test analyze3-4.3.2 {
  sqlite3_finalize $S
} {SQLITE_SCHEMA}
} {SQLITE_AUTH}
db auth {}

#-------------------------------------------------------------------------
# Test that modifying bound variables using the clear_bindings() or
# transfer_bindings() APIs works.
#
#   analyze3-5.1.*: sqlite3_clear_bindings()