Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix triggers to work in an ATTACHed database. Ticket #295. (CVS 915) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
1e5e00fb73c308378efd034cb291caf3 |
User & Date: | drh 2003-04-17 22:57:53.000 |
Context
2003-04-18
| ||
02:31 | The VACUUM command is now functioning (again). Need to do more testing. (CVS 916) (check-in: 6e948d9aae user: drh tags: trunk) | |
2003-04-17
| ||
22:57 | Fix triggers to work in an ATTACHed database. Ticket #295. (CVS 915) (check-in: 1e5e00fb73 user: drh tags: trunk) | |
12:44 | Make sure the min() and max() optimizations work on empty indexed tables. Ticket #296. (CVS 914) (check-in: 98ef611006 user: drh tags: trunk) | |
Changes
Changes to src/build.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | ** DROP INDEX ** creating ID lists ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** PRAGMA ** | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** DROP INDEX ** creating ID lists ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** PRAGMA ** ** $Id: build.c,v 1.146 2003/04/17 22:57:53 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** This routine is called when a new SQL statement is beginning to ** be parsed. Check to see if the schema for the database needs |
︙ | ︙ | |||
105 106 107 108 109 110 111 | pParse->nSet = 0; pParse->nAgg = 0; } /* ** Locate the in-memory structure that describes ** a particular database table given the name | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | pParse->nSet = 0; pParse->nAgg = 0; } /* ** Locate the in-memory structure that describes ** a particular database table given the name ** of that table and (optionally) the name of the database ** containing the table. Return NULL if not found. ** ** See also sqliteLocateTable(). */ Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){ Table *p = 0; int i; for(i=0; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDatabase!=0 && sqliteStrICmp(zDatabase, db->aDb[j].zName) ) continue; p = sqliteHashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1); if( p ) break; } return p; } /* ** Locate the in-memory structure that describes ** a particular database table given the name ** of that table and (optionally) the name of the database ** containing the table. Return NULL if not found. ** ** If pParse->useDb is not negative, then the table must be ** located in that database. If a different database is specified, ** an error message is generated into pParse->zErrMsg. */ Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){ sqlite *db; const char *zUse; Table *p; db = pParse->db; if( pParse->useDb<0 ){ p = sqliteFindTable(db, zName, zDbase); }else { assert( pParse->useDb<db->nDb ); assert( db->aDb[pParse->useDb].pBt!=0 ); zUse = db->aDb[pParse->useDb].zName; if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){ sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase); return 0; } p = sqliteFindTable(db, zName, zUse); if( p==0 && pParse->useDb==1 && zDbase==0 ){ p = sqliteFindTable(db, zName, 0); } } if( p==0 ){ if( zDbase ){ sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName); }else{ sqliteErrorMsg(pParse, "no such table: %s", zName); } } return p; } /* ** Locate the in-memory structure that describes ** a particular index given the name of that index ** and the name of the database that contains the index. ** Return NULL if not found. */ Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){ Index *p = 0; int i; for(i=0; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ |
︙ | ︙ | |||
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 | Vdbe *v; v = sqliteGetVdbe(pParse); if( v==0 ) return; if( pParse->trigStack ) return; /* if this is in a trigger */ if( (pParse->db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 1, 0); if( !tempOnly ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0); sqliteCodeVerifySchema(pParse); } }else if( setCheckpoint ){ sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0); sqliteVdbeAddOp(v, OP_Checkpoint, 1, 0); } } | > > > > > > | 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 | Vdbe *v; v = sqliteGetVdbe(pParse); if( v==0 ) return; if( pParse->trigStack ) return; /* if this is in a trigger */ if( (pParse->db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 1, 0); if( !tempOnly ){ int i; sqlite *db = pParse->db; sqliteVdbeAddOp(v, OP_Transaction, 0, 0); for(i=2; i<db->nDb; i++){ if( db->aDb[i].pBt==0 ) continue; sqliteVdbeAddOp(v, OP_Transaction, i, 0); } sqliteCodeVerifySchema(pParse); } }else if( setCheckpoint ){ sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0); sqliteVdbeAddOp(v, OP_Checkpoint, 1, 0); } } |
︙ | ︙ |
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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** 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.52 2003/04/17 22:57:53 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. */ Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){ Table *pTab = 0; int i; for(i=0; i<pSrc->nSrc; i++){ const char *zTab = pSrc->a[i].zName; const char *zDb = pSrc->a[i].zDatabase; pTab = sqliteLocateTable(pParse, zTab, zDb); pSrc->a[i].pTab = pTab; } return pTab; } /* ** Check to make sure the given table is writable. If it is not |
︙ | ︙ | |||
58 59 60 61 62 63 64 | void sqliteDeleteFrom( Parse *pParse, /* The parser context */ SrcList *pTabList, /* The table from which we should delete things */ Expr *pWhere /* The WHERE clause. May be null */ ){ Vdbe *v; /* The virtual database engine */ Table *pTab; /* The table from which records will be deleted */ | < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > | 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | void sqliteDeleteFrom( Parse *pParse, /* The parser context */ SrcList *pTabList, /* The table from which we should delete things */ Expr *pWhere /* The WHERE clause. May be null */ ){ Vdbe *v; /* The virtual database engine */ Table *pTab; /* The table from which records will be deleted */ 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 ); /* Locate the table which we want to delete. This table has to be ** put in an SrcList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect ** an SrcList* parameter instead of just a Table* parameter. */ pTab = sqliteSrcListLookup(pParse, pTabList); if( pTab==0 ) goto delete_from_cleanup; 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; } if( sqliteIsReadOnly(pParse, pTab) ) goto delete_from_cleanup; assert( pTab->pSelect==0 ); /* This table is not a view */ if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0) ){ goto delete_from_cleanup; } /* Allocate a cursor used to store the old.* data for a trigger. */ |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** ** $Id: main.c,v 1.126 2003/04/17 22:57:54 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include <ctype.h> /* ** A pointer to this structure is used to communicate information |
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 | ** or executed. All the parser does is build the internal data ** structures that describe the table, index, or view. */ memset(&sParse, 0, sizeof(sParse)); sParse.db = pData->db; sParse.initFlag = 1; sParse.iDb = atoi(argv[4]); sParse.newTnum = atoi(argv[2]); sParse.useCallback = 1; sqliteRunParser(&sParse, argv[3], pData->pzErrMsg); }else{ /* If the SQL column is blank it means this is an index that ** was created to be the PRIMARY KEY or to fulfill a UNIQUE ** constraint for a CREATE TABLE. The index should have already | > | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | ** or executed. All the parser does is build the internal data ** structures that describe the table, index, or view. */ memset(&sParse, 0, sizeof(sParse)); sParse.db = pData->db; sParse.initFlag = 1; sParse.iDb = atoi(argv[4]); sParse.useDb = -1; sParse.newTnum = atoi(argv[2]); sParse.useCallback = 1; sqliteRunParser(&sParse, argv[3], pData->pzErrMsg); }else{ /* If the SQL column is blank it means this is an index that ** was created to be the PRIMARY KEY or to fulfill a UNIQUE ** constraint for a CREATE TABLE. The index should have already |
︙ | ︙ | |||
316 317 318 319 320 321 322 323 324 325 326 327 328 329 | /* Read the schema information out of the schema tables */ memset(&sParse, 0, sizeof(sParse)); sParse.db = db; sParse.xCallback = sqliteInitCallback; sParse.pArg = (void*)&initData; sParse.initFlag = 1; sParse.useCallback = 1; if( iDb==0 ){ sqliteRunParser(&sParse, db->file_format>=2 ? init_script : older_init_script, pzErrMsg); }else{ char *zSql = 0; | > | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | /* Read the schema information out of the schema tables */ memset(&sParse, 0, sizeof(sParse)); sParse.db = db; sParse.xCallback = sqliteInitCallback; sParse.pArg = (void*)&initData; sParse.initFlag = 1; sParse.useDb = -1; sParse.useCallback = 1; if( iDb==0 ){ sqliteRunParser(&sParse, db->file_format>=2 ? init_script : older_init_script, pzErrMsg); }else{ char *zSql = 0; |
︙ | ︙ | |||
715 716 717 718 719 720 721 722 723 724 725 726 727 728 | return SQLITE_ERROR; } if( db->pVdbe==0 ){ db->nChange = 0; } memset(&sParse, 0, sizeof(sParse)); sParse.db = db; sParse.xCallback = xCallback; sParse.pArg = pArg; sParse.useCallback = ppVm==0; #ifndef SQLITE_OMIT_TRACE if( db->xTrace ) db->xTrace(db->pTraceArg, zSql); #endif sqliteRunParser(&sParse, zSql, pzErrMsg); if( sqlite_malloc_failed ){ sqliteSetString(pzErrMsg, "out of memory", 0); | > | 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | return SQLITE_ERROR; } if( db->pVdbe==0 ){ db->nChange = 0; } memset(&sParse, 0, sizeof(sParse)); sParse.db = db; sParse.xCallback = xCallback; sParse.pArg = pArg; sParse.useDb = -1; sParse.useCallback = ppVm==0; #ifndef SQLITE_OMIT_TRACE if( db->xTrace ) db->xTrace(db->pTraceArg, zSql); #endif sqliteRunParser(&sParse, zSql, pzErrMsg); if( sqlite_malloc_failed ){ sqliteSetString(pzErrMsg, "out of memory", 0); |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** ************************************************************************* ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** ************************************************************************* ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** ** @(#) $Id: parse.y,v 1.95 2003/04/17 22:57:54 drh Exp $ */ %token_prefix TK_ %token_type {Token} %default_type {Token} %extra_argument {Parse *pParse} %syntax_error { if( pParse->zErrMsg==0 ){ |
︙ | ︙ | |||
790 791 792 793 794 795 796 | %type when_clause {Expr *} when_clause(A) ::= . { A = 0; } when_clause(A) ::= WHEN expr(X). { A = X; } %type trigger_cmd_list {TriggerStep *} trigger_cmd_list(A) ::= trigger_cmd(X) SEMI trigger_cmd_list(Y). { | | > > | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | %type when_clause {Expr *} when_clause(A) ::= . { A = 0; } when_clause(A) ::= WHEN expr(X). { A = X; } %type trigger_cmd_list {TriggerStep *} trigger_cmd_list(A) ::= trigger_cmd(X) SEMI trigger_cmd_list(Y). { X->pNext = Y; A = X; } trigger_cmd_list(A) ::= . { A = 0; } %type trigger_cmd {TriggerStep *} // UPDATE trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z). { A = sqliteTriggerUpdateStep(&X, Y, Z, R); } |
︙ | ︙ |
Changes to src/select.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 SELECT 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 SELECT statements in SQLite. ** ** $Id: select.c,v 1.132 2003/04/17 22:57:54 drh Exp $ */ #include "sqliteInt.h" /* ** Allocate a new Select structure and return a pointer to that ** structure. |
︙ | ︙ | |||
904 905 906 907 908 909 910 | if( pTab==0 ){ return 1; } pTab->isTransient = 1; }else{ /* An ordinary table or view name in the FROM clause */ pTabList->a[i].pTab = pTab = | < | < | 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 | if( pTab==0 ){ return 1; } pTab->isTransient = 1; }else{ /* An ordinary table or view name in the FROM clause */ pTabList->a[i].pTab = pTab = sqliteLocateTable(pParse,pTabList->a[i].zName,pTabList->a[i].zDatabase); if( pTab==0 ){ return 1; } if( pTab->pSelect ){ if( sqliteViewGetColumnNames(pParse, pTab) ){ return 1; } sqliteSelectDelete(pTabList->a[i].pSelect); |
︙ | ︙ |
Changes to src/shell.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 code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** | | | 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 code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** ** $Id: shell.c,v 1.70 2003/04/17 22:57:54 drh Exp $ */ #include <stdlib.h> #include <string.h> #include <stdio.h> #include "sqlite.h" #include <ctype.h> |
︙ | ︙ | |||
281 282 283 284 285 286 287 | if( azArg==0 ) break; for(i=0; i<nArg; i++){ int len = strlen(azCol[i]); if( len>w ) w = len; } if( p->cnt++>0 ) fprintf(p->out,"\n"); for(i=0; i<nArg; i++){ | | > | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | if( azArg==0 ) break; for(i=0; i<nArg; i++){ int len = strlen(azCol[i]); if( len>w ) w = len; } if( p->cnt++>0 ) fprintf(p->out,"\n"); for(i=0; i<nArg; i++){ fprintf(p->out,"%*s = %s\n", w, azCol[i], azArg[i] ? azArg[i] : p->nullvalue); } break; } case MODE_Column: { if( p->cnt++==0 ){ for(i=0; i<nArg; i++){ int w, n; |
︙ | ︙ |
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.175 2003/04/17 22:57:54 drh Exp $ */ #include "config.h" #include "sqlite.h" #include "hash.h" #include "vdbe.h" #include "parse.h" #include "btree.h" |
︙ | ︙ | |||
221 222 223 224 225 226 227 | typedef struct FKey FKey; typedef struct Db Db; /* ** Each database file to be accessed by the system is an instance ** of the following structure. There are normally two of these structures ** in the sqlite.aDb[] array. aDb[0] is the main database file and | | | | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | typedef struct FKey FKey; typedef struct Db Db; /* ** Each database file to be accessed by the system is an instance ** of the following structure. There are normally two of these structures ** in the sqlite.aDb[] array. aDb[0] is the main database file and ** aDb[1] is the database file used to hold temporary tables. Additional ** databases may be attached. */ struct Db { char *zName; /* Name of this database */ Btree *pBt; /* The B*Tree structure for this database file */ int schema_cookie; /* Database schema version number for this file */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ |
︙ | ︙ | |||
821 822 823 824 825 826 827 828 829 830 831 832 833 834 | u8 nameClash; /* A permanent table name clashes with temp table name */ u8 useAgg; /* If true, extract field values from the aggregator ** while generating expressions. Normally false */ u8 schemaVerified; /* True if an OP_VerifySchema has been coded someplace ** other than after an OP_Transaction */ u8 iDb; /* Index of database whose schema is being parsed */ u8 useCallback; /* True if callbacks should be used to report results */ int newTnum; /* Table number to use when reparsing CREATE TABLEs */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int nSet; /* Number of sets used so far */ int nAgg; /* Number of aggregate expressions */ AggExpr *aAgg; /* An array of aggregate expressions */ | > | 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 | u8 nameClash; /* A permanent table name clashes with temp table name */ u8 useAgg; /* If true, extract field values from the aggregator ** while generating expressions. Normally false */ u8 schemaVerified; /* True if an OP_VerifySchema has been coded someplace ** other than after an OP_Transaction */ u8 iDb; /* Index of database whose schema is being parsed */ u8 useCallback; /* True if callbacks should be used to report results */ int useDb; /* Restrict references to tables in this database */ int newTnum; /* Table number to use when reparsing CREATE TABLEs */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int nSet; /* Number of sets used so far */ int nAgg; /* Number of aggregate expressions */ AggExpr *aAgg; /* An array of aggregate expressions */ |
︙ | ︙ | |||
914 915 916 917 918 919 920 921 922 923 924 925 926 927 | * them to. See sqliteUpdate() documentation of "pChanges" * argument. * */ struct TriggerStep { int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ int orconf; /* OE_Rollback etc. */ Select *pSelect; /* Valid for SELECT and sometimes INSERT steps (when pExprList == 0) */ Token target; /* Valid for DELETE, UPDATE, INSERT steps */ Expr *pWhere; /* Valid for DELETE, UPDATE steps */ ExprList *pExprList; /* Valid for UPDATE statements and sometimes INSERT steps (when pSelect == 0) */ | > | 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 | * them to. See sqliteUpdate() documentation of "pChanges" * argument. * */ struct TriggerStep { int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ int orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ Select *pSelect; /* Valid for SELECT and sometimes INSERT steps (when pExprList == 0) */ Token target; /* Valid for DELETE, UPDATE, INSERT steps */ Expr *pWhere; /* Valid for DELETE, UPDATE steps */ ExprList *pExprList; /* Valid for UPDATE statements and sometimes INSERT steps (when pSelect == 0) */ |
︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**); void sqliteWhereEnd(WhereInfo*); void sqliteExprCode(Parse*, Expr*); void sqliteExprIfTrue(Parse*, Expr*, int, int); void sqliteExprIfFalse(Parse*, Expr*, int, int); Table *sqliteFindTable(sqlite*,const char*, const char*); Index *sqliteFindIndex(sqlite*,const char*, const char*); void sqliteUnlinkAndDeleteIndex(sqlite*,Index*); void sqliteCopy(Parse*, SrcList*, Token*, Token*, int); void sqliteVacuum(Parse*, Token*); int sqliteGlobCompare(const unsigned char*,const unsigned char*); int sqliteLikeCompare(const unsigned char*,const unsigned char*); char *sqliteTableNameFromToken(Token*); | > | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**); void sqliteWhereEnd(WhereInfo*); void sqliteExprCode(Parse*, Expr*); void sqliteExprIfTrue(Parse*, Expr*, int, int); void sqliteExprIfFalse(Parse*, Expr*, int, int); Table *sqliteFindTable(sqlite*,const char*, const char*); Table *sqliteLocateTable(Parse*,const char*, const char*); Index *sqliteFindIndex(sqlite*,const char*, const char*); void sqliteUnlinkAndDeleteIndex(sqlite*,Index*); void sqliteCopy(Parse*, SrcList*, Token*, Token*, int); void sqliteVacuum(Parse*, Token*); int sqliteGlobCompare(const unsigned char*,const unsigned char*); int sqliteLikeCompare(const unsigned char*,const unsigned char*); char *sqliteTableNameFromToken(Token*); |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
61 62 63 64 65 66 67 | */ if( sqlite_malloc_failed ) goto trigger_cleanup; assert( pTableName->nSrc==1 ); tab = sqliteSrcListLookup(pParse, pTableName); if( !tab ){ goto trigger_cleanup; } | | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | */ if( sqlite_malloc_failed ) goto trigger_cleanup; assert( pTableName->nSrc==1 ); tab = sqliteSrcListLookup(pParse, pTableName); if( !tab ){ goto trigger_cleanup; } if( tab->iDb>=2 && !pParse->initFlag ){ sqliteErrorMsg(pParse, "triggers may not be added to auxiliary " "database %s", db->aDb[tab->iDb].zName); goto trigger_cleanup; } zName = sqliteStrNDup(pName->z, pName->n); if( sqliteHashFind(&(db->aDb[tab->iDb].trigHash), zName,pName->n+1) ){ |
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | nt->tr_tm = tr_tm; nt->pWhen = sqliteExprDup(pWhen); sqliteExprDelete(pWhen); nt->pColumns = sqliteIdListDup(pColumns); sqliteIdListDelete(pColumns); nt->foreach = foreach; nt->step_list = pStepList; /* if we are not initializing, and this trigger is not on a TEMP table, ** build the sqlite_master entry */ if( !pParse->initFlag ){ static VdbeOp insertTrig[] = { { OP_NewRecno, 0, 0, 0 }, | > > > > > | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | nt->tr_tm = tr_tm; nt->pWhen = sqliteExprDup(pWhen); sqliteExprDelete(pWhen); nt->pColumns = sqliteIdListDup(pColumns); sqliteIdListDelete(pColumns); nt->foreach = foreach; nt->step_list = pStepList; while( pStepList ){ pStepList->pTrig = nt; pStepList = pStepList->pNext; } pStepList = nt->step_list; /* if we are not initializing, and this trigger is not on a TEMP table, ** build the sqlite_master entry */ if( !pParse->initFlag ){ static VdbeOp insertTrig[] = { { OP_NewRecno, 0, 0, 0 }, |
︙ | ︙ | |||
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | int orconfin /* Conflict algorithm. (OE_Abort, etc) */ ){ TriggerStep * pTriggerStep = pStepList; int orconf; while( pTriggerStep ){ int saveNTab = pParse->nTab; orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; pParse->trigStack->orconf = orconf; switch( pTriggerStep->op ){ case TK_SELECT: { Select * ss = sqliteSelectDup(pTriggerStep->pSelect); assert(ss); assert(ss->pSrc); sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0); sqliteSelectDelete(ss); | > > | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | int orconfin /* Conflict algorithm. (OE_Abort, etc) */ ){ TriggerStep * pTriggerStep = pStepList; int orconf; while( pTriggerStep ){ int saveNTab = pParse->nTab; int saveUseDb = pParse->useDb; orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; pParse->trigStack->orconf = orconf; pParse->useDb = pTriggerStep->pTrig->iDb; switch( pTriggerStep->op ){ case TK_SELECT: { Select * ss = sqliteSelectDup(pTriggerStep->pSelect); assert(ss); assert(ss->pSrc); sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0); sqliteSelectDelete(ss); |
︙ | ︙ | |||
550 551 552 553 554 555 556 557 558 559 560 561 562 563 | sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0); break; } default: assert(0); } pParse->nTab = saveNTab; pTriggerStep = pTriggerStep->pNext; } return 0; } /* | > | 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 | sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0); break; } default: assert(0); } pParse->nTab = saveNTab; pParse->useDb = saveUseDb; pTriggerStep = pTriggerStep->pNext; } return 0; } /* |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** | | < < | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ** 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.60 2003/04/17 22:57:54 drh Exp $ */ #include "sqliteInt.h" /* ** Process an UPDATE statement. */ void sqliteUpdate( Parse *pParse, /* The parser context */ SrcList *pTabList, /* The table in which we should change things */ ExprList *pChanges, /* Things to be changed */ Expr *pWhere, /* The WHERE clause. May be null */ int onError /* How to handle constraint errors */ ){ int i, j; /* Loop counters */ Table *pTab; /* The table to be updated */ int addr; /* VDBE instruction address of the start of the loop */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Vdbe *v; /* The virtual database engine */ Index *pIdx; /* For looping over indices */ int nIdx; /* Number of indices that need updating */ int nIdxTotal; /* Total number of indices */ |
︙ | ︙ | |||
54 55 56 57 58 59 60 | 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 ); | < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > | | 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 83 84 | 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 ); /* Locate the table which we want to update. This table has to be ** put in an SrcList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect ** an SrcList* parameter instead of just a Table* parameter. */ pTab = sqliteSrcListLookup(pParse, pTabList); if( pTab==0 ) goto update_cleanup; 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; } if( sqliteIsReadOnly(pParse, pTab) ) goto update_cleanup; assert( pTab->pSelect==0 ); /* This table is not a VIEW */ aXRef = sqliteMalloc( sizeof(int) * pTab->nCol ); if( aXRef==0 ) goto update_cleanup; for(i=0; i<pTab->nCol; i++) aXRef[i] = -1; /* If there are FOR EACH ROW triggers, allocate temp tables */ if( row_triggers_exist ){ |
︙ | ︙ |
Changes to test/attach.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 script is testing the ATTACH and DETACH commands # and related functionality. # | | | 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 script is testing the ATTACH and DETACH commands # and related functionality. # # $Id: attach.test,v 1.3 2003/04/17 22:57:55 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl for {set i 2} {$i<=15} {incr i} { file delete -force test$i.db |
︙ | ︙ | |||
196 197 198 199 200 201 202 203 204 205 206 207 208 209 | } {0 {}} do_test attach-1.29 { execsql { PRAGMA database_list } } {0 main 1 temp} for {set i 2} {$i<=15} {incr i} { catch {db$i close} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 234 235 236 237 238 239 240 241 242 243 244 245 246 | } {0 {}} do_test attach-1.29 { execsql { PRAGMA database_list } } {0 main 1 temp} do_test attach-2.1 { execsql { CREATE TABLE tx(x1,x2,y1,y2); CREATE TRIGGER r1 AFTER UPDATE ON t2 FOR EACH ROW BEGIN INSERT INTO tx(x1,x2,y1,y2) VALUES(OLD.x,NEW.x,OLD.y,NEW.y); END; SELECT * FROM tx; } db2; } {} do_test attach-2.2 { execsql { UPDATE t2 SET x=x+10; SELECT * FROM tx; } db2; } {1 11 x x 2 12 y y} do_test attach-2.3 { execsql { CREATE TABLE tx(x1,x2,y1,y2); SELECT * FROM tx; } } {} do_test attach-2.4 { execsql { ATTACH 'test2.db' AS db2; } } {} do_test attach-2.5 { execsql { UPDATE db2.t2 SET x=x+10; SELECT * FROM db2.tx; } } {1 11 x x 2 12 y y 11 21 x x 12 22 y y} do_test attach-2.6 { execsql { SELECT * FROM main.tx; } } {} for {set i 2} {$i<=15} {incr i} { catch {db$i close} } finish_test |