1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Fix problems with TEMP indices that lead to corrupt databases. These

problems were discovered while working on ticket #317.  No sure yet if
that ticket is fixed. (CVS 981)

FossilOrigin-Name: 01398fb78bab7e5c6f439f2b743f26e82118468a
This commit is contained in:
drh
2003-05-17 17:35:10 +00:00
parent 0594f5b97e
commit 8bf8dc9208
17 changed files with 493 additions and 303 deletions

View File

@@ -1,5 +1,5 @@
C Version\s2.8.1\s(CVS\s980)
D 2003-05-17T02:44:32
C Fix\sproblems\swith\sTEMP\sindices\sthat\slead\sto\scorrupt\sdatabases.\s\sThese\nproblems\swere\sdiscovered\swhile\sworking\son\sticket\s#317.\s\sNo\ssure\syet\sif\nthat\sticket\sis\sfixed.\s(CVS\s981)
D 2003-05-17T17:35:11
F Makefile.in 1ff85c27d4350c74118341024e8a4fb2a04a3a43
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -21,19 +21,19 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
F src/attach.c 7ebc7487de43e357a64226f8abef81f2669f2183
F src/auth.c c8f50d4507e37779d96ff3c55417bc2b612dfed6
F src/btree.c 077d75aee4ed63f3628698611ba43c87097d458d
F src/btree.c 8092dca45dcdb69c61273db0213cbb85760673c7
F src/btree.h 23c50e00709de16a5dce1fcea9c0429cc955ff0e
F src/btree_rb.c 8e00e40be264716e1929987878672e55d9e0e76d
F src/build.c e24461d42381a36de88de6af06c03d9f14588705
F src/copy.c 44b13fd4d2444fb53bff8a5ecee1c5f6f86a8263
F src/delete.c f9536a75b444a21f11b7a1bc0fb8c876f691b013
F src/btree_rb.c 1d809e703aab8bef916ebb0b6ba9254a1c36d9a6
F src/build.c 95dfb188f448e6299108396546a8333ecdcb1716
F src/copy.c 5e5d558d283536592cd67b5dc1519c3152bd7e20
F src/delete.c 0f81e6799c089487615d38e042a2de4d2d6192bc
F src/encode.c ed720e54ec4ef4d4de651592f1dd1c74d422bbd2
F src/expr.c a666ef5220ca90ebcf40c8ccc783966a345a7616
F src/func.c 33bbce6acaf9578ac99aa1f689968ccaf2ce43a2
F src/hash.c 058f077c1f36f266581aa16f907a3903abf64aa3
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c fac16589e644b2d4bb615c5e782bcfd0453a052a
F src/main.c 16e68905793b118552a9cf43a9f77ca1d9e395a9
F src/insert.c 2f26b95cc1055062411cbdea06e2e1b40a8b0d8d
F src/main.c 717aaf32d468667dabeaec80054e11bfdb6309b6
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
F src/os.c 94b618c0c0a76210e53857d77c96d2f042dc33b1
F src/os.h 9e5bbddff123187295e3d00d49af06192cd1cd49
@@ -43,11 +43,11 @@ F src/parse.y 39b5240cb78047dc56d6d37c398baed7ba556779
F src/pragma.c ec64704e61286948f39157617f1ce2f506dd1b74
F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c c06b4605bca03d8237a3fc4098179bf3a7133702
F src/select.c 15d921308065c9320363af6f43c01d9f09ea7118
F src/shell.c 2565cb32cd862024bcfd88400e05437636cf21a1
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in eec06462cba262c0ee03f38462a18a4bc66dda4e
F src/sqliteInt.h 9b64d8225a26f3d5a376370b31060dd70bcc362b
F src/sqliteInt.h cab919e43875a561603ca6e0d060fd0690c2ee7c
F src/table.c 4301926464d88d2c2c7cd21c3360aa75bf068b95
F src/tclsqlite.c 9e25f98f1765afa0716144ef57abda75c88f688d
F src/test1.c 4596acd9d9f2a49fda0160a8a6dee5bfc7c6c325
@@ -56,14 +56,14 @@ F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
F src/tokenize.c 2ba93fe10d5f57f0cc20b07417c3244a30c324b3
F src/trigger.c 8ee811986080de60d9d883ad96daffea82014f27
F src/update.c dc3b680b4cf2cb6d839950b68d632850746639b9
F src/update.c 8e657c7b3d27b5592d8caa362d9c4765e0b3cd6a
F src/util.c 8065b78806a5132119452d9e0417cf071e6f02f9
F src/vacuum.c 0820984615786c9ccdaad8032a792309b354a8eb
F src/vdbe.c e9e560b0c568fb4ffb189d3e0d91628a33d2a367
F src/vdbe.c 81b9868acd7e7d54ddd26af4ffe8442c312ad374
F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
F src/where.c ef11773a07cf075740378d7d1cbabf328146fdc9
F src/where.c 1e645d430cb4b347159c28c6085e9801160f2099
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
F test/attach.test b311c83e370e6b22b79a8279317039440ce64862
F test/attach.test ca8304e0f2236d1bf68e53ccc17cf3e8d76de0f1
F test/auth.test dee78be1f4f920bd6b15c4c947ce4d01bfe2826d
F test/bigfile.test 1cd8256d4619c39bea48147d344f348823e78678
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
@@ -116,11 +116,11 @@ F test/subselect.test f0fea8cf9f386d416d64d152e3c65f9116d0f50f
F test/table.test 371a1fc1c470982b2f68f9732f903a5d96f949c4
F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
F test/tclsqlite.test d9bdfc0afca9ee605c50ecb39e94ae4dea8c222b
F test/temptable.test 6feff1960c707e924d5462356c5303943dac4a8e
F test/temptable.test c82bd6f800f10e8cf96921af6315e5f1c21e2692
F test/tester.tcl d7a5835edaf118539241145d8188f0822b673488
F test/trans.test 75e7a171b5d2d94ee56766459113e2ad0e5f809d
F test/trigger1.test 61ef41666f066ac417730dc26056053a7c36cd11
F test/trigger2.test adf6a9cfd735bd4be4f7be19da629b0968703744
F test/trigger2.test 00ceb8aff6bddd511bbac7c837af2863fa0c9cb4
F test/trigger3.test 870afef7997a5b86bf3ea893ce0c2e85d6356c72
F test/trigger4.test 9a5c1406344d743020c2753ae8d6dfe6eb75f818
F test/unique.test 22a46df72a3e0a3fd1a2d39e96fb59f18448dd5f
@@ -165,7 +165,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
F www/sqlite.tcl 4bd1729e320f5fa9125f0022b281fbe839192125
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
P baea7aca10e30f30b874e1e8b6cd3b05954ba83c
R d79117ebcf990c76ac030f81c35e1389
P 590f963b6599e4e235d7369f19c63cece4b2ad95
R 869048a3307d9ca8d4b394c814d0ad0e
U drh
Z b3ec67d7cdead0321417395bbb89e4c5
Z 2835ef2dc75e2e6d87e08c87fdd8ab29

View File

@@ -1 +1 @@
590f963b6599e4e235d7369f19c63cece4b2ad95
01398fb78bab7e5c6f439f2b743f26e82118468a

View File

@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.92 2003/04/25 15:37:58 drh Exp $
** $Id: btree.c,v 1.93 2003/05/17 17:35:11 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -3229,7 +3229,7 @@ static void checkList(
FreelistInfo *pInfo = (FreelistInfo*)pOvfl->aPayload;
int n = SWAB32(pCheck->pBt, pInfo->nFree);
for(i=0; i<n; i++){
checkRef(pCheck, SWAB32(pCheck->pBt, pInfo->aFree[i]), zMsg);
checkRef(pCheck, SWAB32(pCheck->pBt, pInfo->aFree[i]), zContext);
}
N -= n;
}

View File

@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree_rb.c,v 1.9 2003/04/25 13:22:53 drh Exp $
** $Id: btree_rb.c,v 1.10 2003/05/17 17:35:11 drh Exp $
**
** This file implements an in-core database using Red-Black balanced
** binary trees.
@@ -31,10 +31,12 @@
typedef struct BtRbTree BtRbTree;
typedef struct BtRbNode BtRbNode;
typedef struct BtRollbackOp BtRollbackOp;
typedef struct Rbtree Rbtree;
typedef struct RbtCursor RbtCursor;
/* Forward declarations */
static BtOps sqliteBtreeOps;
static BtCursorOps sqliteBtreeCursorOps;
static BtOps sqliteRbtreeOps;
static BtCursorOps sqliteRbtreeCursorOps;
/*
* During each transaction (or checkpoint), a linked-list of
@@ -68,14 +70,14 @@ struct BtRollbackOp {
#define ROLLBACK_CREATE 3 /* Create a table */
#define ROLLBACK_DROP 4 /* Drop a table */
struct Btree {
struct Rbtree {
BtOps *pOps; /* Function table */
int aMetaData[SQLITE_N_BTREE_META];
int next_idx; /* next available table index */
Hash tblHash; /* All created tables, by index */
u8 isAnonymous; /* True if this Btree is to be deleted when closed */
u8 eTransState; /* State of this Btree wrt transactions */
u8 isAnonymous; /* True if this Rbtree is to be deleted when closed */
u8 eTransState; /* State of this Rbtree wrt transactions */
BtRollbackOp *pTransRollback;
BtRollbackOp *pCheckRollback;
@@ -83,7 +85,7 @@ struct Btree {
};
/*
** Legal values for Btree.eTransState.
** Legal values for Rbtree.eTransState.
*/
#define TRANS_NONE 0 /* No transaction is in progress */
#define TRANS_INTRANSACTION 1 /* A transaction is in progress */
@@ -91,21 +93,21 @@ struct Btree {
#define TRANS_ROLLBACK 3 /* We are currently rolling back a checkpoint or
* transaction. */
struct BtCursor {
struct RbtCursor {
BtCursorOps *pOps; /* Function table */
Btree *pBtree;
Rbtree *pRbtree;
BtRbTree *pTree;
int iTree; /* Index of pTree in pBtree */
int iTree; /* Index of pTree in pRbtree */
BtRbNode *pNode;
u8 eSkip; /* Determines if next step operation is a no-op */
};
/*
** Legal values for BtCursor.eSkip.
** Legal values for RbtCursor.eSkip.
*/
#define SKIP_NONE 0 /* Always step the cursor */
#define SKIP_NEXT 1 /* The next sqliteBtreeNext() is a no-op */
#define SKIP_PREV 2 /* The next sqliteBtreePrevious() is a no-op */
#define SKIP_NEXT 1 /* The next sqliteRbtreeNext() is a no-op */
#define SKIP_PREV 2 /* The next sqliteRbtreePrevious() is a no-op */
#define SKIP_INVALID 3 /* Calls to Next() and Previous() are invalid */
struct BtRbTree {
@@ -126,11 +128,16 @@ struct BtRbNode {
};
/* Forward declarations */
static int memBtreeMoveto(BtCursor* pCur, const void *pKey, int nKey,int *pRes);
static int memBtreeClearTable(Btree* tree, int n);
static int memBtreeNext(BtCursor* pCur, int *pRes);
static int memBtreeLast(BtCursor* pCur, int *pRes);
static int memBtreePrevious(BtCursor* pCur, int *pRes);
static int memRbtreeMoveto(
RbtCursor* pCur,
const void *pKey,
int nKey,
int *pRes
);
static int memRbtreeClearTable(Rbtree* tree, int n);
static int memRbtreeNext(RbtCursor* pCur, int *pRes);
static int memRbtreeLast(RbtCursor* pCur, int *pRes);
static int memRbtreePrevious(RbtCursor* pCur, int *pRes);
/*
* The key-compare function for the red-black trees. Returns as follows:
@@ -359,7 +366,7 @@ static void check_redblack_tree(BtRbTree * tree, char ** msg)
}
/*
* Node pX has just been inserted into pTree (by code in sqliteBtreeInsert()).
* Node pX has just been inserted into pTree (by code in sqliteRbtreeInsert()).
* It is possible that pX is a red node with a red parent, which is a violation
* of the red-black tree properties. This function performs rotations and
* color changes to rebalance the tree
@@ -540,57 +547,61 @@ void do_delete_balancing(BtRbTree *pTree, BtRbNode *pX, BtRbNode *pParent)
}
/*
* Create table n in tree pBtree. Table n must not exist.
* Create table n in tree pRbtree. Table n must not exist.
*/
static void btreeCreateTable(Btree* pBtree, int n)
static void btreeCreateTable(Rbtree* pRbtree, int n)
{
BtRbTree *pNewTbl = sqliteMalloc(sizeof(BtRbTree));
sqliteHashInsert(&pBtree->tblHash, 0, n, pNewTbl);
sqliteHashInsert(&pRbtree->tblHash, 0, n, pNewTbl);
}
/*
* Log a single "rollback-op" for the given Btree. See comments for struct
* Log a single "rollback-op" for the given Rbtree. See comments for struct
* BtRollbackOp.
*/
static void btreeLogRollbackOp(Btree* pBtree, BtRollbackOp *pRollbackOp)
static void btreeLogRollbackOp(Rbtree* pRbtree, BtRollbackOp *pRollbackOp)
{
assert( pBtree->eTransState == TRANS_INCHECKPOINT ||
pBtree->eTransState == TRANS_INTRANSACTION );
if( pBtree->eTransState == TRANS_INTRANSACTION ){
pRollbackOp->pNext = pBtree->pTransRollback;
pBtree->pTransRollback = pRollbackOp;
assert( pRbtree->eTransState == TRANS_INCHECKPOINT ||
pRbtree->eTransState == TRANS_INTRANSACTION );
if( pRbtree->eTransState == TRANS_INTRANSACTION ){
pRollbackOp->pNext = pRbtree->pTransRollback;
pRbtree->pTransRollback = pRollbackOp;
}
if( pBtree->eTransState == TRANS_INCHECKPOINT ){
if( !pBtree->pCheckRollback ){
pBtree->pCheckRollbackTail = pRollbackOp;
if( pRbtree->eTransState == TRANS_INCHECKPOINT ){
if( !pRbtree->pCheckRollback ){
pRbtree->pCheckRollbackTail = pRollbackOp;
}
pRollbackOp->pNext = pBtree->pCheckRollback;
pBtree->pCheckRollback = pRollbackOp;
pRollbackOp->pNext = pRbtree->pCheckRollback;
pRbtree->pCheckRollback = pRollbackOp;
}
}
int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree)
{
*ppBtree = (Btree *)sqliteMalloc(sizeof(Btree));
sqliteHashInit(&(*ppBtree)->tblHash, SQLITE_HASH_INT, 0);
int sqliteRbtreeOpen(
const char *zFilename,
int mode,
int nPg,
Rbtree **ppRbtree
){
*ppRbtree = (Rbtree *)sqliteMalloc(sizeof(Rbtree));
sqliteHashInit(&(*ppRbtree)->tblHash, SQLITE_HASH_INT, 0);
/* Create a binary tree for the SQLITE_MASTER table at location 2 */
btreeCreateTable(*ppBtree, 2);
(*ppBtree)->next_idx = 3;
(*ppBtree)->pOps = &sqliteBtreeOps;
btreeCreateTable(*ppRbtree, 2);
(*ppRbtree)->next_idx = 3;
(*ppRbtree)->pOps = &sqliteRbtreeOps;
/* Set file type to 4; this is so that "attach ':memory:' as ...." does not
** think that the database in uninitialised and refuse to attach
*/
(*ppBtree)->aMetaData[2] = 4;
(*ppRbtree)->aMetaData[2] = 4;
return SQLITE_OK;
}
/*
* Create a new table in the supplied Btree. Set *n to the new table number.
* Create a new table in the supplied Rbtree. Set *n to the new table number.
* Return SQLITE_OK if the operation is a success.
*/
static int memBtreeCreateTable(Btree* tree, int* n)
static int memRbtreeCreateTable(Rbtree* tree, int* n)
{
assert( tree->eTransState != TRANS_NONE );
@@ -610,14 +621,14 @@ static int memBtreeCreateTable(Btree* tree, int* n)
}
/*
* Delete table n from the supplied Btree.
* Delete table n from the supplied Rbtree.
*/
static int memBtreeDropTable(Btree* tree, int n)
static int memRbtreeDropTable(Rbtree* tree, int n)
{
BtRbTree *pTree;
assert( tree->eTransState != TRANS_NONE );
memBtreeClearTable(tree, n);
memRbtreeClearTable(tree, n);
pTree = sqliteHashInsert(&tree->tblHash, 0, n, 0);
assert(pTree);
sqliteFree(pTree);
@@ -632,7 +643,7 @@ static int memBtreeDropTable(Btree* tree, int n)
return SQLITE_OK;
}
static int memBtreeKeyCompare(BtCursor* pCur, const void *pKey, int nKey,
static int memRbtreeKeyCompare(RbtCursor* pCur, const void *pKey, int nKey,
int nIgnore, int *pRes)
{
assert(pCur);
@@ -651,40 +662,49 @@ static int memBtreeKeyCompare(BtCursor* pCur, const void *pKey, int nKey,
}
/*
* Get a new cursor for table iTable of the supplied Btree. The wrFlag
* Get a new cursor for table iTable of the supplied Rbtree. The wrFlag
* parameter is ignored, all cursors are capable of write-operations.
*
* Note that BtCursor.eSkip and BtCursor.pNode both initialize to 0.
* Note that RbtCursor.eSkip and RbtCursor.pNode both initialize to 0.
*/
static int memBtreeCursor(Btree* tree, int iTable, int wrFlag, BtCursor **ppCur)
{
static int memRbtreeCursor(
Rbtree* tree,
int iTable,
int wrFlag,
RbtCursor **ppCur
){
assert(tree);
*ppCur = sqliteMalloc(sizeof(BtCursor));
*ppCur = sqliteMalloc(sizeof(RbtCursor));
(*ppCur)->pTree = sqliteHashFind(&tree->tblHash, 0, iTable);
(*ppCur)->pBtree = tree;
(*ppCur)->pRbtree = tree;
(*ppCur)->iTree = iTable;
(*ppCur)->pOps = &sqliteBtreeCursorOps;
(*ppCur)->pOps = &sqliteRbtreeCursorOps;
assert( (*ppCur)->pTree );
return SQLITE_OK;
}
/*
* Insert a new record into the Btree. The key is given by (pKey,nKey)
* Insert a new record into the Rbtree. The key is given by (pKey,nKey)
* and the data is given by (pData,nData). The cursor is used only to
* define what database the record should be inserted into. The cursor
* is left pointing at the new record.
*
* If the key exists already in the tree, just replace the data.
*/
static int memBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
const void *pDataInput, int nData)
{
static int memRbtreeInsert(
RbtCursor* pCur,
const void *pKey,
int nKey,
const void *pDataInput,
int nData
){
void * pData;
int match;
/* It is illegal to call sqliteBtreeInsert() if we are not in a transaction */
assert( pCur->pBtree->eTransState != TRANS_NONE );
/* It is illegal to call sqliteRbtreeInsert() if we are
** not in a transaction */
assert( pCur->pRbtree->eTransState != TRANS_NONE );
/* Take a copy of the input data now, in case we need it for the
* replace case */
@@ -702,7 +722,7 @@ static int memBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
*
* The new node is initially red.
*/
memBtreeMoveto( pCur, pKey, nKey, &match);
memRbtreeMoveto( pCur, pKey, nKey, &match);
if( match ){
BtRbNode *pNode = sqliteMalloc(sizeof(BtRbNode));
pNode->nKey = nKey;
@@ -736,14 +756,14 @@ static int memBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
do_insert_balancing(pCur->pTree, pNode);
/* Set up a rollback-op in case we have to roll this operation back */
if( pCur->pBtree->eTransState != TRANS_ROLLBACK ){
if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
pOp->eOp = ROLLBACK_DELETE;
pOp->iTab = pCur->iTree;
pOp->nKey = pNode->nKey;
pOp->pKey = sqliteMalloc( pOp->nKey );
memcpy( pOp->pKey, pNode->pKey, pOp->nKey );
btreeLogRollbackOp(pCur->pBtree, pOp);
btreeLogRollbackOp(pCur->pRbtree, pOp);
}
}else{
@@ -751,7 +771,7 @@ static int memBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
* Just clobber the current nodes data. */
/* Set up a rollback-op in case we have to roll this operation back */
if( pCur->pBtree->eTransState != TRANS_ROLLBACK ){
if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
pOp->iTab = pCur->iTree;
pOp->nKey = pCur->pNode->nKey;
@@ -760,7 +780,7 @@ static int memBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
pOp->nData = pCur->pNode->nData;
pOp->pData = pCur->pNode->pData;
pOp->eOp = ROLLBACK_INSERT;
btreeLogRollbackOp(pCur->pBtree, pOp);
btreeLogRollbackOp(pCur->pRbtree, pOp);
}else{
sqliteFree( pCur->pNode->pData );
}
@@ -786,8 +806,12 @@ static int memBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
** *pRes>0 The cursor is left pointing at an entry that
** is larger than pKey.
*/
static int memBtreeMoveto(BtCursor* pCur, const void *pKey, int nKey, int *pRes)
{
static int memRbtreeMoveto(
RbtCursor* pCur,
const void *pKey,
int nKey,
int *pRes
){
BtRbNode *pTmp = 0;
pCur->pNode = pCur->pTree->pHead;
@@ -823,21 +847,22 @@ static int memBtreeMoveto(BtCursor* pCur, const void *pKey, int nKey, int *pRes)
** The cursor is left pointing at either the next or the previous
** entry. If the cursor is left pointing to the next entry, then
** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to
** sqliteBtreeNext() to be a no-op. That way, you can always call
** sqliteBtreeNext() after a delete and the cursor will be left
** sqliteRbtreeNext() to be a no-op. That way, you can always call
** sqliteRbtreeNext() after a delete and the cursor will be left
** pointing to the first entry after the deleted entry. Similarly,
** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to
** the entry prior to the deleted entry so that a subsequent call to
** sqliteBtreePrevious() will always leave the cursor pointing at the
** sqliteRbtreePrevious() will always leave the cursor pointing at the
** entry immediately before the one that was deleted.
*/
static int memBtreeDelete(BtCursor* pCur)
static int memRbtreeDelete(RbtCursor* pCur)
{
BtRbNode *pZ; /* The one being deleted */
BtRbNode *pChild; /* The child of the spliced out node */
/* It is illegal to call sqliteBtreeDelete() if we are not in a transaction */
assert( pCur->pBtree->eTransState != TRANS_NONE );
/* It is illegal to call sqliteRbtreeDelete() if we are
** not in a transaction */
assert( pCur->pRbtree->eTransState != TRANS_NONE );
pZ = pCur->pNode;
if( !pZ ){
@@ -846,7 +871,7 @@ static int memBtreeDelete(BtCursor* pCur)
/* If we are not currently doing a rollback, set up a rollback op for this
* deletion */
if( pCur->pBtree->eTransState != TRANS_ROLLBACK ){
if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
pOp->iTab = pCur->iTree;
pOp->nKey = pZ->nKey;
@@ -854,7 +879,7 @@ static int memBtreeDelete(BtCursor* pCur)
pOp->nData = pZ->nData;
pOp->pData = pZ->pData;
pOp->eOp = ROLLBACK_INSERT;
btreeLogRollbackOp(pCur->pBtree, pOp);
btreeLogRollbackOp(pCur->pRbtree, pOp);
}
/* First do a standard binary-tree delete (node pZ is to be deleted). How
@@ -867,9 +892,9 @@ static int memBtreeDelete(BtCursor* pCur)
BtRbNode *pTmp;
int dummy;
pCur->eSkip = SKIP_NONE;
memBtreeNext(pCur, &dummy);
memRbtreeNext(pCur, &dummy);
assert( dummy == 0 );
if( pCur->pBtree->eTransState == TRANS_ROLLBACK ){
if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){
sqliteFree(pZ->pKey);
sqliteFree(pZ->pData);
}
@@ -884,14 +909,14 @@ static int memBtreeDelete(BtCursor* pCur)
}else{
int res;
pCur->eSkip = SKIP_NONE;
memBtreeNext(pCur, &res);
memRbtreeNext(pCur, &res);
pCur->eSkip = SKIP_NEXT;
if( res ){
memBtreeLast(pCur, &res);
memBtreePrevious(pCur, &res);
memRbtreeLast(pCur, &res);
memRbtreePrevious(pCur, &res);
pCur->eSkip = SKIP_PREV;
}
if( pCur->pBtree->eTransState == TRANS_ROLLBACK ){
if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){
sqliteFree(pZ->pKey);
sqliteFree(pZ->pData);
}
@@ -928,9 +953,9 @@ static int memBtreeDelete(BtCursor* pCur)
}
/*
* Empty table n of the Btree.
* Empty table n of the Rbtree.
*/
static int memBtreeClearTable(Btree* tree, int n)
static int memRbtreeClearTable(Rbtree* tree, int n)
{
BtRbTree *pTree;
BtRbNode *pNode;
@@ -974,7 +999,7 @@ static int memBtreeClearTable(Btree* tree, int n)
return SQLITE_OK;
}
static int memBtreeFirst(BtCursor* pCur, int *pRes)
static int memRbtreeFirst(RbtCursor* pCur, int *pRes)
{
if( pCur->pTree->pHead ){
pCur->pNode = pCur->pTree->pHead;
@@ -991,7 +1016,7 @@ static int memBtreeFirst(BtCursor* pCur, int *pRes)
return SQLITE_OK;
}
static int memBtreeLast(BtCursor* pCur, int *pRes)
static int memRbtreeLast(RbtCursor* pCur, int *pRes)
{
if( pCur->pTree->pHead ){
pCur->pNode = pCur->pTree->pHead;
@@ -1014,7 +1039,7 @@ static int memBtreeLast(BtCursor* pCur, int *pRes)
** was already pointing to the last entry in the database before
** this routine was called, then set *pRes=1.
*/
static int memBtreeNext(BtCursor* pCur, int *pRes)
static int memRbtreeNext(RbtCursor* pCur, int *pRes)
{
if( pCur->pNode && pCur->eSkip != SKIP_NEXT ){
if( pCur->pNode->pRight ){
@@ -1041,7 +1066,7 @@ static int memBtreeNext(BtCursor* pCur, int *pRes)
return SQLITE_OK;
}
static int memBtreePrevious(BtCursor* pCur, int *pRes)
static int memRbtreePrevious(RbtCursor* pCur, int *pRes)
{
if( pCur->pNode && pCur->eSkip != SKIP_PREV ){
if( pCur->pNode->pLeft ){
@@ -1068,7 +1093,7 @@ static int memBtreePrevious(BtCursor* pCur, int *pRes)
return SQLITE_OK;
}
static int memBtreeKeySize(BtCursor* pCur, int *pSize)
static int memRbtreeKeySize(RbtCursor* pCur, int *pSize)
{
if( pCur->pNode ){
*pSize = pCur->pNode->nKey;
@@ -1078,7 +1103,7 @@ static int memBtreeKeySize(BtCursor* pCur, int *pSize)
return SQLITE_OK;
}
static int memBtreeKey(BtCursor* pCur, int offset, int amt, char *zBuf)
static int memRbtreeKey(RbtCursor* pCur, int offset, int amt, char *zBuf)
{
if( !pCur->pNode ) return 0;
if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
@@ -1091,7 +1116,7 @@ static int memBtreeKey(BtCursor* pCur, int offset, int amt, char *zBuf)
assert(0);
}
static int memBtreeDataSize(BtCursor* pCur, int *pSize)
static int memRbtreeDataSize(RbtCursor* pCur, int *pSize)
{
if( pCur->pNode ){
*pSize = pCur->pNode->nData;
@@ -1101,7 +1126,7 @@ static int memBtreeDataSize(BtCursor* pCur, int *pSize)
return SQLITE_OK;
}
static int memBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf)
static int memRbtreeData(RbtCursor *pCur, int offset, int amt, char *zBuf)
{
if( !pCur->pNode ) return 0;
if( (amt + offset) <= pCur->pNode->nData ){
@@ -1114,30 +1139,30 @@ static int memBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf)
assert(0);
}
static int memBtreeCloseCursor(BtCursor* pCur)
static int memRbtreeCloseCursor(RbtCursor* pCur)
{
sqliteFree(pCur);
return SQLITE_OK;
}
static int memBtreeGetMeta(Btree* tree, int* aMeta)
static int memRbtreeGetMeta(Rbtree* tree, int* aMeta)
{
memcpy( aMeta, tree->aMetaData, sizeof(int) * SQLITE_N_BTREE_META );
return SQLITE_OK;
}
static int memBtreeUpdateMeta(Btree* tree, int* aMeta)
static int memRbtreeUpdateMeta(Rbtree* tree, int* aMeta)
{
memcpy( tree->aMetaData, aMeta, sizeof(int) * SQLITE_N_BTREE_META );
return SQLITE_OK;
}
/*
* Check that each table in the Btree meets the requirements for a red-black
* Check that each table in the Rbtree meets the requirements for a red-black
* binary tree. If an error is found, return an explanation of the problem in
* memory obtained from sqliteMalloc(). Parameters aRoot and nRoot are ignored.
*/
static char *memBtreeIntegrityCheck(Btree* tree, int* aRoot, int nRoot)
static char *memRbtreeIntegrityCheck(Rbtree* tree, int* aRoot, int nRoot)
{
char * msg = 0;
HashElem *p;
@@ -1151,30 +1176,30 @@ static char *memBtreeIntegrityCheck(Btree* tree, int* aRoot, int nRoot)
}
/*
* Close the supplied Btree. Delete everything associated with it.
* Close the supplied Rbtree. Delete everything associated with it.
*/
static int memBtreeClose(Btree* tree)
static int memRbtreeClose(Rbtree* tree)
{
HashElem *p;
while( (p=sqliteHashFirst(&tree->tblHash))!=0 ){
tree->eTransState = TRANS_ROLLBACK;
memBtreeDropTable(tree, sqliteHashKeysize(p));
memRbtreeDropTable(tree, sqliteHashKeysize(p));
}
sqliteHashClear(&tree->tblHash);
sqliteFree(tree);
return SQLITE_OK;
}
static int memBtreeSetCacheSize(Btree* tree, int sz)
static int memRbtreeSetCacheSize(Rbtree* tree, int sz)
{
return SQLITE_OK;
}
static int memBtreeSetSafetyLevel(Btree *pBt, int level){
static int memRbtreeSetSafetyLevel(Rbtree *pBt, int level){
return SQLITE_OK;
}
static int memBtreeBeginTrans(Btree* tree)
static int memRbtreeBeginTrans(Rbtree* tree)
{
if( tree->eTransState != TRANS_NONE )
return SQLITE_ERROR;
@@ -1197,7 +1222,7 @@ static void deleteRollbackList(BtRollbackOp *pOp){
}
}
static int memBtreeCommit(Btree* tree){
static int memRbtreeCommit(Rbtree* tree){
/* Just delete pTransRollback and pCheckRollback */
deleteRollbackList(tree->pCheckRollback);
deleteRollbackList(tree->pTransRollback);
@@ -1209,39 +1234,39 @@ static int memBtreeCommit(Btree* tree){
}
/*
* Execute and delete the supplied rollback-list on pBtree.
* Execute and delete the supplied rollback-list on pRbtree.
*/
static void execute_rollback_list(Btree *pBtree, BtRollbackOp *pList)
static void execute_rollback_list(Rbtree *pRbtree, BtRollbackOp *pList)
{
BtRollbackOp *pTmp;
BtCursor cur;
RbtCursor cur;
int res;
cur.pBtree = pBtree;
cur.pRbtree = pRbtree;
while( pList ){
switch( pList->eOp ){
case ROLLBACK_INSERT:
cur.pTree = sqliteHashFind( &pBtree->tblHash, 0, pList->iTab );
cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab );
assert(cur.pTree);
cur.iTree = pList->iTab;
cur.eSkip = SKIP_NONE;
memBtreeInsert( &cur, pList->pKey,
memRbtreeInsert( &cur, pList->pKey,
pList->nKey, pList->pData, pList->nData );
break;
case ROLLBACK_DELETE:
cur.pTree = sqliteHashFind( &pBtree->tblHash, 0, pList->iTab );
cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab );
assert(cur.pTree);
cur.iTree = pList->iTab;
cur.eSkip = SKIP_NONE;
memBtreeMoveto(&cur, pList->pKey, pList->nKey, &res);
memRbtreeMoveto(&cur, pList->pKey, pList->nKey, &res);
assert(res == 0);
memBtreeDelete( &cur );
memRbtreeDelete( &cur );
break;
case ROLLBACK_CREATE:
btreeCreateTable(pBtree, pList->iTab);
btreeCreateTable(pRbtree, pList->iTab);
break;
case ROLLBACK_DROP:
memBtreeDropTable(pBtree, pList->iTab);
memRbtreeDropTable(pRbtree, pList->iTab);
break;
default:
assert(0);
@@ -1254,7 +1279,7 @@ static void execute_rollback_list(Btree *pBtree, BtRollbackOp *pList)
}
}
static int memBtreeRollback(Btree* tree)
static int memRbtreeRollback(Rbtree* tree)
{
tree->eTransState = TRANS_ROLLBACK;
execute_rollback_list(tree, tree->pCheckRollback);
@@ -1266,7 +1291,7 @@ static int memBtreeRollback(Btree* tree)
return SQLITE_OK;
}
static int memBtreeBeginCkpt(Btree* tree)
static int memRbtreeBeginCkpt(Rbtree* tree)
{
if( tree->eTransState != TRANS_INTRANSACTION )
return SQLITE_ERROR;
@@ -1277,7 +1302,7 @@ static int memBtreeBeginCkpt(Btree* tree)
return SQLITE_OK;
}
static int memBtreeCommitCkpt(Btree* tree)
static int memRbtreeCommitCkpt(Rbtree* tree)
{
if( tree->eTransState == TRANS_INCHECKPOINT ){
if( tree->pCheckRollback ){
@@ -1291,7 +1316,7 @@ static int memBtreeCommitCkpt(Btree* tree)
return SQLITE_OK;
}
static int memBtreeRollbackCkpt(Btree* tree)
static int memRbtreeRollbackCkpt(Rbtree* tree)
{
if( tree->eTransState != TRANS_INCHECKPOINT ) return SQLITE_OK;
tree->eTransState = TRANS_ROLLBACK;
@@ -1304,21 +1329,21 @@ static int memBtreeRollbackCkpt(Btree* tree)
}
#ifdef SQLITE_TEST
static int memBtreePageDump(Btree* tree, int pgno, int rec)
static int memRbtreePageDump(Rbtree* tree, int pgno, int rec)
{
assert(!"Cannot call sqliteBtreePageDump");
assert(!"Cannot call sqliteRbtreePageDump");
return SQLITE_OK;
}
static int memBtreeCursorDump(BtCursor* pCur, int* aRes)
static int memRbtreeCursorDump(RbtCursor* pCur, int* aRes)
{
assert(!"Cannot call sqliteBtreeCursorDump");
assert(!"Cannot call sqliteRbtreeCursorDump");
return SQLITE_OK;
}
static struct Pager *memBtreePager(Btree* tree)
static struct Pager *memRbtreePager(Rbtree* tree)
{
assert(!"Cannot call sqliteBtreePager");
assert(!"Cannot call sqliteRbtreePager");
return SQLITE_OK;
}
#endif
@@ -1326,60 +1351,60 @@ static struct Pager *memBtreePager(Btree* tree)
/*
** Return the full pathname of the underlying database file.
*/
static const char *memBtreeGetFilename(Btree *pBt){
static const char *memRbtreeGetFilename(Rbtree *pBt){
return 0; /* A NULL return indicates there is no underlying file */
}
/*
** The copy file function is not implemented for the in-memory database
*/
static int memBtreeCopyFile(Btree *pBt, Btree *pBt2){
static int memRbtreeCopyFile(Rbtree *pBt, Rbtree *pBt2){
return SQLITE_INTERNAL; /* Not implemented */
}
static BtOps sqliteBtreeOps = {
memBtreeClose,
memBtreeSetCacheSize,
memBtreeSetSafetyLevel,
memBtreeBeginTrans,
memBtreeCommit,
memBtreeRollback,
memBtreeBeginCkpt,
memBtreeCommitCkpt,
memBtreeRollbackCkpt,
memBtreeCreateTable,
memBtreeCreateTable,
memBtreeDropTable,
memBtreeClearTable,
memBtreeCursor,
memBtreeGetMeta,
memBtreeUpdateMeta,
memBtreeIntegrityCheck,
memBtreeGetFilename,
memBtreeCopyFile,
static BtOps sqliteRbtreeOps = {
(int(*)(Btree*)) memRbtreeClose,
(int(*)(Btree*,int)) memRbtreeSetCacheSize,
(int(*)(Btree*,int)) memRbtreeSetSafetyLevel,
(int(*)(Btree*)) memRbtreeBeginTrans,
(int(*)(Btree*)) memRbtreeCommit,
(int(*)(Btree*)) memRbtreeRollback,
(int(*)(Btree*)) memRbtreeBeginCkpt,
(int(*)(Btree*)) memRbtreeCommitCkpt,
(int(*)(Btree*)) memRbtreeRollbackCkpt,
(int(*)(Btree*,int*)) memRbtreeCreateTable,
(int(*)(Btree*,int*)) memRbtreeCreateTable,
(int(*)(Btree*,int)) memRbtreeDropTable,
(int(*)(Btree*,int)) memRbtreeClearTable,
(int(*)(Btree*,int,int,BtCursor**)) memRbtreeCursor,
(int(*)(Btree*,int*)) memRbtreeGetMeta,
(int(*)(Btree*,int*)) memRbtreeUpdateMeta,
(char*(*)(Btree*,int*,int)) memRbtreeIntegrityCheck,
(const char*(*)(Btree*)) memRbtreeGetFilename,
(int(*)(Btree*,Btree*)) memRbtreeCopyFile,
#ifdef SQLITE_TEST
memBtreePageDump,
memBtreePager
(int(*)(Btree*,int,int)) memRbtreePageDump,
(struct Pager*(*)(Btree*)) memRbtreePager
#endif
};
static BtCursorOps sqliteBtreeCursorOps = {
memBtreeMoveto,
memBtreeDelete,
memBtreeInsert,
memBtreeFirst,
memBtreeLast,
memBtreeNext,
memBtreePrevious,
memBtreeKeySize,
memBtreeKey,
memBtreeKeyCompare,
memBtreeDataSize,
memBtreeData,
memBtreeCloseCursor,
static BtCursorOps sqliteRbtreeCursorOps = {
(int(*)(BtCursor*,const void*,int,int*)) memRbtreeMoveto,
(int(*)(BtCursor*)) memRbtreeDelete,
(int(*)(BtCursor*,const void*,int,const void*,int)) memRbtreeInsert,
(int(*)(BtCursor*,int*)) memRbtreeFirst,
(int(*)(BtCursor*,int*)) memRbtreeLast,
(int(*)(BtCursor*,int*)) memRbtreeNext,
(int(*)(BtCursor*,int*)) memRbtreePrevious,
(int(*)(BtCursor*,int*)) memRbtreeKeySize,
(int(*)(BtCursor*,int,int,char*)) memRbtreeKey,
(int(*)(BtCursor*,const void*,int,int,int*)) memRbtreeKeyCompare,
(int(*)(BtCursor*,int*)) memRbtreeDataSize,
(int(*)(BtCursor*,int,int,char*)) memRbtreeData,
(int(*)(BtCursor*)) memRbtreeCloseCursor,
#ifdef SQLITE_TEST
memBtreeCursorDump,
(int(*)(BtCursor*,int*)) memRbtreeCursorDump,
#endif
};

View File

@@ -23,7 +23,7 @@
** ROLLBACK
** PRAGMA
**
** $Id: build.c,v 1.152 2003/05/02 14:32:13 drh Exp $
** $Id: build.c,v 1.153 2003/05/17 17:35:11 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -36,6 +36,7 @@
*/
void sqliteBeginParse(Parse *pParse, int explainFlag){
sqlite *db = pParse->db;
int i;
pParse->explain = explainFlag;
if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){
int rc = sqliteInit(db, &pParse->zErrMsg);
@@ -44,6 +45,12 @@ void sqliteBeginParse(Parse *pParse, int explainFlag){
pParse->nErr++;
}
}
for(i=0; i<db->nDb; i++){
DbClearProperty(db, i, DB_Locked);
if( !db->aDb[i].inTrans ){
DbClearProperty(db, i, DB_Cookie);
}
}
}
/*
@@ -96,7 +103,6 @@ void sqliteExec(Parse *pParse){
pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
}
pParse->colNamesSet = 0;
pParse->schemaVerified = 0;
}else if( pParse->useCallback==0 ){
pParse->rc = SQLITE_ERROR;
}
@@ -263,7 +269,7 @@ void sqliteResetInternalSchema(sqlite *db, int iDb){
sqliteDeleteTable(db, pTab);
}
sqliteHashClear(&temp1);
db->aDb[i].flags &= ~SQLITE_Initialized;
DbClearProperty(db, i, DB_SchemaLoaded);
if( iDb>0 ) return;
}
assert( iDb==0 );
@@ -282,8 +288,9 @@ void sqliteResetInternalSchema(sqlite *db, int iDb){
continue;
}
if( j<i ){
db->aDb[j++] = db->aDb[i];
db->aDb[j] = db->aDb[i];
}
j++;
}
memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
db->nDb = j;
@@ -1137,7 +1144,7 @@ int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){
pSelTab->nCol = 0;
pSelTab->aCol = 0;
sqliteDeleteTable(0, pSelTab);
pParse->db->aDb[pTable->iDb].flags |= SQLITE_UnresetViews;
DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews);
}else{
pTable->nCol = 0;
nErr++;
@@ -1171,18 +1178,18 @@ static void sqliteViewResetColumnNames(Table *pTable){
}
/*
** Clear the column names from every VIEW.
** Clear the column names from every VIEW in database idx.
*/
static void sqliteViewResetAll(sqlite *db, int idx){
HashElem *i;
if( (db->aDb[idx].flags & SQLITE_UnresetViews)==0 ) return;
if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
if( pTab->pSelect ){
sqliteViewResetColumnNames(pTab);
}
}
db->aDb[idx].flags &= ~SQLITE_UnresetViews;
DbClearProperty(db, idx, DB_UnresetViews);
}
/*
@@ -1286,12 +1293,12 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
Index *pIdx;
Trigger *pTrigger;
sqliteBeginWriteOperation(pParse, 0, pTable->iDb);
sqliteOpenMasterTable(v, pTable->iDb);
/* Drop all triggers associated with the table being dropped */
pTrigger = pTable->pTrigger;
while( pTrigger ){
SrcList *pNm;
assert( pTrigger->iDb==pTable->iDb );
assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 );
pNm = sqliteSrcListAppend(0, 0, 0);
pNm->a[0].zName = sqliteStrDup(pTrigger->name);
pNm->a[0].zDatabase = sqliteStrDup(db->aDb[pTable->iDb].zName);
@@ -1302,16 +1309,27 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
pTrigger = pTable->pTrigger;
}
}
/* Drop all SQLITE_MASTER entries that refer to the table */
sqliteOpenMasterTable(v, pTable->iDb);
base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);
if( !pTable->iDb ){
/* Drop all SQLITE_TEMP_MASTER entries that refer to the table */
if( pTable->iDb!=1 ){
sqliteOpenMasterTable(v, 1);
base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);
}
if( pTable->iDb==0 ){
sqliteChangeCookie(db, v);
}
sqliteVdbeAddOp(v, OP_Close, 0, 0);
if( !isView ){
sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->iDb);
for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->iDb);
sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pIdx->iDb);
}
}
sqliteEndWriteOperation(pParse);
@@ -2117,15 +2135,15 @@ void sqliteRollbackTransaction(Parse *pParse){
** Generate VDBE code that will verify the schema cookie for all
** named database files.
*/
void sqliteCodeVerifySchema(Parse *pParse){
int i;
void sqliteCodeVerifySchema(Parse *pParse, int iDb){
sqlite *db = pParse->db;
Vdbe *v = sqliteGetVdbe(pParse);
for(i=0; i<db->nDb; i++){
if( i==1 || db->aDb[i].pBt==0 ) continue;
sqliteVdbeAddOp(v, OP_VerifyCookie, i, db->aDb[i].schema_cookie);
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
if( iDb!=1 && !DbHasProperty(db, iDb, DB_Cookie) ){
sqliteVdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie);
DbSetProperty(db, iDb, DB_Cookie);
}
pParse->schemaVerified = 1;
}
/*
@@ -2141,47 +2159,49 @@ void sqliteCodeVerifySchema(Parse *pParse){
** 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.
** Only database iDb and the temp database are made writable by this call.
** If iDb==0, then the main and temp databases are made writable. If
** iDb==1 then only the temp database is made writable. If iDb>1 then the
** specified auxiliary database and the temp database are made writable.
*/
void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){
void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int iDb){
Vdbe *v;
sqlite *db = pParse->db;
if( DbHasProperty(db, iDb, DB_Locked) ) return;
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, 1, 0);
if( !tempOnly ){
int i;
sqlite *db = pParse->db;
sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
for(i=2; i<db->nDb; i++){
if( db->aDb[i].pBt==0 ) continue;
sqliteVdbeAddOp(v, OP_Transaction, i, 0);
}
sqliteCodeVerifySchema(pParse);
if( !db->aDb[iDb].inTrans ){
sqliteVdbeAddOp(v, OP_Transaction, iDb, 0);
DbSetProperty(db, iDb, DB_Locked);
sqliteCodeVerifySchema(pParse, iDb);
if( iDb!=1 ){
sqliteBeginWriteOperation(pParse, setCheckpoint, 1);
}
}else if( setCheckpoint ){
sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
sqliteVdbeAddOp(v, OP_Checkpoint, 1, 0);
sqliteVdbeAddOp(v, OP_Checkpoint, iDb, 0);
DbSetProperty(db, iDb, DB_Locked);
}
}
/*
** Generate code that concludes an operation that may have changed
** the database. This is a companion function to BeginWriteOperation().
** If a transaction was started, then commit it. If a checkpoint was
** started then commit that.
** the database. If a statement transaction was started, then emit
** an OP_Commit that will cause the changes to be committed to disk.
**
** Note that checkpoints are automatically committed at the end of
** a statement. Note also that there can be multiple calls to
** sqliteBeginWriteOperation() but there should only be a single
** call to sqliteEndWriteOperation() at the conclusion of the statement.
*/
void sqliteEndWriteOperation(Parse *pParse){
Vdbe *v;
sqlite *db = pParse->db;
if( pParse->trigStack ) return; /* if this is in a trigger */
v = sqliteGetVdbe(pParse);
if( v==0 ) return;
if( pParse->db->flags & SQLITE_InTrans ){
/* Do Nothing */
if( db->flags & SQLITE_InTrans ){
/* A BEGIN has executed. Do not commit until we see an explicit
** COMMIT statement. */
}else{
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}

View File

@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code used to implement the COPY command.
**
** $Id: copy.c,v 1.4 2003/04/24 01:45:04 drh Exp $
** $Id: copy.c,v 1.5 2003/05/17 17:35:11 drh Exp $
*/
#include "sqliteInt.h"
@@ -57,7 +57,7 @@ void sqliteCopy(
}
v = sqliteGetVdbe(pParse);
if( v ){
sqliteBeginWriteOperation(pParse, 1, pTab->iDb==1);
sqliteBeginWriteOperation(pParse, 1, pTab->iDb);
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
sqliteVdbeDequoteP3(v, addr);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.56 2003/05/02 14:32:13 drh Exp $
** $Id: delete.c,v 1.57 2003/05/17 17:35:11 drh Exp $
*/
#include "sqliteInt.h"
@@ -142,8 +142,7 @@ void sqliteDeleteFrom(
if( v==0 ){
goto delete_from_cleanup;
}
sqliteBeginWriteOperation(pParse, row_triggers_exist,
!row_triggers_exist && pTab->iDb==1);
sqliteBeginWriteOperation(pParse, row_triggers_exist, pTab->iDb);
/* If we are trying to delete from a view, construct that view into
** a temporary table.

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
** $Id: insert.c,v 1.84 2003/05/16 02:30:27 drh Exp $
** $Id: insert.c,v 1.85 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
@@ -159,8 +159,7 @@ void sqliteInsert(
*/
v = sqliteGetVdbe(pParse);
if( v==0 ) goto insert_cleanup;
sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist,
!row_triggers_exist && pTab->iDb==1);
sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb);
/* if there are row triggers, allocate a temp table for new.* references. */
if( row_triggers_exist ){

View File

@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.130 2003/05/04 17:58:26 drh Exp $
** $Id: main.c,v 1.131 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -29,6 +29,13 @@ typedef struct {
char **pzErrMsg; /* Error message stored here */
} InitData;
/*
** Fill the InitData structure with an error message that indicates
** that the database is corrupt.
*/
static void corruptSchema(InitData *pData){
sqliteSetString(pData->pzErrMsg, "malformed database schema", 0);
}
/*
** This is the callback routine for the code that initializes the
@@ -50,15 +57,19 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
Parse sParse;
int nErr = 0;
/* TODO: Do some validity checks on all fields. In particular,
** make sure fields do not contain NULLs. Otherwise we might core
** when attempting to initialize from a corrupt database file. */
assert( argc==5 );
if( argv[0]==0 ){
corruptSchema(pData);
return 1;
}
switch( argv[0][0] ){
case 'v':
case 'i':
case 't': { /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */
if( argv[2]==0 || argv[4]==0 ){
corruptSchema(pData);
return 1;
}
if( argv[3] && argv[3][0] ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because sParse.initFlag is set to 1, no VDBE code is generated
@@ -342,7 +353,10 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
sqliteResetInternalSchema(db, 0);
}
if( sParse.rc==SQLITE_OK ){
db->aDb[iDb].flags |= SQLITE_Initialized;
DbSetProperty(db, iDb, DB_SchemaLoaded);
if( iDb==0 ){
DbSetProperty(db, 1, DB_SchemaLoaded);
}
}else{
sqliteResetInternalSchema(db, iDb);
}
@@ -368,8 +382,8 @@ int sqliteInit(sqlite *db, char **pzErrMsg){
assert( (db->flags & SQLITE_Initialized)==0 );
rc = SQLITE_OK;
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
if( db->aDb[i].flags & SQLITE_Initialized ) continue;
if( i==1 ) continue; /* Skip the temp database - initialized with 0 */
if( DbHasProperty(db, i, DB_SchemaLoaded) ) continue;
assert( i!=1 ); /* Should have been initialized together with 0 */
rc = sqliteInitOne(db, i, pzErrMsg);
}
if( rc==SQLITE_OK ){
@@ -961,14 +975,14 @@ int sqliteBtreeFactory(
if (location == 1) {
return sqliteBtreeOpen(zFilename, omitJournal, nCache, ppBtree);
} else {
return sqliteRBtreeOpen(0, 0, 0, ppBtree);
return sqliteRbtreeOpen(0, 0, 0, ppBtree);
}
} else {
/* Always use in-core DB */
return sqliteRBtreeOpen(0, 0, 0, ppBtree);
return sqliteRbtreeOpen(0, 0, 0, ppBtree);
}
}else if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){
return sqliteRBtreeOpen(0, 0, 0, ppBtree);
return sqliteRbtreeOpen(0, 0, 0, ppBtree);
}else
#endif
{

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.138 2003/05/06 20:35:16 drh Exp $
** $Id: select.c,v 1.139 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
@@ -1845,9 +1845,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
** or last entry in the main table.
*/
if( !pParse->schemaVerified && (pParse->db->flags & SQLITE_InTrans)==0 ){
sqliteCodeVerifySchema(pParse);
}
sqliteCodeVerifySchema(pParse, pTab->iDb);
base = p->pSrc->a[0].iCursor;
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);

View File

@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.184 2003/05/10 03:04:34 jplyon Exp $
** @(#) $Id: sqliteInt.h,v 1.185 2003/05/17 17:35:12 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
@@ -240,6 +240,38 @@ struct Db {
u16 flags; /* Flags associated with this database */
};
/*
** These macros can be used to test, set, or clear bits in the
** Db.flags field.
*/
#define DbHasProperty(D,I,P) (((D)->aDb[I].flags&(P))==(P))
#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].flags&(P))!=0)
#define DbSetProperty(D,I,P) (D)->aDb[I].flags|=(P)
#define DbClearProperty(D,I,P) (D)->aDb[I].flags&=~(P)
/*
** Allowed values for the DB.flags field.
**
** The DB_Locked flag is set when the first OP_Transaction or OP_Checkpoint
** opcode is emitted for a database. This prevents multiple occurances
** of those opcodes for the same database in the same program. Similarly,
** the DB_Cookie flag is set when the OP_VerifyCookie opcode is emitted,
** and prevents duplicate OP_VerifyCookies from taking up space and slowing
** down execution.
**
** The DB_SchemaLoaded flag is set after the database schema has been
** read into internal hash tables.
**
** DB_UnresetViews means that one or more views have column names that
** have been filled out. If the schema changes, these column names might
** changes and so the view will need to be reset.
*/
#define DB_Locked 0x0001 /* OP_Transaction opcode has been emitted */
#define DB_Cookie 0x0002 /* OP_VerifyCookie opcode has been emiited */
#define DB_SchemaLoaded 0x0004 /* The schema has been loaded */
#define DB_UnresetViews 0x0008 /* Some views have defined column names */
/*
** Each database is an instance of the following structure.
**
@@ -293,7 +325,11 @@ struct sqlite {
};
/*
** Possible values for the sqlite.flags.
** Possible values for the sqlite.flags and or Db.flags fields.
**
** On sqlite.flags, the SQLITE_InTrans value means that we have
** executed a BEGIN. On Db.flags, SQLITE_InTrans means a statement
** transaction is active on that particular database file.
*/
#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
#define SQLITE_Initialized 0x00000002 /* True after initialization */
@@ -306,10 +342,7 @@ struct sqlite {
/* the count using a callback. */
#define SQLITE_NullCallback 0x00000080 /* Invoke the callback once if the */
/* result set is empty */
/*#define SQLITE_ResultDetails 0x00000100 * (UNUSED -- flag free for reuse) */
#define SQLITE_UnresetViews 0x00000200 /* True if one or more views have */
/* defined column names */
#define SQLITE_ReportTypes 0x00000400 /* Include information on datatypes */
#define SQLITE_ReportTypes 0x00000200 /* Include information on datatypes */
/* in 4th argument of callback */
/*
@@ -817,8 +850,6 @@ struct Parse {
u8 nameClash; /* A permanent table name clashes with temp table name */
u8 useAgg; /* If true, extract field values from the aggregator
** while generating expressions. Normally false */
u8 schemaVerified; /* True if an OP_VerifySchema has been coded someplace
** other than after an OP_Transaction */
u8 iDb; /* Index of database whose schema is being parsed */
u8 useCallback; /* True if callbacks should be used to report results */
int useDb; /* Restrict references to tables in this database */
@@ -1077,7 +1108,7 @@ Vdbe *sqliteGetVdbe(Parse*);
int sqliteRandomByte(void);
int sqliteRandomInteger(void);
void sqliteRollbackAll(sqlite*);
void sqliteCodeVerifySchema(Parse*);
void sqliteCodeVerifySchema(Parse*, int);
void sqliteBeginTransaction(Parse*, int);
void sqliteCommitTransaction(Parse*);
void sqliteRollbackTransaction(Parse*);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.65 2003/05/02 14:32:14 drh Exp $
** $Id: update.c,v 1.66 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
@@ -203,7 +203,7 @@ void sqliteUpdate(
*/
v = sqliteGetVdbe(pParse);
if( v==0 ) goto update_cleanup;
sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->iDb==1);
sqliteBeginWriteOperation(pParse, 1, pTab->iDb);
/* If we are trying to update a view, construct that view into
** a temporary table.

View File

@@ -36,7 +36,7 @@
** 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.222 2003/05/10 03:36:54 drh Exp $
** $Id: vdbe.c,v 1.223 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -3193,7 +3193,9 @@ case OP_Checkpoint: {
case OP_Transaction: {
int busy = 1;
int i = pOp->p1;
while( i>=0 && i<db->nDb && db->aDb[i].pBt!=0 && busy ){
assert( i>=0 && i<db->nDb );
if( db->aDb[i].inTrans ) break;
while( db->aDb[i].pBt!=0 && busy ){
rc = sqliteBtreeBeginTrans(db->aDb[i].pBt);
switch( rc ){
case SQLITE_BUSY: {
@@ -4547,7 +4549,7 @@ case OP_IdxGE: {
** See also: Clear
*/
case OP_Destroy: {
sqliteBtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1);
rc = sqliteBtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1);
break;
}
@@ -4564,7 +4566,7 @@ case OP_Destroy: {
** See also: Destroy
*/
case OP_Clear: {
sqliteBtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1);
rc = sqliteBtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1);
break;
}

View File

@@ -12,7 +12,7 @@
** This module contains C code that generates VDBE code used to process
** the WHERE clause of SQL statements.
**
** $Id: where.c,v 1.78 2003/05/02 14:32:14 drh Exp $
** $Id: where.c,v 1.79 2003/05/17 17:35:13 drh Exp $
*/
#include "sqliteInt.h"
@@ -680,10 +680,7 @@ WhereInfo *sqliteWhereBegin(
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
sqliteVdbeAddOp(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum);
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
if( i==0 && !pParse->schemaVerified &&
(pParse->db->flags & SQLITE_InTrans)==0 ){
sqliteCodeVerifySchema(pParse);
}
sqliteCodeVerifySchema(pParse, pTab->iDb);
if( pWInfo->a[i].pIdx!=0 ){
sqliteVdbeAddOp(v, OP_Integer, pWInfo->a[i].pIdx->iDb, 0);
sqliteVdbeAddOp(v, OP_OpenRead,

View File

@@ -12,7 +12,7 @@
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.3 2003/04/17 22:57:55 drh Exp $
# $Id: attach.test,v 1.4 2003/05/17 17:35:13 drh Exp $
#
set testdir [file dirname $argv0]
@@ -237,6 +237,60 @@ do_test attach-2.6 {
SELECT * FROM main.tx;
}
} {}
do_test attach-2.7 {
execsql {
SELECT type, name, tbl_name FROM db2.sqlite_master;
}
} {table t2 t2 table tx tx trigger r1 t2}
do_test attach-2.8 {
execsql {
PRAGMA database_list
}
} {0 main 1 temp 2 db2}
do_test attach-2.9 {
execsql {
CREATE INDEX i2 ON t2(x);
SELECT * FROM t2 WHERE x>5;
} db2
} {21 x 22 y}
do_test attach-2.10 {
execsql {
SELECT type, name, tbl_name FROM sqlite_master;
} db2
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
do_test attach-2.11 {
catchsql { pragma vdbe_trace=on;
SELECT * FROM t2 WHERE x>5;
}
} {1 {database schema has changed}}
do_test attach-2.12 {
execsql {
PRAGMA database_list
}
} {0 main 1 temp 2 db2}
do_test attach-2.13 {
catchsql {
SELECT * FROM t2 WHERE x>5;
}
} {0 {21 x 22 y}}
do_test attach-2.14 {
execsql {
SELECT type, name, tbl_name FROM sqlite_master;
}
} {table t1 t1 table tx tx}
do_test attach-2.15 {
execsql {
SELECT type, name, tbl_name FROM db2.sqlite_master;
}
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
do_test attach-2.16 {
db close
sqlite db test.db
execsql {
ATTACH 'test2.db' AS db2;
SELECT type, name, tbl_name FROM db2.sqlite_master;
}
} {table t2 t2 table tx tx trigger r1 t2 index i2 t2}
for {set i 2} {$i<=15} {incr i} {
catch {db$i close}

View File

@@ -12,7 +12,7 @@
#
# This file implements tests for temporary tables and indices.
#
# $Id: temptable.test,v 1.9 2003/03/30 00:19:50 drh Exp $
# $Id: temptable.test,v 1.10 2003/05/17 17:35:13 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -165,7 +165,7 @@ do_test temptable-4.3 {
catchsql {
SELECT * FROM t2;
} db2
} {1 {database schema has changed}}
} {0 {10 20}}
do_test temptable-4.4.1 {
catchsql {
SELECT * FROM temp.t2;
@@ -175,8 +175,23 @@ do_test temptable-4.4.2 {
catchsql {
SELECT * FROM main.t2;
} db2
} {0 {9 8 7}}
} {1 {no such table: main.t2}}
do_test temptable-4.4.3 {
catchsql {
SELECT name FROM main.sqlite_master WHERE type='table';
} db2
} {1 {database schema has changed}}
do_test temptable-4.4.4 {
catchsql {
SELECT name FROM main.sqlite_master WHERE type='table';
} db2
} {0 {t1 t2}}
do_test temptable-4.4.5 {
catchsql {
SELECT * FROM main.t2;
} db2
} {0 {9 8 7}}
do_test temptable-4.4.6 {
# TEMP takes precedence over MAIN
catchsql {
SELECT * FROM t2;
@@ -217,11 +232,21 @@ do_test temptable-4.9 {
SELECT * FROM t2;
}
} {3 4}
do_test temptable-4.10 {
do_test temptable-4.10.1 {
catchsql {
SELECT * FROM t2;
} db2
} {0 {1 2}}
do_test temptable-4.10.2 {
catchsql {
SELECT name FROM sqlite_master WHERE type='table'
} db2
} {1 {database schema has changed}}
do_test temptable-4.10.3 {
catchsql {
SELECT name FROM sqlite_master WHERE type='table'
} db2
} {0 {t1 t2}}
do_test temptable-4.11 {
execsql {
SELECT * FROM t2;

View File

@@ -52,12 +52,16 @@ source $testdir/tester.tcl
# 1.
set ii 0
foreach tbl_defn [ list \
{CREATE TABLE tbl (a, b);} \
{CREATE TEMP TABLE tbl (a, b);} \
{CREATE TABLE tbl (a INTEGER PRIMARY KEY, b);} \
{CREATE TABLE tbl (a, b PRIMARY KEY);} \
{CREATE TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);} ] {
foreach tbl_defn {
{CREATE TEMP TABLE tbl (a, b);}
{CREATE TABLE tbl (a, b);}
{CREATE TABLE tbl (a INTEGER PRIMARY KEY, b);}
{CREATE TEMPORARY TABLE tbl (a INTEGER PRIMARY KEY, b);}
{CREATE TABLE tbl (a, b PRIMARY KEY);}
{CREATE TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);}
{CREATE TEMP TABLE tbl (a, b); CREATE INDEX tbl_idx ON tbl(b);}
{CREATE TABLE tbl (a, b); CREATE TEMP INDEX tbl_idx ON tbl(b);}
} {
incr ii
catchsql { DROP INDEX tbl_idx; }
catchsql {
@@ -102,7 +106,7 @@ foreach tbl_defn [ list \
END;
}
do_test trigger2-1.1.$ii {
do_test trigger2-1.$ii.1 {
execsql {
UPDATE tbl SET a = a * 10, b = b * 10;
SELECT * FROM rlog ORDER BY idx;
@@ -135,7 +139,7 @@ foreach tbl_defn [ list \
0, 0);
END;
}
do_test trigger2-1.2.$ii {
do_test trigger2-1.$ii.2 {
execsql {
DELETE FROM tbl;
SELECT * FROM rlog;
@@ -163,7 +167,7 @@ foreach tbl_defn [ list \
new.a, new.b);
END;
}
do_test trigger2-1.3.$ii {
do_test trigger2-1.$ii.3 {
execsql {
CREATE TABLE other_tbl(a, b);
@@ -177,6 +181,12 @@ foreach tbl_defn [ list \
}
} [list 1 0 0 0 0 5 6 \
2 0 0 5 6 5 6 ]
do_test trigger2-1.$ii.4 {
execsql {
PRAGMA integrity_check;
}
} {ok ok}
}
catchsql {
DROP TABLE rlog;
@@ -187,16 +197,15 @@ catchsql {
# 2.
set ii 0
foreach tr_program [ list \
{UPDATE tbl SET b = old.b;} \
{INSERT INTO log VALUES(new.c, 2, 3);} \
{DELETE FROM log WHERE a = 1;} \
foreach tr_program {
{UPDATE tbl SET b = old.b;}
{INSERT INTO log VALUES(new.c, 2, 3);}
{DELETE FROM log WHERE a = 1;}
{INSERT INTO tbl VALUES(500, new.b * 10, 700);
UPDATE tbl SET c = old.c;
DELETE FROM log;} \
DELETE FROM log;}
{INSERT INTO log select * from tbl;}
] \
{
} {
foreach test_varset [ list \
{
set statement {UPDATE tbl SET c = 10 WHERE a = 1;}
@@ -262,22 +271,25 @@ foreach tr_program [ list \
DROP TABLE tbl;
DROP TABLE log;
}
execsql {
CREATE TABLE tbl(a PRIMARY KEY, b, c);
CREATE TABLE log(a, b, c);
}
set query {SELECT * FROM tbl; SELECT * FROM log;}
set prep "$prep; INSERT INTO log VALUES(1, 2, 3); INSERT INTO log VALUES(10, 20, 30);"
set prep "$prep; INSERT INTO log VALUES(1, 2, 3);\
INSERT INTO log VALUES(10, 20, 30);"
# Check execution of BEFORE programs:
set before_data [ execsql "$prep $tr_program_cooked $statement $query" ]
execsql "DELETE FROM tbl; DELETE FROM log; $prep";
execsql "CREATE TRIGGER the_trigger BEFORE [string range $statement 0 6] ON tbl BEGIN $tr_program_fixed END;"
execsql "CREATE TRIGGER the_trigger BEFORE [string range $statement 0 6]\
ON tbl BEGIN $tr_program_fixed END;"
do_test trigger2-2-$ii-before "execsql {$statement $query}" $before_data
do_test trigger2-2.$ii-before "execsql {$statement $query}" $before_data
execsql "DROP TRIGGER the_trigger;"
execsql "DELETE FROM tbl; DELETE FROM log;"
@@ -286,11 +298,18 @@ foreach tr_program [ list \
set after_data [ execsql "$prep $statement $tr_program_cooked $query" ]
execsql "DELETE FROM tbl; DELETE FROM log; $prep";
execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6]\
ON tbl BEGIN $tr_program_fixed END;"
execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6] ON tbl BEGIN $tr_program_fixed END;"
do_test trigger2-2-$ii-after "execsql {$statement $query}" $after_data
do_test trigger2-2.$ii-after "execsql {$statement $query}" $after_data
execsql "DROP TRIGGER the_trigger;"
do_test trigger2-2.$ii-integrity {
execsql {
PRAGMA integrity_check;
}
} {ok ok}
}
}
catchsql {
@@ -361,6 +380,11 @@ execsql {
DROP TABLE tbl;
DROP TABLE log;
}
do_test trigger2-3.3 {
execsql {
PRAGMA integrity_check;
}
} {ok ok}
# Simple cascaded trigger
execsql {
@@ -585,9 +609,8 @@ do_test trigger2-7.1 {
0, 0, 0, 0, new.a, new.b, new.c, new.d);
END;
}
} {}
} {};
#explain {delete from abcd where a=1;}
do_test trigger2-7.2 {
execsql {
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
@@ -689,5 +712,8 @@ do_test trigger2-8.6 {
}
} {3 103 5 205 4 304 9 109 11 211 10 310}
do_test trigger2-9.9 {
execsql {PRAGMA integrity_check}
} {ok ok}
finish_test