mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Get triggers working on tables with INTEGER PRIMARY KEYs. Ticket #291.
This may also fix #159. Still need to add tests so both bugs remain open for the time being. (CVS 908) FossilOrigin-Name: 0b996959b8d8bc2c82eab9cccc190befd0056505
This commit is contained in:
6
main.mk
6
main.mk
@@ -54,7 +54,7 @@ TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src
|
|||||||
|
|
||||||
# Object files for the SQLite library.
|
# Object files for the SQLite library.
|
||||||
#
|
#
|
||||||
LIBOBJ = attach.o auth.o btree.o build.o copy.o delete.o \
|
LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o delete.o \
|
||||||
expr.o func.o hash.o insert.o \
|
expr.o func.o hash.o insert.o \
|
||||||
main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \
|
main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \
|
||||||
select.o table.o tokenize.o trigger.o update.o util.o \
|
select.o table.o tokenize.o trigger.o update.o util.o \
|
||||||
@@ -67,6 +67,7 @@ SRC = \
|
|||||||
$(TOP)/src/auth.c \
|
$(TOP)/src/auth.c \
|
||||||
$(TOP)/src/btree.c \
|
$(TOP)/src/btree.c \
|
||||||
$(TOP)/src/btree.h \
|
$(TOP)/src/btree.h \
|
||||||
|
$(TOP)/src/btree_rb.c \
|
||||||
$(TOP)/src/build.c \
|
$(TOP)/src/build.c \
|
||||||
$(TOP)/src/copy.c \
|
$(TOP)/src/copy.c \
|
||||||
$(TOP)/src/delete.c \
|
$(TOP)/src/delete.c \
|
||||||
@@ -165,6 +166,9 @@ lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
|
|||||||
btree.o: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
|
btree.o: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
|
||||||
$(TCCX) -c $(TOP)/src/btree.c
|
$(TCCX) -c $(TOP)/src/btree.c
|
||||||
|
|
||||||
|
btree_rb.o: $(TOP)/src/btree_rb.c $(HDR)
|
||||||
|
$(TCCX) -c $(TOP)/src/btree_rb.c
|
||||||
|
|
||||||
build.o: $(TOP)/src/build.c $(HDR)
|
build.o: $(TOP)/src/build.c $(HDR)
|
||||||
$(TCCX) -c $(TOP)/src/build.c
|
$(TCCX) -c $(TOP)/src/build.c
|
||||||
|
|
||||||
|
36
manifest
36
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Added\s\sbtree_rb.c\s(CVS\s907)
|
C Get\striggers\sworking\son\stables\swith\sINTEGER\sPRIMARY\sKEYs.\s\sTicket\s#291.\nThis\smay\salso\sfix\s#159.\s\sStill\sneed\sto\sadd\stests\sso\sboth\sbugs\sremain\sopen\nfor\sthe\stime\sbeing.\s(CVS\s908)
|
||||||
D 2003-04-15T17:22:30
|
D 2003-04-15T19:22:23
|
||||||
F Makefile.in df3a4db41a7450468b5fe934d9dd8f723b631249
|
F Makefile.in df3a4db41a7450468b5fe934d9dd8f723b631249
|
||||||
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
||||||
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
||||||
@@ -14,25 +14,25 @@ F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
|
|||||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
|
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
|
||||||
F libtool bbbea7d79c23323e4100103836028e4fad0d9242
|
F libtool bbbea7d79c23323e4100103836028e4fad0d9242
|
||||||
F ltmain.sh abfb9387049fff6996afc6e325736597795baf11
|
F ltmain.sh abfb9387049fff6996afc6e325736597795baf11
|
||||||
F main.mk 08739679174d858a0f6ced3117ce8dcfdafa540a
|
F main.mk 65302cd32c459607872cae81481ccadd6a998968
|
||||||
F publish.sh 86b5e8535830a2588f62ce1d5d1ef00e1dede23a
|
F publish.sh 86b5e8535830a2588f62ce1d5d1ef00e1dede23a
|
||||||
F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e
|
F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e
|
||||||
F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
|
F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
|
||||||
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
||||||
F src/attach.c 8c98e2c0ca434b94deca1b8694c72bd0303a9a87
|
F src/attach.c 7ebc7487de43e357a64226f8abef81f2669f2183
|
||||||
F src/auth.c f37bfc9451b8c1fa52f34adff474560018892729
|
F src/auth.c f37bfc9451b8c1fa52f34adff474560018892729
|
||||||
F src/btree.c 9949031b6087e9d1b43b359b84c68a491086984f
|
F src/btree.c 9949031b6087e9d1b43b359b84c68a491086984f
|
||||||
F src/btree.h 5cb871546bd6fa58396a6f033e2b29b388241e1b
|
F src/btree.h 5cb871546bd6fa58396a6f033e2b29b388241e1b
|
||||||
F src/btree_rb.c 85727e7ba6277a55385bf86ff5da6d1093dd8978
|
F src/btree_rb.c c917cdcc401df1075b257a96e8a1e3e46a506805
|
||||||
F src/build.c daed1dacdb70e5d4def9df2e34a1cabeeb8467c9
|
F src/build.c daed1dacdb70e5d4def9df2e34a1cabeeb8467c9
|
||||||
F src/copy.c ddd204d5dddac09d71a07f4ceded4c9926d5512b
|
F src/copy.c 8699e571994934c78f70761a1458d7b9e9e75073
|
||||||
F src/delete.c 58d698779a6b7f819718ecd45b310a9de8537088
|
F src/delete.c 6021fd293a78ebeb35e8177bd811d752fe090f89
|
||||||
F src/encode.c faf03741efe921755ec371cf4a6984536de00042
|
F src/encode.c faf03741efe921755ec371cf4a6984536de00042
|
||||||
F src/expr.c b8daee83f837b24a22d889200bdd74973ca2d8db
|
F src/expr.c 942f535c8906ef81df00bac62223868feb78424b
|
||||||
F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605
|
F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605
|
||||||
F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
|
F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
|
||||||
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
|
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
|
||||||
F src/insert.c e2f5e7feecb507d904a7da48874595f440b715aa
|
F src/insert.c 45d27e3e8447bff4025db2f0dc3bb4e318e602f4
|
||||||
F src/main.c daf5b7c256340fb9aa77df7254865218a47d5a63
|
F src/main.c daf5b7c256340fb9aa77df7254865218a47d5a63
|
||||||
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
|
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
|
||||||
F src/os.c c33ebb320921b8df6d09ea19fe846348df86a0c9
|
F src/os.c c33ebb320921b8df6d09ea19fe846348df86a0c9
|
||||||
@@ -47,7 +47,7 @@ F src/select.c 14e2e2a512f4edfc75fb310ebcb502ff3ee87402
|
|||||||
F src/shell.c 97f397c0c108176ccbc52ea5b8ec688f995eba7a
|
F src/shell.c 97f397c0c108176ccbc52ea5b8ec688f995eba7a
|
||||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||||
F src/sqlite.h.in f49c2cdec7d24cb03e496a1ca519e16306495ee1
|
F src/sqlite.h.in f49c2cdec7d24cb03e496a1ca519e16306495ee1
|
||||||
F src/sqliteInt.h 048303eafaf5811a0977528756386873931cd914
|
F src/sqliteInt.h c34ae7f78cb3c2f8e7136ff091155f97f7ec9327
|
||||||
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
||||||
F src/tclsqlite.c 7a072c3c8ba9796edc25e5ffa62b68558134e192
|
F src/tclsqlite.c 7a072c3c8ba9796edc25e5ffa62b68558134e192
|
||||||
F src/test1.c 7ad4e6308dde0bf5a0f0775ce20cb2ec37a328f8
|
F src/test1.c 7ad4e6308dde0bf5a0f0775ce20cb2ec37a328f8
|
||||||
@@ -55,11 +55,11 @@ F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700
|
|||||||
F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
|
F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
|
||||||
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
|
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
|
||||||
F src/tokenize.c 675b4718d17c69fe7609dc8e85e426ef002be811
|
F src/tokenize.c 675b4718d17c69fe7609dc8e85e426ef002be811
|
||||||
F src/trigger.c bd5a5b234b47f28f9f21a46243dcaf1c5b2383a3
|
F src/trigger.c 4ca4499d367548385b8e9fc67eb360cd1ca95b8a
|
||||||
F src/update.c b368369f1fbe6d7f56a53e5ffad3b75dae9e3e1a
|
F src/update.c a60470d07cdd4ff3c11c5418f8055f2f41b3d751
|
||||||
F src/util.c 8953c612a036e30f24c1c1f5a1498176173daa37
|
F src/util.c 8953c612a036e30f24c1c1f5a1498176173daa37
|
||||||
F src/vacuum.c ac65e9578506a0cdf70ece2668e5b22f4895477c
|
F src/vacuum.c ac65e9578506a0cdf70ece2668e5b22f4895477c
|
||||||
F src/vdbe.c 0fa25d177b02523dbdcb48395053ed5cc21829ee
|
F src/vdbe.c 6738153dc8ebd68cf5d6ccf7e1e08d608f5420c6
|
||||||
F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
|
F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
|
||||||
F src/where.c e5733f7d5e9cc4ed3590dc3401f779e7b7bb8127
|
F src/where.c e5733f7d5e9cc4ed3590dc3401f779e7b7bb8127
|
||||||
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
|
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
|
||||||
@@ -102,7 +102,7 @@ F test/pragma.test d45d130f532bfe86ebd5ba74862d88b36ded8998
|
|||||||
F test/printf.test a29b8afa24edb4411adfe473b12ac32c84098fce
|
F test/printf.test a29b8afa24edb4411adfe473b12ac32c84098fce
|
||||||
F test/quick.test c527bdb899b12a8cd8ceecce45f72922099f4095
|
F test/quick.test c527bdb899b12a8cd8ceecce45f72922099f4095
|
||||||
F test/quote.test 08f23385c685d3dc7914ec760d492cacea7f6e3d
|
F test/quote.test 08f23385c685d3dc7914ec760d492cacea7f6e3d
|
||||||
F test/rowid.test 82bd07c959eb56ce14feb85cac591750af714ddf
|
F test/rowid.test 532b3d8aa01d05222a0e0fd503b8da89fe3764be
|
||||||
F test/select1.test 0d708cec567104653ec9aa49fecf3444a2e7d150
|
F test/select1.test 0d708cec567104653ec9aa49fecf3444a2e7d150
|
||||||
F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4
|
F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4
|
||||||
F test/select3.test 445a1a3dde4e2fd32541b311f55da5e2f8079d76
|
F test/select3.test 445a1a3dde4e2fd32541b311f55da5e2f8079d76
|
||||||
@@ -162,7 +162,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
|
|||||||
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
|
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
|
||||||
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
||||||
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
||||||
P 96a717661a3b7108fe0cacb588d81fd8e91eb640
|
P 93eb6c52aca8de15a88247ec986c36245527ec7b
|
||||||
R cbe58bcf88d6fb97e9a813f970d2df5a
|
R 97b4c68da27841d236c7d5fe42a0a220
|
||||||
U paul
|
U drh
|
||||||
Z df689c3d6a38a6ae7133718deb76cd51
|
Z 2af580d6307d67cd3da5d48cc8031613
|
||||||
|
@@ -1 +1 @@
|
|||||||
93eb6c52aca8de15a88247ec986c36245527ec7b
|
0b996959b8d8bc2c82eab9cccc190befd0056505
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** This file contains code used to implement the ATTACH and DETACH commands.
|
** This file contains code used to implement the ATTACH and DETACH commands.
|
||||||
**
|
**
|
||||||
** $Id: attach.c,v 1.2 2003/04/13 18:26:51 paul Exp $
|
** $Id: attach.c,v 1.3 2003/04/15 19:22:23 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -119,6 +119,7 @@ void sqliteDetach(Parse *pParse, Token *pDbname){
|
|||||||
}
|
}
|
||||||
sqliteBtreeClose(db->aDb[i].pBt);
|
sqliteBtreeClose(db->aDb[i].pBt);
|
||||||
db->aDb[i].pBt = 0;
|
db->aDb[i].pBt = 0;
|
||||||
|
sqliteFree(db->aDb[i].zName);
|
||||||
sqliteResetInternalSchema(db, i);
|
sqliteResetInternalSchema(db, i);
|
||||||
db->nDb--;
|
db->nDb--;
|
||||||
if( i<db->nDb ){
|
if( i<db->nDb ){
|
||||||
|
@@ -9,20 +9,26 @@
|
|||||||
** May you share freely, never taking more than you give.
|
** May you share freely, never taking more than you give.
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree_rb.c,v 1.1 2003/04/15 17:22:30 paul Exp $
|
** $Id: btree_rb.c,v 1.2 2003/04/15 19:22:23 drh Exp $
|
||||||
**
|
**
|
||||||
** This file implements an in-core database using Red-Black balanced
|
** This file implements an in-core database using Red-Black balanced
|
||||||
** binary trees.
|
** binary trees.
|
||||||
**
|
**
|
||||||
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
|
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SQLITE_NO_BTREE_DEFS
|
#define SQLITE_NO_BTREE_DEFS
|
||||||
|
|
||||||
#include "btree.h"
|
#include "btree.h"
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Omit this whole file if the SQLITE_OMIT_INMEMORYDB macro is
|
||||||
|
** defined. This allows a lot of code to be omitted for installations
|
||||||
|
** that do not need it.
|
||||||
|
*/
|
||||||
|
#ifndef SQLITE_OMIT_INMEMORYDB
|
||||||
|
|
||||||
|
|
||||||
typedef struct BtRbTree BtRbTree;
|
typedef struct BtRbTree BtRbTree;
|
||||||
typedef struct BtRbNode BtRbNode;
|
typedef struct BtRbNode BtRbNode;
|
||||||
typedef struct BtRollbackOp BtRollbackOp;
|
typedef struct BtRollbackOp BtRollbackOp;
|
||||||
@@ -565,7 +571,6 @@ static void btreeLogRollbackOp(Btree* pBtree, BtRollbackOp *pRollbackOp)
|
|||||||
|
|
||||||
int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree)
|
int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree)
|
||||||
{
|
{
|
||||||
int tnum;
|
|
||||||
*ppBtree = (Btree *)sqliteMalloc(sizeof(Btree));
|
*ppBtree = (Btree *)sqliteMalloc(sizeof(Btree));
|
||||||
sqliteHashInit(&(*ppBtree)->tblHash, SQLITE_HASH_INT, 0);
|
sqliteHashInit(&(*ppBtree)->tblHash, SQLITE_HASH_INT, 0);
|
||||||
|
|
||||||
@@ -590,7 +595,6 @@ int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree)
|
|||||||
*/
|
*/
|
||||||
static int sqliteBtreeCreateTable(Btree* tree, int* n)
|
static int sqliteBtreeCreateTable(Btree* tree, int* n)
|
||||||
{
|
{
|
||||||
BtRbTree *pNewTbl;
|
|
||||||
assert( tree->eTransState != TRANS_NONE );
|
assert( tree->eTransState != TRANS_NONE );
|
||||||
|
|
||||||
*n = tree->next_idx++;
|
*n = tree->next_idx++;
|
||||||
@@ -690,7 +694,6 @@ static int sqliteBtreeCursor(Btree* tree, int iTable, int wrFlag, BtCursor **ppC
|
|||||||
static int sqliteBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
|
static int sqliteBtreeInsert(BtCursor* pCur, const void *pKey, int nKey,
|
||||||
const void *pDataInput, int nData)
|
const void *pDataInput, int nData)
|
||||||
{
|
{
|
||||||
BtRbNode *pNode; /* The new node that is begin inserted */
|
|
||||||
void * pData;
|
void * pData;
|
||||||
int match;
|
int match;
|
||||||
|
|
||||||
@@ -1161,7 +1164,6 @@ static int sqliteBtreeClose(Btree* tree)
|
|||||||
{
|
{
|
||||||
HashElem *p;
|
HashElem *p;
|
||||||
for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){
|
for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){
|
||||||
BtRbTree *pTree = sqliteHashData(p);
|
|
||||||
tree->eTransState = TRANS_ROLLBACK;
|
tree->eTransState = TRANS_ROLLBACK;
|
||||||
sqliteBtreeClearTable(tree, sqliteHashKeysize(p));
|
sqliteBtreeClearTable(tree, sqliteHashKeysize(p));
|
||||||
sqliteFree(sqliteHashData(p));
|
sqliteFree(sqliteHashData(p));
|
||||||
@@ -1223,9 +1225,9 @@ static void execute_rollback_list(Btree *pBtree, BtRollbackOp *pList)
|
|||||||
{
|
{
|
||||||
BtRollbackOp *pTmp;
|
BtRollbackOp *pTmp;
|
||||||
BtCursor cur;
|
BtCursor cur;
|
||||||
cur.pBtree = pBtree;
|
|
||||||
|
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
cur.pBtree = pBtree;
|
||||||
while( pList ){
|
while( pList ){
|
||||||
switch( pList->eOp ){
|
switch( pList->eOp ){
|
||||||
case ROLLBACK_INSERT:
|
case ROLLBACK_INSERT:
|
||||||
@@ -1391,3 +1393,5 @@ static BtCursorOps sqliteBtreeCursorOps = {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif /* SQLITE_OMIT_INMEMORYDB */
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** This file contains code used to implement the COPY command.
|
** This file contains code used to implement the COPY command.
|
||||||
**
|
**
|
||||||
** $Id: copy.c,v 1.1 2003/04/06 21:08:24 drh Exp $
|
** $Id: copy.c,v 1.2 2003/04/15 19:22:23 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ void sqliteCopy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, 0, 0, onError, addr);
|
sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, 0, 0, onError, addr);
|
||||||
sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0);
|
sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0, -1);
|
||||||
if( (db->flags & SQLITE_CountRows)!=0 ){
|
if( (db->flags & SQLITE_CountRows)!=0 ){
|
||||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */
|
sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */
|
||||||
}
|
}
|
||||||
|
37
src/delete.c
37
src/delete.c
@@ -12,7 +12,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle DELETE FROM statements.
|
** to handle DELETE FROM statements.
|
||||||
**
|
**
|
||||||
** $Id: delete.c,v 1.50 2003/03/31 02:12:47 drh Exp $
|
** $Id: delete.c,v 1.51 2003/04/15 19:22:23 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -71,8 +71,10 @@ void sqliteDeleteFrom(
|
|||||||
int base; /* Index of the first available table cursor */
|
int base; /* Index of the first available table cursor */
|
||||||
sqlite *db; /* Main database structure */
|
sqlite *db; /* Main database structure */
|
||||||
|
|
||||||
int row_triggers_exist = 0;
|
int row_triggers_exist = 0; /* True if any triggers exist */
|
||||||
int oldIdx = -1;
|
int before_triggers; /* True if there are BEFORE triggers */
|
||||||
|
int after_triggers; /* True if there are AFTER triggers */
|
||||||
|
int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
|
||||||
|
|
||||||
if( pParse->nErr || sqlite_malloc_failed ){
|
if( pParse->nErr || sqlite_malloc_failed ){
|
||||||
pTabList = 0;
|
pTabList = 0;
|
||||||
@@ -89,11 +91,11 @@ void sqliteDeleteFrom(
|
|||||||
if( zTab != 0 ){
|
if( zTab != 0 ){
|
||||||
pTab = sqliteFindTable(pParse->db, zTab, zDb);
|
pTab = sqliteFindTable(pParse->db, zTab, zDb);
|
||||||
if( pTab ){
|
if( pTab ){
|
||||||
row_triggers_exist =
|
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||||
sqliteTriggersExist(pParse, pTab->pTrigger,
|
TK_DELETE, TK_BEFORE, TK_ROW, 0);
|
||||||
TK_DELETE, TK_BEFORE, TK_ROW, 0) ||
|
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||||
sqliteTriggersExist(pParse, pTab->pTrigger,
|
TK_DELETE, TK_AFTER, TK_ROW, 0);
|
||||||
TK_DELETE, TK_AFTER, TK_ROW, 0);
|
row_triggers_exist = before_triggers || after_triggers;
|
||||||
}
|
}
|
||||||
if( row_triggers_exist && pTab->pSelect ){
|
if( row_triggers_exist && pTab->pSelect ){
|
||||||
/* Just fire VIEW triggers */
|
/* Just fire VIEW triggers */
|
||||||
@@ -195,6 +197,12 @@ void sqliteDeleteFrom(
|
|||||||
*/
|
*/
|
||||||
sqliteWhereEnd(pWInfo);
|
sqliteWhereEnd(pWInfo);
|
||||||
|
|
||||||
|
/* Open the pseudo-table used to store OLD if there are triggers.
|
||||||
|
*/
|
||||||
|
if( row_triggers_exist ){
|
||||||
|
sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Delete every item whose key was written to the list during the
|
/* Delete every item whose key was written to the list during the
|
||||||
** database scan. We have to delete items after the scan is complete
|
** database scan. We have to delete items after the scan is complete
|
||||||
** because deleting an item can change the scan order.
|
** because deleting an item can change the scan order.
|
||||||
@@ -211,20 +219,11 @@ void sqliteDeleteFrom(
|
|||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
||||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||||
sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
|
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_Integer, 13, 0);
|
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
||||||
for(i = 0; i<pTab->nCol; i++){
|
sqliteVdbeAddOp(v, OP_RowData, base, 0);
|
||||||
if( i==pTab->iPKey ){
|
|
||||||
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
|
||||||
} else {
|
|
||||||
sqliteVdbeAddOp(v, OP_Column, base, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0);
|
|
||||||
|
|
||||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
||||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||||
|
12
src/expr.c
12
src/expr.c
@@ -12,7 +12,7 @@
|
|||||||
** This file contains routines used for analyzing expressions and
|
** This file contains routines used for analyzing expressions and
|
||||||
** for generating VDBE code that evaluates expressions in SQLite.
|
** for generating VDBE code that evaluates expressions in SQLite.
|
||||||
**
|
**
|
||||||
** $Id: expr.c,v 1.91 2003/03/31 02:12:47 drh Exp $
|
** $Id: expr.c,v 1.92 2003/04/15 19:22:23 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -547,12 +547,8 @@ int sqliteExprResolveIds(
|
|||||||
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
||||||
cnt++;
|
cnt++;
|
||||||
pExpr->iTable = i + base;
|
pExpr->iTable = i + base;
|
||||||
if( j==pTab->iPKey ){
|
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
|
||||||
/* Substitute the record number for the INTEGER PRIMARY KEY */
|
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
|
||||||
pExpr->iColumn = -1;
|
|
||||||
}else{
|
|
||||||
pExpr->iColumn = j;
|
|
||||||
}
|
|
||||||
pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
|
pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,7 +576,7 @@ int sqliteExprResolveIds(
|
|||||||
for(j=0; j < pTab->nCol; j++) {
|
for(j=0; j < pTab->nCol; j++) {
|
||||||
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
||||||
cnt++;
|
cnt++;
|
||||||
pExpr->iColumn = j;
|
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
|
||||||
pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
|
pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
62
src/insert.c
62
src/insert.c
@@ -12,7 +12,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle INSERT statements in SQLite.
|
** to handle INSERT statements in SQLite.
|
||||||
**
|
**
|
||||||
** $Id: insert.c,v 1.78 2003/04/03 01:50:44 drh Exp $
|
** $Id: insert.c,v 1.79 2003/04/15 19:22:23 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -110,7 +110,9 @@ void sqliteInsert(
|
|||||||
int iCntMem; /* Memory cell used for the row counter */
|
int iCntMem; /* Memory cell used for the row counter */
|
||||||
|
|
||||||
int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
|
int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
|
||||||
int newIdx = -1;
|
int before_triggers; /* True if there are BEFORE triggers */
|
||||||
|
int after_triggers; /* True if there are AFTER triggers */
|
||||||
|
int newIdx = -1; /* Cursor for the NEW table */
|
||||||
|
|
||||||
if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
|
if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
|
||||||
db = pParse->db;
|
db = pParse->db;
|
||||||
@@ -132,10 +134,11 @@ void sqliteInsert(
|
|||||||
* (a) the table is not read-only,
|
* (a) the table is not read-only,
|
||||||
* (b) that if it is a view then ON INSERT triggers exist
|
* (b) that if it is a view then ON INSERT triggers exist
|
||||||
*/
|
*/
|
||||||
row_triggers_exist =
|
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
|
||||||
sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
|
TK_BEFORE, TK_ROW, 0);
|
||||||
TK_BEFORE, TK_ROW, 0) ||
|
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
|
||||||
sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT, TK_AFTER, TK_ROW, 0);
|
TK_AFTER, TK_ROW, 0);
|
||||||
|
row_triggers_exist = before_triggers || after_triggers;
|
||||||
if( pTab->readOnly || (pTab->pSelect && !row_triggers_exist) ){
|
if( pTab->readOnly || (pTab->pSelect && !row_triggers_exist) ){
|
||||||
sqliteErrorMsg(pParse, "%s %s may not be modified",
|
sqliteErrorMsg(pParse, "%s %s may not be modified",
|
||||||
pTab->pSelect ? "view" : "table",
|
pTab->pSelect ? "view" : "table",
|
||||||
@@ -311,7 +314,7 @@ void sqliteInsert(
|
|||||||
/* Open the temp table for FOR EACH ROW triggers
|
/* Open the temp table for FOR EACH ROW triggers
|
||||||
*/
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
|
sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the count of rows to be inserted
|
/* Initialize the count of rows to be inserted
|
||||||
@@ -350,11 +353,33 @@ void sqliteInsert(
|
|||||||
sqliteVdbeResolveLabel(v, iInsertBlock);
|
sqliteVdbeResolveLabel(v, iInsertBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Run the BEFORE triggers, if there are any
|
||||||
|
*/
|
||||||
endOfLoop = sqliteVdbeMakeLabel(v);
|
endOfLoop = sqliteVdbeMakeLabel(v);
|
||||||
if( row_triggers_exist ){
|
if( before_triggers ){
|
||||||
|
|
||||||
/* build the new.* reference row */
|
/* build the NEW.* reference row. Note that if there is an INTEGER
|
||||||
sqliteVdbeAddOp(v, OP_Integer, 13, 0);
|
** PRIMARY KEY into which a NULL is being inserted, that NULL will be
|
||||||
|
** translated into a unique ID for the row. But on a BEFORE trigger,
|
||||||
|
** we do not know what the unique ID will be (because the insert has
|
||||||
|
** not happened yet) so we substitute a rowid of -1
|
||||||
|
*/
|
||||||
|
if( keyColumn<0 ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Integer, -1, 0);
|
||||||
|
}else if( useTempTable ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
|
||||||
|
}else if( pSelect ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
|
||||||
|
}else{
|
||||||
|
sqliteExprCode(pParse, pList->a[keyColumn].pExpr);
|
||||||
|
sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3);
|
||||||
|
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_Integer, -1, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the new column data
|
||||||
|
*/
|
||||||
for(i=0; i<pTab->nCol; i++){
|
for(i=0; i<pTab->nCol; i++){
|
||||||
if( pColumn==0 ){
|
if( pColumn==0 ){
|
||||||
j = i;
|
j = i;
|
||||||
@@ -383,8 +408,12 @@ void sqliteInsert(
|
|||||||
onError, endOfLoop) ){
|
onError, endOfLoop) ){
|
||||||
goto insert_cleanup;
|
goto insert_cleanup;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Open the tables and indices for the INSERT */
|
/* If any triggers exists, the opening of tables and indices is deferred
|
||||||
|
** until now.
|
||||||
|
*/
|
||||||
|
if( row_triggers_exist ){
|
||||||
if( !pTab->pSelect ){
|
if( !pTab->pSelect ){
|
||||||
base = pParse->nTab;
|
base = pParse->nTab;
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
@@ -459,7 +488,8 @@ void sqliteInsert(
|
|||||||
** do the insertion.
|
** do the insertion.
|
||||||
*/
|
*/
|
||||||
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
|
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
|
||||||
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0);
|
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0,
|
||||||
|
after_triggers ? newIdx : -1);
|
||||||
|
|
||||||
/* Update the count of rows that are inserted
|
/* Update the count of rows that are inserted
|
||||||
*/
|
*/
|
||||||
@@ -807,7 +837,8 @@ void sqliteCompleteInsertion(
|
|||||||
int base, /* Index of a read/write cursor pointing at pTab */
|
int base, /* Index of a read/write cursor pointing at pTab */
|
||||||
char *aIdxUsed, /* Which indices are used. NULL means all are used */
|
char *aIdxUsed, /* Which indices are used. NULL means all are used */
|
||||||
int recnoChng, /* True if the record number will change */
|
int recnoChng, /* True if the record number will change */
|
||||||
int isUpdate /* True for UPDATE, False for INSERT */
|
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||||
|
int newIdx /* Index of NEW table for triggers. -1 if none */
|
||||||
){
|
){
|
||||||
int i;
|
int i;
|
||||||
Vdbe *v;
|
Vdbe *v;
|
||||||
@@ -823,6 +854,11 @@ void sqliteCompleteInsertion(
|
|||||||
sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0);
|
sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0);
|
||||||
}
|
}
|
||||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
||||||
|
if( newIdx>=0 ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Dup, 1, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_Dup, 1, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||||
|
}
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, base, pParse->trigStack?0:1);
|
sqliteVdbeAddOp(v, OP_PutIntKey, base, pParse->trigStack?0:1);
|
||||||
if( isUpdate && recnoChng ){
|
if( isUpdate && recnoChng ){
|
||||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.172 2003/04/15 01:19:49 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.173 2003/04/15 19:22:24 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "sqlite.h"
|
#include "sqlite.h"
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
** a minimum.
|
** a minimum.
|
||||||
*/
|
*/
|
||||||
/* #define SQLITE_OMIT_AUTHORIZATION 1 */
|
/* #define SQLITE_OMIT_AUTHORIZATION 1 */
|
||||||
#define SQLITE_OMIT_INMEMORYDB 1
|
/* #define SQLITE_OMIT_INMEMORYDB 1 */
|
||||||
/* #define SQLITE_OMIT_TRACE 1 */
|
/* #define SQLITE_OMIT_TRACE 1 */
|
||||||
/* #define SQLITE_OMIT_VACUUM 1 */
|
/* #define SQLITE_OMIT_VACUUM 1 */
|
||||||
|
|
||||||
@@ -1088,7 +1088,7 @@ int sqliteIsRowid(const char*);
|
|||||||
void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int);
|
void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int);
|
||||||
void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*);
|
void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*);
|
||||||
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
|
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
|
||||||
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
|
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int, int);
|
||||||
void sqliteBeginWriteOperation(Parse*, int, int);
|
void sqliteBeginWriteOperation(Parse*, int, int);
|
||||||
void sqliteEndWriteOperation(Parse*);
|
void sqliteEndWriteOperation(Parse*);
|
||||||
Expr *sqliteExprDup(Expr*);
|
Expr *sqliteExprDup(Expr*);
|
||||||
|
@@ -714,10 +714,10 @@ void sqliteViewTriggers(
|
|||||||
|
|
||||||
/* Allocate temp tables */
|
/* Allocate temp tables */
|
||||||
oldIdx = pParse->nTab++;
|
oldIdx = pParse->nTab++;
|
||||||
sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
|
sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
|
||||||
if( pChanges ){
|
if( pChanges ){
|
||||||
newIdx = pParse->nTab++;
|
newIdx = pParse->nTab++;
|
||||||
sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
|
sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Snapshot the view */
|
/* Snapshot the view */
|
||||||
|
70
src/update.c
70
src/update.c
@@ -12,7 +12,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle UPDATE statements.
|
** to handle UPDATE statements.
|
||||||
**
|
**
|
||||||
** $Id: update.c,v 1.58 2003/03/31 02:12:48 drh Exp $
|
** $Id: update.c,v 1.59 2003/04/15 19:22:24 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -47,7 +47,9 @@ void sqliteUpdate(
|
|||||||
Expr *pRecnoExpr; /* Expression defining the new record number */
|
Expr *pRecnoExpr; /* Expression defining the new record number */
|
||||||
int openAll; /* True if all indices need to be opened */
|
int openAll; /* True if all indices need to be opened */
|
||||||
|
|
||||||
int row_triggers_exist = 0;
|
int before_triggers; /* True if there are any BEFORE triggers */
|
||||||
|
int after_triggers; /* True if there are any AFTER triggers */
|
||||||
|
int row_triggers_exist = 0; /* True if any row triggers exist */
|
||||||
|
|
||||||
int newIdx = -1; /* index of trigger "new" temp table */
|
int newIdx = -1; /* index of trigger "new" temp table */
|
||||||
int oldIdx = -1; /* index of trigger "old" temp table */
|
int oldIdx = -1; /* index of trigger "old" temp table */
|
||||||
@@ -64,11 +66,13 @@ void sqliteUpdate(
|
|||||||
if( zTab != 0 ){
|
if( zTab != 0 ){
|
||||||
pTab = sqliteFindTable(pParse->db, zTab, zDb);
|
pTab = sqliteFindTable(pParse->db, zTab, zDb);
|
||||||
if( pTab ){
|
if( pTab ){
|
||||||
row_triggers_exist =
|
before_triggers =
|
||||||
sqliteTriggersExist(pParse, pTab->pTrigger,
|
sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||||
TK_UPDATE, TK_BEFORE, TK_ROW, pChanges) ||
|
TK_UPDATE, TK_BEFORE, TK_ROW, pChanges);
|
||||||
|
after_triggers =
|
||||||
sqliteTriggersExist(pParse, pTab->pTrigger,
|
sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||||
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
|
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
|
||||||
|
row_triggers_exist = before_triggers || after_triggers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( row_triggers_exist && pTab->pSelect ){
|
if( row_triggers_exist && pTab->pSelect ){
|
||||||
@@ -219,50 +223,56 @@ void sqliteUpdate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
int ii;
|
/* Create pseudo-tables for NEW and OLD
|
||||||
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
|
sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
|
||||||
sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
|
sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
|
||||||
|
|
||||||
|
/* The top of the update loop for when there are triggers.
|
||||||
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||||
|
|
||||||
|
/* Open a cursor and make it point to the record that is
|
||||||
|
** being updated.
|
||||||
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
||||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_Integer, 13, 0);
|
/* Generate the OLD table
|
||||||
for(ii = 0; ii < pTab->nCol; ii++){
|
*/
|
||||||
if( ii == pTab->iPKey ){
|
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
sqliteVdbeAddOp(v, OP_RowData, base, 0);
|
||||||
}else{
|
|
||||||
sqliteVdbeAddOp(v, OP_Column, base, ii);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_Integer, 13, 0);
|
/* Generate the NEW table
|
||||||
for(ii = 0; ii < pTab->nCol; ii++){
|
*/
|
||||||
if( aXRef[ii] < 0 ){
|
if( chngRecno ){
|
||||||
if( ii == pTab->iPKey ){
|
sqliteExprCode(pParse, pRecnoExpr);
|
||||||
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
}else{
|
||||||
}else{
|
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Column, base, ii);
|
}
|
||||||
}
|
for(i=0; i<pTab->nCol; i++){
|
||||||
|
if( i==pTab->iPKey ){
|
||||||
|
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
j = aXRef[i];
|
||||||
|
if( j<0 ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Column, base, i);
|
||||||
}else{
|
}else{
|
||||||
sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
|
sqliteExprCode(pParse, pChanges->a[j].pExpr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0);
|
/* Fire the BEFORE triggers
|
||||||
sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
|
*/
|
||||||
|
|
||||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
|
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
|
||||||
newIdx, oldIdx, onError, addr) ){
|
newIdx, oldIdx, onError, addr) ){
|
||||||
goto update_cleanup;
|
goto update_cleanup;
|
||||||
@@ -350,7 +360,7 @@ void sqliteUpdate(
|
|||||||
|
|
||||||
/* Create the new index entries and the new record.
|
/* Create the new index entries and the new record.
|
||||||
*/
|
*/
|
||||||
sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno, 1);
|
sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno, 1, -1);
|
||||||
|
|
||||||
/* Increment the row counter
|
/* Increment the row counter
|
||||||
*/
|
*/
|
||||||
|
210
src/vdbe.c
210
src/vdbe.c
@@ -36,7 +36,7 @@
|
|||||||
** in this file for details. If in doubt, do not deviate from existing
|
** in this file for details. If in doubt, do not deviate from existing
|
||||||
** commenting and indentation practices when changing or adding code.
|
** commenting and indentation practices when changing or adding code.
|
||||||
**
|
**
|
||||||
** $Id: vdbe.c,v 1.214 2003/04/15 14:01:43 drh Exp $
|
** $Id: vdbe.c,v 1.215 2003/04/15 19:22:24 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -79,6 +79,11 @@ typedef unsigned char Bool;
|
|||||||
**
|
**
|
||||||
** Every cursor that the virtual machine has open is represented by an
|
** Every cursor that the virtual machine has open is represented by an
|
||||||
** instance of the following structure.
|
** instance of the following structure.
|
||||||
|
**
|
||||||
|
** If the Cursor.isTriggerRow flag is set it means that this cursor is
|
||||||
|
** really a single row that represents the NEW or OLD pseudo-table of
|
||||||
|
** a row trigger. The data for the row is stored in Cursor.pData and
|
||||||
|
** the rowid is in Cursor.iKey.
|
||||||
*/
|
*/
|
||||||
struct Cursor {
|
struct Cursor {
|
||||||
BtCursor *pCursor; /* The cursor structure of the backend */
|
BtCursor *pCursor; /* The cursor structure of the backend */
|
||||||
@@ -90,7 +95,11 @@ struct Cursor {
|
|||||||
Bool useRandomRowid; /* Generate new record numbers semi-randomly */
|
Bool useRandomRowid; /* Generate new record numbers semi-randomly */
|
||||||
Bool nullRow; /* True if pointing to a row with no data */
|
Bool nullRow; /* True if pointing to a row with no data */
|
||||||
Bool nextRowidValid; /* True if the nextRowid field is valid */
|
Bool nextRowidValid; /* True if the nextRowid field is valid */
|
||||||
|
Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */
|
||||||
Btree *pBt; /* Separate file holding temporary table */
|
Btree *pBt; /* Separate file holding temporary table */
|
||||||
|
int nData; /* Number of bytes in pData */
|
||||||
|
char *pData; /* Data for a NEW or OLD pseudo-table */
|
||||||
|
int iKey; /* Key for the NEW or OLD pseudo-table row */
|
||||||
};
|
};
|
||||||
typedef struct Cursor Cursor;
|
typedef struct Cursor Cursor;
|
||||||
|
|
||||||
@@ -1123,6 +1132,7 @@ static void cleanupCursor(Cursor *pCx){
|
|||||||
if( pCx->pBt ){
|
if( pCx->pBt ){
|
||||||
sqliteBtreeClose(pCx->pBt);
|
sqliteBtreeClose(pCx->pBt);
|
||||||
}
|
}
|
||||||
|
sqliteFree(pCx->pData);
|
||||||
memset(pCx, 0, sizeof(Cursor));
|
memset(pCx, 0, sizeof(Cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3522,6 +3532,29 @@ case OP_OpenTemp: {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Opcode: OpenPseudo P1 * *
|
||||||
|
**
|
||||||
|
** Open a new cursor that points to a fake table that contains a single
|
||||||
|
** row of data. Any attempt to write a second row of data causes the
|
||||||
|
** first row to be deleted. All data is deleted when the cursor is
|
||||||
|
** closed.
|
||||||
|
**
|
||||||
|
** A pseudo-table created by this opcode is useful for holding the
|
||||||
|
** NEW or OLD tables in a trigger.
|
||||||
|
*/
|
||||||
|
case OP_OpenPseudo: {
|
||||||
|
int i = pOp->p1;
|
||||||
|
Cursor *pCx;
|
||||||
|
VERIFY( if( i<0 ) goto bad_instruction; )
|
||||||
|
if( expandCursorArraySize(p, i) ) goto no_mem;
|
||||||
|
pCx = &p->aCsr[i];
|
||||||
|
cleanupCursor(pCx);
|
||||||
|
memset(pCx, 0, sizeof(*pCx));
|
||||||
|
pCx->nullRow = 1;
|
||||||
|
pCx->pseudoTable = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Opcode: RenameCursor P1 P2 *
|
** Opcode: RenameCursor P1 P2 *
|
||||||
**
|
**
|
||||||
@@ -3550,7 +3583,7 @@ case OP_RenameCursor: {
|
|||||||
*/
|
*/
|
||||||
case OP_Close: {
|
case OP_Close: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){
|
if( i>=0 && i<p->nCursor ){
|
||||||
cleanupCursor(&p->aCsr[i]);
|
cleanupCursor(&p->aCsr[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -3584,7 +3617,9 @@ case OP_MoveTo: {
|
|||||||
Cursor *pC;
|
Cursor *pC;
|
||||||
|
|
||||||
VERIFY( if( tos<0 ) goto not_enough_stack; )
|
VERIFY( if( tos<0 ) goto not_enough_stack; )
|
||||||
if( i>=0 && i<p->nCursor && (pC = &p->aCsr[i])->pCursor!=0 ){
|
assert( i>=0 && i<p->nCursor );
|
||||||
|
pC = &p->aCsr[i];
|
||||||
|
if( pC->pCursor!=0 ){
|
||||||
int res, oc;
|
int res, oc;
|
||||||
if( aStack[tos].flags & STK_Int ){
|
if( aStack[tos].flags & STK_Int ){
|
||||||
int iKey = intToKey(aStack[tos].i);
|
int iKey = intToKey(aStack[tos].i);
|
||||||
@@ -3926,6 +3961,8 @@ case OP_NewRecno: {
|
|||||||
** entry is overwritten. The data is the value on the top of the
|
** entry is overwritten. The data is the value on the top of the
|
||||||
** stack. The key is the next value down on the stack. The key must
|
** stack. The key is the next value down on the stack. The key must
|
||||||
** be a string. The stack is popped twice by this instruction.
|
** be a string. The stack is popped twice by this instruction.
|
||||||
|
**
|
||||||
|
** P1 may not be a pseudo-table opened using the OpenPseudo opcode.
|
||||||
*/
|
*/
|
||||||
case OP_PutIntKey:
|
case OP_PutIntKey:
|
||||||
case OP_PutStrKey: {
|
case OP_PutStrKey: {
|
||||||
@@ -3934,7 +3971,8 @@ case OP_PutStrKey: {
|
|||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
Cursor *pC;
|
Cursor *pC;
|
||||||
VERIFY( if( nos<0 ) goto not_enough_stack; )
|
VERIFY( if( nos<0 ) goto not_enough_stack; )
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){
|
if( VERIFY( i>=0 && i<p->nCursor && )
|
||||||
|
((pC = &p->aCsr[i])->pCursor!=0 || pC->pseudoTable) ){
|
||||||
char *zKey;
|
char *zKey;
|
||||||
int nKey, iKey;
|
int nKey, iKey;
|
||||||
if( pOp->opcode==OP_PutStrKey ){
|
if( pOp->opcode==OP_PutStrKey ){
|
||||||
@@ -3954,8 +3992,30 @@ case OP_PutStrKey: {
|
|||||||
pC->nextRowidValid = 0;
|
pC->nextRowidValid = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc = sqliteBtreeInsert(pC->pCursor, zKey, nKey,
|
if( pC->pseudoTable ){
|
||||||
zStack[tos], aStack[tos].n);
|
/* PutStrKey does not work for pseudo-tables.
|
||||||
|
** The following assert makes sure we are not trying to use
|
||||||
|
** PutStrKey on a pseudo-table
|
||||||
|
*/
|
||||||
|
assert( pOp->opcode==OP_PutIntKey );
|
||||||
|
sqliteFree(pC->pData);
|
||||||
|
pC->iKey = iKey;
|
||||||
|
pC->nData = aStack[tos].n;
|
||||||
|
if( aStack[tos].flags & STK_Dyn ){
|
||||||
|
pC->pData = zStack[tos];
|
||||||
|
zStack[tos] = 0;
|
||||||
|
aStack[tos].flags = STK_Null;
|
||||||
|
}else{
|
||||||
|
pC->pData = sqliteMallocRaw( pC->nData );
|
||||||
|
if( pC->pData ){
|
||||||
|
memcpy(pC->pData, zStack[tos], pC->nData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pC->nullRow = 0;
|
||||||
|
}else{
|
||||||
|
rc = sqliteBtreeInsert(pC->pCursor, zKey, nKey,
|
||||||
|
zStack[tos], aStack[tos].n);
|
||||||
|
}
|
||||||
pC->recnoIsValid = 0;
|
pC->recnoIsValid = 0;
|
||||||
}
|
}
|
||||||
POPSTACK;
|
POPSTACK;
|
||||||
@@ -3974,11 +4034,15 @@ case OP_PutStrKey: {
|
|||||||
**
|
**
|
||||||
** The row change counter is incremented if P2==1 and is unmodified
|
** The row change counter is incremented if P2==1 and is unmodified
|
||||||
** if P2==0.
|
** if P2==0.
|
||||||
|
**
|
||||||
|
** If P1 is a pseudo-table, then this instruction is a no-op.
|
||||||
*/
|
*/
|
||||||
case OP_Delete: {
|
case OP_Delete: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
Cursor *pC;
|
Cursor *pC;
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){
|
assert( i>=0 && i<p->nCursor );
|
||||||
|
pC = &p->aCsr[i];
|
||||||
|
if( pC->pCursor!=0 ){
|
||||||
rc = sqliteBtreeDelete(pC->pCursor);
|
rc = sqliteBtreeDelete(pC->pCursor);
|
||||||
pC->nextRowidValid = 0;
|
pC->nextRowidValid = 0;
|
||||||
}
|
}
|
||||||
@@ -3995,8 +4059,61 @@ case OP_Delete: {
|
|||||||
*/
|
*/
|
||||||
case OP_KeyAsData: {
|
case OP_KeyAsData: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){
|
assert( i>=0 && i<p->nCursor );
|
||||||
p->aCsr[i].keyAsData = pOp->p2;
|
p->aCsr[i].keyAsData = pOp->p2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opcode: RowData P1 * *
|
||||||
|
**
|
||||||
|
** Push onto the stack the complete row data for cursor P1.
|
||||||
|
** There is no interpretation of the data. It is just copied
|
||||||
|
** onto the stack exactly as it is found in the database file.
|
||||||
|
**
|
||||||
|
** If the cursor is not pointing to a valid row, a NULL is pushed
|
||||||
|
** onto the stack.
|
||||||
|
*/
|
||||||
|
case OP_RowData: {
|
||||||
|
int i = pOp->p1;
|
||||||
|
int tos = ++p->tos;
|
||||||
|
Cursor *pC;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
assert( i>=0 && i<p->nCursor );
|
||||||
|
pC = &p->aCsr[i];
|
||||||
|
if( pC->nullRow ){
|
||||||
|
aStack[tos].flags = STK_Null;
|
||||||
|
}else if( pC->pCursor!=0 ){
|
||||||
|
BtCursor *pCrsr = pC->pCursor;
|
||||||
|
if( pC->nullRow ){
|
||||||
|
aStack[tos].flags = STK_Null;
|
||||||
|
break;
|
||||||
|
}else if( pC->keyAsData ){
|
||||||
|
sqliteBtreeKeySize(pCrsr, &n);
|
||||||
|
}else{
|
||||||
|
sqliteBtreeDataSize(pCrsr, &n);
|
||||||
|
}
|
||||||
|
aStack[tos].n = n;
|
||||||
|
if( n<=NBFS ){
|
||||||
|
aStack[tos].flags = STK_Str;
|
||||||
|
zStack[tos] = aStack[tos].z;
|
||||||
|
}else{
|
||||||
|
char *z = sqliteMallocRaw( n );
|
||||||
|
if( z==0 ) goto no_mem;
|
||||||
|
aStack[tos].flags = STK_Str | STK_Dyn;
|
||||||
|
zStack[tos] = z;
|
||||||
|
}
|
||||||
|
if( pC->keyAsData ){
|
||||||
|
sqliteBtreeKey(pCrsr, 0, n, zStack[tos]);
|
||||||
|
}else{
|
||||||
|
sqliteBtreeData(pCrsr, 0, n, zStack[tos]);
|
||||||
|
}
|
||||||
|
}else if( pC->pseudoTable ){
|
||||||
|
aStack[tos].n = pC->nData;
|
||||||
|
zStack[tos] = pC->pData;
|
||||||
|
aStack[tos].flags = STK_Str|STK_Ephem;
|
||||||
|
}else{
|
||||||
|
aStack[tos].flags = STK_Null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4031,12 +4148,13 @@ case OP_Column: {
|
|||||||
int idxWidth;
|
int idxWidth;
|
||||||
unsigned char aHdr[10];
|
unsigned char aHdr[10];
|
||||||
|
|
||||||
|
assert( i<p->nCursor );
|
||||||
if( i<0 ){
|
if( i<0 ){
|
||||||
VERIFY( if( tos+i<0 ) goto bad_instruction; )
|
VERIFY( if( tos+i<0 ) goto bad_instruction; )
|
||||||
VERIFY( if( (aStack[tos+i].flags & STK_Str)==0 ) goto bad_instruction; )
|
VERIFY( if( (aStack[tos+i].flags & STK_Str)==0 ) goto bad_instruction; )
|
||||||
zRec = zStack[tos+i];
|
zRec = zStack[tos+i];
|
||||||
payloadSize = aStack[tos+i].n;
|
payloadSize = aStack[tos+i].n;
|
||||||
}else if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){
|
}else if( (pC = &p->aCsr[i])->pCursor!=0 ){
|
||||||
zRec = 0;
|
zRec = 0;
|
||||||
pCrsr = pC->pCursor;
|
pCrsr = pC->pCursor;
|
||||||
if( pC->nullRow ){
|
if( pC->nullRow ){
|
||||||
@@ -4046,6 +4164,10 @@ case OP_Column: {
|
|||||||
}else{
|
}else{
|
||||||
sqliteBtreeDataSize(pCrsr, &payloadSize);
|
sqliteBtreeDataSize(pCrsr, &payloadSize);
|
||||||
}
|
}
|
||||||
|
}else if( pC->pseudoTable ){
|
||||||
|
payloadSize = pC->nData;
|
||||||
|
zRec = pC->pData;
|
||||||
|
assert( payloadSize==0 || zRec!=0 );
|
||||||
}else{
|
}else{
|
||||||
payloadSize = 0;
|
payloadSize = 0;
|
||||||
}
|
}
|
||||||
@@ -4135,22 +4257,24 @@ case OP_Column: {
|
|||||||
case OP_Recno: {
|
case OP_Recno: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
int tos = ++p->tos;
|
int tos = ++p->tos;
|
||||||
BtCursor *pCrsr;
|
Cursor *pC;
|
||||||
|
int v;
|
||||||
|
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
assert( i>=0 && i<p->nCursor );
|
||||||
int v;
|
if( (pC = &p->aCsr[i])->recnoIsValid ){
|
||||||
if( p->aCsr[i].recnoIsValid ){
|
v = pC->lastRecno;
|
||||||
v = p->aCsr[i].lastRecno;
|
}else if( pC->nullRow ){
|
||||||
}else if( p->aCsr[i].nullRow ){
|
aStack[tos].flags = STK_Null;
|
||||||
aStack[tos].flags = STK_Null;
|
break;
|
||||||
break;
|
}else if( pC->pseudoTable ){
|
||||||
}else{
|
v = keyToInt(pC->iKey);
|
||||||
sqliteBtreeKey(pCrsr, 0, sizeof(u32), (char*)&v);
|
}else{
|
||||||
v = keyToInt(v);
|
assert( pC->pCursor!=0 );
|
||||||
}
|
sqliteBtreeKey(pC->pCursor, 0, sizeof(u32), (char*)&v);
|
||||||
aStack[tos].i = v;
|
v = keyToInt(v);
|
||||||
aStack[tos].flags = STK_Int;
|
|
||||||
}
|
}
|
||||||
|
aStack[tos].i = v;
|
||||||
|
aStack[tos].flags = STK_Int;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4162,6 +4286,8 @@ case OP_Recno: {
|
|||||||
** Compare this opcode to Recno. The Recno opcode extracts the first
|
** Compare this opcode to Recno. The Recno opcode extracts the first
|
||||||
** 4 bytes of the key and pushes those bytes onto the stack as an
|
** 4 bytes of the key and pushes those bytes onto the stack as an
|
||||||
** integer. This instruction pushes the entire key as a string.
|
** integer. This instruction pushes the entire key as a string.
|
||||||
|
**
|
||||||
|
** This opcode may not be used on a pseudo-table.
|
||||||
*/
|
*/
|
||||||
case OP_FullKey: {
|
case OP_FullKey: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
@@ -4169,6 +4295,7 @@ case OP_FullKey: {
|
|||||||
BtCursor *pCrsr;
|
BtCursor *pCrsr;
|
||||||
|
|
||||||
VERIFY( if( !p->aCsr[i].keyAsData ) goto bad_instruction; )
|
VERIFY( if( !p->aCsr[i].keyAsData ) goto bad_instruction; )
|
||||||
|
VERIFY( if( p->aCsr[i].pseudoTable ) goto bad_instruction; )
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||||
int amt;
|
int amt;
|
||||||
char *z;
|
char *z;
|
||||||
@@ -4201,12 +4328,10 @@ case OP_FullKey: {
|
|||||||
*/
|
*/
|
||||||
case OP_NullRow: {
|
case OP_NullRow: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
BtCursor *pCrsr;
|
|
||||||
|
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
assert( i>=0 && i<p->nCursor );
|
||||||
p->aCsr[i].nullRow = 1;
|
p->aCsr[i].nullRow = 1;
|
||||||
p->aCsr[i].recnoIsValid = 0;
|
p->aCsr[i].recnoIsValid = 0;
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4220,15 +4345,20 @@ case OP_NullRow: {
|
|||||||
*/
|
*/
|
||||||
case OP_Last: {
|
case OP_Last: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
|
Cursor *pC;
|
||||||
BtCursor *pCrsr;
|
BtCursor *pCrsr;
|
||||||
|
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
assert( i>=0 && i<p->nCursor );
|
||||||
|
pC = &p->aCsr[i];
|
||||||
|
if( (pCrsr = pC->pCursor)!=0 ){
|
||||||
int res;
|
int res;
|
||||||
sqliteBtreeLast(pCrsr, &res);
|
sqliteBtreeLast(pCrsr, &res);
|
||||||
p->aCsr[i].nullRow = res;
|
p->aCsr[i].nullRow = res;
|
||||||
if( res && pOp->p2>0 ){
|
if( res && pOp->p2>0 ){
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
pC->nullRow = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4243,16 +4373,21 @@ case OP_Last: {
|
|||||||
*/
|
*/
|
||||||
case OP_Rewind: {
|
case OP_Rewind: {
|
||||||
int i = pOp->p1;
|
int i = pOp->p1;
|
||||||
|
Cursor *pC;
|
||||||
BtCursor *pCrsr;
|
BtCursor *pCrsr;
|
||||||
|
|
||||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
assert( i>=0 && i<p->nCursor );
|
||||||
|
pC = &p->aCsr[i];
|
||||||
|
if( (pCrsr = pC->pCursor)!=0 ){
|
||||||
int res;
|
int res;
|
||||||
sqliteBtreeFirst(pCrsr, &res);
|
sqliteBtreeFirst(pCrsr, &res);
|
||||||
p->aCsr[i].atFirst = res==0;
|
pC->atFirst = res==0;
|
||||||
p->aCsr[i].nullRow = res;
|
pC->nullRow = res;
|
||||||
if( res && pOp->p2>0 ){
|
if( res && pOp->p2>0 ){
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
pC->nullRow = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4279,8 +4414,9 @@ case OP_Next: {
|
|||||||
BtCursor *pCrsr;
|
BtCursor *pCrsr;
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPT;
|
CHECK_FOR_INTERRUPT;
|
||||||
if( VERIFY( pOp->p1>=0 && pOp->p1<p->nCursor && )
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||||
(pCrsr = (pC = &p->aCsr[pOp->p1])->pCursor)!=0 ){
|
pC = &p->aCsr[pOp->p1];
|
||||||
|
if( (pCrsr = pC->pCursor)!=0 ){
|
||||||
int res;
|
int res;
|
||||||
if( pC->nullRow ){
|
if( pC->nullRow ){
|
||||||
res = 1;
|
res = 1;
|
||||||
@@ -4293,8 +4429,10 @@ case OP_Next: {
|
|||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
sqlite_search_count++;
|
sqlite_search_count++;
|
||||||
}
|
}
|
||||||
pC->recnoIsValid = 0;
|
}else{
|
||||||
|
pC->nullRow = 1;
|
||||||
}
|
}
|
||||||
|
pC->recnoIsValid = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
# focus of this file is testing the magic ROWID column that is
|
# focus of this file is testing the magic ROWID column that is
|
||||||
# found on all tables.
|
# found on all tables.
|
||||||
#
|
#
|
||||||
# $Id: rowid.test,v 1.9 2003/04/15 14:01:44 drh Exp $
|
# $Id: rowid.test,v 1.10 2003/04/15 19:22:24 drh Exp $
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@@ -403,7 +403,7 @@ do_test rowid-8.8 {
|
|||||||
execsql {
|
execsql {
|
||||||
SELECT rowid, * FROM t4;
|
SELECT rowid, * FROM t4;
|
||||||
}
|
}
|
||||||
} {1 1 2 133 3 {}}
|
} {1 1 2 133 3 134}
|
||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
Reference in New Issue
Block a user