/ Check-in [7202d4f1]
Login

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

Overview
Comment:Tighter binding of views, triggers, and indices to their respective databases. Ticket #323. Much more testing needs to be done to the sqliteFix...() routines in attach.c. (CVS 990)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7202d4f1a8853368954a967b7ccca9d8a6645a2e
User & Date: drh 2003-05-31 16:21:12
Context
2003-06-01
01:10
Add the ability to INSERT or UPDATE using the "rowid" column even on tables that lack an integer primary key. (CVS 991) check-in: 903adbe8 user: drh tags: trunk
2003-05-31
16:21
Tighter binding of views, triggers, and indices to their respective databases. Ticket #323. Much more testing needs to be done to the sqliteFix...() routines in attach.c. (CVS 990) check-in: 7202d4f1 user: drh tags: trunk
2003-05-29
17:50
Change the row-size limit back to 1MB. It was temporarily raised to 16MB. We'll probably move it to 16MB eventually, but not just yet. (CVS 989) check-in: b84c4035 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/attach.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
124
125
126
127
128
129
130





































































































































**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
** $Id: attach.c,v 1.3 2003/04/15 19:22:23 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is called by the parser to process an ATTACH statement:
**
**     ATTACH DATABASE filename AS dbname
................................................................................
  db->nDb--;
  if( i<db->nDb ){
    db->aDb[i] = db->aDb[db->nDb];
    memset(&db->aDb[db->nDb], 0, sizeof(db->aDb[0]));
    sqliteResetInternalSchema(db, i);
  }
}












































































































































|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
** $Id: attach.c,v 1.4 2003/05/31 16:21:12 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is called by the parser to process an ATTACH statement:
**
**     ATTACH DATABASE filename AS dbname
................................................................................
  db->nDb--;
  if( i<db->nDb ){
    db->aDb[i] = db->aDb[db->nDb];
    memset(&db->aDb[db->nDb], 0, sizeof(db->aDb[0]));
    sqliteResetInternalSchema(db, i);
  }
}

/*
** Initialize a DbFixer structure.  This routine must be called prior
** to passing the structure to one of the sqliteFixAAAA() routines below.
**
** The return value indicates whether or not fixation is required.  TRUE
** means we do need to fix the database references, FALSE means we do not.
*/
int sqliteFixInit(
  DbFixer *pFix,      /* The fixer to be initialized */
  Parse *pParse,      /* Error messages will be written here */
  int iDb,            /* This is the database that must must be used */
  const char *zType,  /* "view", "trigger", or "index" */
  const Token *pName  /* Name of the view, trigger, or index */
){
  sqlite *db;

  if( iDb<0 || iDb==1 ) return 0;
  db = pParse->db;
  assert( db->nDb>iDb );
  pFix->zDb = db->aDb[iDb].zName;
  pFix->zType = zType;
  pFix->pName = pName;
  return 1;
}

/*
** The following set of routines walk through the parse tree and assign
** a specific database to all table references where the database name
** was left unspecified in the original SQL statement.  The pFix structure
** must have been initialized by a prior call to sqliteFixInit().
**
** These routines are used to make sure that an index, trigger, or
** view in one database does not refer to objects in a different database.
** (Exception: indices, triggers, and views in the TEMP database are
** allowed to refer to anything.)  If a reference is explicitly made
** to an object in a different database, an error message is added to
** pParse->zErrMsg and these routines return non-zero.  If everything
** checks out, these routines return 0.
*/
int sqliteFixSrcList(
  DbFixer *pFix,       /* Context of the fixation */
  SrcList *pList       /* The Source list to check and modify */
){
  int i;
  const char *zDb;

  if( pList==0 ) return 0;
  zDb = pFix->zDb;
  for(i=0; i<pList->nSrc; i++){
    if( pList->a[i].zDatabase==0 ){
      pList->a[i].zDatabase = sqliteStrDup(zDb);
    }else if( sqliteStrICmp(pList->a[i].zDatabase,zDb)!=0 ){
      sqliteErrorMsg(pFix->pParse,
         "%s %.*s cannot reference objects in database %s",
         pFix->zType, pFix->pName->n, pFix->pName->z, pList->a[i].zDatabase);
      return 1;
    }
    if( sqliteFixSelect(pFix, pList->a[i].pSelect) ) return 1;
    if( sqliteFixExpr(pFix, pList->a[i].pOn) ) return 1;
  }
  return 0;
}
int sqliteFixSelect(
  DbFixer *pFix,       /* Context of the fixation */
  Select *pSelect      /* The SELECT statement to be fixed to one database */
){
  while( pSelect ){
    if( sqliteFixExprList(pFix, pSelect->pEList) ){
      return 1;
    }
    if( sqliteFixSrcList(pFix, pSelect->pSrc) ){
      return 1;
    }
    if( sqliteFixExpr(pFix, pSelect->pWhere) ){
      return 1;
    }
    if( sqliteFixExpr(pFix, pSelect->pHaving) ){
      return 1;
    }
    pSelect = pSelect->pPrior;
  }
  return 0;
}
int sqliteFixExpr(
  DbFixer *pFix,     /* Context of the fixation */
  Expr *pExpr        /* The expression to be fixed to one database */
){
  while( pExpr ){
    if( sqliteFixSelect(pFix, pExpr->pSelect) ){
      return 1;
    }
    if( sqliteFixExprList(pFix, pExpr->pList) ){
      return 1;
    }
    if( sqliteFixExpr(pFix, pExpr->pRight) ){
      return 1;
    }
    pExpr = pExpr->pLeft;
  }
  return 0;
}
int sqliteFixExprList(
  DbFixer *pFix,     /* Context of the fixation */
  ExprList *pList    /* The expression to be fixed to one database */
){
  int i;
  if( pList==0 ) return 0;
  for(i=0; i<pList->nExpr; i++){
    if( sqliteFixExpr(pFix, pList->a[i].pExpr) ){
      return 1;
    }
  }
  return 0;
}
int sqliteFixTriggerStep(
  DbFixer *pFix,     /* Context of the fixation */
  TriggerStep *pStep /* The trigger step be fixed to one database */
){
  while( pStep ){
    if( sqliteFixSelect(pFix, pStep->pSelect) ){
      return 1;
    }
    if( sqliteFixExpr(pFix, pStep->pWhere) ){
      return 1;
    }
    if( sqliteFixExprList(pFix, pStep->pExprList) ){
      return 1;
    }
    pStep = pStep->pNext;
  }
  return 0;
}

Changes to src/build.c.

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...
113
114
115
116
117
118
119






120
121
122
123
124
125
126
...
133
134
135
136
137
138
139

140
141
142
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183






184
185
186
187
188
189
190
....
1042
1043
1044
1045
1046
1047
1048

1049
1050
1051
1052






1053
1054
1055
1056
1057
1058
1059
....
1530
1531
1532
1533
1534
1535
1536
1537

1538
1539
1540






1541
1542
1543
1544
1545
1546
1547
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.154 2003/05/17 19:04:04 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
................................................................................
}

/*
** Locate the in-memory structure that describes 
** a particular database table given the name
** of that table and (optionally) the name of the database
** containing the table.  Return NULL if not found.






**
** See also sqliteLocateTable().
*/
Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
  Table *p = 0;
  int i;
  for(i=0; i<db->nDb; i++){
................................................................................
}

/*
** Locate the in-memory structure that describes 
** a particular database table given the name
** of that table and (optionally) the name of the database
** containing the table.  Return NULL if not found.

**
** If pParse->useDb is not negative, then the table must be
** located in that database.  If a different database is specified,
** an error message is generated into pParse->zErrMsg.

*/
Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){
  sqlite *db;
  const char *zUse;
  Table *p;
  db = pParse->db;
  if( pParse->useDb<0 ){
    p = sqliteFindTable(db, zName, zDbase);
  }else {
    assert( pParse->useDb<db->nDb );
    assert( db->aDb[pParse->useDb].pBt!=0 );
    zUse = db->aDb[pParse->useDb].zName;
    if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){
      sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase);
      return 0;
    }
    p = sqliteFindTable(db, zName, zUse);
    if( p==0 && pParse->useDb==1 && zDbase==0 ){
      p = sqliteFindTable(db, zName, 0);
    }
  }
  if( p==0 ){
    if( zDbase ){
      sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
    }else if( (pParse->useDb==0 || pParse->useDb>=2) 
               && sqliteFindTable(db, zName, 0)!=0 ){
      sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"",
         zName, zUse);
    }else{
      sqliteErrorMsg(pParse, "no such table: %s", zName);
    }
  }
  return p;
}

/*
** Locate the in-memory structure that describes 
** a particular index given the name of that index
** and the name of the database that contains the index.
** Return NULL if not found.






*/
Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){
  Index *p = 0;
  int i;
  for(i=0; i<db->nDb; i++){
    int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */
    if( zDb && sqliteStrICmp(zDb, db->aDb[j].zName) ) continue;
................................................................................
  Select *pSelect,   /* A SELECT statement that will become the new view */
  int isTemp         /* TRUE for a TEMPORARY view */
){
  Table *p;
  int n;
  const char *z;
  Token sEnd;


  sqliteStartTable(pParse, pBegin, pName, isTemp, 1);
  p = pParse->pNewTable;
  if( p==0 || pParse->nErr ){






    sqliteSelectDelete(pSelect);
    return;
  }

  /* Make a copy of the entire SELECT statement that defines the view.
  ** This will force all the Expr.token.z values to be dynamically
  ** allocated rather than point to the input string - which means that
................................................................................
  Token *pStart,   /* The CREATE token that begins a CREATE TABLE statement */
  Token *pEnd      /* The ")" that closes the CREATE INDEX statement */
){
  Table *pTab;     /* Table to be indexed */
  Index *pIndex;   /* The index to be created */
  char *zName = 0;
  int i, j;
  Token nullId;             /* Fake token for an empty ID list */

  sqlite *db = pParse->db;

  if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;







  /*
  ** Find the table that is to be indexed.  Return early if not found.
  */
  if( pTable!=0 ){
    assert( pName!=0 );
    assert( pTable->nSrc==1 );







|







 







>
>
>
>
>
>







 







>

<
|
|
>


<
<

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



<
|

|












>
>
>
>
>
>







 







>




>
>
>
>
>
>







 







|
>



>
>
>
>
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
139
140
141
142
143
144
145
146
147

148
149
150
151
152


153










154


155


156
157
158

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
....
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
....
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.155 2003/05/31 16:21:12 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
................................................................................
}

/*
** Locate the in-memory structure that describes 
** a particular database table given the name
** of that table and (optionally) the name of the database
** containing the table.  Return NULL if not found.
**
** If zDatabase is 0, all databases are searched for the
** table and the first matching table is returned.  (No checking
** for duplicate table names is done.)  The search order is
** TEMP first, then MAIN, then any auxiliary databases added
** using the ATTACH command.
**
** See also sqliteLocateTable().
*/
Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
  Table *p = 0;
  int i;
  for(i=0; i<db->nDb; i++){
................................................................................
}

/*
** Locate the in-memory structure that describes 
** a particular database table given the name
** of that table and (optionally) the name of the database
** containing the table.  Return NULL if not found.
** Also leave an error message in pParse->zErrMsg.
**

** The difference between this routine and sqliteFindTable()
** is that this routine leaves an error message in pParse->zErrMsg
** where sqliteFindTable() does not.
*/
Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){


  Table *p;













  p = sqliteFindTable(pParse->db, zName, zDbase);


  if( p==0 ){
    if( zDbase ){
      sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);

    }else if( sqliteFindTable(pParse->db, zName, 0)!=0 ){
      sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"",
         zName, zDbase);
    }else{
      sqliteErrorMsg(pParse, "no such table: %s", zName);
    }
  }
  return p;
}

/*
** Locate the in-memory structure that describes 
** a particular index given the name of that index
** and the name of the database that contains the index.
** Return NULL if not found.
**
** If zDatabase is 0, all databases are searched for the
** table and the first matching index is returned.  (No checking
** for duplicate index names is done.)  The search order is
** TEMP first, then MAIN, then any auxiliary databases added
** using the ATTACH command.
*/
Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){
  Index *p = 0;
  int i;
  for(i=0; i<db->nDb; i++){
    int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */
    if( zDb && sqliteStrICmp(zDb, db->aDb[j].zName) ) continue;
................................................................................
  Select *pSelect,   /* A SELECT statement that will become the new view */
  int isTemp         /* TRUE for a TEMPORARY view */
){
  Table *p;
  int n;
  const char *z;
  Token sEnd;
  DbFixer sFix;

  sqliteStartTable(pParse, pBegin, pName, isTemp, 1);
  p = pParse->pNewTable;
  if( p==0 || pParse->nErr ){
    sqliteSelectDelete(pSelect);
    return;
  }
  if( sqliteFixInit(&sFix, pParse, p->iDb, "view", pName)
    && sqliteFixSelect(&sFix, pSelect)
  ){
    sqliteSelectDelete(pSelect);
    return;
  }

  /* Make a copy of the entire SELECT statement that defines the view.
  ** This will force all the Expr.token.z values to be dynamically
  ** allocated rather than point to the input string - which means that
................................................................................
  Token *pStart,   /* The CREATE token that begins a CREATE TABLE statement */
  Token *pEnd      /* The ")" that closes the CREATE INDEX statement */
){
  Table *pTab;     /* Table to be indexed */
  Index *pIndex;   /* The index to be created */
  char *zName = 0;
  int i, j;
  Token nullId;    /* Fake token for an empty ID list */
  DbFixer sFix;    /* For assigning database names to pTable */
  sqlite *db = pParse->db;

  if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
  if( !isTemp && pParse->initFlag 
     && sqliteFixInit(&sFix, pParse, pParse->iDb, "index", pName)
     && sqliteFixSrcList(&sFix, pTable)
  ){
    goto exit_create_index;
  }

  /*
  ** Find the table that is to be indexed.  Return early if not found.
  */
  if( pTable!=0 ){
    assert( pName!=0 );
    assert( pTable->nSrc==1 );

Changes to src/expr.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
187
188
189
190
191
192
193

194
195
196
197
198
199
200
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.95 2003/05/02 14:32:13 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** Construct a new expression node and return a pointer to it.  Memory
** for this node is obtained from sqliteMalloc().  The calling function
................................................................................
  int nByte;
  if( p==0 ) return 0;
  nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
  pNew = sqliteMalloc( nByte );
  if( pNew==0 ) return 0;
  pNew->nSrc = p->nSrc;
  for(i=0; i<p->nSrc; i++){

    pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
    pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias);
    pNew->a[i].jointype = p->a[i].jointype;
    pNew->a[i].iCursor = p->a[i].iCursor;
    pNew->a[i].pTab = 0;
    pNew->a[i].pSelect = sqliteSelectDup(p->a[i].pSelect);
    pNew->a[i].pOn = sqliteExprDup(p->a[i].pOn);







|







 







>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.96 2003/05/31 16:21:12 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** Construct a new expression node and return a pointer to it.  Memory
** for this node is obtained from sqliteMalloc().  The calling function
................................................................................
  int nByte;
  if( p==0 ) return 0;
  nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
  pNew = sqliteMalloc( nByte );
  if( pNew==0 ) return 0;
  pNew->nSrc = p->nSrc;
  for(i=0; i<p->nSrc; i++){
    pNew->a[i].zDatabase = sqliteStrDup(p->a[i].zDatabase);
    pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
    pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias);
    pNew->a[i].jointype = p->a[i].jointype;
    pNew->a[i].iCursor = p->a[i].iCursor;
    pNew->a[i].pTab = 0;
    pNew->a[i].pSelect = sqliteSelectDup(p->a[i].pSelect);
    pNew->a[i].pOn = sqliteExprDup(p->a[i].pOn);

Changes to src/main.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
...
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.131 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................
        ** or executed.  All the parser does is build the internal data
        ** structures that describe the table, index, or view.
        */
        memset(&sParse, 0, sizeof(sParse));
        sParse.db = pData->db;
        sParse.initFlag = 1;
        sParse.iDb = atoi(argv[4]);
        sParse.useDb = -1;
        sParse.newTnum = atoi(argv[2]);
        sParse.useCallback = 1;
        sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
      }else{
        /* If the SQL column is blank it means this is an index that
        ** was created to be the PRIMARY KEY or to fulfill a UNIQUE
        ** constraint for a CREATE TABLE.  The index should have already
................................................................................
  /* Read the schema information out of the schema tables
  */
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.xCallback = sqliteInitCallback;
  sParse.pArg = (void*)&initData;
  sParse.initFlag = 1;
  sParse.useDb = -1;
  sParse.useCallback = 1;
  if( iDb==0 ){
    sqliteRunParser(&sParse,
        db->file_format>=2 ? init_script : older_init_script,
        pzErrMsg);
  }else{
    char *zSql = 0;
................................................................................
    return SQLITE_ERROR;
  }
  if( db->pVdbe==0 ){ db->nChange = 0; }
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.xCallback = xCallback;
  sParse.pArg = pArg;
  sParse.useDb = -1;
  sParse.useCallback = ppVm==0;
  if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
  sqliteRunParser(&sParse, zSql, pzErrMsg);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
    sqliteRollbackAll(db);







|







 







<







 







<







 







<







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
76
77
78
79
80
81
82

83
84
85
86
87
88
89
...
327
328
329
330
331
332
333

334
335
336
337
338
339
340
...
617
618
619
620
621
622
623

624
625
626
627
628
629
630
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.132 2003/05/31 16:21:12 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................
        ** or executed.  All the parser does is build the internal data
        ** structures that describe the table, index, or view.
        */
        memset(&sParse, 0, sizeof(sParse));
        sParse.db = pData->db;
        sParse.initFlag = 1;
        sParse.iDb = atoi(argv[4]);

        sParse.newTnum = atoi(argv[2]);
        sParse.useCallback = 1;
        sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
      }else{
        /* If the SQL column is blank it means this is an index that
        ** was created to be the PRIMARY KEY or to fulfill a UNIQUE
        ** constraint for a CREATE TABLE.  The index should have already
................................................................................
  /* Read the schema information out of the schema tables
  */
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.xCallback = sqliteInitCallback;
  sParse.pArg = (void*)&initData;
  sParse.initFlag = 1;

  sParse.useCallback = 1;
  if( iDb==0 ){
    sqliteRunParser(&sParse,
        db->file_format>=2 ? init_script : older_init_script,
        pzErrMsg);
  }else{
    char *zSql = 0;
................................................................................
    return SQLITE_ERROR;
  }
  if( db->pVdbe==0 ){ db->nChange = 0; }
  memset(&sParse, 0, sizeof(sParse));
  sParse.db = db;
  sParse.xCallback = xCallback;
  sParse.pArg = pArg;

  sParse.useCallback = ppVm==0;
  if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
  sqliteRunParser(&sParse, zSql, pzErrMsg);
  if( sqlite_malloc_failed ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    sParse.rc = SQLITE_NOMEM;
    sqliteRollbackAll(db);

Changes to src/select.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1653
1654
1655
1656
1657
1658
1659

1660
1661
1662
1663
1664
1665
1666
**    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.139 2003/05/17 17:35:12 drh Exp $
*/
#include "sqliteInt.h"


/*
** Allocate a new Select structure and return a pointer to that
** structure.
................................................................................
  {
    int nSubSrc = pSubSrc->nSrc;
    int jointype = pSrc->a[iFrom].jointype;

    if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
      sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
    }

    sqliteFree(pSrc->a[iFrom].zName);
    sqliteFree(pSrc->a[iFrom].zAlias);
    if( nSubSrc>1 ){
      int extra = nSubSrc - 1;
      for(i=1; i<nSubSrc; i++){
        pSrc = sqliteSrcListAppend(pSrc, 0, 0);
      }







|







 







>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
**    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.140 2003/05/31 16:21:13 drh Exp $
*/
#include "sqliteInt.h"


/*
** Allocate a new Select structure and return a pointer to that
** structure.
................................................................................
  {
    int nSubSrc = pSubSrc->nSrc;
    int jointype = pSrc->a[iFrom].jointype;

    if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
      sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
    }
    sqliteFree(pSrc->a[iFrom].zDatabase);
    sqliteFree(pSrc->a[iFrom].zName);
    sqliteFree(pSrc->a[iFrom].zAlias);
    if( nSubSrc>1 ){
      int extra = nSubSrc - 1;
      for(i=1; i<nSubSrc; i++){
        pSrc = sqliteSrcListAppend(pSrc, 0, 0);
      }

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
...
901
902
903
904
905
906
907

908
909
910
911
912
913
914
...
996
997
998
999
1000
1001
1002













1003
1004
1005
1006
1007
1008
1009
....
1167
1168
1169
1170
1171
1172
1173






**    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.188 2003/05/29 17:50:55 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
  u8 explain;          /* True if the EXPLAIN flag is found on the query */
  u8 initFlag;         /* True if reparsing CREATE TABLEs */
  u8 nameClash;        /* A permanent table name clashes with temp table name */
  u8 useAgg;           /* If true, extract field values from the aggregator
                       ** while generating expressions.  Normally false */
  u8 iDb;              /* Index of database whose schema is being parsed */
  u8 useCallback;      /* True if callbacks should be used to report results */
  int useDb;           /* Restrict references to tables in this database */
  int newTnum;         /* Table number to use when reparsing CREATE TABLEs */
  int nErr;            /* Number of errors seen */
  int nTab;            /* Number of previously allocated VDBE cursors */
  int nMem;            /* Number of memory cells used so far */
  int nSet;            /* Number of sets used so far */
  int nAgg;            /* Number of aggregate expressions */
  AggExpr *aAgg;       /* An array of aggregate expressions */
................................................................................
  u8 iTabDb;              /* Database containing Trigger.table              */
  u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  u8 tr_tm;               /* One of TK_BEFORE, TK_AFTER */
  Expr *pWhen;            /* The WHEN clause of the expresion (may be NULL) */
  IdList *pColumns;       /* If this is an UPDATE OF <column-list> trigger,
                             the <column-list> is stored here */
  int foreach;            /* One of TK_ROW or TK_STATEMENT */


  TriggerStep *step_list; /* Link list of trigger program steps             */
  Trigger *pNext;         /* Next trigger associated with the table */
};

/*
 * An instance of struct TriggerStep is used to store a single SQL statement
................................................................................
  int newIdx;          /* Index of vdbe cursor to "new" temp table */
  int oldIdx;          /* Index of vdbe cursor to "old" temp table */
  int orconf;          /* Current orconf policy */
  int ignoreJump;      /* where to jump to for a RAISE(IGNORE) */
  Trigger *pTrigger;   /* The trigger currently being coded */
  TriggerStack *pNext; /* Next trigger down on the trigger stack */
};














/*
 * This global flag is set for performance testing of triggers. When it is set
 * SQLite will perform the overhead of building new and old trigger references 
 * even when no triggers exist
 */
extern int always_code_trigger_setup;
................................................................................
# define sqliteAuthContextPush(a,b,c)
# define sqliteAuthContextPop(a)
#endif
void sqliteAttach(Parse*, Token*, Token*);
void sqliteDetach(Parse*, Token*);
int sqliteBtreeFactory(const sqlite *db, const char *zFilename,
                       int mode, int nPg, Btree **ppBtree);













|







 







<







 







>







 







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







 







>
>
>
>
>
>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
853
854
855
856
857
858
859

860
861
862
863
864
865
866
...
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
...
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
....
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
**    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.189 2003/05/31 16:21:13 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
  u8 explain;          /* True if the EXPLAIN flag is found on the query */
  u8 initFlag;         /* True if reparsing CREATE TABLEs */
  u8 nameClash;        /* A permanent table name clashes with temp table name */
  u8 useAgg;           /* If true, extract field values from the aggregator
                       ** while generating expressions.  Normally false */
  u8 iDb;              /* Index of database whose schema is being parsed */
  u8 useCallback;      /* True if callbacks should be used to report results */

  int newTnum;         /* Table number to use when reparsing CREATE TABLEs */
  int nErr;            /* Number of errors seen */
  int nTab;            /* Number of previously allocated VDBE cursors */
  int nMem;            /* Number of memory cells used so far */
  int nSet;            /* Number of sets used so far */
  int nAgg;            /* Number of aggregate expressions */
  AggExpr *aAgg;       /* An array of aggregate expressions */
................................................................................
  u8 iTabDb;              /* Database containing Trigger.table              */
  u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  u8 tr_tm;               /* One of TK_BEFORE, TK_AFTER */
  Expr *pWhen;            /* The WHEN clause of the expresion (may be NULL) */
  IdList *pColumns;       /* If this is an UPDATE OF <column-list> trigger,
                             the <column-list> is stored here */
  int foreach;            /* One of TK_ROW or TK_STATEMENT */
  Token *pNameToken;      /* Token containing zName. Use during parsing only */

  TriggerStep *step_list; /* Link list of trigger program steps             */
  Trigger *pNext;         /* Next trigger associated with the table */
};

/*
 * An instance of struct TriggerStep is used to store a single SQL statement
................................................................................
  int newIdx;          /* Index of vdbe cursor to "new" temp table */
  int oldIdx;          /* Index of vdbe cursor to "old" temp table */
  int orconf;          /* Current orconf policy */
  int ignoreJump;      /* where to jump to for a RAISE(IGNORE) */
  Trigger *pTrigger;   /* The trigger currently being coded */
  TriggerStack *pNext; /* Next trigger down on the trigger stack */
};

/*
** The following structure contains information used by the sqliteFix...
** routines as they walk the parse tree to make database references
** explicit.  
*/
typedef struct DbFixer DbFixer;
struct DbFixer {
  Parse *pParse;      /* The parsing context.  Error messages written here */
  const char *zDb;    /* Make sure all objects are contained in this database */
  const char *zType;  /* Type of the container - used for error messages */
  const Token *pName; /* Name of the container - used for error messages */
};

/*
 * This global flag is set for performance testing of triggers. When it is set
 * SQLite will perform the overhead of building new and old trigger references 
 * even when no triggers exist
 */
extern int always_code_trigger_setup;
................................................................................
# define sqliteAuthContextPush(a,b,c)
# define sqliteAuthContextPop(a)
#endif
void sqliteAttach(Parse*, Token*, Token*);
void sqliteDetach(Parse*, Token*);
int sqliteBtreeFactory(const sqlite *db, const char *zFilename,
                       int mode, int nPg, Btree **ppBtree);
int sqliteFixInit(DbFixer*, Parse*, int, const char*, const Token*);
int sqliteFixSrcList(DbFixer*, SrcList*);
int sqliteFixSelect(DbFixer*, Select*);
int sqliteFixExpr(DbFixer*, Expr*);
int sqliteFixExprList(DbFixer*, ExprList*);
int sqliteFixTriggerStep(DbFixer*, TriggerStep*);

Changes to src/trigger.c.

60
61
62
63
64
65
66




67

68
69
70
71
72
73
74
...
129
130
131
132
133
134
135

136
137
138
139
140
141
142
...
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165





166
167
168
169
170
171
172
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212

213
214
215
216
217
218
219
...
542
543
544
545
546
547
548






























549
550
551
552
553
554
555
...
557
558
559
560
561
562
563
564

565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
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
  ** 2. the table (or view) does exist in the same database as the trigger.
  ** 3. that we are not trying to create a trigger on the sqlite_master table
  ** 4. That we are not trying to create an INSTEAD OF trigger on a table.
  ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
  */
  if( sqlite_malloc_failed ) goto trigger_cleanup;
  assert( pTableName->nSrc==1 );




  tab = sqliteSrcListLookup(pParse, pTableName);

  if( !tab ){
    goto trigger_cleanup;
  }
  iDb = isTemp ? 1 : tab->iDb;
  if( iDb>=2 && !pParse->initFlag ){
    sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
       "database %s", db->aDb[tab->iDb].zName);
................................................................................
  nt->iDb = iDb;
  nt->iTabDb = tab->iDb;
  nt->op = op;
  nt->tr_tm = tr_tm;
  nt->pWhen = sqliteExprDup(pWhen);
  nt->pColumns = sqliteIdListDup(pColumns);
  nt->foreach = foreach;

  assert( pParse->pNewTrigger==0 );
  pParse->pNewTrigger = nt;

trigger_cleanup:
  sqliteFree(zName);
  sqliteSrcListDelete(pTableName);
  sqliteIdListDelete(pColumns);
................................................................................
** in order to complete the process of building the trigger.
*/
void sqliteFinishTrigger(
  Parse *pParse,          /* Parser context */
  TriggerStep *pStepList, /* The triggered program */
  Token *pAll             /* Token that describes the complete CREATE TRIGGER */
){
  Trigger *nt;              /* The trigger whose construction is finishing up */
  sqlite *db = pParse->db;  /* The database */


  if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
  nt = pParse->pNewTrigger;
  pParse->pNewTrigger = 0;
  nt->step_list = pStepList;
  while( pStepList ){
    pStepList->pTrig = nt;
    pStepList = pStepList->pNext;
  }






  /* if we are not initializing, and this trigger is not on a TEMP table, 
  ** build the sqlite_master entry
  */
  if( !pParse->initFlag ){
    static VdbeOp insertTrig[] = {
      { OP_NewRecno,   0, 0,  0          },
................................................................................
    sqliteEndWriteOperation(pParse);
  }

  if( !pParse->explain ){
    Table *pTab;
    sqliteHashInsert(&db->aDb[nt->iDb].trigHash, 
                     nt->name, strlen(nt->name)+1, nt);
    pTab = sqliteLocateTable(pParse, nt->table, 0);
    assert( pTab!=0 );
    nt->pNext = pTab->pTrigger;
    pTab->pTrigger = nt;
  }else{
    sqliteDeleteTrigger(nt);
  }

triggerfinish_cleanup:

  sqliteDeleteTrigger(pParse->pNewTrigger);
  pParse->pNewTrigger = 0;
  sqliteDeleteTriggerStep(pStepList);
}

/*
** Make a copy of all components of the given trigger step.  This has
................................................................................
      if( !ss )return 1;
    }
    pTriggerCursor = pTriggerCursor->pNext;
  }

  return 0;
}































/*
** Generate VDBE code for zero or more statements inside the body of a
** trigger.  
*/
static int codeTriggerProgram(
  Parse *pParse,            /* The parser context */
................................................................................
  int orconfin              /* Conflict algorithm. (OE_Abort, etc) */  
){
  TriggerStep * pTriggerStep = pStepList;
  int orconf;

  while( pTriggerStep ){
    int saveNTab = pParse->nTab;
    int saveUseDb = pParse->useDb;

    orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
    pParse->trigStack->orconf = orconf;
    pParse->useDb = pTriggerStep->pTrig->iDb;
    if( pParse->useDb==1 ) pParse->useDb = -1;
    switch( pTriggerStep->op ){
      case TK_SELECT: {
	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);		  
	assert(ss);
	assert(ss->pSrc);
	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
	sqliteSelectDelete(ss);
	break;
      }
      case TK_UPDATE: {
        SrcList *pSrc;
        pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
        sqliteUpdate(pParse, pSrc,
		sqliteExprListDup(pTriggerStep->pExprList), 
		sqliteExprDup(pTriggerStep->pWhere), orconf);
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
        break;
      }
      case TK_INSERT: {
        SrcList *pSrc;
        pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
        sqliteInsert(pParse, pSrc,
          sqliteExprListDup(pTriggerStep->pExprList), 
          sqliteSelectDup(pTriggerStep->pSelect), 
          sqliteIdListDup(pTriggerStep->pIdList), orconf);
        break;
      }
      case TK_DELETE: {
        SrcList *pSrc;
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
        pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
        sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
        break;
      }
      default:
        assert(0);
    } 
    pParse->nTab = saveNTab;
    pParse->useDb = saveUseDb;
    pTriggerStep = pTriggerStep->pNext;
  }

  return 0;
}

/*







>
>
>
>

>







 







>







 







|

>









>
>
>
>
>







 







|



|
<



>







 







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







 







<
>


<
<











|









|









|








<







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
...
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
180
181
182
183
184
...
209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228
229
230
231
...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
...
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
  ** 2. the table (or view) does exist in the same database as the trigger.
  ** 3. that we are not trying to create a trigger on the sqlite_master table
  ** 4. That we are not trying to create an INSTEAD OF trigger on a table.
  ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
  */
  if( sqlite_malloc_failed ) goto trigger_cleanup;
  assert( pTableName->nSrc==1 );
  assert( pTableName->a[0].zDatabase==0 );
  if( pParse->initFlag ){
    pTableName->a[0].zDatabase = db->aDb[pParse->iDb].zName;
  }
  tab = sqliteSrcListLookup(pParse, pTableName);
  pTableName->a[0].zDatabase = 0;
  if( !tab ){
    goto trigger_cleanup;
  }
  iDb = isTemp ? 1 : tab->iDb;
  if( iDb>=2 && !pParse->initFlag ){
    sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
       "database %s", db->aDb[tab->iDb].zName);
................................................................................
  nt->iDb = iDb;
  nt->iTabDb = tab->iDb;
  nt->op = op;
  nt->tr_tm = tr_tm;
  nt->pWhen = sqliteExprDup(pWhen);
  nt->pColumns = sqliteIdListDup(pColumns);
  nt->foreach = foreach;
  nt->pNameToken = pName;
  assert( pParse->pNewTrigger==0 );
  pParse->pNewTrigger = nt;

trigger_cleanup:
  sqliteFree(zName);
  sqliteSrcListDelete(pTableName);
  sqliteIdListDelete(pColumns);
................................................................................
** in order to complete the process of building the trigger.
*/
void sqliteFinishTrigger(
  Parse *pParse,          /* Parser context */
  TriggerStep *pStepList, /* The triggered program */
  Token *pAll             /* Token that describes the complete CREATE TRIGGER */
){
  Trigger *nt = 0;          /* The trigger whose construction is finishing up */
  sqlite *db = pParse->db;  /* The database */
  DbFixer sFix;

  if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
  nt = pParse->pNewTrigger;
  pParse->pNewTrigger = 0;
  nt->step_list = pStepList;
  while( pStepList ){
    pStepList->pTrig = nt;
    pStepList = pStepList->pNext;
  }
  if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", nt->pNameToken) 
          && sqliteFixTriggerStep(&sFix, nt->step_list) ){
    goto triggerfinish_cleanup;
  }
  nt->pNameToken = 0;

  /* if we are not initializing, and this trigger is not on a TEMP table, 
  ** build the sqlite_master entry
  */
  if( !pParse->initFlag ){
    static VdbeOp insertTrig[] = {
      { OP_NewRecno,   0, 0,  0          },
................................................................................
    sqliteEndWriteOperation(pParse);
  }

  if( !pParse->explain ){
    Table *pTab;
    sqliteHashInsert(&db->aDb[nt->iDb].trigHash, 
                     nt->name, strlen(nt->name)+1, nt);
    pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
    assert( pTab!=0 );
    nt->pNext = pTab->pTrigger;
    pTab->pTrigger = nt;
    nt = 0;

  }

triggerfinish_cleanup:
  sqliteDeleteTrigger(nt);
  sqliteDeleteTrigger(pParse->pNewTrigger);
  pParse->pNewTrigger = 0;
  sqliteDeleteTriggerStep(pStepList);
}

/*
** Make a copy of all components of the given trigger step.  This has
................................................................................
      if( !ss )return 1;
    }
    pTriggerCursor = pTriggerCursor->pNext;
  }

  return 0;
}

/*
** Convert the pStep->target token into a SrcList and return a pointer
** to that SrcList.
**
** This routine adds a specific database name, if needed, to the target when
** forming the SrcList.  This prevents a trigger in one database from
** referring to a target in another database.  An exception is when the
** trigger is in TEMP in which case it can refer to any other database it
** wants.
*/
static SrcList *targetSrcList(
  Parse *pParse,       /* The parsing context */
  TriggerStep *pStep   /* The trigger containing the target token */
){
  Token sDb;           /* Dummy database name token */
  int iDb;             /* Index of the database to use */
  SrcList *pSrc;       /* SrcList to be returned */

  iDb = pStep->pTrig->iDb;
  if( iDb==0 || iDb>=2 ){
    assert( iDb<pParse->db->nDb );
    sDb.z = pParse->db->aDb[iDb].zName;
    sDb.n = strlen(sDb.z);
    pSrc = sqliteSrcListAppend(0, &sDb, &pStep->target);
  } else {
    pSrc = sqliteSrcListAppend(0, &pStep->target, 0);
  }
  return pSrc;
}

/*
** Generate VDBE code for zero or more statements inside the body of a
** trigger.  
*/
static int codeTriggerProgram(
  Parse *pParse,            /* The parser context */
................................................................................
  int orconfin              /* Conflict algorithm. (OE_Abort, etc) */  
){
  TriggerStep * pTriggerStep = pStepList;
  int orconf;

  while( pTriggerStep ){
    int saveNTab = pParse->nTab;

 
    orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
    pParse->trigStack->orconf = orconf;


    switch( pTriggerStep->op ){
      case TK_SELECT: {
	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);		  
	assert(ss);
	assert(ss->pSrc);
	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
	sqliteSelectDelete(ss);
	break;
      }
      case TK_UPDATE: {
        SrcList *pSrc;
        pSrc = targetSrcList(pParse, pTriggerStep);
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
        sqliteUpdate(pParse, pSrc,
		sqliteExprListDup(pTriggerStep->pExprList), 
		sqliteExprDup(pTriggerStep->pWhere), orconf);
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
        break;
      }
      case TK_INSERT: {
        SrcList *pSrc;
        pSrc = targetSrcList(pParse, pTriggerStep);
        sqliteInsert(pParse, pSrc,
          sqliteExprListDup(pTriggerStep->pExprList), 
          sqliteSelectDup(pTriggerStep->pSelect), 
          sqliteIdListDup(pTriggerStep->pIdList), orconf);
        break;
      }
      case TK_DELETE: {
        SrcList *pSrc;
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
        pSrc = targetSrcList(pParse, pTriggerStep);
        sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
        break;
      }
      default:
        assert(0);
    } 
    pParse->nTab = saveNTab;

    pTriggerStep = pTriggerStep->pNext;
  }

  return 0;
}

/*

Changes to test/attach.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
364
365
366
367
368
369
370
371





































































































372
373
374
375
376
377
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.5 2003/05/17 19:23:52 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

for {set i 2} {$i<=15} {incr i} {
  file delete -force test$i.db
................................................................................
  catchsql {UPDATE t2 SET x=x+1 WHERE x=50}
} {1 {database is locked}}
do_test attach-3.14 {
  # the "database is locked" error on the previous test should have
  # caused a rollback.
  execsql {SELECT * FROM t1}
} {1 2 3 4}






































































































for {set i 2} {$i<=15} {incr i} {
  catch {db$i close}
}


finish_test







|







 








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






8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
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
393
394
395
396
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
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.6 2003/05/31 16:21:13 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

for {set i 2} {$i<=15} {incr i} {
  file delete -force test$i.db
................................................................................
  catchsql {UPDATE t2 SET x=x+1 WHERE x=50}
} {1 {database is locked}}
do_test attach-3.14 {
  # the "database is locked" error on the previous test should have
  # caused a rollback.
  execsql {SELECT * FROM t1}
} {1 2 3 4}

# Ticket #323
do_test attach-4.1 {
  execsql {DETACH db2}
  db2 close
  sqlite db2 test2.db
  execsql {
    CREATE TABLE t3(x,y);
    CREATE UNIQUE INDEX t3i1 ON t3(x);
    INSERT INTO t3 VALUES(1,2);
    SELECT * FROM t3;
  } db2;
} {1 2}
do_test attach-4.2 {
  execsql {
    CREATE TABLE t3(a,b);
    CREATE UNIQUE INDEX t3i1b ON t3(a);
    INSERT INTO t3 VALUES(9,10);
    SELECT * FROM t3;
  }
} {9 10}
do_test attach-4.3 {
  execsql {
    ATTACH DATABASE 'test2.db' AS db2;
    SELECT * FROM db2.t3;
  }
} {1 2}
do_test attach-4.4 {
  execsql {
    SELECT * FROM main.t3;
  }
} {9 10}
do_test attach-4.5 {
  execsql {
    INSERT INTO db2.t3 VALUES(9,10);
    SELECT * FROM db2.t3;
  }
} {1 2 9 10}
do_test attach-4.6 {
  execsql {
    DETACH db2;
  }
  execsql {
    CREATE TABLE t4(x);
    CREATE TRIGGER t3r3 AFTER INSERT ON t3 BEGIN
      INSERT INTO t4 VALUES('db2.' || NEW.x);
    END;
    INSERT INTO t3 VALUES(6,7);
    SELECT * FROM t4;
  } db2
} {db2.6}
do_test attach-4.7 {
  execsql {
    CREATE TABLE t4(y);
    CREATE TRIGGER t3r3 AFTER INSERT ON t3 BEGIN
      INSERT INTO t4 VALUES('main.' || NEW.a);
    END;
    INSERT INTO main.t3 VALUES(11,12);
    SELECT * FROM main.t4;
  }
} {main.11}
do_test attach-4.8 {
  execsql {
    ATTACH DATABASE 'test2.db' AS db2;
    INSERT INTO db2.t3 VALUES(13,14);
    SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4;
  }
} {db2.6 db2.13 main.11}
do_test attach-4.9 {
  execsql {
    INSERT INTO main.t3 VALUES(15,16);
    SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4;
  }
} {db2.6 db2.13 main.11 main.15}
do_test attach-4.10 {
  execsql {
    DETACH DATABASE db2;
  }
  execsql {
    CREATE VIEW v3 AS SELECT x*100+y FROM t3;
    SELECT * FROM v3;
  } db2
} {102 910 607 1314}
do_test attach-4.11 {
  execsql {
    CREATE VIEW v3 AS SELECT a*100+b FROM t3;
    SELECT * FROM v3;
  }
} {910 1112 1516}
do_test attach-4.12 {
  execsql {
    ATTACH DATABASE 'test2.db' AS db2;
    SELECT * FROM db2.v3;
  }
} {102 910 607 1314}
do_test attach-4.13 {
  execsql {
    SELECT * FROM main.v3;
  }
} {910 1112 1516}


for {set i 2} {$i<=15} {incr i} {
  catch {db$i close}
}


finish_test

Changes to test/trigger1.test.

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
  }
} {0 {}}
do_test trigger-3.2 {
  catchsql {
    INSERT INTO t1 VALUES(1,2);
    SELECT * FROM t2;
  }
} {1 {table "t2" is not in database "main"}}
do_test trigger-3.3 {
  db close
  set rc [catch {sqlite db test.db} err]
  if {$rc} {lappend rc $err}
  set rc
} {0}
do_test trigger-3.4 {
  catchsql {
    INSERT INTO t1 VALUES(1,2);
    SELECT * FROM t2;
  }
} {1 {no such table: t2}}
do_test trigger-3.5 {
  catchsql {
    CREATE TEMP TABLE t2(x,y);
    INSERT INTO t1 VALUES(1,2);
    SELECT * FROM t2;
  }
} {1 {table "t2" is not in database "main"}}
do_test trigger-3.6 {
  catchsql {
    DROP TRIGGER r1;
    CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN
      INSERT INTO t2 VALUES(NEW.a,NEW.b);
    END;
    INSERT INTO t1 VALUES(1,2);







|











|






|







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
  }
} {0 {}}
do_test trigger-3.2 {
  catchsql {
    INSERT INTO t1 VALUES(1,2);
    SELECT * FROM t2;
  }
} {1 {no such table: main.t2}}
do_test trigger-3.3 {
  db close
  set rc [catch {sqlite db test.db} err]
  if {$rc} {lappend rc $err}
  set rc
} {0}
do_test trigger-3.4 {
  catchsql {
    INSERT INTO t1 VALUES(1,2);
    SELECT * FROM t2;
  }
} {1 {no such table: main.t2}}
do_test trigger-3.5 {
  catchsql {
    CREATE TEMP TABLE t2(x,y);
    INSERT INTO t1 VALUES(1,2);
    SELECT * FROM t2;
  }
} {1 {no such table: main.t2}}
do_test trigger-3.6 {
  catchsql {
    DROP TRIGGER r1;
    CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN
      INSERT INTO t2 VALUES(NEW.a,NEW.b);
    END;
    INSERT INTO t1 VALUES(1,2);

Changes to test/trigger4.test.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
} {1 3 4 66}

do_test trigger4-3.1 {
  catchsql {
    drop table test2;
    insert into test values(7,8,9);
  }
} {1 {no such table: test2}}
do_test trigger4-3.2 {
  db close
  sqlite db test.db
  catchsql {
    insert into test values(7,8,9);
  }
} {1 {no such table: test2}}
do_test trigger4-3.3 {
  catchsql {
    update test set a=222 where id=1;
  }
} {1 {no such table: test2}}
do_test trigger4-3.4 {
  execsql {
    select * from test1;
  }
} {1 22 4 5}
do_test trigger4-3.5 {
  execsql {







|






|




|







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
} {1 3 4 66}

do_test trigger4-3.1 {
  catchsql {
    drop table test2;
    insert into test values(7,8,9);
  }
} {1 {no such table: main.test2}}
do_test trigger4-3.2 {
  db close
  sqlite db test.db
  catchsql {
    insert into test values(7,8,9);
  }
} {1 {no such table: main.test2}}
do_test trigger4-3.3 {
  catchsql {
    update test set a=222 where id=1;
  }
} {1 {no such table: main.test2}}
do_test trigger4-3.4 {
  execsql {
    select * from test1;
  }
} {1 22 4 5}
do_test trigger4-3.5 {
  execsql {

Changes to test/view.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#    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.15 2003/05/02 16:04:17 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);
................................................................................
  }
} {1 2 4 5 7 8}
do_test view-1.6 {
  catchsql {
    DROP TABLE t1;
    SELECT * FROM v1 ORDER BY a;
  }
} {1 {no such table: t1}}
do_test view-1.7 {
  execsql {
    CREATE TABLE t1(x,a,b,c);
    INSERT INTO t1 VALUES(1,2,3,4);
    INSERT INTO t1 VALUES(4,5,6,7);
    INSERT INTO t1 VALUES(7,8,9,10);
    SELECT * FROM v1 ORDER BY a;







|







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#    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.16 2003/05/31 16:21:13 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);
................................................................................
  }
} {1 2 4 5 7 8}
do_test view-1.6 {
  catchsql {
    DROP TABLE t1;
    SELECT * FROM v1 ORDER BY a;
  }
} {1 {no such table: main.t1}}
do_test view-1.7 {
  execsql {
    CREATE TABLE t1(x,a,b,c);
    INSERT INTO t1 VALUES(1,2,3,4);
    INSERT INTO t1 VALUES(4,5,6,7);
    INSERT INTO t1 VALUES(7,8,9,10);
    SELECT * FROM v1 ORDER BY a;