/ Check-in [206b1739]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix some issues with INSTEAD OF triggers. (CVS 930)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:206b17397b1d2b55179c935927ff1d8215728c32
User & Date: drh 2003-04-24 01:45:04
Context
2003-04-25
02:43
Get VACUUM working again - with test cases. Some other minor cleanups. (CVS 931) check-in: 5afb8800 user: drh tags: trunk
2003-04-24
01:45
Fix some issues with INSTEAD OF triggers. (CVS 930) check-in: 206b1739 user: drh tags: trunk
2003-04-23
12:25
Remove the begin_hook and commit_hook APIs. They were a bad idea. Add a "trace" method to the TCL interface. (CVS 929) check-in: 6289b863 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/auth.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
...
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
**
*************************************************************************
** This file contains code used to implement the sqlite_set_authorizer()
** API.  This facility is an optional feature of the library.  Embedded
** systems that do not need this facility may omit it by recompiling
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
**
** $Id: auth.c,v 1.6 2003/04/22 20:30:38 drh Exp $
*/
#include "sqliteInt.h"

/*
** All of the code in this file may be omitted by defining a single
** macro.
*/
................................................................................
){
  sqlite *db = pParse->db;
  int rc;
  Table *pTab;          /* The table being read */
  const char *zCol;     /* Name of the column of the table */
  int iSrc;             /* Index in pTabList->a[] of table being read */
  const char *zDBase;   /* Name of database being accessed */
  const char *zTrig;    /* Name of the trigger doing the accessing */
  TriggerStack *pStack; /* The stack of current triggers */

  pStack = pParse->trigStack;
  zTrig = pStack ? pStack->pTrigger->name : 0;
  if( db->xAuth==0 ) return;
  assert( pExpr->op==TK_COLUMN );
  iSrc = pExpr->iTable - base;
  if( iSrc>=0 && iSrc<pTabList->nSrc ){
    pTab = pTabList->a[iSrc].pTab;
  }else{
    /* This must be an attempt to read the NEW or OLD pseudo-tables
    ** of a trigger.
    */


    assert( pStack!=0 );
    assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
    pTab = pStack->pTab;
  }
  if( pTab==0 ) return;
  if( pExpr->iColumn>=0 ){
    assert( pExpr->iColumn<pTab->nCol );
................................................................................
    zCol = pTab->aCol[pExpr->iColumn].zName;
  }else if( pTab->iPKey>=0 ){
    assert( pTab->iPKey<pTab->nCol );
    zCol = pTab->aCol[pTab->iPKey].zName;
  }else{
    zCol = "ROWID";
  }
  assert( pExpr->iDb>=0 && pExpr->iDb<db->nDb );
  zDBase = db->aDb[pExpr->iDb].zName;
  rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, zTrig);

  if( rc==SQLITE_IGNORE ){
    pExpr->op = TK_NULL;
  }else if( rc==SQLITE_DENY ){
    if( db->nDb>2 || pExpr->iDb!=0 ){
      sqliteSetString(&pParse->zErrMsg,"access to ", zDBase, ".",
          pTab->zName, ".", zCol, " is prohibited", 0);
    }else{
................................................................................
  int code,
  const char *zArg1,
  const char *zArg2,
  const char *zArg3
){
  sqlite *db = pParse->db;
  int rc;
  const char *zTrigName;

  if( db->xAuth==0 ){
    return SQLITE_OK;
  }
  zTrigName = pParse->trigStack ? pParse->trigStack->pTrigger->name : 0;
  rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, zTrigName);
  if( rc==SQLITE_DENY ){
    sqliteSetString(&pParse->zErrMsg, "not authorized", 0);
    pParse->rc = SQLITE_AUTH;
    pParse->nErr++;
  }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
    rc = SQLITE_DENY;
    sqliteAuthBadReturnCode(pParse, rc);
  }
  return rc;
}

#endif /* SQLITE_OMIT_AUTHORIZATION */







|







 







<
<

<
<









>
>







 







|

|
>







 







<




<
|












10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
91
92
93
94
95
96
97


98


99
100
101
102
103
104
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
...
153
154
155
156
157
158
159

160
161
162
163

164
165
166
167
168
169
170
171
172
173
174
175
176
**
*************************************************************************
** This file contains code used to implement the sqlite_set_authorizer()
** API.  This facility is an optional feature of the library.  Embedded
** systems that do not need this facility may omit it by recompiling
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
**
** $Id: auth.c,v 1.7 2003/04/24 01:45:04 drh Exp $
*/
#include "sqliteInt.h"

/*
** All of the code in this file may be omitted by defining a single
** macro.
*/
................................................................................
){
  sqlite *db = pParse->db;
  int rc;
  Table *pTab;          /* The table being read */
  const char *zCol;     /* Name of the column of the table */
  int iSrc;             /* Index in pTabList->a[] of table being read */
  const char *zDBase;   /* Name of database being accessed */





  if( db->xAuth==0 ) return;
  assert( pExpr->op==TK_COLUMN );
  iSrc = pExpr->iTable - base;
  if( iSrc>=0 && iSrc<pTabList->nSrc ){
    pTab = pTabList->a[iSrc].pTab;
  }else{
    /* This must be an attempt to read the NEW or OLD pseudo-tables
    ** of a trigger.
    */
    TriggerStack *pStack; /* The stack of current triggers */
    pStack = pParse->trigStack;
    assert( pStack!=0 );
    assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
    pTab = pStack->pTab;
  }
  if( pTab==0 ) return;
  if( pExpr->iColumn>=0 ){
    assert( pExpr->iColumn<pTab->nCol );
................................................................................
    zCol = pTab->aCol[pExpr->iColumn].zName;
  }else if( pTab->iPKey>=0 ){
    assert( pTab->iPKey<pTab->nCol );
    zCol = pTab->aCol[pTab->iPKey].zName;
  }else{
    zCol = "ROWID";
  }
  assert( pExpr->iDb<db->nDb );
  zDBase = db->aDb[pExpr->iDb].zName;
  rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, 
                 pParse->zAuthContext);
  if( rc==SQLITE_IGNORE ){
    pExpr->op = TK_NULL;
  }else if( rc==SQLITE_DENY ){
    if( db->nDb>2 || pExpr->iDb!=0 ){
      sqliteSetString(&pParse->zErrMsg,"access to ", zDBase, ".",
          pTab->zName, ".", zCol, " is prohibited", 0);
    }else{
................................................................................
  int code,
  const char *zArg1,
  const char *zArg2,
  const char *zArg3
){
  sqlite *db = pParse->db;
  int rc;


  if( db->xAuth==0 ){
    return SQLITE_OK;
  }

  rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
  if( rc==SQLITE_DENY ){
    sqliteSetString(&pParse->zErrMsg, "not authorized", 0);
    pParse->rc = SQLITE_AUTH;
    pParse->nErr++;
  }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
    rc = SQLITE_DENY;
    sqliteAuthBadReturnCode(pParse, rc);
  }
  return rc;
}

#endif /* SQLITE_OMIT_AUTHORIZATION */

Changes to src/btree_rb.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
....
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
** 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.6 2003/04/20 17:29:24 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.
*/
#include "btree.h"
................................................................................
  return SQLITE_OK;
}

static int memBtreeKey(BtCursor* pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
    memcpy(zBuf, pCur->pNode->pKey+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, pCur->pNode->pKey+offset ,pCur->pNode->nKey-offset);
    return pCur->pNode->nKey-offset;
  }
  assert(0);
}

static int memBtreeDataSize(BtCursor* pCur, int *pSize)
{
................................................................................
  return SQLITE_OK;
}

static int memBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( (amt + offset) <= pCur->pNode->nData ){
    memcpy(zBuf, pCur->pNode->pData+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, pCur->pNode->pData+offset ,pCur->pNode->nData-offset);
    return pCur->pNode->nData-offset;
  }
  assert(0);
}

static int memBtreeCloseCursor(BtCursor* pCur)
{







|







 







|


|







 







|


|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
....
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
** 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.7 2003/04/24 01:45:04 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.
*/
#include "btree.h"
................................................................................
  return SQLITE_OK;
}

static int memBtreeKey(BtCursor* pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
    memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, pCur->pNode->nKey-offset);
    return pCur->pNode->nKey-offset;
  }
  assert(0);
}

static int memBtreeDataSize(BtCursor* pCur, int *pSize)
{
................................................................................
  return SQLITE_OK;
}

static int memBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf)
{
  if( !pCur->pNode ) return 0;
  if( (amt + offset) <= pCur->pNode->nData ){
    memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt);
    return amt;
  }else{
    memcpy(zBuf, ((char*)pCur->pNode->pData)+offset ,pCur->pNode->nData-offset);
    return pCur->pNode->nData-offset;
  }
  assert(0);
}

static int memBtreeCloseCursor(BtCursor* pCur)
{

Changes to src/copy.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
**    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.3 2003/04/22 20:30:39 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:
................................................................................
  const char *zDb;
  sqlite *db = pParse->db;


  if( sqlite_malloc_failed  ) goto copy_cleanup;
  assert( pTableName->nSrc==1 );
  pTab = sqliteSrcListLookup(pParse, pTableName);
  if( pTab==0 || sqliteIsReadOnly(pParse, pTab) ) goto copy_cleanup;
  zFile = sqliteStrNDup(pFilename->z, pFilename->n);
  sqliteDequote(zFile);
  assert( pTab->iDb>=0 && pTab->iDb<db->nDb );
  zDb = db->aDb[pTab->iDb].zName;
  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
      || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
    goto copy_cleanup;
  }
  v = sqliteGetVdbe(pParse);
  if( v ){







|







 







|


|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
**    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.4 2003/04/24 01:45:04 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:
................................................................................
  const char *zDb;
  sqlite *db = pParse->db;


  if( sqlite_malloc_failed  ) goto copy_cleanup;
  assert( pTableName->nSrc==1 );
  pTab = sqliteSrcListLookup(pParse, pTableName);
  if( pTab==0 || sqliteIsReadOnly(pParse, pTab, 0) ) goto copy_cleanup;
  zFile = sqliteStrNDup(pFilename->z, pFilename->n);
  sqliteDequote(zFile);
  assert( pTab->iDb<db->nDb );
  zDb = db->aDb[pTab->iDb].zName;
  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
      || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
    goto copy_cleanup;
  }
  v = sqliteGetVdbe(pParse);
  if( v ){

Changes to src/delete.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
34
35
36
37
38
39
40
41
42
43


44
45

46
47
48
49
50
51
52
..
61
62
63
64
65
66
67

68
69
70
71
72
73
74
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105






106
107
108
109
110
111
112
...
126
127
128
129
130
131
132









133
134
135
136
137
138
139
...
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
...
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
247
248

249
250
251
252
253
254
255
**    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.53 2003/04/22 20:30:39 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.
................................................................................
}

/*
** Check to make sure the given table is writable.  If it is not
** writable, generate an error message and return 1.  If it is
** writable return 0;
*/
int sqliteIsReadOnly(Parse *pParse, Table *pTab){
  if( pTab->readOnly || pTab->pSelect ){
    sqliteErrorMsg(pParse, "%s %s may not be modified",


      pTab->pSelect ? "view" : "table",
      pTab->zName);

    return 1;
  }
  return 0;
}

/*
** Process a DELETE FROM statement.
................................................................................
  const char *zDb;       /* Name of database holding pTab */
  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 ){
................................................................................
  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 */
  assert( pTab->iDb<db->nDb );
  zDb = db->aDb[pTab->iDb].zName;
  if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
    goto delete_from_cleanup;
  }







  /* Allocate a cursor used to store the old.* data for a trigger.
  */
  if( row_triggers_exist ){ 
    oldIdx = pParse->nTab++;
  }

................................................................................
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ){
    goto delete_from_cleanup;
  }
  sqliteBeginWriteOperation(pParse, row_triggers_exist,
       !row_triggers_exist && pTab->iDb==1);










  /* Initialize the counter of the number of rows deleted, if
  ** we are counting rows.
  */
  if( db->flags & SQLITE_CountRows ){
    sqliteVdbeAddOp(v, OP_Integer, 0, 0);
  }
................................................................................
  */
  if( pWhere==0 && !row_triggers_exist ){
    if( db->flags & SQLITE_CountRows ){
      /* If counting rows deleted, just count the total number of
      ** entries in the table. */
      int endOfLoop = sqliteVdbeMakeLabel(v);
      int addr;

      sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
      sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);

      sqliteVdbeAddOp(v, OP_Rewind, base, sqliteVdbeCurrentAddr(v)+2);
      addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
      sqliteVdbeAddOp(v, OP_Next, base, addr);
      sqliteVdbeResolveLabel(v, endOfLoop);
      sqliteVdbeAddOp(v, OP_Close, base, 0);
    }

    sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
      sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);

    }
  }

  /* The usual case: There is a WHERE clause so we have to scan through
  ** the table an pick which records to delete.
  */
  else{
................................................................................

    /* 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
    ** indices.  If there are row triggers, this happens inside the
    ** OP_ListRead loop because the cursor have to all be closed
    ** before the trigger fires.  If there are no row triggers, the
    ** cursors are opened only once on the outside the loop.
    */
    pParse->nTab = base + 1;
    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
    sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
    for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
      sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
      sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum);
    }

    /* This is the beginning of the delete loop when there are no
    ** row triggers */
    if( !row_triggers_exist ){ 
      addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
    }

    /* Delete the row */
    sqliteGenerateRowDelete(db, v, pTab, base, pParse->trigStack==0);


    /* If there are row triggers, close all cursors then invoke
    ** the AFTER triggers
    */
    if( row_triggers_exist ){

      for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
        sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);
      }
      sqliteVdbeAddOp(v, OP_Close, base, 0);

      sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, 
          oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
	  addr);
    }

    /* End of the delete loop */
    sqliteVdbeAddOp(v, OP_Goto, 0, addr);







|







 







|
|
|
>
>
|
<
>







 







>







 







|
|
|
<
<

<
<





>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>







 







>
|
|
>






>
|
|
|
>







 







>
|
|
>





>
|
>






>
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|

|
|
|
>




>
|
|
|
|
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
34
35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
..
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
..
89
90
91
92
93
94
95
96
97
98


99


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
...
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
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
**    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.54 2003/04/24 01:45:04 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.
................................................................................
}

/*
** Check to make sure the given table is writable.  If it is not
** writable, generate an error message and return 1.  If it is
** writable return 0;
*/
int sqliteIsReadOnly(Parse *pParse, Table *pTab, int viewOk){
  if( pTab->readOnly ){
    sqliteErrorMsg(pParse, "table %s may not be modified", pTab->zName);
    return 1;
  }
  if( !viewOk && pTab->pSelect ){

    sqliteErrorMsg(pParse, "cannot modify %s because it is a view",pTab->zName);
    return 1;
  }
  return 0;
}

/*
** Process a DELETE FROM statement.
................................................................................
  const char *zDb;       /* Name of database holding pTab */
  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 isView;            /* True if attempting to delete from a view */

  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 ){
................................................................................
  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;
  isView = pTab->pSelect!=0;
  if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
    goto delete_from_cleanup;


  }


  assert( pTab->iDb<db->nDb );
  zDb = db->aDb[pTab->iDb].zName;
  if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
    goto delete_from_cleanup;
  }

  /* If pTab is really a view, make sure it has been initialized.
  */
  if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
    goto delete_from_cleanup;
  }

  /* Allocate a cursor used to store the old.* data for a trigger.
  */
  if( row_triggers_exist ){ 
    oldIdx = pParse->nTab++;
  }

................................................................................
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ){
    goto delete_from_cleanup;
  }
  sqliteBeginWriteOperation(pParse, row_triggers_exist,
       !row_triggers_exist && pTab->iDb==1);

  /* If we are trying to delete from a view, construct that view into
  ** a temporary table.
  */
  if( isView ){
    Select *pView = sqliteSelectDup(pTab->pSelect);
    sqliteSelect(pParse, pView, SRT_TempTable, base, 0, 0, 0);
    sqliteSelectDelete(pView);
  }

  /* Initialize the counter of the number of rows deleted, if
  ** we are counting rows.
  */
  if( db->flags & SQLITE_CountRows ){
    sqliteVdbeAddOp(v, OP_Integer, 0, 0);
  }
................................................................................
  */
  if( pWhere==0 && !row_triggers_exist ){
    if( db->flags & SQLITE_CountRows ){
      /* If counting rows deleted, just count the total number of
      ** entries in the table. */
      int endOfLoop = sqliteVdbeMakeLabel(v);
      int addr;
      if( !isView ){
        sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
        sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
      }
      sqliteVdbeAddOp(v, OP_Rewind, base, sqliteVdbeCurrentAddr(v)+2);
      addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
      sqliteVdbeAddOp(v, OP_Next, base, addr);
      sqliteVdbeResolveLabel(v, endOfLoop);
      sqliteVdbeAddOp(v, OP_Close, base, 0);
    }
    if( !isView ){
      sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
        sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
      }
    }
  }

  /* The usual case: There is a WHERE clause so we have to scan through
  ** the table an pick which records to delete.
  */
  else{
................................................................................

    /* 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);
      if( !isView ){
        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);
      if( !isView ){
        sqliteVdbeAddOp(v, OP_Close, base, 0);
      }

      sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, 
          oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
	  addr);
    }

    if( !isView ){
      /* Open cursors for the table we are deleting from and all its
      ** indices.  If there are row triggers, this happens inside the
      ** OP_ListRead loop because the cursor have to all be closed
      ** before the trigger fires.  If there are no row triggers, the
      ** cursors are opened only once on the outside the loop.
      */
      pParse->nTab = base + 1;
      sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
      sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
      for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
        sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
        sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum);
      }

      /* This is the beginning of the delete loop when there are no
      ** row triggers */
      if( !row_triggers_exist ){ 
        addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
      }

      /* Delete the row */
      sqliteGenerateRowDelete(db, v, pTab, base, pParse->trigStack==0);
    }

    /* If there are row triggers, close all cursors then invoke
    ** the AFTER triggers
    */
    if( row_triggers_exist ){
      if( !isView ){
        for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
          sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);
        }
        sqliteVdbeAddOp(v, OP_Close, base, 0);
      }
      sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, 
          oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
	  addr);
    }

    /* End of the delete loop */
    sqliteVdbeAddOp(v, OP_Goto, 0, addr);

Changes to src/insert.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
105
106
107
108
109
110
111

112
113
114
115
116
117
118
...
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
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
...
488
489
490
491
492
493
494
495

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
**    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.81 2003/04/22 20:30:39 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is call to handle SQL of the following forms:
**
**    insert into TABLE (IDLIST) values(EXPRLIST)
................................................................................
  int endOfLoop;        /* Label for the end of the insertion loop */
  int useTempTable;     /* Store SELECT results in intermediate table */
  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;
................................................................................
  *  (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;
  }

  if( pTab==0 ) goto insert_cleanup;

  /* If pTab is really a view, make sure it has been initialized.
  */
  if( pTab->pSelect ){
    if( sqliteViewGetColumnNames(pParse, pTab) ){
      goto insert_cleanup;
    }
  }

  /* Allocate a VDBE
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;
  sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist,
................................................................................
    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,
................................................................................
      }else{
        sqliteExprCode(pParse, pList->a[j].pExpr);
      }
    }
    sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
    sqliteVdbeAddOp(v, OP_PutIntKey, 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);
        sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
        sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
      }
      pParse->nTab += idx;
    }
  }

  /* Push the record number for the new entry onto the stack.  The
  ** record number is a randomly generate integer created by NewRecno
  ** except when the table has an INTEGER PRIMARY KEY column, in which
  ** case the record number is the same as that column. 
  */
  if( !pTab->pSelect ){
    if( keyColumn>=0 ){
      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);
................................................................................

    /* 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);
    }
  }

  if( row_triggers_exist ){
    /* Close all tables opened */
    if( !pTab->pSelect ){
      sqliteVdbeAddOp(v, OP_Close, base, 0);
      for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
        sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
      }
    }

    /* Code AFTER triggers */







|







 







>







 







|
|
<
<


<




<
|
|
<







 







|







 







|









|
<
|
|
|
|
|
|
|
|
|
|
<







|







 







|
>
|
|
|
|
<




|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
139
140
141
142
143
144
145
146
147


148
149

150
151
152
153

154
155

156
157
158
159
160
161
162
...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
423
424

425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
...
482
483
484
485
486
487
488
489
490
491
492
493
494

495
496
497
498
499
500
501
502
503
504
505
506
**    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.82 2003/04/24 01:45:04 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is call to handle SQL of the following forms:
**
**    insert into TABLE (IDLIST) values(EXPRLIST)
................................................................................
  int endOfLoop;        /* Label for the end of the insertion loop */
  int useTempTable;     /* Store SELECT results in intermediate table */
  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 isView;           /* True if attempting to insert into a view */

  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;
................................................................................
  *  (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;
  isView = pTab->pSelect!=0;
  if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){


    goto insert_cleanup;
  }

  if( pTab==0 ) goto insert_cleanup;

  /* If pTab is really a view, make sure it has been initialized.
  */

  if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
    goto insert_cleanup;

  }

  /* Allocate a VDBE
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;
  sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist,
................................................................................
    sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak);
    iCont = sqliteVdbeCurrentAddr(v);
  }else if( pSelect ){
    sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop);
    sqliteVdbeResolveLabel(v, iInsertBlock);
  }

  /* Run the BEFORE and INSTEAD OF 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,
................................................................................
      }else{
        sqliteExprCode(pParse, pList->a[j].pExpr);
      }
    }
    sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
    sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);

    /* Fire BEFORE or INSTEAD OF 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 && !isView ){

    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);
      sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
      sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
    }
    pParse->nTab += idx;

  }

  /* Push the record number for the new entry onto the stack.  The
  ** record number is a randomly generate integer created by NewRecno
  ** except when the table has an INTEGER PRIMARY KEY column, in which
  ** case the record number is the same as that column. 
  */
  if( !isView ){
    if( keyColumn>=0 ){
      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);
................................................................................

    /* 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);

  }

  if( row_triggers_exist ){
    /* Close all tables opened */
    if( !isView ){
      sqliteVdbeAddOp(v, OP_Close, base, 0);
      for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
        sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
      }
    }

    /* Code AFTER triggers */

Changes to src/os.h.

12
13
14
15
16
17
18





19
20
21
22
23
24
25
**
** This header file (together with is companion C source-code file
** "os.c") attempt to abstract the underlying operating system so that
** the SQLite library will work on both POSIX and windows systems.
*/
#ifndef _SQLITE_OS_H_
#define _SQLITE_OS_H_






/*
** These #defines should enable >2GB file support on Posix if the
** underlying operating system supports it.  If the OS lacks
** large file support, or if the OS is windows, these should be no-ops.
**
** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch







>
>
>
>
>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
**
** This header file (together with is companion C source-code file
** "os.c") attempt to abstract the underlying operating system so that
** the SQLite library will work on both POSIX and windows systems.
*/
#ifndef _SQLITE_OS_H_
#define _SQLITE_OS_H_

/*
** Helpful hint:  To get this to compile on HP/UX, add -D_INCLUDE_POSIX_SOURCE
** to the compiler command line.
*/

/*
** These #defines should enable >2GB file support on Posix if the
** underlying operating system supports it.  If the OS lacks
** large file support, or if the OS is windows, these should be no-ops.
**
** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch

Changes to src/select.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
900
901
902
903
904
905
906




907
908
909
910
911
912
913
....
1044
1045
1046
1047
1048
1049
1050



1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
....
2109
2110
2111
2112
2113
2114
2115

2116




2117
2118



2119
2120
2121
2122
2123
2124
2125
**    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.133 2003/04/22 20:30:39 drh Exp $
*/
#include "sqliteInt.h"


/*
** Allocate a new Select structure and return a pointer to that
** structure.
................................................................................
      }
      pTabList->a[i].pTab = pTab = 
        sqliteResultSetOfSelect(pParse, pTabList->a[i].zAlias,
                                        pTabList->a[i].pSelect);
      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;
................................................................................
** in a select structure.  It just sets the pointers to NULL.  This
** routine is recursive in the sense that if the Select.pSrc.a[].pSelect
** pointer is not NULL, this routine is called recursively on that pointer.
**
** This routine is called on the Select structure that defines a
** VIEW in order to undo any bindings to tables.  This is necessary
** because those tables might be DROPed by a subsequent SQL command.



*/
void sqliteSelectUnbind(Select *p){
  int i;
  SrcList *pSrc = p->pSrc;
  Table *pTab;
  if( p==0 ) return;
  for(i=0; i<pSrc->nSrc; i++){
    if( (pTab = pSrc->a[i].pTab)!=0 ){
      if( pTab->isTransient ){
        sqliteDeleteTable(0, pTab);
#if 0
        sqliteSelectDelete(pSrc->a[i].pSelect);
        pSrc->a[i].pSelect = 0;
#endif
      }
      pSrc->a[i].pTab = 0;
      if( pSrc->a[i].pSelect ){
        sqliteSelectUnbind(pSrc->a[i].pSelect);
      }
    }
  }
................................................................................
      p->nOffset = iMem;
    }
  }

  /* Generate code for all sub-queries in the FROM clause
  */
  for(i=0; i<pTabList->nSrc; i++){

    if( pTabList->a[i].pSelect==0 ) continue;




    sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_TempTable, base+i,
                 p, i, &isAgg);



    pTabList = p->pSrc;
    pWhere = p->pWhere;
    if( eDest==SRT_Callback ){
      pOrderBy = p->pOrderBy;
    }
    pGroupBy = p->pGroupBy;
    pHaving = p->pHaving;







|







 







>
>
>
>







 







>
>
>










<
<
<
<







 







>

>
>
>
>


>
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
....
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067




1068
1069
1070
1071
1072
1073
1074
....
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
**    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.134 2003/04/24 01:45:04 drh Exp $
*/
#include "sqliteInt.h"


/*
** Allocate a new Select structure and return a pointer to that
** structure.
................................................................................
      }
      pTabList->a[i].pTab = pTab = 
        sqliteResultSetOfSelect(pParse, pTabList->a[i].zAlias,
                                        pTabList->a[i].pSelect);
      if( pTab==0 ){
        return 1;
      }
      /* The isTransient flag indicates that the Table structure has been
      ** dynamically allocated and may be freed at any time.  In other words,
      ** pTab is not pointing to a persistent table structure that defines
      ** part of the schema. */
      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;
................................................................................
** in a select structure.  It just sets the pointers to NULL.  This
** routine is recursive in the sense that if the Select.pSrc.a[].pSelect
** pointer is not NULL, this routine is called recursively on that pointer.
**
** This routine is called on the Select structure that defines a
** VIEW in order to undo any bindings to tables.  This is necessary
** because those tables might be DROPed by a subsequent SQL command.
** If the bindings are not removed, then the Select.pSrc->a[].pTab field
** will be left pointing to a deallocated Table structure after the
** DROP and a coredump will occur the next time the VIEW is used.
*/
void sqliteSelectUnbind(Select *p){
  int i;
  SrcList *pSrc = p->pSrc;
  Table *pTab;
  if( p==0 ) return;
  for(i=0; i<pSrc->nSrc; i++){
    if( (pTab = pSrc->a[i].pTab)!=0 ){
      if( pTab->isTransient ){
        sqliteDeleteTable(0, pTab);




      }
      pSrc->a[i].pTab = 0;
      if( pSrc->a[i].pSelect ){
        sqliteSelectUnbind(pSrc->a[i].pSelect);
      }
    }
  }
................................................................................
      p->nOffset = iMem;
    }
  }

  /* Generate code for all sub-queries in the FROM clause
  */
  for(i=0; i<pTabList->nSrc; i++){
    const char *zSavedAuthContext;
    if( pTabList->a[i].pSelect==0 ) continue;
    if( pTabList->a[i].zName!=0 ){
      zSavedAuthContext = pParse->zAuthContext;
      pParse->zAuthContext = pTabList->a[i].zName;
    }
    sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_TempTable, base+i,
                 p, i, &isAgg);
    if( pTabList->a[i].zName!=0 ){
      pParse->zAuthContext = zSavedAuthContext;
    }
    pTabList = p->pSrc;
    pWhere = p->pWhere;
    if( eDest==SRT_Callback ){
      pOrderBy = p->pOrderBy;
    }
    pGroupBy = p->pGroupBy;
    pHaving = p->pHaving;

Changes to src/shell.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
**    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>

................................................................................
      if( rc!=0 && zErrMsg!=0 ){
        fprintf(stderr,"SQL error: %s\n", zErrMsg);
        exit(1);
      }
    }
  }else{
    extern int isatty();
    if( isatty(fileno(stdout)) ){
      char *zHome;
      char *zHistory = 0;
      printf(
        "SQLite version %s\n"
        "Enter \".help\" for instructions\n",
        sqlite_version
      );







|







 







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
**    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.71 2003/04/24 01:45:04 drh Exp $
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sqlite.h"
#include <ctype.h>

................................................................................
      if( rc!=0 && zErrMsg!=0 ){
        fprintf(stderr,"SQL error: %s\n", zErrMsg);
        exit(1);
      }
    }
  }else{
    extern int isatty();
    if( isatty(fileno(stdout)) && isatty(fileno(stdin)) ){
      char *zHome;
      char *zHistory = 0;
      printf(
        "SQLite version %s\n"
        "Enter \".help\" for instructions\n",
        sqlite_version
      );

Changes to src/sqlite.h.in.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
520
521
522
523
524
525
526
527
528

529
530
531
532
533
534
535
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.46 2003/04/23 12:25:24 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
/*
** The second parameter to the access authorization function above will
** be one of the values below.  These values signify what kind of operation
** is to be authorized.  The 3rd and 4th parameters to the authorization
** function will be parameters or NULL depending on which of the following
** codes is used as the second parameter.  The 5th parameter is the name
** of the database ("main", "temp", etc.) if applicable.  The 6th parameter
** is the name of the trigger that is responsible for the access attempt,
** or NULL if this access attempt is directly from input SQL code.

**
**                                          Arg-3           Arg-4
*/
#define SQLITE_COPY                  0   /* Table Name      File Name       */
#define SQLITE_CREATE_INDEX          1   /* Index Name      Table Name      */
#define SQLITE_CREATE_TABLE          2   /* Table Name      NULL            */
#define SQLITE_CREATE_TEMP_INDEX     3   /* Index Name      Table Name      */







|







 







|
|
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.47 2003/04/24 01:45:04 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
/*
** The second parameter to the access authorization function above will
** be one of the values below.  These values signify what kind of operation
** is to be authorized.  The 3rd and 4th parameters to the authorization
** function will be parameters or NULL depending on which of the following
** codes is used as the second parameter.  The 5th parameter is the name
** of the database ("main", "temp", etc.) if applicable.  The 6th parameter
** is the name of the inner-most trigger or view that is responsible for
** the access attempt or NULL if this access attempt is directly from 
** input SQL code.
**
**                                          Arg-3           Arg-4
*/
#define SQLITE_COPY                  0   /* Table Name      File Name       */
#define SQLITE_CREATE_INDEX          1   /* Index Name      Table Name      */
#define SQLITE_CREATE_TABLE          2   /* Table Name      NULL            */
#define SQLITE_CREATE_TEMP_INDEX     3   /* Index Name      Table Name      */

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
825
826
827
828
829
830
831

832
833
834
835
836
837
838
....
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
**    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.179 2003/04/23 12:25:24 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
  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 */

  Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
  TriggerStack *trigStack;  /* Trigger actions being coded */
};

/*
 * Each trigger present in the database schema is stored as an instance of
 * struct Trigger. 
................................................................................
void sqliteAddIdxKeyType(Vdbe*, Index*);
int sqliteSelect(Parse*, Select*, int, int, Select*, int, int*);
Select *sqliteSelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
                        int,int,int);
void sqliteSelectDelete(Select*);
void sqliteSelectUnbind(Select*);
Table *sqliteSrcListLookup(Parse*, SrcList*);
int sqliteIsReadOnly(Parse*, Table*);
void sqliteDeleteFrom(Parse*, SrcList*, Expr*);
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);







|







 







>







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
....
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
**    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.180 2003/04/24 01:45:04 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
  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 */
  const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
  Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
  TriggerStack *trigStack;  /* Trigger actions being coded */
};

/*
 * Each trigger present in the database schema is stored as an instance of
 * struct Trigger. 
................................................................................
void sqliteAddIdxKeyType(Vdbe*, Index*);
int sqliteSelect(Parse*, Select*, int, int, Select*, int, int*);
Select *sqliteSelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
                        int,int,int);
void sqliteSelectDelete(Select*);
void sqliteSelectUnbind(Select*);
Table *sqliteSrcListLookup(Parse*, SrcList*);
int sqliteIsReadOnly(Parse*, Table*, int);
void sqliteDeleteFrom(Parse*, SrcList*, Expr*);
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);

Changes to src/trigger.c.

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
106
107
108
109
110
111
112





113
114
115
116
117
118
119
...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
...
652
653
654
655
656
657
658

659
660
661
662
663
664
665
666
667
668
669


670
671
672
673
674
675
676
...
680
681
682
683
684
685
686

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
** in pParse->pNewTrigger.  After the trigger actions have been parsed, the
** sqliteFinishTrigger() function is called to complete the trigger
** construction process.
*/
void sqliteBeginTrigger(
  Parse *pParse,      /* The parse context of the CREATE TRIGGER statement */
  Token *pName,       /* The name of the trigger */
  int tr_tm,          /* One of TK_BEFORE, TK_AFTER , TK_INSTEAD */
  int op,             /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
  IdList *pColumns,   /* column list if this is an UPDATE OF trigger */
  SrcList *pTableName,/* The name of the table/view the trigger applies to */
  int foreach,        /* One of TK_ROW or TK_STATEMENT */
  Expr *pWhen,        /* WHEN clause */
  int isTemp          /* True if the TEMPORARY keyword is present */
){
................................................................................
    }
    if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0, zDb)){
      goto trigger_cleanup;
    }
  }
#endif






  if (tr_tm == TK_INSTEAD){
    tr_tm = TK_BEFORE;
  }

  /* Build the Trigger object */
  nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
  if( nt==0 ) goto trigger_cleanup;
................................................................................
  int orconf,          /* ON CONFLICT policy */
  int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
){
  Trigger * pTrigger;
  TriggerStack * pTriggerStack;

  assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
  assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);

  assert(newIdx != -1 || oldIdx != -1);

  pTrigger = pTab->pTrigger;
  while( pTrigger ){
    int fire_this = 0;

................................................................................
      }
    }

    if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
      int endTrigger;
      SrcList dummyTablist;
      Expr * whenExpr;


      dummyTablist.nSrc = 0;

      /* Push an entry on to the trigger stack */
      pTriggerStack->pTrigger = pTrigger;
      pTriggerStack->newIdx = newIdx;
      pTriggerStack->oldIdx = oldIdx;
      pTriggerStack->pTab = pTab;
      pTriggerStack->pNext = pParse->trigStack;
      pTriggerStack->ignoreJump = ignoreJump;
      pParse->trigStack = pTriggerStack;



      /* code the WHEN clause */
      endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
      whenExpr = sqliteExprDup(pTrigger->pWhen);
      if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
        pParse->trigStack = pParse->trigStack->pNext;
        sqliteFree(pTriggerStack);
................................................................................
      sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
      sqliteExprDelete(whenExpr);

      codeTriggerProgram(pParse, pTrigger->step_list, orconf); 

      /* Pop the entry off the trigger stack */
      pParse->trigStack = pParse->trigStack->pNext;

      sqliteFree(pTriggerStack);

      sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
    }
    pTrigger = pTrigger->pNext;
  }

  return 0;
}

/*
 * This function is called to code ON UPDATE and ON DELETE triggers on 
 * views. 
 *
 * This function deletes the data pointed at by the pWhere and pChanges
 * arguments before it completes.
 */
void sqliteViewTriggers(
  Parse *pParse, 
  Table *pTab,         /* The view to code triggers on */
  Expr *pWhere,        /* The WHERE clause of the statement causing triggers*/
  int orconf,          /* The ON CONFLICT policy specified as part of the
			  statement causing these triggers */
  ExprList *pChanges   /* If this is an statement causing triggers to fire
			  is an UPDATE, then this list holds the columns
			  to update and the expressions to update them to.
			  See comments for sqliteUpdate(). */
){
  int oldIdx = -1;
  int newIdx = -1;
  int *aXRef = 0;   
  Vdbe *v;
  int endOfLoop;
  int startOfLoop;
  Select theSelect;
  Token tblNameToken;

  assert(pTab->pSelect);

  tblNameToken.z = pTab->zName;
  tblNameToken.n = strlen(pTab->zName);

  theSelect.isDistinct = 0;
  theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
  theSelect.pSrc   = sqliteSrcListAppend(0, &tblNameToken, 0);
  theSelect.pWhere = pWhere;    pWhere = 0;
  theSelect.pGroupBy = 0;
  theSelect.pHaving = 0;
  theSelect.pOrderBy = 0;
  theSelect.op = TK_SELECT; /* ?? */
  theSelect.pPrior = 0;
  theSelect.nLimit = -1;
  theSelect.nOffset = -1;
  theSelect.zSelect = 0;
  theSelect.base = 0;

  v = sqliteGetVdbe(pParse);
  assert(v);
  sqliteBeginWriteOperation(pParse, 1, 0);

  /* Allocate temp tables */
  oldIdx = pParse->nTab++;
  sqliteVdbeAddOp(v, OP_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;
  }

  /* loop thru the view snapshot, executing triggers for each row */
  endOfLoop = sqliteVdbeMakeLabel(v);
  sqliteVdbeAddOp(v, OP_Rewind, oldIdx, endOfLoop);

  /* Loop thru the view snapshot, executing triggers for each row */
  startOfLoop = sqliteVdbeCurrentAddr(v);

  /* Build the updated row if required */
  if( pChanges ){
    int ii;

    aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
    if( aXRef==0 ) goto trigger_cleanup;
    for(ii = 0; ii < pTab->nCol; ii++){
      aXRef[ii] = -1;
    }

    for(ii=0; ii<pChanges->nExpr; ii++){
      int jj;
      if( sqliteExprResolveIds(pParse, oldIdx, theSelect.pSrc , 0, 
            pChanges->a[ii].pExpr) ){
        goto trigger_cleanup;
      }

      if( sqliteExprCheck(pParse, pChanges->a[ii].pExpr, 0, 0) )
        goto trigger_cleanup;

      for(jj=0; jj<pTab->nCol; jj++){
        if( sqliteStrICmp(pTab->aCol[jj].zName, pChanges->a[ii].zName)==0 ){
          aXRef[jj] = ii;
          break;
        }
      }
      if( jj>=pTab->nCol ){
        sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[ii].zName);
        goto trigger_cleanup;
      }
    }

    sqliteVdbeAddOp(v, OP_Integer, 13, 0);

    for(ii = 0; ii<pTab->nCol; ii++){
      if( aXRef[ii] < 0 ){ 
        sqliteVdbeAddOp(v, OP_Column, oldIdx, ii);
      }else{
        sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
      }
    }

    sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
    sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
    sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);

    sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, 
        pTab, newIdx, oldIdx, orconf, endOfLoop);
    sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, 
        pTab, newIdx, oldIdx, orconf, endOfLoop);
  }else{
    sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx, 
        orconf, endOfLoop);
    sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx, 
        orconf, endOfLoop);
  }

  sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);

  sqliteVdbeResolveLabel(v, endOfLoop);
  sqliteEndWriteOperation(pParse);

trigger_cleanup:
  sqliteFree(aXRef);
  sqliteExprListDelete(pChanges);
  sqliteExprDelete(pWhere);
  sqliteExprListDelete(theSelect.pEList);
  sqliteSrcListDelete(theSelect.pSrc);
  sqliteExprDelete(theSelect.pWhere);
  return;
}







|







 







>
>
>
>
>







 







|







 







>











>
>







 







>









<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
...
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
...
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704














































































































































** in pParse->pNewTrigger.  After the trigger actions have been parsed, the
** sqliteFinishTrigger() function is called to complete the trigger
** construction process.
*/
void sqliteBeginTrigger(
  Parse *pParse,      /* The parse context of the CREATE TRIGGER statement */
  Token *pName,       /* The name of the trigger */
  int tr_tm,          /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
  int op,             /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
  IdList *pColumns,   /* column list if this is an UPDATE OF trigger */
  SrcList *pTableName,/* The name of the table/view the trigger applies to */
  int foreach,        /* One of TK_ROW or TK_STATEMENT */
  Expr *pWhen,        /* WHEN clause */
  int isTemp          /* True if the TEMPORARY keyword is present */
){
................................................................................
    }
    if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0, zDb)){
      goto trigger_cleanup;
    }
  }
#endif

  /* INSTEAD OF triggers can only appear on views and BEGIN triggers
  ** cannot appear on views.  So we might as well translate every
  ** INSTEAD OF trigger into a BEFORE trigger.  It simplifies code
  ** elsewhere.
  */
  if (tr_tm == TK_INSTEAD){
    tr_tm = TK_BEFORE;
  }

  /* Build the Trigger object */
  nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
  if( nt==0 ) goto trigger_cleanup;
................................................................................
  int orconf,          /* ON CONFLICT policy */
  int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
){
  Trigger * pTrigger;
  TriggerStack * pTriggerStack;

  assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
  assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER );

  assert(newIdx != -1 || oldIdx != -1);

  pTrigger = pTab->pTrigger;
  while( pTrigger ){
    int fire_this = 0;

................................................................................
      }
    }

    if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
      int endTrigger;
      SrcList dummyTablist;
      Expr * whenExpr;
      const char *zSavedAuthContext;

      dummyTablist.nSrc = 0;

      /* Push an entry on to the trigger stack */
      pTriggerStack->pTrigger = pTrigger;
      pTriggerStack->newIdx = newIdx;
      pTriggerStack->oldIdx = oldIdx;
      pTriggerStack->pTab = pTab;
      pTriggerStack->pNext = pParse->trigStack;
      pTriggerStack->ignoreJump = ignoreJump;
      pParse->trigStack = pTriggerStack;
      zSavedAuthContext = pParse->zAuthContext;
      pParse->zAuthContext = pTrigger->name;

      /* code the WHEN clause */
      endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
      whenExpr = sqliteExprDup(pTrigger->pWhen);
      if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
        pParse->trigStack = pParse->trigStack->pNext;
        sqliteFree(pTriggerStack);
................................................................................
      sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
      sqliteExprDelete(whenExpr);

      codeTriggerProgram(pParse, pTrigger->step_list, orconf); 

      /* Pop the entry off the trigger stack */
      pParse->trigStack = pParse->trigStack->pNext;
      pParse->zAuthContext = zSavedAuthContext;
      sqliteFree(pTriggerStack);

      sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
    }
    pTrigger = pTrigger->pNext;
  }

  return 0;
}














































































































































Changes to src/update.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
44
45
46
47
48
49
50

51
52
53
54
55
56
57
..
65
66
67
68
69
70
71
72
73
74
75
76
77

78
79

80
81
82
83
84
85
86
...
186
187
188
189
190
191
192









193
194
195
196
197
198
199
...
222
223
224
225
226
227
228

229
230

231
232
233
234
235
236
237
...
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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
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
**    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.62 2003/04/22 20:30:40 drh Exp $
*/
#include "sqliteInt.h"

/*
** Process an UPDATE statement.
**
**   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
................................................................................
  char *aIdxUsed = 0;    /* aIdxUsed[i]==1 if the i-th index is used */
  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       */
................................................................................
  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 cursors for the
  ** special OLD and NEW tables
  */
................................................................................
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->iDb==1);










  /* Begin the database scan
  */
  pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0);
  if( pWInfo==0 ) goto update_cleanup;

  /* Remember the index of every item to be updated.
................................................................................
    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);
................................................................................
        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


  ** open every index that needs updating.  Note that if any
  ** index could potentially invoke a REPLACE conflict resolution 
  ** action, then we need to open all indices because we might need
  ** to be deleting some records.
  */
  sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
  sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
  if( onError==OE_Replace ){
    openAll = 1;
  }else{
    openAll = 0;
    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
      if( pIdx->onError==OE_Replace ){
        openAll = 1;
        break;
      }
    }
  }
  for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
    if( openAll || aIdxUsed[i] ){
      sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
      sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum);
      assert( pParse->nTab>base+i+1 );
    }
  }

  /* Loop over every record that needs updating.  We have to load
  ** the old data for each record to be updated because some columns
  ** might not change and we will need to copy the old value.
  ** Also, the old data is needed to delete the old index entires.
  ** So make the cursor point at the old record.
  */
  if( !row_triggers_exist ){
    sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
    addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
    sqliteVdbeAddOp(v, OP_Dup, 0, 0);
  }
  sqliteVdbeAddOp(v, OP_NotExists, base, addr);

  /* If the record number will change, push the record number as it
  ** will be after the update. (The old record number is currently
  ** on top of the stack.)
  */
  if( chngRecno ){
    sqliteExprCode(pParse, pRecnoExpr);
    sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
  }

  /* Compute new data for this record.  
  */
  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);
    }
  }

  /* Do constraint checks
  */
  sqliteGenerateConstraintChecks(pParse, pTab, base, aIdxUsed, chngRecno, 1,
                                 onError, addr);

  /* Delete the old indices for the current record.
  */
  sqliteGenerateRowIndexDelete(db, v, pTab, base, aIdxUsed);

  /* If changing the record number, delete the old record.
  */
  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);
  }




  if( row_triggers_exist ){

    for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
      if( openAll || aIdxUsed[i] )
        sqliteVdbeAddOp(v, OP_Close, base+i+1, 0);
    }
    sqliteVdbeAddOp(v, OP_Close, base, 0);
    pParse->nTab = base;

    if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, 
          newIdx, oldIdx, onError, addr) ){
      goto update_cleanup;
    }
  }

  /* Repeat the above with the next record to be updated, until







|







 







>







 







|
|
|
<
<

>
|
<
>







 







>
>
>
>
>
>
>
>
>







 







>
|
|
>







 







>
|
|
>
|







<
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|

|
|
|

|
|
|
|
|

|
|
|
|
>






>
>
>

>
|
|
|
|
|
|
|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
..
66
67
68
69
70
71
72
73
74
75


76
77
78

79
80
81
82
83
84
85
86
...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
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
390
391
392
**    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.63 2003/04/24 01:45:05 drh Exp $
*/
#include "sqliteInt.h"

/*
** Process an UPDATE statement.
**
**   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
................................................................................
  char *aIdxUsed = 0;    /* aIdxUsed[i]==1 if the i-th index is used */
  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 isView;            /* Trying to update a view */

  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       */
................................................................................
  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;
  isView = pTab->pSelect!=0;
  if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
    goto update_cleanup;


  }
  if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
    goto update_cleanup;

  }
  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 cursors for the
  ** special OLD and NEW tables
  */
................................................................................
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto update_cleanup;
  sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->iDb==1);

  /* If we are trying to update a view, construct that view into
  ** a temporary table.
  */
  if( isView ){
    Select *pView = sqliteSelectDup(pTab->pSelect);
    sqliteSelect(pParse, pView, SRT_TempTable, base, 0, 0, 0);
    sqliteSelectDelete(pView);
  }

  /* Begin the database scan
  */
  pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0);
  if( pWInfo==0 ) goto update_cleanup;

  /* Remember the index of every item to be updated.
................................................................................
    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);
    if( !isView ){
      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);
................................................................................
        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);
    if( !isView ){
      sqliteVdbeAddOp(v, OP_Close, base, 0);
    }

    /* Fire the BEFORE and INSTEAD OF triggers
    */
    if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, 
          newIdx, oldIdx, onError, addr) ){
      goto update_cleanup;
    }
  }


  if( !isView ){
    /* 
    ** Open every index that needs updating.  Note that if any
    ** index could potentially invoke a REPLACE conflict resolution 
    ** action, then we need to open all indices because we might need
    ** to be deleting some records.
    */
    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
    sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
    if( onError==OE_Replace ){
      openAll = 1;
    }else{
      openAll = 0;
      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
        if( pIdx->onError==OE_Replace ){
          openAll = 1;
          break;
        }
      }
    }
    for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
      if( openAll || aIdxUsed[i] ){
        sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
        sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum);
        assert( pParse->nTab>base+i+1 );
      }
    }

    /* Loop over every record that needs updating.  We have to load
    ** the old data for each record to be updated because some columns
    ** might not change and we will need to copy the old value.
    ** Also, the old data is needed to delete the old index entires.
    ** So make the cursor point at the old record.
    */
    if( !row_triggers_exist ){
      sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
      addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
      sqliteVdbeAddOp(v, OP_Dup, 0, 0);
    }
    sqliteVdbeAddOp(v, OP_NotExists, base, addr);

    /* If the record number will change, push the record number as it
    ** will be after the update. (The old record number is currently
    ** on top of the stack.)
    */
    if( chngRecno ){
      sqliteExprCode(pParse, pRecnoExpr);
      sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
    }

    /* Compute new data for this record.  
    */
    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);
      }
    }

    /* Do constraint checks
    */
    sqliteGenerateConstraintChecks(pParse, pTab, base, aIdxUsed, chngRecno, 1,
                                   onError, addr);

    /* Delete the old indices for the current record.
    */
    sqliteGenerateRowIndexDelete(db, v, pTab, base, aIdxUsed);

    /* If changing the record number, delete the old record.
    */
    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);
  }

  /* If there are triggers, close all the cursors after each iteration
  ** through the loop.  The fire the after triggers.
  */
  if( row_triggers_exist ){
    if( !isView ){
      for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
        if( openAll || aIdxUsed[i] )
          sqliteVdbeAddOp(v, OP_Close, base+i+1, 0);
      }
      sqliteVdbeAddOp(v, OP_Close, base, 0);
      pParse->nTab = base;
    }
    if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, 
          newIdx, oldIdx, onError, addr) ){
      goto update_cleanup;
    }
  }

  /* Repeat the above with the next record to be updated, until

Changes to src/where.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1140
1141
1142
1143
1144
1145
1146
1147


1148
1149
1150
1151
1152
1153
1154
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
** the WHERE clause of SQL statements.
**
** $Id: where.c,v 1.76 2003/04/20 17:29:24 drh Exp $
*/
#include "sqliteInt.h"

/*
** The query generator uses an array of instances of this structure to
** help it analyze the subexpressions of the WHERE clause.  Each WHERE
** clause subexpression is separated from the others by an AND operator.
................................................................................
        sqliteVdbeAddOp(v, OP_NullRow, pLevel->iCur, 0);
      }
      sqliteVdbeAddOp(v, OP_Goto, 0, pLevel->top);
    }
  }
  sqliteVdbeResolveLabel(v, pWInfo->iBreak);
  for(i=0; i<pTabList->nSrc; i++){
    if( pTabList->a[i].pTab->isTransient ) continue;


    pLevel = &pWInfo->a[i];
    sqliteVdbeAddOp(v, OP_Close, base+i, 0);
    if( pLevel->pIdx!=0 ){
      sqliteVdbeAddOp(v, OP_Close, pLevel->iCur, 0);
    }
  }
#if 0  /* Never reuse a cursor */







|







 







|
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
** the WHERE clause of SQL statements.
**
** $Id: where.c,v 1.77 2003/04/24 01:45:05 drh Exp $
*/
#include "sqliteInt.h"

/*
** The query generator uses an array of instances of this structure to
** help it analyze the subexpressions of the WHERE clause.  Each WHERE
** clause subexpression is separated from the others by an AND operator.
................................................................................
        sqliteVdbeAddOp(v, OP_NullRow, pLevel->iCur, 0);
      }
      sqliteVdbeAddOp(v, OP_Goto, 0, pLevel->top);
    }
  }
  sqliteVdbeResolveLabel(v, pWInfo->iBreak);
  for(i=0; i<pTabList->nSrc; i++){
    Table *pTab = pTabList->a[i].pTab;
    assert( pTab!=0 );
    if( pTab->isTransient || pTab->pSelect ) continue;
    pLevel = &pWInfo->a[i];
    sqliteVdbeAddOp(v, OP_Close, base+i, 0);
    if( pLevel->pIdx!=0 ){
      sqliteVdbeAddOp(v, OP_Close, pLevel->iCur, 0);
    }
  }
#if 0  /* Never reuse a cursor */

Changes to test/trigger2.test.

583
584
585
586
587
588
589

590
591
592
593
594
595
596
597
598
599
600
601
602
603
























































































604
   CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN
    INSERT INTO tlog VALUES(NULL, 
	0, 0, 0, 0, new.a, new.b, new.c, new.d);
   END;
  }
} {}


do_test trigger2-7.2 {
  execsql {
    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
    DELETE FROM abcd WHERE a = 1;
    INSERT INTO abcd VALUES(10, 20, 30, 40);
    SELECT * FROM tlog;
  }
} [ list 1 1 2 3 4 100 25 3 4 \
         2 1 2 3 4 100 25 3 4 \
	 3 1 2 3 4 0 0 0 0 \
	 4 1 2 3 4 0 0 0 0 \
	 5 0 0 0 0 10 20 30 40 \
	 6 0 0 0 0 10 20 30 40 ]

























































































finish_test







>














>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
   CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN
    INSERT INTO tlog VALUES(NULL, 
	0, 0, 0, 0, new.a, new.b, new.c, new.d);
   END;
  }
} {}

#explain {delete from abcd where a=1;}
do_test trigger2-7.2 {
  execsql {
    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
    DELETE FROM abcd WHERE a = 1;
    INSERT INTO abcd VALUES(10, 20, 30, 40);
    SELECT * FROM tlog;
  }
} [ list 1 1 2 3 4 100 25 3 4 \
         2 1 2 3 4 100 25 3 4 \
	 3 1 2 3 4 0 0 0 0 \
	 4 1 2 3 4 0 0 0 0 \
	 5 0 0 0 0 10 20 30 40 \
	 6 0 0 0 0 10 20 30 40 ]

do_test trigger2-7.3 {
  execsql {
    DELETE FROM tlog;
    INSERT INTO abcd VALUES(10, 20, 30, 40);
    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
    DELETE FROM abcd WHERE a = 1;
    SELECT * FROM tlog;
  }
} [ list \
   1 0 0 0 0 10 20 30 40 \
   2 0 0 0 0 10 20 30 40 \
   3 1 2 3 4 100 25 3 4 \
   4 1 2 3 4 100 25 3 4 \
   5 1 2 3 4 0 0 0 0 \
   6 1 2 3 4 0 0 0 0 \
]
do_test trigger2-7.4 {
  execsql {
    DELETE FROM tlog;
    DELETE FROM abcd WHERE a = 1;
    INSERT INTO abcd VALUES(10, 20, 30, 40);
    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
    SELECT * FROM tlog;
  }
} [ list \
   1 1 2 3 4 0 0 0 0 \
   2 1 2 3 4 0 0 0 0 \
   3 0 0 0 0 10 20 30 40 \
   4 0 0 0 0 10 20 30 40 \
   5 1 2 3 4 100 25 3 4 \
   6 1 2 3 4 100 25 3 4 \
]

do_test trigger2-8.1 {
  execsql {
    CREATE TABLE t1(a,b,c);
    INSERT INTO t1 VALUES(1,2,3);
    CREATE VIEW v1 AS
      SELECT a+b AS x, b+c AS y, a+c AS z FROM t1;
    SELECT * FROM v1;
  }
} {3 5 4}
do_test trigger2-8.2 {
  execsql {
    CREATE TABLE v1log(a,b,c,d,e,f);
    CREATE TRIGGER r1 INSTEAD OF DELETE ON v1 BEGIN
      INSERT INTO v1log VALUES(OLD.x,NULL,OLD.y,NULL,OLD.z,NULL);
    END;
    DELETE FROM v1 WHERE x=1;
    SELECT * FROM v1log;
  }
} {}
do_test trigger2-8.3 {
  execsql {
    DELETE FROM v1 WHERE x=3;
    SELECT * FROM v1log;
  }
} {3 {} 5 {} 4 {}}
do_test trigger2-8.4 {
  execsql {
    INSERT INTO t1 VALUES(4,5,6);
    DELETE FROM v1log;
    DELETE FROM v1 WHERE y=11;
    SELECT * FROM v1log;
  }
} {9 {} 11 {} 10 {}}
do_test trigger2-8.5 {
  execsql {
    CREATE TRIGGER r2 INSTEAD OF INSERT ON v1 BEGIN
      INSERT INTO v1log VALUES(NULL,NEW.x,NULL,NEW.y,NULL,NEW.z);
    END;
    DELETE FROM v1log;
    INSERT INTO v1 VALUES(1,2,3);
    SELECT * FROM v1log;
  }
} {{} 1 {} 2 {} 3}
do_test trigger2-8.6 {
  execsql {
    CREATE TRIGGER r3 INSTEAD OF UPDATE ON v1 BEGIN
      INSERT INTO v1log VALUES(OLD.x,NEW.x,OLD.y,NEW.y,OLD.z,NEW.z);
    END;
    DELETE FROM v1log;
    UPDATE v1 SET x=x+100, y=y+200, z=z+300;
    SELECT * FROM v1log;
  }
} {3 103 5 205 4 304 9 109 11 211 10 310}


finish_test

Changes to test/view.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing VIEW statements.
#
# $Id: view.test,v 1.12 2002/12/03 02:22:53 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test view-1.0 {
  execsql {
    CREATE TABLE t1(a,b,c);
    INSERT INTO t1 VALUES(1,2,3);
................................................................................
    SELECT * FROM v2;
  }
} {x 7 a 8 b 9 c 10}
do_test view-2.2 {
  catchsql {
    INSERT INTO v2 VALUES(1,2,3,4);
  }
} {1 {view v2 may not be modified}}
do_test view-2.3 {
  catchsql {
    UPDATE v2 SET a=10 WHERE a=5;
  }
} {1 {view v2 may not be modified}}
do_test view-2.4 {
  catchsql {
    DELETE FROM v2;
  }
} {1 {view v2 may not be modified}}
do_test view-2.5 {
  execsql {
    INSERT INTO t1 VALUES(11,12,13,14);
    SELECT * FROM v2 ORDER BY x;
  }
} {7 8 9 10 11 12 13 14}
do_test view-2.6 {







|







 







|




|




|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing VIEW statements.
#
# $Id: view.test,v 1.13 2003/04/24 01:45:05 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test view-1.0 {
  execsql {
    CREATE TABLE t1(a,b,c);
    INSERT INTO t1 VALUES(1,2,3);
................................................................................
    SELECT * FROM v2;
  }
} {x 7 a 8 b 9 c 10}
do_test view-2.2 {
  catchsql {
    INSERT INTO v2 VALUES(1,2,3,4);
  }
} {1 {cannot modify v2 because it is a view}}
do_test view-2.3 {
  catchsql {
    UPDATE v2 SET a=10 WHERE a=5;
  }
} {1 {cannot modify v2 because it is a view}}
do_test view-2.4 {
  catchsql {
    DELETE FROM v2;
  }
} {1 {cannot modify v2 because it is a view}}
do_test view-2.5 {
  execsql {
    INSERT INTO t1 VALUES(11,12,13,14);
    SELECT * FROM v2 ORDER BY x;
  }
} {7 8 9 10 11 12 13 14}
do_test view-2.6 {