Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | 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) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
0b996959b8d8bc2c82eab9cccc190bef |
User & Date: | drh 2003-04-15 19:22:23.000 |
Context
2003-04-16
| ||
01:28 | Remove some unnecessary code and complication from the btree interface. (CVS 909) (check-in: 35cc7c7d37 user: drh tags: trunk) | |
2003-04-15
| ||
19:22 | 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) (check-in: 0b996959b8 user: drh tags: trunk) | |
17:22 | Added btree_rb.c (CVS 907) (check-in: 93eb6c52ac user: paul tags: trunk) | |
Changes
Changes to main.mk.
︙ | ︙ | |||
50 51 52 53 54 55 56 | # This is how we compile # TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src # Object files for the SQLite library. # | | > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | # This is how we compile # TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src # Object files for the SQLite library. # LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o delete.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 \ select.o table.o tokenize.o trigger.o update.o util.o \ vacuum.o vdbe.o where.o tclsqlite.o # All of the source code files. # SRC = \ $(TOP)/src/attach.c \ $(TOP)/src/auth.c \ $(TOP)/src/btree.c \ $(TOP)/src/btree.h \ $(TOP)/src/btree_rb.c \ $(TOP)/src/build.c \ $(TOP)/src/copy.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ |
︙ | ︙ | |||
160 161 162 163 164 165 166 167 168 169 170 171 172 173 | # lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c cp $(TOP)/tool/lempar.c . btree.o: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h $(TCCX) -c $(TOP)/src/btree.c build.o: $(TOP)/src/build.c $(HDR) $(TCCX) -c $(TOP)/src/build.c main.o: $(TOP)/src/main.c $(HDR) $(TCCX) -c $(TOP)/src/main.c | > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | # lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c cp $(TOP)/tool/lempar.c . btree.o: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h $(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) $(TCCX) -c $(TOP)/src/build.c main.o: $(TOP)/src/main.c $(HDR) $(TCCX) -c $(TOP)/src/main.c |
︙ | ︙ |
Changes to src/attach.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2003 April 6 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2003 April 6 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** ** $Id: attach.c,v 1.3 2003/04/15 19:22:23 drh Exp $ */ #include "sqliteInt.h" /* ** This routine is called by the parser to process an ATTACH statement: ** ** ATTACH DATABASE filename AS dbname |
︙ | ︙ | |||
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | } if( i<2 ){ sqliteErrorMsg(pParse, "cannot detach database %T", pDbname); return; } sqliteBtreeClose(db->aDb[i].pBt); db->aDb[i].pBt = 0; sqliteResetInternalSchema(db, i); db->nDb--; if( i<db->nDb ){ db->aDb[i] = db->aDb[db->nDb]; memset(&db->aDb[db->nDb], 0, sizeof(db->aDb[0])); sqliteResetInternalSchema(db, i); } } | > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | } if( i<2 ){ sqliteErrorMsg(pParse, "cannot detach database %T", pDbname); return; } sqliteBtreeClose(db->aDb[i].pBt); db->aDb[i].pBt = 0; sqliteFree(db->aDb[i].zName); sqliteResetInternalSchema(db, i); db->nDb--; if( i<db->nDb ){ db->aDb[i] = db->aDb[db->nDb]; memset(&db->aDb[db->nDb], 0, sizeof(db->aDb[0])); sqliteResetInternalSchema(db, i); } } |
Changes to src/btree_rb.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2003 Feb 4 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* | | < < > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /* ** 2003 Feb 4 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** $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 ** binary trees. ** ** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC. */ #define SQLITE_NO_BTREE_DEFS #include "btree.h" #include "sqliteInt.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 BtRbNode BtRbNode; typedef struct BtRollbackOp BtRollbackOp; /* Forward declarations */ static BtOps sqliteBtreeOps; |
︙ | ︙ | |||
561 562 563 564 565 566 567 | pRollbackOp->pNext = pBtree->pCheckRollback; pBtree->pCheckRollback = pRollbackOp; } } int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree) { | < | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 | pRollbackOp->pNext = pBtree->pCheckRollback; pBtree->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); /* Create binary trees for tables 0, 1 and 2. SQLite assumes these * tables always exist. At least I think so? */ btreeCreateTable(*ppBtree, 0); btreeCreateTable(*ppBtree, 1); |
︙ | ︙ | |||
586 587 588 589 590 591 592 | /* * Create a new table in the supplied Btree. Set *n to the new table number. * Return SQLITE_OK if the operation is a success. */ static int sqliteBtreeCreateTable(Btree* tree, int* n) { | < | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | /* * Create a new table in the supplied Btree. Set *n to the new table number. * Return SQLITE_OK if the operation is a success. */ static int sqliteBtreeCreateTable(Btree* tree, int* n) { assert( tree->eTransState != TRANS_NONE ); *n = tree->next_idx++; btreeCreateTable(tree, *n); /* Set up the rollback structure (if we are not doing this as part of a * rollback) */ |
︙ | ︙ | |||
686 687 688 689 690 691 692 | * is left pointing at the new record. * * If the key exists already in the tree, just replace the data. */ static int sqliteBtreeInsert(BtCursor* pCur, const void *pKey, int nKey, const void *pDataInput, int nData) { | < | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 | * is left pointing at the new record. * * If the key exists already in the tree, just replace the data. */ static int sqliteBtreeInsert(BtCursor* 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 ); /* Take a copy of the input data now, in case we need it for the |
︙ | ︙ | |||
1157 1158 1159 1160 1161 1162 1163 | /* * Close the supplied Btree. Delete everything associated with it. */ static int sqliteBtreeClose(Btree* tree) { HashElem *p; for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){ | < | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 | /* * Close the supplied Btree. Delete everything associated with it. */ static int sqliteBtreeClose(Btree* tree) { HashElem *p; for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){ tree->eTransState = TRANS_ROLLBACK; sqliteBtreeClearTable(tree, sqliteHashKeysize(p)); sqliteFree(sqliteHashData(p)); } sqliteFree(tree); return SQLITE_OK; } |
︙ | ︙ | |||
1219 1220 1221 1222 1223 1224 1225 | /* * Execute and delete the supplied rollback-list on pBtree. */ static void execute_rollback_list(Btree *pBtree, BtRollbackOp *pList) { BtRollbackOp *pTmp; BtCursor cur; | | | | 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 | /* * Execute and delete the supplied rollback-list on pBtree. */ static void execute_rollback_list(Btree *pBtree, BtRollbackOp *pList) { BtRollbackOp *pTmp; BtCursor cur; int res; cur.pBtree = pBtree; while( pList ){ switch( pList->eOp ){ case ROLLBACK_INSERT: cur.pTree = sqliteHashFind( &pBtree->tblHash, 0, pList->iTab ); assert(cur.pTree); cur.iTree = pList->iTab; cur.eSkip = SKIP_NONE; |
︙ | ︙ | |||
1387 1388 1389 1390 1391 1392 1393 | sqliteBtreeData, sqliteBtreeCloseCursor, #ifdef SQLITE_TEST sqliteBtreeCursorDump, #endif }; | > > | 1389 1390 1391 1392 1393 1394 1395 1396 1397 | sqliteBtreeData, sqliteBtreeCloseCursor, #ifdef SQLITE_TEST sqliteBtreeCursorDump, #endif }; #endif /* SQLITE_OMIT_INMEMORYDB */ |
Changes to src/copy.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2003 April 6 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the COPY command. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2003 April 6 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the COPY command. ** ** $Id: copy.c,v 1.2 2003/04/15 19:22:23 drh Exp $ */ #include "sqliteInt.h" /* ** The COPY command is for compatibility with PostgreSQL and specificially ** for the ability to read the output of pg_dump. The format is as ** follows: |
︙ | ︙ | |||
90 91 92 93 94 95 96 | ** value is always pulled from the record number */ sqliteVdbeAddOp(v, OP_String, 0, 0); }else{ sqliteVdbeAddOp(v, OP_FileColumn, i, 0); } } sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, 0, 0, onError, addr); | | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | ** value is always pulled from the record number */ sqliteVdbeAddOp(v, OP_String, 0, 0); }else{ sqliteVdbeAddOp(v, OP_FileColumn, i, 0); } } sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, 0, 0, onError, addr); sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0, -1); if( (db->flags & SQLITE_CountRows)!=0 ){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */ } sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeResolveLabel(v, end); sqliteVdbeAddOp(v, OP_Noop, 0, 0); sqliteEndWriteOperation(pParse); |
︙ | ︙ |
Changes to src/delete.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** ** $Id: delete.c,v 1.51 2003/04/15 19:22:23 drh Exp $ */ #include "sqliteInt.h" /* ** Look up every table that is named in pSrc. If any table is not found, ** add an error message to pParse->zErrMsg and return NULL. If all tables ** are found, return a pointer to the last table. |
︙ | ︙ | |||
67 68 69 70 71 72 73 | int end, addr; /* A couple addresses of generated code */ int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int base; /* Index of the first available table cursor */ sqlite *db; /* Main database structure */ | | > > | < | | | | > | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | int end, addr; /* A couple addresses of generated code */ int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int base; /* Index of the first available table cursor */ sqlite *db; /* Main database structure */ int row_triggers_exist = 0; /* True if any triggers exist */ 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 ){ pTabList = 0; goto delete_from_cleanup; } db = pParse->db; assert( pTabList->nSrc==1 ); /* Check for the special case of a VIEW with one or more ON DELETE triggers ** defined */ zTab = pTabList->a[0].zName; zDb = pTabList->a[0].zDatabase; if( zTab != 0 ){ pTab = sqliteFindTable(pParse->db, zTab, zDb); if( pTab ){ before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_DELETE, TK_BEFORE, TK_ROW, 0); after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_DELETE, TK_AFTER, TK_ROW, 0); row_triggers_exist = before_triggers || after_triggers; } if( row_triggers_exist && pTab->pSelect ){ /* Just fire VIEW triggers */ sqliteSrcListDelete(pTabList); sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0); return; } |
︙ | ︙ | |||
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); } /* End the database scan loop. */ sqliteWhereEnd(pWInfo); /* Delete every item whose key was written to the list during the ** database scan. We have to delete items after the scan is complete ** because deleting an item can change the scan order. */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); end = sqliteVdbeMakeLabel(v); /* This is the beginning of the delete loop when there are ** row triggers. */ if( row_triggers_exist ){ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); | > > > > > > < < < < | < | < < < < | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); } /* End the database scan loop. */ 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 ** database scan. We have to delete items after the scan is complete ** because deleting an item can change the scan order. */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); end = sqliteVdbeMakeLabel(v); /* This is the beginning of the delete loop when there are ** row triggers. */ if( row_triggers_exist ){ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteVdbeAddOp(v, OP_Recno, base, 0); sqliteVdbeAddOp(v, OP_RowData, base, 0); sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0); sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, addr); } /* Open cursors for the table we are deleting from and all its |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** ** $Id: expr.c,v 1.92 2003/04/15 19:22:23 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** Construct a new expression node and return a pointer to it. Memory ** for this node is obtained from sqliteMalloc(). The calling function |
︙ | ︙ | |||
543 544 545 546 547 548 549 | } } if( 0==(cntTab++) ) pExpr->iTable = i + base; for(j=0; j<pTab->nCol; j++){ if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ cnt++; pExpr->iTable = i + base; | < | < < | < | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | } } if( 0==(cntTab++) ) pExpr->iTable = i + base; for(j=0; j<pTab->nCol; j++){ if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ cnt++; pExpr->iTable = i + base; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr->iColumn = j==pTab->iPKey ? -1 : j; pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK; } } } /* If we have not already resolved this *.* expression, then maybe * it is a new.* or old.* trigger argument reference */ |
︙ | ︙ | |||
576 577 578 579 580 581 582 | if( t ){ int j; Table *pTab = pTriggerStack->pTab; for(j=0; j < pTab->nCol; j++) { if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ cnt++; | | | 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 | if( t ){ int j; Table *pTab = pTriggerStack->pTab; for(j=0; j < pTab->nCol; j++) { if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ cnt++; pExpr->iColumn = j==pTab->iPKey ? -1 : j; pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK; } } } } if( cnt==0 && cntTab==1 && sqliteIsRowid(zRight) ){ |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** ** $Id: insert.c,v 1.79 2003/04/15 19:22:23 drh Exp $ */ #include "sqliteInt.h" /* ** This routine is call to handle SQL of the following forms: ** ** insert into TABLE (IDLIST) values(EXPRLIST) |
︙ | ︙ | |||
106 107 108 109 110 111 112 | int srcTab; /* Data comes from this temporary cursor if >=0 */ int iSelectLoop; /* Address of code that implements the SELECT */ int iCleanup; /* Address of the cleanup code */ int iInsertBlock; /* Address of the subroutine used to insert data */ int iCntMem; /* Memory cell used for the row counter */ int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ | > > | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | int srcTab; /* Data comes from this temporary cursor if >=0 */ int iSelectLoop; /* Address of code that implements the SELECT */ int iCleanup; /* Address of the cleanup code */ int iInsertBlock; /* Address of the subroutine used to insert data */ int iCntMem; /* Memory cell used for the row counter */ int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ 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; db = pParse->db; /* Locate the table into which we will be inserting new information. */ assert( pTabList->nSrc==1 ); |
︙ | ︙ | |||
128 129 130 131 132 133 134 | goto insert_cleanup; } /* Ensure that: * (a) the table is not read-only, * (b) that if it is a view then ON INSERT triggers exist */ | < | | | > > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | goto insert_cleanup; } /* Ensure that: * (a) the table is not read-only, * (b) that if it is a view then ON INSERT triggers exist */ before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT, TK_BEFORE, TK_ROW, 0); after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT, TK_AFTER, TK_ROW, 0); row_triggers_exist = before_triggers || after_triggers; if( pTab->readOnly || (pTab->pSelect && !row_triggers_exist) ){ sqliteErrorMsg(pParse, "%s %s may not be modified", pTab->pSelect ? "view" : "table", zTab); goto insert_cleanup; } |
︙ | ︙ | |||
307 308 309 310 311 312 313 | if( pColumn==0 ){ keyColumn = pTab->iPKey; } /* Open the temp table for FOR EACH ROW triggers */ if( row_triggers_exist ){ | | | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | if( pColumn==0 ){ keyColumn = pTab->iPKey; } /* Open the temp table for FOR EACH ROW triggers */ if( row_triggers_exist ){ sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0); } /* Initialize the count of rows to be inserted */ if( db->flags & SQLITE_CountRows ){ iCntMem = pParse->nMem++; sqliteVdbeAddOp(v, OP_Integer, 0, 0); |
︙ | ︙ | |||
346 347 348 349 350 351 352 353 | sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak); iCont = sqliteVdbeCurrentAddr(v); }else if( pSelect ){ sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop); sqliteVdbeResolveLabel(v, iInsertBlock); } endOfLoop = sqliteVdbeMakeLabel(v); | > > | | > > > > > > | > > > > > > > > > > > > > > | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak); iCont = sqliteVdbeCurrentAddr(v); }else if( pSelect ){ sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop); sqliteVdbeResolveLabel(v, iInsertBlock); } /* Run the BEFORE triggers, if there are any */ endOfLoop = sqliteVdbeMakeLabel(v); if( before_triggers ){ /* build the NEW.* reference row. Note that if there is an INTEGER ** 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++){ if( pColumn==0 ){ j = i; }else{ for(j=0; j<pColumn->nId; j++){ if( pColumn->a[j].idx==i ) break; } |
︙ | ︙ | |||
379 380 381 382 383 384 385 | sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); /* Fire BEFORE triggers */ if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1, onError, endOfLoop) ){ goto insert_cleanup; } | | | > > > > | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); /* Fire BEFORE triggers */ if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1, onError, endOfLoop) ){ goto insert_cleanup; } } /* If any triggers exists, the opening of tables and indices is deferred ** until now. */ if( row_triggers_exist ){ if( !pTab->pSelect ){ base = pParse->nTab; sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); |
︙ | ︙ | |||
455 456 457 458 459 460 461 | } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop); | | > | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop); sqliteCompleteInsertion(pParse, pTab, base, 0,0,0, after_triggers ? newIdx : -1); /* Update the count of rows that are inserted */ if( (db->flags & SQLITE_CountRows)!=0 ){ sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0); } } |
︙ | ︙ | |||
803 804 805 806 807 808 809 | */ void sqliteCompleteInsertion( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int base, /* Index of a read/write cursor pointing at pTab */ char *aIdxUsed, /* Which indices are used. NULL means all are used */ int recnoChng, /* True if the record number will change */ | | > > > > > > | 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 | */ void sqliteCompleteInsertion( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int base, /* Index of a read/write cursor pointing at pTab */ char *aIdxUsed, /* Which indices are used. NULL means all are used */ int recnoChng, /* True if the record number will change */ int isUpdate, /* True for UPDATE, False for INSERT */ int newIdx /* Index of NEW table for triggers. -1 if none */ ){ int i; Vdbe *v; int nIdx; Index *pIdx; v = sqliteGetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} for(i=nIdx-1; i>=0; i--){ if( aIdxUsed && aIdxUsed[i]==0 ) continue; sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 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); if( isUpdate && recnoChng ){ sqliteVdbeAddOp(v, OP_Pop, 1, 0); } } |
Changes to src/sqliteInt.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** ** @(#) $Id: sqliteInt.h,v 1.173 2003/04/15 19:22:24 drh Exp $ */ #include "config.h" #include "sqlite.h" #include "hash.h" #include "vdbe.h" #include "parse.h" #include "btree.h" |
︙ | ︙ | |||
81 82 83 84 85 86 87 | /* ** When building SQLite for embedded systems where memory is scarce, ** you can define one or more of the following macros to omit extra ** features of the library and thus keep the size of the library to ** a minimum. */ /* #define SQLITE_OMIT_AUTHORIZATION 1 */ | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | /* ** When building SQLite for embedded systems where memory is scarce, ** you can define one or more of the following macros to omit extra ** features of the library and thus keep the size of the library to ** a minimum. */ /* #define SQLITE_OMIT_AUTHORIZATION 1 */ /* #define SQLITE_OMIT_INMEMORYDB 1 */ /* #define SQLITE_OMIT_TRACE 1 */ /* #define SQLITE_OMIT_VACUUM 1 */ /* ** Integers of known sizes. These typedefs might change for architectures ** where the sizes very. Preprocessor macros are available so that the ** types can be conveniently redefined at compile-type. Like this: |
︙ | ︙ | |||
1084 1085 1086 1087 1088 1089 1090 | void sqliteRollbackTransaction(Parse*); int sqliteExprIsConstant(Expr*); int sqliteExprIsInteger(Expr*, int*); int sqliteIsRowid(const char*); void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int); void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); | | | 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 | void sqliteRollbackTransaction(Parse*); int sqliteExprIsConstant(Expr*); int sqliteExprIsInteger(Expr*, int*); int sqliteIsRowid(const char*); void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int); void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int, int); void sqliteBeginWriteOperation(Parse*, int, int); void sqliteEndWriteOperation(Parse*); Expr *sqliteExprDup(Expr*); void sqliteTokenCopy(Token*, Token*); ExprList *sqliteExprListDup(ExprList*); SrcList *sqliteSrcListDup(SrcList*); IdList *sqliteIdListDup(IdList*); |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
710 711 712 713 714 715 716 | v = sqliteGetVdbe(pParse); assert(v); sqliteBeginWriteOperation(pParse, 1, 0); /* Allocate temp tables */ oldIdx = pParse->nTab++; | | | | 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 | v = sqliteGetVdbe(pParse); assert(v); sqliteBeginWriteOperation(pParse, 1, 0); /* Allocate temp tables */ oldIdx = pParse->nTab++; sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); if( pChanges ){ newIdx = pParse->nTab++; sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0); } /* Snapshot the view */ if( sqliteSelect(pParse, &theSelect, SRT_Table, oldIdx, 0, 0, 0) ){ goto trigger_cleanup; } |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** ** $Id: update.c,v 1.59 2003/04/15 19:22:24 drh Exp $ */ #include "sqliteInt.h" /* ** Process an UPDATE statement. */ void sqliteUpdate( |
︙ | ︙ | |||
43 44 45 46 47 48 49 | int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ int chngRecno; /* True if the record number is being changed */ Expr *pRecnoExpr; /* Expression defining the new record number */ int openAll; /* True if all indices need to be opened */ | > > | | | > > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ int chngRecno; /* True if the record number is being changed */ Expr *pRecnoExpr; /* Expression defining the new record number */ int openAll; /* True if all indices need to be opened */ 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 oldIdx = -1; /* index of trigger "old" temp table */ if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup; db = pParse->db; assert( pTabList->nSrc==1 ); /* Check for the special case of a VIEW with one or more ON UPDATE triggers * defined */ zTab = pTabList->a[0].zName; zDb = pTabList->a[0].zDatabase; if( zTab != 0 ){ pTab = sqliteFindTable(pParse->db, zTab, zDb); if( pTab ){ before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_UPDATE, TK_BEFORE, TK_ROW, pChanges); after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_UPDATE, TK_AFTER, TK_ROW, pChanges); row_triggers_exist = before_triggers || after_triggers; } if( row_triggers_exist && pTab->pSelect ){ /* Just fire VIEW triggers */ sqliteSrcListDelete(pTabList); sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges); return; |
︙ | ︙ | |||
215 216 217 218 219 220 221 | /* Initialize the count of updated rows */ if( db->flags & SQLITE_CountRows && !pParse->trigStack ){ sqliteVdbeAddOp(v, OP_Integer, 0, 0); } if( row_triggers_exist ){ | | | | | > > > > > > > | < < | < | | > > > > | < | | < | < | | | < | > > > | | < | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | /* Initialize the count of updated rows */ if( db->flags & SQLITE_CountRows && !pParse->trigStack ){ sqliteVdbeAddOp(v, OP_Integer, 0, 0); } if( row_triggers_exist ){ /* Create pseudo-tables for NEW and OLD */ sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0); /* The top of the update loop for when there are triggers. */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); addr = sqliteVdbeAddOp(v, OP_ListRead, 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_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); /* Generate the OLD table */ sqliteVdbeAddOp(v, OP_Recno, base, 0); sqliteVdbeAddOp(v, OP_RowData, base, 0); sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0); /* Generate the NEW table */ if( chngRecno ){ sqliteExprCode(pParse, pRecnoExpr); }else{ sqliteVdbeAddOp(v, OP_Recno, base, 0); } 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{ sqliteExprCode(pParse, pChanges->a[j].pExpr); } } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); sqliteVdbeAddOp(v, OP_Close, base, 0); /* Fire the BEFORE triggers */ if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, newIdx, oldIdx, onError, addr) ){ goto update_cleanup; } } /* Rewind the list of records that need to be updated and |
︙ | ︙ | |||
346 347 348 349 350 351 352 | */ if( chngRecno ){ sqliteVdbeAddOp(v, OP_Delete, base, 0); } /* Create the new index entries and the new record. */ | | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | */ if( chngRecno ){ sqliteVdbeAddOp(v, OP_Delete, base, 0); } /* Create the new index entries and the new record. */ sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno, 1, -1); /* Increment the row counter */ if( db->flags & SQLITE_CountRows && !pParse->trigStack){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); } |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | ** ** Various scripts scan this source file in order to generate HTML ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ** ** Various scripts scan this source file in order to generate HTML ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** ** $Id: vdbe.c,v 1.215 2003/04/15 19:22:24 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** The makefile scans this source file and creates the following ** array of string constants which are the names of all VDBE opcodes. |
︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | ** The cursor can seek to a BTree entry with a particular key, or ** loop over all entries of the Btree. You can also insert new BTree ** entries or retrieve the key or data from the entry that the cursor ** is currently pointing to. ** ** Every cursor that the virtual machine has open is represented by an ** instance of the following structure. */ struct Cursor { BtCursor *pCursor; /* The cursor structure of the backend */ int lastRecno; /* Last recno from a Next or NextIdx operation */ int nextRowid; /* Next rowid returned by OP_NewRowid */ Bool recnoIsValid; /* True if lastRecno is valid */ Bool keyAsData; /* The OP_Column command works on key instead of data */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ Bool nextRowidValid; /* True if the nextRowid field is valid */ Btree *pBt; /* Separate file holding temporary table */ }; typedef struct Cursor Cursor; /* ** A sorter builds a list of elements to be sorted. Each element of ** the list is an instance of the following structure. */ | > > > > > > > > > | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | ** The cursor can seek to a BTree entry with a particular key, or ** loop over all entries of the Btree. You can also insert new BTree ** entries or retrieve the key or data from the entry that the cursor ** is currently pointing to. ** ** Every cursor that the virtual machine has open is represented by an ** 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 { BtCursor *pCursor; /* The cursor structure of the backend */ int lastRecno; /* Last recno from a Next or NextIdx operation */ int nextRowid; /* Next rowid returned by OP_NewRowid */ Bool recnoIsValid; /* True if lastRecno is valid */ Bool keyAsData; /* The OP_Column command works on key instead of data */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ 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 */ 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; /* ** A sorter builds a list of elements to be sorted. Each element of ** the list is an instance of the following structure. */ |
︙ | ︙ | |||
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 | static void cleanupCursor(Cursor *pCx){ if( pCx->pCursor ){ sqliteBtreeCloseCursor(pCx->pCursor); } if( pCx->pBt ){ sqliteBtreeClose(pCx->pBt); } memset(pCx, 0, sizeof(Cursor)); } /* ** Close all cursors */ static void closeAllCursors(Vdbe *p){ | > | 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 | static void cleanupCursor(Cursor *pCx){ if( pCx->pCursor ){ sqliteBtreeCloseCursor(pCx->pCursor); } if( pCx->pBt ){ sqliteBtreeClose(pCx->pBt); } sqliteFree(pCx->pData); memset(pCx, 0, sizeof(Cursor)); } /* ** Close all cursors */ static void closeAllCursors(Vdbe *p){ |
︙ | ︙ | |||
3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 | } }else{ rc = sqliteBtreeCursor(pCx->pBt, 2, 1, &pCx->pCursor); } } break; } /* ** Opcode: RenameCursor P1 P2 * ** ** Rename cursor number P1 as cursor number P2. If P2 was previously ** opened is is closed before the renaming occurs. */ | > > > > > > > > > > > > > > > > > > > > > > > | 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 | } }else{ rc = sqliteBtreeCursor(pCx->pBt, 2, 1, &pCx->pCursor); } } 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 * ** ** Rename cursor number P1 as cursor number P2. If P2 was previously ** opened is is closed before the renaming occurs. */ |
︙ | ︙ | |||
3546 3547 3548 3549 3550 3551 3552 | /* Opcode: Close P1 * * ** ** Close a cursor previously opened as P1. If P1 is not ** currently open, this instruction is a no-op. */ case OP_Close: { int i = pOp->p1; | | | 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 | /* Opcode: Close P1 * * ** ** Close a cursor previously opened as P1. If P1 is not ** currently open, this instruction is a no-op. */ case OP_Close: { int i = pOp->p1; if( i>=0 && i<p->nCursor ){ cleanupCursor(&p->aCsr[i]); } break; } /* Opcode: MoveTo P1 P2 * ** |
︙ | ︙ | |||
3580 3581 3582 3583 3584 3585 3586 | case OP_MoveLt: case OP_MoveTo: { int i = pOp->p1; int tos = p->tos; Cursor *pC; VERIFY( if( tos<0 ) goto not_enough_stack; ) | | > > | 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 | case OP_MoveLt: case OP_MoveTo: { int i = pOp->p1; int tos = p->tos; Cursor *pC; VERIFY( if( tos<0 ) goto not_enough_stack; ) assert( i>=0 && i<p->nCursor ); pC = &p->aCsr[i]; if( pC->pCursor!=0 ){ int res, oc; if( aStack[tos].flags & STK_Int ){ int iKey = intToKey(aStack[tos].i); sqliteBtreeMoveto(pC->pCursor, (char*)&iKey, sizeof(int), &res); pC->lastRecno = aStack[tos].i; pC->recnoIsValid = res==0; }else{ |
︙ | ︙ | |||
3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 | /* Opcode: PutStrKey P1 * * ** ** Write an entry into the database file P1. A new entry is ** created if it doesn't already exist or the data for an existing ** 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 ** be a string. The stack is popped twice by this instruction. */ case OP_PutIntKey: case OP_PutStrKey: { int tos = p->tos; int nos = p->tos-1; int i = pOp->p1; Cursor *pC; VERIFY( if( nos<0 ) goto not_enough_stack; ) | > > | > > > > > > > > > > > > > > > > > > > > > > | | > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 | /* Opcode: PutStrKey P1 * * ** ** Write an entry into the database file P1. A new entry is ** created if it doesn't already exist or the data for an existing ** 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 ** 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_PutStrKey: { int tos = p->tos; int nos = p->tos-1; int i = pOp->p1; Cursor *pC; VERIFY( if( nos<0 ) goto not_enough_stack; ) if( VERIFY( i>=0 && i<p->nCursor && ) ((pC = &p->aCsr[i])->pCursor!=0 || pC->pseudoTable) ){ char *zKey; int nKey, iKey; if( pOp->opcode==OP_PutStrKey ){ Stringify(p, nos); nKey = aStack[nos].n; zKey = zStack[nos]; }else{ assert( aStack[nos].flags & STK_Int ); nKey = sizeof(int); iKey = intToKey(aStack[nos].i); zKey = (char*)&iKey; if( pOp->p2 ){ db->nChange++; db->lastRowid = aStack[nos].i; } if( pC->nextRowidValid && aStack[nos].i>=pC->nextRowid ){ pC->nextRowidValid = 0; } } if( pC->pseudoTable ){ /* 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; } POPSTACK; POPSTACK; break; } /* Opcode: Delete P1 P2 * ** ** Delete the record at which the P1 cursor is currently pointing. ** ** The cursor will be left pointing at either the next or the previous ** record in the table. If it is left pointing at the next record, then ** the next Next instruction will be a no-op. Hence it is OK to delete ** a record from within an Next loop. ** ** The row change counter is incremented if P2==1 and is unmodified ** if P2==0. ** ** If P1 is a pseudo-table, then this instruction is a no-op. */ case OP_Delete: { int i = pOp->p1; Cursor *pC; assert( i>=0 && i<p->nCursor ); pC = &p->aCsr[i]; if( pC->pCursor!=0 ){ rc = sqliteBtreeDelete(pC->pCursor); pC->nextRowidValid = 0; } if( pOp->p2 ) db->nChange++; break; } /* Opcode: KeyAsData P1 P2 * ** ** Turn the key-as-data mode for cursor P1 either on (if P2==1) or ** off (if P2==0). In key-as-data mode, the Field opcode pulls ** data off of the key rather than the data. This is useful for ** processing compound selects. */ case OP_KeyAsData: { int i = pOp->p1; assert( i>=0 && i<p->nCursor ); 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; } /* Opcode: Column P1 P2 * ** ** Interpret the data that cursor P1 points to as |
︙ | ︙ | |||
4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 | int tos = p->tos+1; Cursor *pC; char *zRec; BtCursor *pCrsr; int idxWidth; unsigned char aHdr[10]; if( i<0 ){ VERIFY( if( tos+i<0 ) goto bad_instruction; ) VERIFY( if( (aStack[tos+i].flags & STK_Str)==0 ) goto bad_instruction; ) zRec = zStack[tos+i]; payloadSize = aStack[tos+i].n; | > | > > > > | 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 | int tos = p->tos+1; Cursor *pC; char *zRec; BtCursor *pCrsr; int idxWidth; unsigned char aHdr[10]; assert( i<p->nCursor ); if( i<0 ){ VERIFY( if( tos+i<0 ) goto bad_instruction; ) VERIFY( if( (aStack[tos+i].flags & STK_Str)==0 ) goto bad_instruction; ) zRec = zStack[tos+i]; payloadSize = aStack[tos+i].n; }else if( (pC = &p->aCsr[i])->pCursor!=0 ){ zRec = 0; pCrsr = pC->pCursor; if( pC->nullRow ){ payloadSize = 0; }else if( pC->keyAsData ){ sqliteBtreeKeySize(pCrsr, &payloadSize); }else{ sqliteBtreeDataSize(pCrsr, &payloadSize); } }else if( pC->pseudoTable ){ payloadSize = pC->nData; zRec = pC->pData; assert( payloadSize==0 || zRec!=0 ); }else{ payloadSize = 0; } /* Figure out how many bytes in the column data and where the column ** data begins. */ |
︙ | ︙ | |||
4131 4132 4133 4134 4135 4136 4137 | ** the key to the current entry in a sequential scan of the database ** file P1. The sequential scan should have been started using the ** Next opcode. */ case OP_Recno: { int i = pOp->p1; int tos = ++p->tos; | | > | < | | | | | > > | > | | | | | < > > > | 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 | ** the key to the current entry in a sequential scan of the database ** file P1. The sequential scan should have been started using the ** Next opcode. */ case OP_Recno: { int i = pOp->p1; int tos = ++p->tos; Cursor *pC; int v; assert( i>=0 && i<p->nCursor ); if( (pC = &p->aCsr[i])->recnoIsValid ){ v = pC->lastRecno; }else if( pC->nullRow ){ aStack[tos].flags = STK_Null; break; }else if( pC->pseudoTable ){ v = keyToInt(pC->iKey); }else{ assert( pC->pCursor!=0 ); sqliteBtreeKey(pC->pCursor, 0, sizeof(u32), (char*)&v); v = keyToInt(v); } aStack[tos].i = v; aStack[tos].flags = STK_Int; break; } /* Opcode: FullKey P1 * * ** ** Extract the complete key from the record that cursor P1 is currently ** pointing to and push the key onto the stack as a string. ** ** 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 ** integer. This instruction pushes the entire key as a string. ** ** This opcode may not be used on a pseudo-table. */ case OP_FullKey: { int i = pOp->p1; int tos = ++p->tos; BtCursor *pCrsr; 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 ){ int amt; char *z; sqliteBtreeKeySize(pCrsr, &amt); if( amt<=0 ){ rc = SQLITE_CORRUPT; |
︙ | ︙ | |||
4197 4198 4199 4200 4201 4202 4203 | ** ** Move the cursor P1 to a null row. Any OP_Column operations ** that occur while the cursor is on the null row will always push ** a NULL onto the stack. */ case OP_NullRow: { int i = pOp->p1; | < | | | < > | > > > > > | > > | | > > | 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 | ** ** Move the cursor P1 to a null row. Any OP_Column operations ** that occur while the cursor is on the null row will always push ** a NULL onto the stack. */ case OP_NullRow: { int i = pOp->p1; assert( i>=0 && i<p->nCursor ); p->aCsr[i].nullRow = 1; p->aCsr[i].recnoIsValid = 0; break; } /* Opcode: Last P1 P2 * ** ** The next use of the Recno or Column or Next instruction for P1 ** will refer to the last entry in the database table or index. ** If the table or index is empty and P2>0, then jump immediately to P2. ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ case OP_Last: { int i = pOp->p1; Cursor *pC; BtCursor *pCrsr; assert( i>=0 && i<p->nCursor ); pC = &p->aCsr[i]; if( (pCrsr = pC->pCursor)!=0 ){ int res; sqliteBtreeLast(pCrsr, &res); p->aCsr[i].nullRow = res; if( res && pOp->p2>0 ){ pc = pOp->p2 - 1; } }else{ pC->nullRow = 0; } break; } /* Opcode: Rewind P1 P2 * ** ** The next use of the Recno or Column or Next instruction for P1 ** will refer to the first entry in the database table or index. ** If the table or index is empty and P2>0, then jump immediately to P2. ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ case OP_Rewind: { int i = pOp->p1; Cursor *pC; BtCursor *pCrsr; assert( i>=0 && i<p->nCursor ); pC = &p->aCsr[i]; if( (pCrsr = pC->pCursor)!=0 ){ int res; sqliteBtreeFirst(pCrsr, &res); pC->atFirst = res==0; pC->nullRow = res; if( res && pOp->p2>0 ){ pc = pOp->p2 - 1; } }else{ pC->nullRow = 0; } break; } /* Opcode: Next P1 P2 * ** ** Advance cursor P1 so that it points to the next key/data pair in its |
︙ | ︙ | |||
4275 4276 4277 4278 4279 4280 4281 | */ case OP_Prev: case OP_Next: { Cursor *pC; BtCursor *pCrsr; CHECK_FOR_INTERRUPT; | | | > > | > | 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 | */ case OP_Prev: case OP_Next: { Cursor *pC; BtCursor *pCrsr; CHECK_FOR_INTERRUPT; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = &p->aCsr[pOp->p1]; if( (pCrsr = pC->pCursor)!=0 ){ int res; if( pC->nullRow ){ res = 1; }else{ rc = pOp->opcode==OP_Next ? sqliteBtreeNext(pCrsr, &res) : sqliteBtreePrevious(pCrsr, &res); pC->nullRow = res; } if( res==0 ){ pc = pOp->p2 - 1; sqlite_search_count++; } }else{ pC->nullRow = 1; } pC->recnoIsValid = 0; break; } /* Opcode: IdxPut P1 P2 P3 ** ** The top of the stack hold an SQL index key made using the ** MakeIdxKey instruction. This opcode writes that key into the |
︙ | ︙ |
Changes to test/rowid.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the magic ROWID column that is # found on all tables. # | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the magic ROWID column that is # found on all tables. # # $Id: rowid.test,v 1.10 2003/04/15 19:22:24 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Basic ROWID functionality tests. # do_test rowid-1.1 { |
︙ | ︙ | |||
399 400 401 402 403 404 405 | SELECT * FROM t3; } } {123 124} do_test rowid-8.8 { execsql { SELECT rowid, * FROM t4; } | | | 399 400 401 402 403 404 405 406 407 408 409 | SELECT * FROM t3; } } {123 124} do_test rowid-8.8 { execsql { SELECT rowid, * FROM t4; } } {1 1 2 133 3 134} finish_test |