Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
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 |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
3f253afe15d4f7392555f340a41d780d |
User & Date: | drh 2002-09-14 13:47:32.000 |
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: f12c3a25ba 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: 3f253afe15 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: 6cb80ae10a user: drh tags: trunk) | |
Changes
Changes to src/build.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | ** COPY ** VACUUM ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** PRAGMA ** | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** 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 |
︙ | ︙ | |||
394 395 396 397 398 399 400 | ** 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 ){ | | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | ** 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); |
︙ | ︙ | |||
1101 1102 1103 1104 1105 1106 1107 | { 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; | | | 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 | { 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); |
︙ | ︙ | |||
1533 1534 1535 1536 1537 1538 1539 | int i; int addr; int isTemp = pTab->isTemp; v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; if( pTable!=0 ){ | | | 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 | 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); |
︙ | ︙ | |||
1639 1640 1641 1642 1643 1644 1645 | { OP_Next, 0, ADDR(3), 0}, { OP_Goto, 0, ADDR(9), 0}, { OP_Delete, 0, 0, 0}, /* 8 */ }; int base; Table *pTab = pIndex->pTable; | | | 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 | { 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); |
︙ | ︙ | |||
1820 1821 1822 1823 1824 1825 1826 | 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; | | | 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 | 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++){ |
︙ | ︙ | |||
1906 1907 1908 1909 1910 1911 1912 | 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; } | | | 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 | 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 */ |
︙ | ︙ | |||
1965 1966 1967 1968 1969 1970 1971 1972 | ** 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. */ | > > > > > | | > | | > | 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 | ** 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 |
︙ | ︙ | |||
2077 2078 2079 2080 2081 2082 2083 | 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; | | | 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 | 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); |
︙ | ︙ | |||
2168 2169 2170 2171 2172 2173 2174 | 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; | | | 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 | 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 | ** 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. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** 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 |
︙ | ︙ | |||
146 147 148 149 150 151 152 | /* Begin generating code. */ v = sqliteGetVdbe(pParse); if( v==0 ){ goto delete_from_cleanup; } | | > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | /* 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 | ** 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. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** 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) |
︙ | ︙ | |||
156 157 158 159 160 161 162 | } } /* Allocate a VDBE */ v = sqliteGetVdbe(pParse); if( v==0 ) goto insert_cleanup; | | > | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | } } /* 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** 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. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** 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> |
︙ | ︙ | |||
973 974 975 976 977 978 979 | 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); | | | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** 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 ** | | > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** 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> #include <assert.h> /* ** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we ** have to do a translation when going between the two. Set the ** 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; |
︙ | ︙ | |||
235 236 237 238 239 240 241 242 243 244 245 246 247 248 | /* ** 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); } /* | > > > > > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | /* ** 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); } /* |
︙ | ︙ | |||
265 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 | 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: ** ** sqlite db1 "my_database" ** db1 close ** ** The first command opens a connection to the "my_database" database ** and calls that connection "db1". The second command causes 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", | > > > > > > > > > > > > > > > > > > > > > > > | | > | | | 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 335 336 337 338 339 340 341 342 343 344 345 | 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: ** ** sqlite db1 "my_database" ** db1 close ** ** The first command opens a connection to the "my_database" database ** and calls that connection "db1". The second command causes 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) ){ |
︙ | ︙ | |||
463 464 465 466 467 468 469 470 471 472 473 474 475 476 | } 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: { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } 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 | }; int addr; Vdbe *v; /* Make an entry in the sqlite_master table */ v = sqliteGetVdbe(pParse); if( v==0 ) goto trigger_cleanup; | | | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | }; 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); |
︙ | ︙ | |||
382 383 384 385 386 387 388 | { 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 */ }; | | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | { 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); |
︙ | ︙ | |||
670 671 672 673 674 675 676 | theSelect.nLimit = -1; theSelect.nOffset = -1; theSelect.zSelect = 0; theSelect.base = 0; v = sqliteGetVdbe(pParse); assert(v); | | | 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | 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 | ** 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. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** 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( |
︙ | ︙ | |||
182 183 184 185 186 187 188 | } } /* Begin generating code. */ v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; | | | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | } } /* 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 | ** ** 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. ** | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ** ** 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. |
︙ | ︙ | |||
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 | 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 | > | 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 | 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 |
︙ | ︙ | |||
2915 2916 2917 2918 2919 2920 2921 | rc = sqliteBtreeBeginCkpt(pBt); if( rc==SQLITE_OK && db->pBeTemp ){ rc = sqliteBtreeBeginCkpt(db->pBeTemp); } break; } | | > > > > > | > | > | 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 | 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; } } |
︙ | ︙ | |||
2972 2973 2974 2975 2976 2977 2978 | ** 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 ){ | | > | 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 | ** 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 2001 September 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # 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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is database locks. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2001 September 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # 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. # #*********************************************************************** # 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 # |
︙ | ︙ | |||
266 267 268 269 270 271 272 273 | 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}} | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | 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 | # 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. # | | | | 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" } |
︙ | ︙ |