/ Check-in [18e10f81]
Login

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

Overview
Comment:Fix database corrupting code generation error for UPDATE OR REPLACE. ticket #2832. Still need to add test cases and additional defensive logic to avoid future occurrences of similar problems. (CVS 4613)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 18e10f816782ca7842f651e9b2a23da1aab645c8
User & Date: drh 2007-12-12 12:25:22
Context
2007-12-12
14:46
Test file containing minimal example of bug #2832. (CVS 4614) check-in: ed2e61a9 user: danielk1977 tags: trunk
12:25
Fix database corrupting code generation error for UPDATE OR REPLACE. ticket #2832. Still need to add test cases and additional defensive logic to avoid future occurrences of similar problems. (CVS 4613) check-in: 18e10f81 user: drh tags: trunk
12:00
Add a new OP_StackDepth opcode to help detect VDBE stack leaks early, before they cause damage. For diagnostics in ticket #2832. (CVS 4612) check-in: 3fd6a267 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/sqlite.h.in.

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
** on how SQLite interfaces are suppose to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
** @(#) $Id: sqlite.h.in,v 1.276 2007/12/06 02:42:08 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
**
** See also: [SQLITE_IOERR_READ | extended result codes]
**
*/
#define SQLITE_OK           0   /* Successful result */
/* beginning-of-error-codes */
#define SQLITE_ERROR        1   /* SQL error or missing database */
#define SQLITE_INTERNAL     2   /* NOT USED. Internal logic error in SQLite */
#define SQLITE_PERM         3   /* Access permission denied */
#define SQLITE_ABORT        4   /* Callback routine requested an abort */
#define SQLITE_BUSY         5   /* The database file is locked */
#define SQLITE_LOCKED       6   /* A table in the database is locked */
#define SQLITE_NOMEM        7   /* A malloc() failed */
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/







|







 







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
** on how SQLite interfaces are suppose to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
** @(#) $Id: sqlite.h.in,v 1.277 2007/12/12 12:25:22 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
**
** See also: [SQLITE_IOERR_READ | extended result codes]
**
*/
#define SQLITE_OK           0   /* Successful result */
/* beginning-of-error-codes */
#define SQLITE_ERROR        1   /* SQL error or missing database */
#define SQLITE_INTERNAL     2   /* Internal logic error in SQLite */
#define SQLITE_PERM         3   /* Access permission denied */
#define SQLITE_ABORT        4   /* Callback routine requested an abort */
#define SQLITE_BUSY         5   /* The database file is locked */
#define SQLITE_LOCKED       6   /* A table in the database is locked */
#define SQLITE_NOMEM        7   /* A malloc() failed */
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/

Changes to src/update.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
101
102
103
104
105
106
107

108
109
110
111
112
113
114
...
257
258
259
260
261
262
263

264
265
266
267
268
269
270
...
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
...
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
...
425
426
427
428
429
430
431
432
433
434

435
436
437
438
439
440
441
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.142 2007/12/12 12:00:46 drh Exp $
*/
#include "sqliteInt.h"

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
................................................................................
  int chngRowid;         /* True if the record number is being changed */
  Expr *pRowidExpr = 0;  /* Expression defining the new record number */
  int openAll = 0;       /* True if all indices need to be opened */
  AuthContext sContext;  /* The authorization context */
  NameContext sNC;       /* The name-context to resolve expressions in */
  int iDb;               /* Database containing the table being updated */
  int memCnt = 0;        /* Memory cell used for counting rows changed */


#ifndef SQLITE_OMIT_TRIGGER
  int isView;                  /* Trying to update a view */
  int triggers_exist = 0;      /* True if any row triggers exist */
#endif

  int newIdx      = -1;  /* index of trigger "new" temp table       */
................................................................................

  /* Begin generating code.
  */
  v = sqlite3GetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
  sqlite3BeginWriteOperation(pParse, 1, iDb);


#ifndef SQLITE_OMIT_VIRTUALTABLE
  /* Virtual tables must be handled separately */
  if( IsVirtual(pTab) ){
    updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
                       pWhere);
    pWhere = 0;
................................................................................
  */
  if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
    memCnt = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
  }

  if( triggers_exist ){
    int mem1;      /* Memory address storing the rowid for next row to update */
    
    /* Create pseudo-tables for NEW and OLD
    */
    sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
    sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);

    /* The top of the update loop for when there are triggers.
    */
    addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
    sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
    mem1 = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
    
    if( !isView ){
      /* Open a cursor and make it point to the record that is
      ** being updated.
      */
      sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
................................................................................
    if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
          newIdx, oldIdx, onError, addr) ){
      goto update_cleanup;
    }
    
    if( !isView ){
      sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);
      sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
    }
  }

  if( !isView && !IsVirtual(pTab) ){
    /* 
    ** Open every index that needs updating.  Note that if any
    ** index could potentially invoke a REPLACE conflict resolution 
................................................................................
    ** might not change and we will need to copy the old value.
    ** Also, the old data is needed to delete the old index entries.
    ** So make the cursor point at the old record.
    */
    if( !triggers_exist ){
      addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
      sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
      sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
    }
    sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);


    /* If the record number will change, push the record number as it
    ** will be after the update. (The old record number is currently
    ** on top of the stack.)
    */
    if( chngRowid ){
      sqlite3ExprCode(pParse, pRowidExpr);







|







 







>







 







>







 







<












<







 







<







 







|


>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
...
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
...
383
384
385
386
387
388
389

390
391
392
393
394
395
396
...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.143 2007/12/12 12:25:22 drh Exp $
*/
#include "sqliteInt.h"

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
................................................................................
  int chngRowid;         /* True if the record number is being changed */
  Expr *pRowidExpr = 0;  /* Expression defining the new record number */
  int openAll = 0;       /* True if all indices need to be opened */
  AuthContext sContext;  /* The authorization context */
  NameContext sNC;       /* The name-context to resolve expressions in */
  int iDb;               /* Database containing the table being updated */
  int memCnt = 0;        /* Memory cell used for counting rows changed */
  int mem1;      /* Memory address storing the rowid for next row to update */

#ifndef SQLITE_OMIT_TRIGGER
  int isView;                  /* Trying to update a view */
  int triggers_exist = 0;      /* True if any row triggers exist */
#endif

  int newIdx      = -1;  /* index of trigger "new" temp table       */
................................................................................

  /* Begin generating code.
  */
  v = sqlite3GetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
  sqlite3BeginWriteOperation(pParse, 1, iDb);
  mem1 = pParse->nMem++;

#ifndef SQLITE_OMIT_VIRTUALTABLE
  /* Virtual tables must be handled separately */
  if( IsVirtual(pTab) ){
    updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
                       pWhere);
    pWhere = 0;
................................................................................
  */
  if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
    memCnt = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
  }

  if( triggers_exist ){

    
    /* Create pseudo-tables for NEW and OLD
    */
    sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
    sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);

    /* The top of the update loop for when there are triggers.
    */
    addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
    sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);

    sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
    
    if( !isView ){
      /* Open a cursor and make it point to the record that is
      ** being updated.
      */
      sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
................................................................................
    if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
          newIdx, oldIdx, onError, addr) ){
      goto update_cleanup;
    }
    
    if( !isView ){
      sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);

    }
  }

  if( !isView && !IsVirtual(pTab) ){
    /* 
    ** Open every index that needs updating.  Note that if any
    ** index could potentially invoke a REPLACE conflict resolution 
................................................................................
    ** might not change and we will need to copy the old value.
    ** Also, the old data is needed to delete the old index entries.
    ** So make the cursor point at the old record.
    */
    if( !triggers_exist ){
      addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
      sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
      sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
    }
    sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
    sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);

    /* If the record number will change, push the record number as it
    ** will be after the update. (The old record number is currently
    ** on top of the stack.)
    */
    if( chngRowid ){
      sqlite3ExprCode(pParse, pRowidExpr);

Changes to src/vdbe.c.

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
...
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
**
** 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.657 2007/12/12 12:00:46 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
................................................................................
** This opcode is used for internal consistency checking.
*/
case OP_StackDepth: {       /* no-push */
  if( pOp->p1<0 ){
    pOp->p1 = pTos - p->aStack + 1;
  }else if( pOp->p1!=pTos - p->aStack + 1 ){
    p->pTos = pTos;
    p->rc = SQLITE_ERROR;
    p->pc = pc;
    p->errorAction = OE_Rollback;
    sqlite3SetString(&p->zErrMsg, "internal VDBE stack overflow", (char*)0);
    goto vdbe_return;
  }
  break;
}

/* Opcode: Integer P1 * *
**







|







 







|


|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
...
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
**
** 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.658 2007/12/12 12:25:22 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
................................................................................
** This opcode is used for internal consistency checking.
*/
case OP_StackDepth: {       /* no-push */
  if( pOp->p1<0 ){
    pOp->p1 = pTos - p->aStack + 1;
  }else if( pOp->p1!=pTos - p->aStack + 1 ){
    p->pTos = pTos;
    p->rc = rc = SQLITE_INTERNAL;
    p->pc = pc;
    p->errorAction = OE_Rollback;
    sqlite3SetString(&p->zErrMsg, "internal error: VDBE stack leak", (char*)0);
    goto vdbe_return;
  }
  break;
}

/* Opcode: Integer P1 * *
**