/ Check-in [3f253afe]
Login
Overview
Comment:Do not put a write lock on the main database file when writing to a temporary table. (CVS 750)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:3f253afe15d4f7392555f340a41d780d1248087f
User & Date: drh 2002-09-14 13:47:32
Context
2002-09-16
11:44
Modify the sqlite_encode_binary() routine to return the strlen() of the encoded string. Also fix a bug that occurs when attempting to encode a zero-length buffer. (CVS 751) check-in: f12c3a25 user: drh tags: trunk
2002-09-14
13:47
Do not put a write lock on the main database file when writing to a temporary table. (CVS 750) check-in: 3f253afe user: drh tags: trunk
12:04
Rename all tests so that the first part of the test name corresponds to the file that contains that test. This makes it much easier to find a particular test after it fail. (CVS 749) check-in: 6cb80ae1 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/build.c.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
...
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
....
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
....
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
....
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
....
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
....
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
....
1965
1966
1967
1968
1969
1970
1971





1972
1973
1974
1975
1976
1977
1978
1979

1980
1981

1982
1983
1984
1985
1986
1987
1988
....
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
....
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
**     COPY
**     VACUUM
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.111 2002/08/31 18:53:06 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
................................................................................
  ** and allocate the record number for the table entry now.  Before any
  ** PRIMARY KEY or UNIQUE keywords are parsed.  Those keywords will cause
  ** indices to be created and the table record must come before the 
  ** indices.  Hence, the record number for the table must be allocated
  ** now.
  */
  if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){
    sqliteBeginWriteOperation(pParse, 0);
    if( !isTemp ){
      sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0);
      sqliteVdbeAddOp(v, OP_SetCookie, 0, 1);
    }
    sqliteOpenMasterTable(v, isTemp);
    sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
    sqliteVdbeAddOp(v, OP_Dup, 0, 0);
................................................................................
      { OP_Column,     0, 2,        0},
      { OP_Ne,         0, ADDR(7),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };
    Index *pIdx;
    Trigger *pTrigger;
    sqliteBeginWriteOperation(pParse, 0);
    sqliteOpenMasterTable(v, pTable->isTemp);
    /* Drop all triggers associated with the table being dropped */
    pTrigger = pTable->pTrigger;
    while( pTrigger ){
      Token tt;
      tt.z = pTable->pTrigger->name;
      tt.n = strlen(pTable->pTrigger->name);
................................................................................
    int i;
    int addr;
    int isTemp = pTab->isTemp;

    v = sqliteGetVdbe(pParse);
    if( v==0 ) goto exit_create_index;
    if( pTable!=0 ){
      sqliteBeginWriteOperation(pParse, 0);
      sqliteOpenMasterTable(v, isTemp);
    }
    sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
    sqliteVdbeAddOp(v, OP_String, 0, 0);
    sqliteVdbeChangeP3(v, -1, "index", P3_STATIC);
    sqliteVdbeAddOp(v, OP_String, 0, 0);
    sqliteVdbeChangeP3(v, -1, pIndex->zName, P3_STATIC);
................................................................................
      { OP_Next,       0, ADDR(3), 0},
      { OP_Goto,       0, ADDR(9), 0},
      { OP_Delete,     0, 0,       0}, /* 8 */
    };
    int base;
    Table *pTab = pIndex->pTable;

    sqliteBeginWriteOperation(pParse, 0);
    sqliteOpenMasterTable(v, pTab->isTemp);
    base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
    sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0);
    if( !pTab->isTemp ){
      sqliteChangeCookie(db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
................................................................................
  if( sqlite_malloc_failed || zTab==0 ) goto copy_cleanup;
  pTab = sqliteTableNameToTable(pParse, zTab);
  sqliteFree(zTab);
  if( pTab==0 ) goto copy_cleanup;
  v = sqliteGetVdbe(pParse);
  if( v ){
    int openOp;
    sqliteBeginWriteOperation(pParse, 1);
    addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
    sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
    sqliteVdbeDequoteP3(v, addr);
    openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
    sqliteVdbeAddOp(v, openOp, 0, pTab->tnum);
    sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
    for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
................................................................................
  if( pParse->nErr || sqlite_malloc_failed ) return;
  if( db->flags & SQLITE_InTrans ){
    pParse->nErr++;
    sqliteSetString(&pParse->zErrMsg, "cannot start a transaction "
       "within a transaction", 0);
    return;
  }
  sqliteBeginWriteOperation(pParse, 0);
  db->flags |= SQLITE_InTrans;
  db->onError = onError;
}

/*
** Commit a transaction
*/
................................................................................
** a transaction.  If we are already within a transaction, then a checkpoint
** is set if the setCheckpoint parameter is true.  A checkpoint should
** be set for operations that might fail (due to a constraint) part of
** the way through and which will need to undo some writes without having to
** rollback the whole transaction.  For operations where all constraints
** can be checked before any changes are made to the database, it is never
** necessary to undo a write and the checkpoint should not be set.





*/
void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint){
  Vdbe *v;
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if( pParse->trigStack ) return; /* if this is in a trigger */
  if( (pParse->db->flags & SQLITE_InTrans)==0 ){
    sqliteVdbeAddOp(v, OP_Transaction, 0, 0);

    sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
    pParse->schemaVerified = 1;

  }else if( setCheckpoint ){
    sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
  }
}

/*
** Generate code that concludes an operation that may have changed
................................................................................
    if( v==0 ) return;
    if( pRight->z==pLeft->z ){
      sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
    }else{
      int addr;
      int size = atoi(zRight);
      if( size<0 ) size = -size;
      sqliteBeginWriteOperation(pParse, 0);
      sqliteVdbeAddOp(v, OP_Integer, size, 0);
      sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
      addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
      sqliteVdbeAddOp(v, OP_Ge, 0, addr+3);
      sqliteVdbeAddOp(v, OP_Negative, 0, 0);
      sqliteVdbeAddOp(v, OP_SetCookie, 0, 2);
      sqliteEndWriteOperation(pParse);
................................................................................
    if( v==0 ) return;
    if( pRight->z==pLeft->z ){
      sqliteVdbeAddOpList(v, ArraySize(getSync), getSync);
    }else{
      int addr;
      int size = db->cache_size;
      if( size<0 ) size = -size;
      sqliteBeginWriteOperation(pParse, 0);
      sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
      sqliteVdbeAddOp(v, OP_Dup, 0, 0);
      addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
      sqliteVdbeAddOp(v, OP_Ne, 0, addr+3);
      sqliteVdbeAddOp(v, OP_AddImm, MAX_PAGES, 0);
      sqliteVdbeAddOp(v, OP_AbsValue, 0, 0);
      if( !getBoolean(zRight) ){







|







 







|







 







|







 







|







 







|







 







|







 







|







 







>
>
>
>
>

|





|
>
|
|
>







 







|







 







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
...
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
....
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
....
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
....
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
....
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
....
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
....
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
....
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
....
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
**     COPY
**     VACUUM
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.112 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
................................................................................
  ** and allocate the record number for the table entry now.  Before any
  ** PRIMARY KEY or UNIQUE keywords are parsed.  Those keywords will cause
  ** indices to be created and the table record must come before the 
  ** indices.  Hence, the record number for the table must be allocated
  ** now.
  */
  if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){
    sqliteBeginWriteOperation(pParse, 0, isTemp);
    if( !isTemp ){
      sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0);
      sqliteVdbeAddOp(v, OP_SetCookie, 0, 1);
    }
    sqliteOpenMasterTable(v, isTemp);
    sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
    sqliteVdbeAddOp(v, OP_Dup, 0, 0);
................................................................................
      { OP_Column,     0, 2,        0},
      { OP_Ne,         0, ADDR(7),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };
    Index *pIdx;
    Trigger *pTrigger;
    sqliteBeginWriteOperation(pParse, 0, pTable->isTemp);
    sqliteOpenMasterTable(v, pTable->isTemp);
    /* Drop all triggers associated with the table being dropped */
    pTrigger = pTable->pTrigger;
    while( pTrigger ){
      Token tt;
      tt.z = pTable->pTrigger->name;
      tt.n = strlen(pTable->pTrigger->name);
................................................................................
    int i;
    int addr;
    int isTemp = pTab->isTemp;

    v = sqliteGetVdbe(pParse);
    if( v==0 ) goto exit_create_index;
    if( pTable!=0 ){
      sqliteBeginWriteOperation(pParse, 0, isTemp);
      sqliteOpenMasterTable(v, isTemp);
    }
    sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
    sqliteVdbeAddOp(v, OP_String, 0, 0);
    sqliteVdbeChangeP3(v, -1, "index", P3_STATIC);
    sqliteVdbeAddOp(v, OP_String, 0, 0);
    sqliteVdbeChangeP3(v, -1, pIndex->zName, P3_STATIC);
................................................................................
      { OP_Next,       0, ADDR(3), 0},
      { OP_Goto,       0, ADDR(9), 0},
      { OP_Delete,     0, 0,       0}, /* 8 */
    };
    int base;
    Table *pTab = pIndex->pTable;

    sqliteBeginWriteOperation(pParse, 0, pTab->isTemp);
    sqliteOpenMasterTable(v, pTab->isTemp);
    base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
    sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0);
    if( !pTab->isTemp ){
      sqliteChangeCookie(db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
................................................................................
  if( sqlite_malloc_failed || zTab==0 ) goto copy_cleanup;
  pTab = sqliteTableNameToTable(pParse, zTab);
  sqliteFree(zTab);
  if( pTab==0 ) goto copy_cleanup;
  v = sqliteGetVdbe(pParse);
  if( v ){
    int openOp;
    sqliteBeginWriteOperation(pParse, 1, pTab->isTemp);
    addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
    sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
    sqliteVdbeDequoteP3(v, addr);
    openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
    sqliteVdbeAddOp(v, openOp, 0, pTab->tnum);
    sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
    for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
................................................................................
  if( pParse->nErr || sqlite_malloc_failed ) return;
  if( db->flags & SQLITE_InTrans ){
    pParse->nErr++;
    sqliteSetString(&pParse->zErrMsg, "cannot start a transaction "
       "within a transaction", 0);
    return;
  }
  sqliteBeginWriteOperation(pParse, 0, 0);
  db->flags |= SQLITE_InTrans;
  db->onError = onError;
}

/*
** Commit a transaction
*/
................................................................................
** a transaction.  If we are already within a transaction, then a checkpoint
** is set if the setCheckpoint parameter is true.  A checkpoint should
** be set for operations that might fail (due to a constraint) part of
** the way through and which will need to undo some writes without having to
** rollback the whole transaction.  For operations where all constraints
** can be checked before any changes are made to the database, it is never
** necessary to undo a write and the checkpoint should not be set.
**
** The tempOnly flag indicates that only temporary tables will be changed
** during this write operation.  The primary database table is not
** write-locked.  Only the temporary database file gets a write lock.
** Other processes can continue to read or write the primary database file.
*/
void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){
  Vdbe *v;
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if( pParse->trigStack ) return; /* if this is in a trigger */
  if( (pParse->db->flags & SQLITE_InTrans)==0 ){
    sqliteVdbeAddOp(v, OP_Transaction, tempOnly, 0);
    if( !tempOnly ){
      sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
      pParse->schemaVerified = 1;
    }
  }else if( setCheckpoint ){
    sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
  }
}

/*
** Generate code that concludes an operation that may have changed
................................................................................
    if( v==0 ) return;
    if( pRight->z==pLeft->z ){
      sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
    }else{
      int addr;
      int size = atoi(zRight);
      if( size<0 ) size = -size;
      sqliteBeginWriteOperation(pParse, 0, 0);
      sqliteVdbeAddOp(v, OP_Integer, size, 0);
      sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
      addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
      sqliteVdbeAddOp(v, OP_Ge, 0, addr+3);
      sqliteVdbeAddOp(v, OP_Negative, 0, 0);
      sqliteVdbeAddOp(v, OP_SetCookie, 0, 2);
      sqliteEndWriteOperation(pParse);
................................................................................
    if( v==0 ) return;
    if( pRight->z==pLeft->z ){
      sqliteVdbeAddOpList(v, ArraySize(getSync), getSync);
    }else{
      int addr;
      int size = db->cache_size;
      if( size<0 ) size = -size;
      sqliteBeginWriteOperation(pParse, 0, 0);
      sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
      sqliteVdbeAddOp(v, OP_Dup, 0, 0);
      addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
      sqliteVdbeAddOp(v, OP_Ne, 0, addr+3);
      sqliteVdbeAddOp(v, OP_AddImm, MAX_PAGES, 0);
      sqliteVdbeAddOp(v, OP_AbsValue, 0, 0);
      if( !getBoolean(zRight) ){

Changes to src/delete.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
**    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 DELETE FROM statements.
**
** $Id: delete.c,v 1.41 2002/07/19 18:52:41 drh Exp $
*/
#include "sqliteInt.h"


/*
** Given a table name, find the corresponding table and make sure the
** table is writeable.  Generate an error and return NULL if not.  If
................................................................................

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ){
    goto delete_from_cleanup;
  }
  sqliteBeginWriteOperation(pParse, row_triggers_exist);


  /* Initialize the counter of the number of rows deleted, if
  ** we are counting rows.
  */
  if( db->flags & SQLITE_CountRows ){
    sqliteVdbeAddOp(v, OP_Integer, 0, 0);
  }







|







 







|
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
**    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 DELETE FROM statements.
**
** $Id: delete.c,v 1.42 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"


/*
** Given a table name, find the corresponding table and make sure the
** table is writeable.  Generate an error and return NULL if not.  If
................................................................................

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ){
    goto delete_from_cleanup;
  }
  sqliteBeginWriteOperation(pParse, row_triggers_exist,
       !row_triggers_exist && pTab->isTemp);

  /* Initialize the counter of the number of rows deleted, if
  ** we are counting rows.
  */
  if( db->flags & SQLITE_CountRows ){
    sqliteVdbeAddOp(v, OP_Integer, 0, 0);
  }

Changes to src/insert.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
**    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 INSERT statements in SQLite.
**
** $Id: insert.c,v 1.66 2002/08/28 03:00:58 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is call to handle SQL of the following forms:
**
**    insert into TABLE (IDLIST) values(EXPRLIST)
................................................................................
    }
  }

  /* Allocate a VDBE
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;
  sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist);


  /* if there are row triggers, allocate a temp table for new.* references. */
  if( row_triggers_exist ){
    newIdx = pParse->nTab++;
  }

  /* Figure out how many columns of data are supplied.  If the data







|







 







|
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
**    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 INSERT statements in SQLite.
**
** $Id: insert.c,v 1.67 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is call to handle SQL of the following forms:
**
**    insert into TABLE (IDLIST) values(EXPRLIST)
................................................................................
    }
  }

  /* Allocate a VDBE
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;
  sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist,
         !row_triggers_exist && pTab->isTemp);

  /* if there are row triggers, allocate a temp table for new.* references. */
  if( row_triggers_exist ){
    newIdx = pParse->nTab++;
  }

  /* Figure out how many columns of data are supplied.  If the data

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
**    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.145 2002/08/31 18:53:07 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
#include <stdio.h>
................................................................................
int sqliteExprIsConstant(Expr*);
int sqliteExprIsInteger(Expr*, int*);
int sqliteIsRowid(const char*);
void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int);
void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*);
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
void sqliteBeginWriteOperation(Parse*, int);
void sqliteEndWriteOperation(Parse*);
Expr *sqliteExprDup(Expr*);
void sqliteTokenCopy(Token*, Token*);
ExprList *sqliteExprListDup(ExprList*);
SrcList *sqliteSrcListDup(SrcList*);
IdList *sqliteIdListDup(IdList*);
Select *sqliteSelectDup(Select*);







|







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
**    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.146 2002/09/14 13:47:32 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
#include <stdio.h>
................................................................................
int sqliteExprIsConstant(Expr*);
int sqliteExprIsInteger(Expr*, int*);
int sqliteIsRowid(const char*);
void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int);
void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*);
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
void sqliteBeginWriteOperation(Parse*, int, int);
void sqliteEndWriteOperation(Parse*);
Expr *sqliteExprDup(Expr*);
void sqliteTokenCopy(Token*, Token*);
ExprList *sqliteExprListDup(ExprList*);
SrcList *sqliteSrcListDup(SrcList*);
IdList *sqliteIdListDup(IdList*);
Select *sqliteSelectDup(Select*);

Changes to src/tclsqlite.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
27
28
29
30
31
32
33











34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
...
235
236
237
238
239
240
241





242
243
244
245
246
247
248
...
265
266
267
268
269
270
271























272
273
274
275
276
277
278
...
284
285
286
287
288
289
290
291
292

293
294
295
296
297
298
299
300
301
302
303
304
...
463
464
465
466
467
468
469




























470
471
472
473
474
475
476
**    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.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.41 2002/09/03 19:43:24 drh Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
** UTF_TRANSLATION_NEEDED macro to indicate that we need to do
** this translation.  
*/
#if defined(TCL_UTF_MAX) && !defined(SQLITE_UTF8)
# define UTF_TRANSLATION_NEEDED 1
#endif












/*
** There is one instance of this structure for each SQLite database
** that has been opened by the SQLite TCL interface.
*/
typedef struct SqliteDb SqliteDb;
struct SqliteDb {
  sqlite *db;           /* The "real" database structure */
  Tcl_Interp *interp;   /* The interpreter used for this database */
  char *zBusy;          /* The busy callback routine */

};

/*
** An instance of this structure passes information thru the sqlite
** logic from the original TCL command into the callback routine.
*/
typedef struct CallbackData CallbackData;
................................................................................

/*
** Called when the command is deleted.
*/
static void DbDeleteCmd(void *db){
  SqliteDb *pDb = (SqliteDb*)db;
  sqlite_close(pDb->db);





  if( pDb->zBusy ){
    Tcl_Free(pDb->zBusy);
  }
  Tcl_Free((char*)pDb);
}

/*
................................................................................
  rc = Tcl_Eval(pDb->interp, zCmd);
  Tcl_DStringFree(&cmd);
  if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
    return 0;
  }
  return 1;
}
























/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database.  This routine is invoked
** whenever one of those connection-specific commands is executed
** in Tcl.  For example, if you run Tcl code like this:
**
................................................................................
** subroutine to be invoked.
*/
static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
  SqliteDb *pDb = (SqliteDb*)cd;
  int choice;
  static const char *DB_strs[] = {
    "busy",               "changes",           "close",
    "complete",           "eval",              "last_insert_rowid",
    "open_aux_file",      "timeout",           0

  };
  enum DB_enum {
    DB_BUSY,              DB_CHANGES,          DB_CLOSE,
    DB_COMPLETE,          DB_EVAL,             DB_LAST_INSERT_ROWID,
    DB_OPEN_AUX_FILE,     DB_TIMEOUT,          
  };

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){
................................................................................
      }
      free(cbData.azColName);
      cbData.azColName = 0;
    }
#endif
    return rc;
  }





























  /*
  **     $db last_insert_rowid 
  **
  ** Return an integer which is the ROWID for the most recent insert.
  */
  case DB_LAST_INSERT_ROWID: {







|







 







>
>
>
>
>
>
>
>
>
>
>









>







 







>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
>



|
|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
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
...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
504
505
506
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
540
541
542
543
544
545
**    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.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.42 2002/09/14 13:47:32 drh Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
................................................................................
** UTF_TRANSLATION_NEEDED macro to indicate that we need to do
** this translation.  
*/
#if defined(TCL_UTF_MAX) && !defined(SQLITE_UTF8)
# define UTF_TRANSLATION_NEEDED 1
#endif

/*
** New SQL functions can be created as TCL scripts.  Each such function
** is described by an instance of the following structure.
*/
typedef struct SqlFunc SqlFunc;
struct SqlFunc {
  Tcl_Interp *interp;   /* The TCL interpret to execute the function */
  char *zScript;        /* The script to be run */
  SqlFunc *pNext;       /* Next function on the list of them all */
};

/*
** There is one instance of this structure for each SQLite database
** that has been opened by the SQLite TCL interface.
*/
typedef struct SqliteDb SqliteDb;
struct SqliteDb {
  sqlite *db;           /* The "real" database structure */
  Tcl_Interp *interp;   /* The interpreter used for this database */
  char *zBusy;          /* The busy callback routine */
  SqlFunc *pFunc;       /* List of SQL functions */
};

/*
** An instance of this structure passes information thru the sqlite
** logic from the original TCL command into the callback routine.
*/
typedef struct CallbackData CallbackData;
................................................................................

/*
** Called when the command is deleted.
*/
static void DbDeleteCmd(void *db){
  SqliteDb *pDb = (SqliteDb*)db;
  sqlite_close(pDb->db);
  while( pDb->pFunc ){
    SqlFunc *pFunc = pDb->pFunc;
    pDb->pFunc = pFunc->pNext;
    Tcl_Free((char*)pFunc);
  }
  if( pDb->zBusy ){
    Tcl_Free(pDb->zBusy);
  }
  Tcl_Free((char*)pDb);
}

/*
................................................................................
  rc = Tcl_Eval(pDb->interp, zCmd);
  Tcl_DStringFree(&cmd);
  if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){
    return 0;
  }
  return 1;
}

/*
** This routine is called to evaluate an SQL function implemented
** using TCL script.
*/
static void tclSqlFunc(sqlite_func *context, int argc, const char **argv){
  SqlFunc *p = sqlite_user_data(context);
  Tcl_DString cmd;
  int i;
  int rc;

  Tcl_DStringInit(&cmd);
  Tcl_DStringAppend(&cmd, p->zScript, -1);
  for(i=0; i<argc; i++){
    Tcl_DStringAppendElement(&cmd, argv[i] ? argv[i] : "");
  }
  rc = Tcl_Eval(p->interp, Tcl_DStringValue(&cmd));
  if( rc ){
    sqlite_set_result_error(context, Tcl_GetStringResult(p->interp), -1); 
  }else{
    sqlite_set_result_string(context, Tcl_GetStringResult(p->interp), -1);
  }
}

/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database.  This routine is invoked
** whenever one of those connection-specific commands is executed
** in Tcl.  For example, if you run Tcl code like this:
**
................................................................................
** subroutine to be invoked.
*/
static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
  SqliteDb *pDb = (SqliteDb*)cd;
  int choice;
  static const char *DB_strs[] = {
    "busy",               "changes",           "close",
    "complete",           "eval",              "function",
    "last_insert_rowid",  "open_aux_file",     "timeout",
    0                    
  };
  enum DB_enum {
    DB_BUSY,              DB_CHANGES,          DB_CLOSE,
    DB_COMPLETE,          DB_EVAL,             DB_FUNCTION,
    DB_LAST_INSERT_ROWID, DB_OPEN_AUX_FILE,    DB_TIMEOUT,
  };

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){
................................................................................
      }
      free(cbData.azColName);
      cbData.azColName = 0;
    }
#endif
    return rc;
  }

  /*
  **     $db function NAME SCRIPT
  **
  ** Create a new SQL function called NAME.  Whenever that function is
  ** called, invoke SCRIPT to evaluate the function.
  */
  case DB_FUNCTION: {
    SqlFunc *pFunc;
    char *zName;
    char *zScript;
    int nScript;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
      return TCL_ERROR;
    }
    zName = Tcl_GetStringFromObj(objv[2], 0);
    zScript = Tcl_GetStringFromObj(objv[3], &nScript);
    pFunc = (SqlFunc*)Tcl_Alloc( sizeof(*pFunc) + nScript + 1 );
    if( pFunc==0 ) return TCL_ERROR;
    pFunc->interp = interp;
    pFunc->pNext = pDb->pFunc;
    pFunc->zScript = (char*)&pFunc[1];
    strcpy(pFunc->zScript, zScript);
    sqlite_create_function(pDb->db, zName, -1, tclSqlFunc, pFunc);
    sqlite_function_type(pDb->db, zName, SQLITE_NUMERIC);
    break;
  }

  /*
  **     $db last_insert_rowid 
  **
  ** Return an integer which is the ROWID for the most recent insert.
  */
  case DB_LAST_INSERT_ROWID: {

Changes to src/trigger.c.

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
...
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
    };
    int addr;
    Vdbe *v;

    /* Make an entry in the sqlite_master table */
    v = sqliteGetVdbe(pParse);
    if( v==0 ) goto trigger_cleanup;
    sqliteBeginWriteOperation(pParse, 0);
    sqliteOpenMasterTable(v, tab->isTemp);
    addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
    sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME,
                       P3_STATIC);
    sqliteVdbeChangeP3(v, addr+2, nt->name, 0); 
    sqliteVdbeChangeP3(v, addr+3, nt->table, 0); 
    sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
................................................................................
      { OP_MemLoad,    1, 0,        0}, /* 3 */
      { OP_Column,     0, 1,        0},
      { OP_Ne,         0, ADDR(7),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };

    sqliteBeginWriteOperation(pParse, 0);
    sqliteOpenMasterTable(v, pTable->isTemp);
    base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
    sqliteVdbeChangeP3(v, base+1, zName, 0);
    if( !pTable->isTemp ){
      sqliteChangeCookie(pParse->db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
................................................................................
  theSelect.nLimit = -1;
  theSelect.nOffset = -1;
  theSelect.zSelect = 0;
  theSelect.base = 0;

  v = sqliteGetVdbe(pParse);
  assert(v);
  sqliteBeginWriteOperation(pParse, 1);

  /* Allocate temp tables */
  oldIdx = pParse->nTab++;
  sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
  if( pChanges ){
    newIdx = pParse->nTab++;
    sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);







|







 







|







 







|







139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
...
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
    };
    int addr;
    Vdbe *v;

    /* Make an entry in the sqlite_master table */
    v = sqliteGetVdbe(pParse);
    if( v==0 ) goto trigger_cleanup;
    sqliteBeginWriteOperation(pParse, 0, 0);
    sqliteOpenMasterTable(v, tab->isTemp);
    addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
    sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME,
                       P3_STATIC);
    sqliteVdbeChangeP3(v, addr+2, nt->name, 0); 
    sqliteVdbeChangeP3(v, addr+3, nt->table, 0); 
    sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
................................................................................
      { OP_MemLoad,    1, 0,        0}, /* 3 */
      { OP_Column,     0, 1,        0},
      { OP_Ne,         0, ADDR(7),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };

    sqliteBeginWriteOperation(pParse, 0, 0);
    sqliteOpenMasterTable(v, pTable->isTemp);
    base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
    sqliteVdbeChangeP3(v, base+1, zName, 0);
    if( !pTable->isTemp ){
      sqliteChangeCookie(pParse->db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
................................................................................
  theSelect.nLimit = -1;
  theSelect.nOffset = -1;
  theSelect.zSelect = 0;
  theSelect.base = 0;

  v = sqliteGetVdbe(pParse);
  assert(v);
  sqliteBeginWriteOperation(pParse, 1, 0);

  /* Allocate temp tables */
  oldIdx = pParse->nTab++;
  sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
  if( pChanges ){
    newIdx = pParse->nTab++;
    sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);

Changes to src/update.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
**    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.49 2002/07/21 23:09:55 danielk1977 Exp $
*/
#include "sqliteInt.h"

/*
** Process an UPDATE statement.
*/
void sqliteUpdate(
................................................................................
    }
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  sqliteBeginWriteOperation(pParse, 1);

  /* Begin the database scan
  */
  pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0);
  if( pWInfo==0 ) goto update_cleanup;

  /* Remember the index of every item to be updated.







|







 







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
**    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.50 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"

/*
** Process an UPDATE statement.
*/
void sqliteUpdate(
................................................................................
    }
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->isTemp);

  /* Begin the database scan
  */
  pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0);
  if( pWInfo==0 ) goto update_cleanup;

  /* Remember the index of every item to be updated.

Changes to src/vdbe.c.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
1332
1333
1334
1335
1336
1337
1338

1339
1340
1341
1342
1343
1344
1345
....
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926





2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940

2941
2942

2943
2944
2945
2946
2947
2948
2949
....
2950
2951
2952
2953
2954
2955
2956

2957
2958
2959
2960
2961
2962
2963
....
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987

2988
2989
2990
2991
2992
2993
2994
**
** 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.177 2002/09/08 17:23:43 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
  Btree *pBt = p->pBt;       /* The backend driver */
  sqlite *db = p->db;        /* The database */
  char **zStack;             /* Text stack */
  Stack *aStack;             /* Additional stack information */
  unsigned uniqueCnt = 0;     /* Used by OP_MakeRecord when P2!=0 */
  int errorAction = OE_Abort; /* Recovery action to do in case of an error */
  int undoTransOnError = 0;   /* If error, either ROLLBACK or COMMIT */

  char zBuf[100];             /* Space to sprintf() an integer */
  int returnStack[100];     /* Return address stack for OP_Gosub & OP_Return */
  int returnDepth = 0;      /* Next unused element in returnStack[] */


  /* No instruction ever pushes more than a single element onto the
  ** stack.  And the stack never grows on successive executions of the
................................................................................
  rc = sqliteBtreeBeginCkpt(pBt);
  if( rc==SQLITE_OK && db->pBeTemp ){
     rc = sqliteBtreeBeginCkpt(db->pBeTemp);
  }
  break;
}

/* Opcode: Transaction * * *
**
** Begin a transaction.  The transaction ends when a Commit or Rollback
** opcode is encountered.  Depending on the ON CONFLICT setting, the
** transaction might also be rolled back if an error is encountered.





**
** A write lock is obtained on the database file when a transaction is
** started.  No other process can read or write the file while the
** transaction is underway.  Starting a transaction also creates a
** rollback journal.  A transaction must be started before any changes
** can be made to the database.
*/
case OP_Transaction: {
  int busy = 0;
  if( db->pBeTemp ){
    rc = sqliteBtreeBeginTrans(db->pBeTemp);
    if( rc!=SQLITE_OK ){
      goto abort_due_to_error;
    }

  }
  do{

    rc = sqliteBtreeBeginTrans(pBt);
    switch( rc ){
      case SQLITE_BUSY: {
        if( xBusy==0 || (*xBusy)(pBusyArg, "", ++busy)==0 ){
          sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
          busy = 0;
        }
................................................................................
        break;
      }
      case SQLITE_READONLY: {
        rc = SQLITE_OK;
        /* Fall thru into the next case */
      }
      case SQLITE_OK: {

        busy = 0;
        break;
      }
      default: {
        goto abort_due_to_error;
      }
    }
................................................................................
** last Transaction to actually take effect.  No additional modifications
** are allowed until another transaction is started.  The Commit instruction
** deletes the journal file and releases the write lock on the database.
** A read lock continues to be held if there are still cursors open.
*/
case OP_Commit: {
  if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){
    rc = sqliteBtreeCommit(pBt);
  }
  if( rc==SQLITE_OK ){
    sqliteCommitInternalChanges(db);
  }else{
    if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp);
    sqliteBtreeRollback(pBt);
    sqliteRollbackInternalChanges(db);
  }

  break;
}

/* Opcode: Rollback * * *
**
** Cause all modifications to the database that have been made since the
** last Transaction to be undone. The database is restored to its state







|







 







>







 







|




>
>
>
>
>









|




>

<
>







 







>







 







|








>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
....
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
....
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948

2949
2950
2951
2952
2953
2954
2955
2956
....
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
....
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
**
** 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.178 2002/09/14 13:47:32 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** The makefile scans this source file and creates the following
** array of string constants which are the names of all VDBE opcodes.
................................................................................
  Btree *pBt = p->pBt;       /* The backend driver */
  sqlite *db = p->db;        /* The database */
  char **zStack;             /* Text stack */
  Stack *aStack;             /* Additional stack information */
  unsigned uniqueCnt = 0;     /* Used by OP_MakeRecord when P2!=0 */
  int errorAction = OE_Abort; /* Recovery action to do in case of an error */
  int undoTransOnError = 0;   /* If error, either ROLLBACK or COMMIT */
  int inTempTrans = 0;        /* True if temp database is transactioned */
  char zBuf[100];             /* Space to sprintf() an integer */
  int returnStack[100];     /* Return address stack for OP_Gosub & OP_Return */
  int returnDepth = 0;      /* Next unused element in returnStack[] */


  /* No instruction ever pushes more than a single element onto the
  ** stack.  And the stack never grows on successive executions of the
................................................................................
  rc = sqliteBtreeBeginCkpt(pBt);
  if( rc==SQLITE_OK && db->pBeTemp ){
     rc = sqliteBtreeBeginCkpt(db->pBeTemp);
  }
  break;
}

/* Opcode: Transaction P1 * *
**
** Begin a transaction.  The transaction ends when a Commit or Rollback
** opcode is encountered.  Depending on the ON CONFLICT setting, the
** transaction might also be rolled back if an error is encountered.
**
** If P1 is true, then the transaction is started on the temporary
** tables of the database only.  The main database file is not write
** locked and other processes can continue to read the main database
** file.
**
** A write lock is obtained on the database file when a transaction is
** started.  No other process can read or write the file while the
** transaction is underway.  Starting a transaction also creates a
** rollback journal.  A transaction must be started before any changes
** can be made to the database.
*/
case OP_Transaction: {
  int busy = 0;
  if( db->pBeTemp && !inTempTrans ){
    rc = sqliteBtreeBeginTrans(db->pBeTemp);
    if( rc!=SQLITE_OK ){
      goto abort_due_to_error;
    }
    inTempTrans = 1;
  }

  if( pOp->p1==0 ) do{
    rc = sqliteBtreeBeginTrans(pBt);
    switch( rc ){
      case SQLITE_BUSY: {
        if( xBusy==0 || (*xBusy)(pBusyArg, "", ++busy)==0 ){
          sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
          busy = 0;
        }
................................................................................
        break;
      }
      case SQLITE_READONLY: {
        rc = SQLITE_OK;
        /* Fall thru into the next case */
      }
      case SQLITE_OK: {
        inTempTrans = 0;
        busy = 0;
        break;
      }
      default: {
        goto abort_due_to_error;
      }
    }
................................................................................
** last Transaction to actually take effect.  No additional modifications
** are allowed until another transaction is started.  The Commit instruction
** deletes the journal file and releases the write lock on the database.
** A read lock continues to be held if there are still cursors open.
*/
case OP_Commit: {
  if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){
    rc = inTempTrans ? SQLITE_OK : sqliteBtreeCommit(pBt);
  }
  if( rc==SQLITE_OK ){
    sqliteCommitInternalChanges(db);
  }else{
    if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp);
    sqliteBtreeRollback(pBt);
    sqliteRollbackInternalChanges(db);
  }
  inTempTrans = 0;
  break;
}

/* Opcode: Rollback * * *
**
** Cause all modifications to the database that have been made since the
** last Transaction to be undone. The database is restored to its state

Changes to test/lock.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
266
267
268
269
270
271
272

273



274



















































275
276
277
278
279
#    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 database locks.
#
# $Id: lock.test,v 1.17 2002/08/29 23:59:50 drh Exp $


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

# Create an alternative connection to the database
#
................................................................................
    lappend ::callback_value $count
    if {$count>4} break
  }
  db2 busy callback
  set rc [catch {db2 eval {SELECT * FROM t1}} msg]
  lappend rc $msg $::callback_value
} {1 {database is locked} {1 2 3 4 5}}

























































do_test lock-999.1 {
  rename db2 {}
} {}

finish_test







|







 







>

>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#    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 database locks.
#
# $Id: lock.test,v 1.18 2002/09/14 13:47:33 drh Exp $


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

# Create an alternative connection to the database
#
................................................................................
    lappend ::callback_value $count
    if {$count>4} break
  }
  db2 busy callback
  set rc [catch {db2 eval {SELECT * FROM t1}} msg]
  lappend rc $msg $::callback_value
} {1 {database is locked} {1 2 3 4 5}}
execsql {ROLLBACK}

# When one thread is writing, other threads cannot read.  Except if the
# writing thread is writing to its temporary tables, the other threads
# can still read.
#
proc tx_exec {sql} {
  db2 eval $sql
}
do_test lock-5.1 {
  execsql {
    SELECT * FROM t1
  }
} {2 1}
do_test lock-5.2 {
  db function tx_exec tx_exec
  catchsql {
    INSERT INTO t1(a,b) SELECT 3, tx_exec('SELECT y FROM t2 LIMIT 1');
  }
} {1 {database is locked}}
do_test lock-5.3 {
  execsql {
    CREATE TEMP TABLE t3(x);
    SELECT * FROM t3;
  }
} {}
do_test lock-5.4 {
  catchsql {
    INSERT INTO t3 SELECT tx_exec('SELECT y FROM t2 LIMIT 1');
  }
} {0 {}}
do_test lock-5.5 {
  execsql {
    SELECT * FROM t3;
  }
} {8}
do_test lock-5.6 {
  catchsql {
    UPDATE t1 SET a=tx_exec('SELECT x FROM t2');
  }
} {1 {database is locked}}
do_test lock-5.7 {
  execsql {
    SELECT * FROM t1;
  }
} {2 1}
do_test lock-5.8 {
  catchsql {
    UPDATE t3 SET x=tx_exec('SELECT x FROM t2');
  }
} {0 {}}
do_test lock-5.9 {
  execsql {
    SELECT * FROM t3;
  }
} {9}

do_test lock-999.1 {
  rename db2 {}
} {}

finish_test

Changes to test/tclsqlite.test.

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
# This file implements regression tests for TCL interface to the
# SQLite library. 
#
# Actually, all tests are based on the TCL interface, so the main
# interface is pretty well tested.  This file contains some addition
# tests for fringe issues that the main test suite does not cover.
#
# $Id: tclsqlite.test,v 1.7 2002/06/25 19:31:18 drh Exp $

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

# Check the error messages generated by tclsqlite
#
do_test tcl-1.1 {
  set v [catch {sqlite bogus} msg]
  lappend v $msg
} {1 {wrong # args: should be "sqlite HANDLE FILENAME ?MODE?"}}
do_test tcl-1.2 {
  set v [catch {db bogus} msg]
  lappend v $msg
} {1 {bad option "bogus": must be busy, changes, close, complete, eval, last_insert_rowid, open_aux_file, or timeout}}
do_test tcl-1.3 {
  execsql {CREATE TABLE t1(a int, b int)}
  execsql {INSERT INTO t1 VALUES(10,20)}
  set v [catch {
    db eval {SELECT * FROM t1} data {
      error "The error message"
    }







|













|







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
# This file implements regression tests for TCL interface to the
# SQLite library. 
#
# Actually, all tests are based on the TCL interface, so the main
# interface is pretty well tested.  This file contains some addition
# tests for fringe issues that the main test suite does not cover.
#
# $Id: tclsqlite.test,v 1.8 2002/09/14 13:47:33 drh Exp $

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

# Check the error messages generated by tclsqlite
#
do_test tcl-1.1 {
  set v [catch {sqlite bogus} msg]
  lappend v $msg
} {1 {wrong # args: should be "sqlite HANDLE FILENAME ?MODE?"}}
do_test tcl-1.2 {
  set v [catch {db bogus} msg]
  lappend v $msg
} {1 {bad option "bogus": must be busy, changes, close, complete, eval, function, last_insert_rowid, open_aux_file, or timeout}}
do_test tcl-1.3 {
  execsql {CREATE TABLE t1(a int, b int)}
  execsql {INSERT INTO t1 VALUES(10,20)}
  set v [catch {
    db eval {SELECT * FROM t1} data {
      error "The error message"
    }