SQLite

Check-in [4060a37d0b]
Login

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

Overview
Comment:Allow CREATE and DROP TRIGGER on attached databases. (CVS 1488)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 4060a37d0baaa60c50f2dde4a1ab344133fcabbb
User & Date: danielk1977 2004-05-29 02:37:19.000
Context
2004-05-29
02:44
Avoid arithmetic on void pointers. (CVS 1489) (check-in: 3d68703e2e user: danielk1977 tags: trunk)
02:37
Allow CREATE and DROP TRIGGER on attached databases. (CVS 1488) (check-in: 4060a37d0b user: danielk1977 tags: trunk)
2004-05-28
16:00
Factor common code for generating index keys into a procedure. Other speed improvements and bug fixes. (CVS 1487) (check-in: 6661bb5f9c user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/build.c.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.197 2004/05/28 16:00:22 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







|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.198 2004/05/29 02:37:19 danielk1977 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
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
        0==sqlite3StrNICmp(db->aDb[i].zName, pName->z, pName->n) ){
      return i;
    }
  }
  return -1;
}

static int resolveSchemaName(
  Parse *pParse, 
  Token *pName1, 
  Token *pName2, 
  Token **pUnqual
){
  int iDb;
  sqlite3 *db = pParse->db;







|







421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
        0==sqlite3StrNICmp(db->aDb[i].zName, pName->z, pName->n) ){
      return i;
    }
  }
  return -1;
}

int sqlite3TwoPartName(
  Parse *pParse, 
  Token *pName1, 
  Token *pName2, 
  Token **pUnqual
){
  int iDb;
  sqlite3 *db = pParse->db;
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  ** Then pName1 is set to "yyy" and pName2 is "".
  **
  ** The call below sets the pName pointer to point at the token (pName1 or
  ** pName2) that stores the unqualified table name. The variable iDb is
  ** set to the index of the database that the table or view is to be
  ** created in.
  */
  iDb = resolveSchemaName(pParse, pName1, pName2, &pName);
  if( iDb<0 ) return;
  if( isTemp && iDb>1 ){
    /* If creating a temp table, the name may not be qualified */
    sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
    pParse->nErr++;
    return;
  }







|







497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  ** Then pName1 is set to "yyy" and pName2 is "".
  **
  ** The call below sets the pName pointer to point at the token (pName1 or
  ** pName2) that stores the unqualified table name. The variable iDb is
  ** set to the index of the database that the table or view is to be
  ** created in.
  */
  iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
  if( iDb<0 ) return;
  if( isTemp && iDb>1 ){
    /* If creating a temp table, the name may not be qualified */
    sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
    pParse->nErr++;
    return;
  }
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176

  sqlite3StartTable(pParse, pBegin, pName1, pName2, isTemp, 1);
  p = pParse->pNewTable;
  if( p==0 || pParse->nErr ){
    sqlite3SelectDelete(pSelect);
    return;
  }
  resolveSchemaName(pParse, pName1, pName2, &pName);
  if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName)
    && sqlite3FixSelect(&sFix, pSelect)
  ){
    sqlite3SelectDelete(pSelect);
    return;
  }








|







1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176

  sqlite3StartTable(pParse, pBegin, pName1, pName2, isTemp, 1);
  p = pParse->pNewTable;
  if( p==0 || pParse->nErr ){
    sqlite3SelectDelete(pSelect);
    return;
  }
  sqlite3TwoPartName(pParse, pName1, pName2, &pName);
  if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName)
    && sqlite3FixSelect(&sFix, pSelect)
  ){
    sqlite3SelectDelete(pSelect);
    return;
  }

1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663


1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
** is a primary key or unique-constraint on the most recent column added
** to the table currently under construction.  
*/
void sqlite3CreateIndex(
  Parse *pParse,   /* All information about this parse */
  Token *pName1,   /* First part of index name. May be NULL */
  Token *pName2,   /* Second part of index name. May be NULL */
  Token *pTblName, /* Name of the table to index. Use pParse->pNewTable if 0 */
  IdList *pList,   /* A list of columns to be indexed */
  int onError,     /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
  Token *pStart,   /* The CREATE token that begins a CREATE TABLE statement */
  Token *pEnd      /* The ")" that closes the CREATE INDEX statement */
){
  Table *pTab = 0; /* 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 */
  int isTemp;      /* True for a temporary index */
  sqlite *db = pParse->db;

  int iDb;          /* Index of the database that is being written */
  Token *pName = 0; /* Unqualified name of the index to create */

/*
  if( pParse->nErr || sqlite3_malloc_failed ) goto exit_create_index;
  if( db->init.busy 
     && sqlite3FixInit(&sFix, pParse, db->init.iDb, "index", pName)
     && sqlite3FixSrcList(&sFix, pTable)
  ){
    goto exit_create_index;
  }
*/

  /*
  ** Find the table that is to be indexed.  Return early if not found.
  */
  if( pTblName!=0 ){
    char *zTblName;

    /* Use the two-part index name to determine the database 
    ** to search for the table. If no database name is specified, 
    ** iDb is set to 0. In this case search both the temp and main
    ** databases for the named table.
    */
    assert( pName1 && pName2 );
    iDb = resolveSchemaName(pParse, pName1, pName2, &pName);
    if( iDb<0 ) goto exit_create_index;

    /* Now search for the table in the database iDb. If iDb is
    ** zero, then search both the "main" and "temp" databases.
    */
    zTblName = sqlite3TableNameFromToken(pTblName);
    if( !zTblName ){
      pParse->nErr++;
      pParse->rc = SQLITE_NOMEM;
      goto exit_create_index;
    }
    assert( pName1!=0 );
    if( iDb==0 ){
      pTab = sqlite3FindTable(db, zTblName, "temp");


    }
    if( !pTab ){
      pTab = sqlite3LocateTable(pParse, zTblName, db->aDb[iDb].zName);
    }
    sqliteFree( zTblName );
    if( !pTab ) goto exit_create_index;
    iDb = pTab->iDb;
  }else{
    assert( pName==0 );
    pTab =  pParse->pNewTable;
    iDb = pTab->iDb;
  }

  if( pTab==0 || pParse->nErr ) goto exit_create_index;
  if( pTab->readOnly ){
    sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
    goto exit_create_index;
  }
/*
  if( pTab->iDb>=2 && db->init.busy==0 ){
    sqlite3ErrorMsg(pParse, "table %s may not have indices added", pTab->zName);
    goto exit_create_index;
  }
*/
  if( pTab->pSelect ){
    sqlite3ErrorMsg(pParse, "views may not be indexed");
    goto exit_create_index;
  }
  isTemp = pTab->iDb==1;

  /*







|

















<

<
<
<
<
<
<
<





<


|
<
|


|


|
|

|
|
<
|
<

|
|
|
>
>

<
|
<
|

|











<
<
<
<
<
<







1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626

1627







1628
1629
1630
1631
1632

1633
1634
1635

1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646

1647

1648
1649
1650
1651
1652
1653
1654

1655

1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669






1670
1671
1672
1673
1674
1675
1676
** is a primary key or unique-constraint on the most recent column added
** to the table currently under construction.  
*/
void sqlite3CreateIndex(
  Parse *pParse,   /* All information about this parse */
  Token *pName1,   /* First part of index name. May be NULL */
  Token *pName2,   /* Second part of index name. May be NULL */
  SrcList *pTblName, /* Name of the table to index. Use pParse->pNewTable if 0 */
  IdList *pList,   /* A list of columns to be indexed */
  int onError,     /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
  Token *pStart,   /* The CREATE token that begins a CREATE TABLE statement */
  Token *pEnd      /* The ")" that closes the CREATE INDEX statement */
){
  Table *pTab = 0; /* 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 */
  int isTemp;      /* True for a temporary index */
  sqlite *db = pParse->db;

  int iDb;          /* Index of the database that is being written */
  Token *pName = 0; /* Unqualified name of the index to create */


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








  /*
  ** Find the table that is to be indexed.  Return early if not found.
  */
  if( pTblName!=0 ){


    /* Use the two-part index name to determine the database 
    ** to search for the table. 'Fix' the table name to this db

    ** before looking up the table.
    */
    assert( pName1 && pName2 );
    iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
    if( iDb<0 ) goto exit_create_index;

    /* If the index name was unqualified, check if the the table
    ** is a temp table. If so, set the database to 1.
    */
    pTab = sqlite3SrcListLookup(pParse, pTblName);
    if( pName2 && pName2->n==0 && pTab && pTab->iDb==1 ){

      iDb = 1;

    }

    if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) &&
        sqlite3FixSrcList(&sFix, pTblName)
    ){
      goto exit_create_index;
    }

    pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName, 

        pTblName->a[0].zDatabase);
    if( !pTab ) goto exit_create_index;
    assert( iDb==pTab->iDb );
  }else{
    assert( pName==0 );
    pTab =  pParse->pNewTable;
    iDb = pTab->iDb;
  }

  if( pTab==0 || pParse->nErr ) goto exit_create_index;
  if( pTab->readOnly ){
    sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
    goto exit_create_index;
  }






  if( pTab->pSelect ){
    sqlite3ErrorMsg(pParse, "views may not be indexed");
    goto exit_create_index;
  }
  isTemp = pTab->iDb==1;

  /*
Changes to src/parse.y.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** This file contains SQLite's grammar for SQL.  Process this file
** using the lemon parser generator to generate C code that runs
** the parser.  Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.122 2004/05/28 12:33:31 danielk1977 Exp $
*/
%token_prefix TK_
%token_type {Token}
%default_type {Token}
%extra_argument {Parse *pParse}
%syntax_error {
  if( pParse->zErrMsg==0 ){







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** This file contains SQLite's grammar for SQL.  Process this file
** using the lemon parser generator to generate C code that runs
** the parser.  Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.123 2004/05/29 02:37:19 danielk1977 Exp $
*/
%token_prefix TK_
%token_type {Token}
%default_type {Token}
%extra_argument {Parse *pParse}
%syntax_error {
  if( pParse->zErrMsg==0 ){
726
727
728
729
730
731
732
733
734
735
736

737
738
739
740
741
742
743
exprlist(A) ::= expritem(X).            {A = sqlite3ExprListAppend(0,X,0);}
expritem(A) ::= expr(X).                {A = X;}
expritem(A) ::= .                       {A = 0;}

///////////////////////////// The CREATE INDEX command ///////////////////////
//
cmd ::= CREATE(S) uniqueflag(U) INDEX nm(X) dbnm(D)
        ON nm(Y) LP idxlist(Z) RP(E) onconf(R). {
  if( U!=OE_None ) U = R;
  if( U==OE_Default) U = OE_Abort;
  sqlite3CreateIndex(pParse, &X, &D, &Y, Z, U, &S, &E);

}

%type uniqueflag {int}
uniqueflag(A) ::= UNIQUE.  { A = OE_Abort; }
uniqueflag(A) ::= .        { A = OE_None; }

%type idxlist {IdList*}







|


|
>







726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
exprlist(A) ::= expritem(X).            {A = sqlite3ExprListAppend(0,X,0);}
expritem(A) ::= expr(X).                {A = X;}
expritem(A) ::= .                       {A = 0;}

///////////////////////////// The CREATE INDEX command ///////////////////////
//
cmd ::= CREATE(S) uniqueflag(U) INDEX nm(X) dbnm(D)
        ON nm(Y) dbnm(C) LP idxlist(Z) RP(E) onconf(R). {
  if( U!=OE_None ) U = R;
  if( U==OE_Default) U = OE_Abort;
  sqlite3CreateIndex(pParse, &X, &D, sqlite3SrcListAppend(0,&Y,&C),
      Z, U, &S, &E);
}

%type uniqueflag {int}
uniqueflag(A) ::= UNIQUE.  { A = OE_Abort; }
uniqueflag(A) ::= .        { A = OE_None; }

%type idxlist {IdList*}
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
cmd ::= CREATE(A) trigger_decl BEGIN trigger_cmd_list(S) END(Z). {
  Token all;
  all.z = A.z;
  all.n = (Z.z - A.z) + Z.n;
  sqlite3FinishTrigger(pParse, S, &all);
}

trigger_decl ::= temp(T) TRIGGER nm(B) trigger_time(C) trigger_event(D)
                 ON nm(E) dbnm(DB) foreach_clause(F) when_clause(G). {
  SrcList *pTab = sqlite3SrcListAppend(0, &E, &DB);
  sqlite3BeginTrigger(pParse, &B, C, D.a, D.b, pTab, F, G, T);
}

%type trigger_time  {int}
trigger_time(A) ::= BEFORE.      { A = TK_BEFORE; }
trigger_time(A) ::= AFTER.       { A = TK_AFTER;  }
trigger_time(A) ::= INSTEAD OF.  { A = TK_INSTEAD;}
trigger_time(A) ::= .            { A = TK_BEFORE; }







|


|







785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
cmd ::= CREATE(A) trigger_decl BEGIN trigger_cmd_list(S) END(Z). {
  Token all;
  all.z = A.z;
  all.n = (Z.z - A.z) + Z.n;
  sqlite3FinishTrigger(pParse, S, &all);
}

trigger_decl ::= temp(T) TRIGGER nm(B) dbnm(Z) trigger_time(C) trigger_event(D)
                 ON nm(E) dbnm(DB) foreach_clause(F) when_clause(G). {
  SrcList *pTab = sqlite3SrcListAppend(0, &E, &DB);
  sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, pTab, F, G, T);
}

%type trigger_time  {int}
trigger_time(A) ::= BEFORE.      { A = TK_BEFORE; }
trigger_time(A) ::= AFTER.       { A = TK_AFTER;  }
trigger_time(A) ::= INSTEAD OF.  { A = TK_INSTEAD;}
trigger_time(A) ::= .            { A = TK_BEFORE; }
Changes to src/sqliteInt.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** 2001 September 15
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.260 2004/05/28 16:00:22 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "parse.h"
#include <stdio.h>
#include <stdlib.h>













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** 2001 September 15
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.261 2004/05/29 02:37:19 danielk1977 Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "parse.h"
#include <stdio.h>
#include <stdlib.h>
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
** The root-page of the master database table.
*/
#define MASTER_ROOT       1

/*
** The name of the schema table.
*/
#define SCHEMA_TABLE(x)  (x?TEMP_MASTER_NAME:MASTER_NAME)

/*
** A convenience macro that returns the number of elements in
** an array.
*/
#define ArraySize(X)    (sizeof(X)/sizeof(X[0]))








|







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
** The root-page of the master database table.
*/
#define MASTER_ROOT       1

/*
** The name of the schema table.
*/
#define SCHEMA_TABLE(x)  (x==1?TEMP_MASTER_NAME:MASTER_NAME)

/*
** A convenience macro that returns the number of elements in
** an array.
*/
#define ArraySize(X)    (sizeof(X)/sizeof(X[0]))

1217
1218
1219
1220
1221
1222
1223
1224

1225
1226
1227
1228
1229
1230
1231
IdList *sqlite3IdListAppend(IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*);
void sqlite3SrcListAddAlias(SrcList*, Token*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(IdList*);
void sqlite3SrcListDelete(SrcList*);
void sqlite3CreateIndex(Parse*,Token*,Token*,Token*,IdList*,int,Token*,Token*);

void sqlite3DropIndex(Parse*, SrcList*);
void sqlite3AddKeyType(Vdbe*, ExprList*);
void sqlite3AddIdxKeyType(Vdbe*, Index*);
int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff);
Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
                        int,int,int);
void sqlite3SelectDelete(Select*);







|
>







1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
IdList *sqlite3IdListAppend(IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*);
void sqlite3SrcListAddAlias(SrcList*, Token*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(IdList*);
void sqlite3SrcListDelete(SrcList*);
void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,IdList*,int,Token*,
                        Token*);
void sqlite3DropIndex(Parse*, SrcList*);
void sqlite3AddKeyType(Vdbe*, ExprList*);
void sqlite3AddIdxKeyType(Vdbe*, Index*);
int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff);
Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
                        int,int,int);
void sqlite3SelectDelete(Select*);
1282
1283
1284
1285
1286
1287
1288
1289

1290
1291
1292
1293
1294
1295
1296
FuncDef *sqlite3FindFunction(sqlite*,const char*,int,int,int);
void sqlite3RegisterBuiltinFunctions(sqlite*);
void sqlite3RegisterDateTimeFunctions(sqlite*);
int sqlite3SafetyOn(sqlite*);
int sqlite3SafetyOff(sqlite*);
int sqlite3SafetyCheck(sqlite*);
void sqlite3ChangeCookie(sqlite*, Vdbe*, int);
void sqlite3BeginTrigger(Parse*, Token*,int,int,IdList*,SrcList*,int,Expr*,int);

void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*);
void sqlite3DropTrigger(Parse*, SrcList*);
void sqlite3DropTriggerPtr(Parse*, Trigger*, int);
int sqlite3TriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, 
                         int, int);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);







|
>







1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
FuncDef *sqlite3FindFunction(sqlite*,const char*,int,int,int);
void sqlite3RegisterBuiltinFunctions(sqlite*);
void sqlite3RegisterDateTimeFunctions(sqlite*);
int sqlite3SafetyOn(sqlite*);
int sqlite3SafetyOff(sqlite*);
int sqlite3SafetyCheck(sqlite*);
void sqlite3ChangeCookie(sqlite*, Vdbe*, int);
void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*,
                         int,Expr*,int);
void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*);
void sqlite3DropTrigger(Parse*, SrcList*);
void sqlite3DropTriggerPtr(Parse*, Trigger*, int);
int sqlite3TriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, 
                         int, int);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
1348
1349
1350
1351
1352
1353
1354

int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
char sqlite3ExprAffinity(Expr *pExpr);
int sqlite3atoi64(const char*, i64*);
void sqlite3Error(sqlite *, int, const char*,...);
int sqlite3utfTranslate(const void *, int , u8 , void **, int *, u8);
u8 sqlite3UtfReadBom(const void *zData, int nData);
void *sqlite3HexToBlob(const char *z);








>
1350
1351
1352
1353
1354
1355
1356
1357
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
char sqlite3ExprAffinity(Expr *pExpr);
int sqlite3atoi64(const char*, i64*);
void sqlite3Error(sqlite *, int, const char*,...);
int sqlite3utfTranslate(const void *, int , u8 , void **, int *, u8);
u8 sqlite3UtfReadBom(const void *zData, int nData);
void *sqlite3HexToBlob(const char *z);
int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
Changes to src/tclsqlite.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** 2001 September 15
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.75 2004/05/27 13:35:20 danielk1977 Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** 2001 September 15
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.76 2004/05/29 02:37:19 danielk1977 Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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
  Tcl_Obj *pCode;           /* The code to execute for each row */
  int once;                 /* Set for first callback only */
  int tcl_rc;               /* Return code from TCL script */
  int nColName;             /* Number of entries in the azColName[] array */
  char **azColName;         /* Column names translated to UTF-8 */
};

#ifdef UTF_TRANSLATION_NEEDED
/*
** Called for each row of the result.
**
** This version is used when TCL expects UTF-8 data but the database
** uses the ISO8859 format.  A translation must occur from ISO8859 into
** UTF-8.
*/
static int DbEvalCallback(
  void *clientData,      /* An instance of CallbackData */
  int nCol,              /* Number of columns in the result */
  char ** azCol,         /* Data for each column */
  char ** azN            /* Name for each column */
){
  CallbackData *cbData = (CallbackData*)clientData;
  int i, rc;
  Tcl_DString dCol;
  Tcl_DStringInit(&dCol);
  if( cbData->azColName==0 ){
    assert( cbData->once );
    cbData->once = 0;
    if( cbData->zArray[0] ){
      Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0);
    }
    cbData->azColName = malloc( nCol*sizeof(char*) );
    if( cbData->azColName==0 ){ return 1; }
    cbData->nColName = nCol;
    for(i=0; i<nCol; i++){
      Tcl_ExternalToUtfDString(NULL, azN[i], -1, &dCol);
      cbData->azColName[i] = malloc( Tcl_DStringLength(&dCol) + 1 );
      if( cbData->azColName[i] ){
        strcpy(cbData->azColName[i], Tcl_DStringValue(&dCol));
      }else{
        return 1;
      }
      if( cbData->zArray[0] ){
        Tcl_SetVar2(cbData->interp, cbData->zArray, "*",
             Tcl_DStringValue(&dCol), TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
        if( azN[nCol]!=0 ){
          Tcl_DString dType;
          Tcl_DStringInit(&dType);
          Tcl_DStringAppend(&dType, "typeof:", -1);
          Tcl_DStringAppend(&dType, Tcl_DStringValue(&dCol), -1);
          Tcl_DStringFree(&dCol);
          Tcl_ExternalToUtfDString(NULL, azN[i+nCol], -1, &dCol);
          Tcl_SetVar2(cbData->interp, cbData->zArray, 
               Tcl_DStringValue(&dType), Tcl_DStringValue(&dCol),
               TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
          Tcl_DStringFree(&dType);
        }
      }
      
      Tcl_DStringFree(&dCol);
    }
  }
  if( azCol!=0 ){
    if( cbData->zArray[0] ){
      for(i=0; i<nCol; i++){
        char *z = azCol[i];
        if( z==0 ) z = "";
        Tcl_DStringInit(&dCol);
        Tcl_ExternalToUtfDString(NULL, z, -1, &dCol);
        Tcl_SetVar2(cbData->interp, cbData->zArray, cbData->azColName[i], 
              Tcl_DStringValue(&dCol), 0);
        Tcl_DStringFree(&dCol);
      }
    }else{
      for(i=0; i<nCol; i++){
        char *z = azCol[i];
        if( z==0 ) z = "";
        Tcl_DStringInit(&dCol);
        Tcl_ExternalToUtfDString(NULL, z, -1, &dCol);
        Tcl_SetVar(cbData->interp, cbData->azColName[i],
                   Tcl_DStringValue(&dCol), 0);
        Tcl_DStringFree(&dCol);
      }
    }
  }
  rc = Tcl_EvalObj(cbData->interp, cbData->pCode);
  if( rc==TCL_CONTINUE ) rc = TCL_OK;
  cbData->tcl_rc = rc;
  return rc!=TCL_OK;
}
#endif /* UTF_TRANSLATION_NEEDED */

#ifndef UTF_TRANSLATION_NEEDED
/*
** Called for each row of the result.
**
** This version is used when either of the following is true:
**
**    (1) This version of TCL uses UTF-8 and the data in the
**        SQLite database is already in the UTF-8 format.
**
**    (2) This version of TCL uses ISO8859 and the data in the
**        SQLite database is already in the ISO8859 format.
*/
static int DbEvalCallback(
  void *clientData,      /* An instance of CallbackData */
  int nCol,              /* Number of columns in the result */
  char ** azCol,         /* Data for each column */
  char ** azN            /* Name for each column */
){
  CallbackData *cbData = (CallbackData*)clientData;
  int i, rc;
  if( azCol==0 || (cbData->once && cbData->zArray[0]) ){
    Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0);
    for(i=0; i<nCol; i++){
      Tcl_SetVar2(cbData->interp, cbData->zArray, "*", azN[i],
         TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
      if( azN[nCol] ){
        char *z = sqlite3_mprintf("typeof:%s", azN[i]);
        Tcl_SetVar2(cbData->interp, cbData->zArray, z, azN[i+nCol],
           TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
        sqlite3_freemem(z);
      }
    }
    cbData->once = 0;
  }
  if( azCol!=0 ){
    if( cbData->zArray[0] ){
      for(i=0; i<nCol; i++){
        char *z = azCol[i];
        if( z==0 ) z = "";
        Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], z, 0);
      }
    }else{
      for(i=0; i<nCol; i++){
        char *z = azCol[i];
        if( z==0 ) z = "";
        Tcl_SetVar(cbData->interp, azN[i], z, 0);
      }
    }
  }
  rc = Tcl_EvalObj(cbData->interp, cbData->pCode);
  if( rc==TCL_CONTINUE ) rc = TCL_OK;
  cbData->tcl_rc = rc;
  return rc!=TCL_OK;
}
#endif

/*
** This is an alternative callback for database queries.  Instead
** of invoking a TCL script to handle the result, this callback just
** appends each column of the result to a list.  After the query
** is complete, the list is returned.
*/
static int DbEvalCallback2(
  void *clientData,      /* An instance of CallbackData */
  int nCol,              /* Number of columns in the result */
  char ** azCol,         /* Data for each column */
  char ** azN            /* Name for each column */
){
  Tcl_Obj *pList = (Tcl_Obj*)clientData;
  int i;
  if( azCol==0 ) return 0;
  for(i=0; i<nCol; i++){
    Tcl_Obj *pElem;
    if( azCol[i] && *azCol[i] ){
#ifdef UTF_TRANSLATION_NEEDED
      Tcl_DString dCol;
      Tcl_DStringInit(&dCol);
      Tcl_ExternalToUtfDString(NULL, azCol[i], -1, &dCol);
      pElem = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1);
      Tcl_DStringFree(&dCol);
#else
      pElem = Tcl_NewStringObj(azCol[i], -1);
#endif
    }else{
      pElem = Tcl_NewObj();
    }
    Tcl_ListObjAppendElement(0, pList, pElem);
  }
  return 0;
}

/*
** This is a second alternative callback for database queries.  A the
** first column of the first row of the result is made the TCL result.
*/
static int DbEvalCallback3(
  void *clientData,      /* An instance of CallbackData */
  int nCol,              /* Number of columns in the result */







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







71
72
73
74
75
76
77
















































































































































































78
79
80
81
82
83
84
  Tcl_Obj *pCode;           /* The code to execute for each row */
  int once;                 /* Set for first callback only */
  int tcl_rc;               /* Return code from TCL script */
  int nColName;             /* Number of entries in the azColName[] array */
  char **azColName;         /* Column names translated to UTF-8 */
};

















































































































































































/*
** This is a second alternative callback for database queries.  A the
** first column of the first row of the result is made the TCL result.
*/
static int DbEvalCallback3(
  void *clientData,      /* An instance of CallbackData */
  int nCol,              /* Number of columns in the result */
470
471
472
473
474
475
476




















477
478
479
480
481
482
483
    rc = SQLITE_IGNORE;
  }else{
    rc = 999;
  }
  return rc;
}
#endif /* SQLITE_OMIT_AUTHORIZATION */





















/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database.  This routine is invoked
** whenever one of those connection-specific commands is executed
** in Tcl.  For example, if you run Tcl code like this:
**







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







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
    rc = SQLITE_IGNORE;
  }else{
    rc = 999;
  }
  return rc;
}
#endif /* SQLITE_OMIT_AUTHORIZATION */

/*
** zText is a pointer to text obtained via an sqlite3_result_text()
** or similar interface. This routine returns a Tcl string object, 
** reference count set to 0, containing the text. If a translation
** between iso8859 and UTF-8 is required, it is preformed.
*/
static Tcl_Obj *dbTextToObj(char const *zText){
  Tcl_Obj *pVal;
#ifdef UTF_TRANSLATION_NEEDED
  Tcl_DString dCol;
  Tcl_DStringInit(&dCol);
  Tcl_ExternalToUtfDString(NULL, zText, -1, &dCol);
  pVal = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1);
  Tcl_DStringFree(&dCol);
#else
  pVal = Tcl_NewStringObj(zText, -1);
#endif
  return pVal;
}

/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database.  This routine is invoked
** whenever one of those connection-specific commands is executed
** in Tcl.  For example, if you run Tcl code like this:
**
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
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
  ** If "array" is an empty string, then the values are placed in variables
  ** that have the same name as the fields extracted by the query.
  */
  case DB_EVAL: {
    char const *zSql;
    char const *zLeft;
    sqlite3_stmt *pStmt;

    Tcl_Obj *pRet = 0;


    if( objc!=5 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?");
      return TCL_ERROR;
    }

    pDb->nChange = 0;
    zSql = Tcl_GetStringFromObj(objv[2], 0);
    while( zSql[0] ){
      int i;
  
      if( SQLITE_OK!=sqlite3_prepare(pDb->db, zSql, -1, &pStmt, &zLeft) ){
        Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_STATIC);
        rc = TCL_ERROR;
        break;
      }
  
      if( pStmt && objc==5 ){
        Tcl_Obj *pColList = Tcl_NewObj();
        Tcl_IncrRefCount(pColList);

        for(i=0; i<sqlite3_column_count(pStmt); i++){
          Tcl_ListObjAppendElement(interp, pColList,
              Tcl_NewStringObj(sqlite3_column_name(pStmt, i), -1)
          );
        }
        Tcl_ObjSetVar2(interp,objv[3],Tcl_NewStringObj("*",-1),pColList,0);
      }

      while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
        for(i=0; i<sqlite3_column_count(pStmt); i++){
          Tcl_Obj *pVal;
          
          /* Set pVal to contain the i'th column of this row. */
          if( SQLITE3_BLOB!=sqlite3_column_type(pStmt, i) ){
            pVal = Tcl_NewStringObj(sqlite3_column_text(pStmt, i), -1);
          }else{
            int bytes = sqlite3_column_bytes(pStmt, i);
            pVal = Tcl_NewByteArrayObj(sqlite3_column_blob(pStmt, i), bytes);
          }
  
          if( objc==5 ){
            Tcl_Obj *pName = Tcl_NewStringObj(sqlite3_column_name(pStmt, i), -1);
            Tcl_IncrRefCount(pName);
            if( !strcmp("", Tcl_GetString(objv[3])) ){
              Tcl_ObjSetVar2(interp, pName, 0, pVal, 0);
            }else{
              Tcl_ObjSetVar2(interp, objv[3], pName, pVal, 0);
            }
            Tcl_DecrRefCount(pName);
          }else{
            if( !pRet ){
              pRet = Tcl_NewObj();
              Tcl_IncrRefCount(pRet);
            }
            Tcl_ListObjAppendElement(interp, pRet, pVal);
          }
        }
  
        if( objc==5 ){
          rc = Tcl_EvalObjEx(interp, objv[4], 0);
          if( rc!=TCL_ERROR ) rc = TCL_OK;
        }
      }
       
      if( pStmt && SQLITE_SCHEMA==sqlite3_finalize(pStmt) ){
        continue;
      }
  
      if( pStmt && SQLITE_OK!=sqlite3_errcode(pDb->db) ){
        Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_STATIC);
        rc = TCL_ERROR;
        break;
      }

      pDb->nChange += sqlite3_changes(pDb->db);
      zSql = zLeft;
    }

    if( rc==TCL_OK && pRet ){
      Tcl_SetObjResult(interp, pRet);
      Tcl_DecrRefCount(pRet);
    }

    break;
  }
#if 0
  case DB_EVAL: {
    CallbackData cbData;
    char *zErrMsg;
    char *zSql;
#ifdef UTF_TRANSLATION_NEEDED
    Tcl_DString dSql;
    int i;
#endif

    if( objc!=5 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?");
      return TCL_ERROR;
    }
    pDb->interp = interp;
    zSql = Tcl_GetStringFromObj(objv[2], 0);
#ifdef UTF_TRANSLATION_NEEDED
    Tcl_DStringInit(&dSql);
    Tcl_UtfToExternalDString(NULL, zSql, -1, &dSql);
    zSql = Tcl_DStringValue(&dSql);
#endif
    Tcl_IncrRefCount(objv[2]);
    if( objc==5 ){
      cbData.interp = interp;
      cbData.once = 1;
      cbData.zArray = Tcl_GetStringFromObj(objv[3], 0);
      cbData.pCode = objv[4];
      cbData.tcl_rc = TCL_OK;
      cbData.nColName = 0;
      cbData.azColName = 0;
      zErrMsg = 0;
      Tcl_IncrRefCount(objv[3]);
      Tcl_IncrRefCount(objv[4]);
      rc = sqlite3_exec(pDb->db, zSql, DbEvalCallback, &cbData, &zErrMsg);
      Tcl_DecrRefCount(objv[4]);
      Tcl_DecrRefCount(objv[3]);
      if( cbData.tcl_rc==TCL_BREAK ){ cbData.tcl_rc = TCL_OK; }
    }else{
      Tcl_Obj *pList = Tcl_NewObj();
      cbData.tcl_rc = TCL_OK;
      rc = sqlite3_exec(pDb->db, zSql, DbEvalCallback2, pList, &zErrMsg);
      Tcl_SetObjResult(interp, pList);
    }
    pDb->rc = rc;
    if( rc==SQLITE_ABORT ){
      if( zErrMsg ) free(zErrMsg);
      rc = cbData.tcl_rc;
    }else if( zErrMsg ){
      Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
      free(zErrMsg);
      rc = TCL_ERROR;
    }else if( rc!=SQLITE_OK ){
      Tcl_AppendResult(interp, sqlite3_error_string(rc), 0);
      rc = TCL_ERROR;
    }else{
    }
    Tcl_DecrRefCount(objv[2]);
#ifdef UTF_TRANSLATION_NEEDED
    Tcl_DStringFree(&dSql);
    if( objc==5 && cbData.azColName ){
      for(i=0; i<cbData.nColName; i++){
        if( cbData.azColName[i] ) free(cbData.azColName[i]);
      }
      free(cbData.azColName);
      cbData.azColName = 0;
    }
#endif
    return rc;
  }
#endif

  /*
  **     $db function NAME SCRIPT
  **
  ** Create a new SQL function called NAME.  Whenever that function is
  ** called, invoke SCRIPT to evaluate the function.
  */







>
|
>












|










|











|






|








<
<
<
<















|








|

<

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

<







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
694
695
696
697
698
699
700
701
702
703

704





































705







706
























707
708

709
710
711
712
713
714
715
  ** If "array" is an empty string, then the values are placed in variables
  ** that have the same name as the fields extracted by the query.
  */
  case DB_EVAL: {
    char const *zSql;
    char const *zLeft;
    sqlite3_stmt *pStmt;

    Tcl_Obj *pRet = Tcl_NewObj();
    Tcl_IncrRefCount(pRet);

    if( objc!=5 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?");
      return TCL_ERROR;
    }

    pDb->nChange = 0;
    zSql = Tcl_GetStringFromObj(objv[2], 0);
    while( zSql[0] ){
      int i;
  
      if( SQLITE_OK!=sqlite3_prepare(pDb->db, zSql, -1, &pStmt, &zLeft) ){
        Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
        rc = TCL_ERROR;
        break;
      }
  
      if( pStmt && objc==5 ){
        Tcl_Obj *pColList = Tcl_NewObj();
        Tcl_IncrRefCount(pColList);

        for(i=0; i<sqlite3_column_count(pStmt); i++){
          Tcl_ListObjAppendElement(interp, pColList,
              dbTextToObj(sqlite3_column_name(pStmt, i))
          );
        }
        Tcl_ObjSetVar2(interp,objv[3],Tcl_NewStringObj("*",-1),pColList,0);
      }

      while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
        for(i=0; i<sqlite3_column_count(pStmt); i++){
          Tcl_Obj *pVal;
          
          /* Set pVal to contain the i'th column of this row. */
          if( SQLITE3_BLOB!=sqlite3_column_type(pStmt, i) ){
            pVal = dbTextToObj(sqlite3_column_text(pStmt, i));
          }else{
            int bytes = sqlite3_column_bytes(pStmt, i);
            pVal = Tcl_NewByteArrayObj(sqlite3_column_blob(pStmt, i), bytes);
          }
  
          if( objc==5 ){
            Tcl_Obj *pName = dbTextToObj(sqlite3_column_name(pStmt, i));
            Tcl_IncrRefCount(pName);
            if( !strcmp("", Tcl_GetString(objv[3])) ){
              Tcl_ObjSetVar2(interp, pName, 0, pVal, 0);
            }else{
              Tcl_ObjSetVar2(interp, objv[3], pName, pVal, 0);
            }
            Tcl_DecrRefCount(pName);
          }else{




            Tcl_ListObjAppendElement(interp, pRet, pVal);
          }
        }
  
        if( objc==5 ){
          rc = Tcl_EvalObjEx(interp, objv[4], 0);
          if( rc!=TCL_ERROR ) rc = TCL_OK;
        }
      }
       
      if( pStmt && SQLITE_SCHEMA==sqlite3_finalize(pStmt) ){
        continue;
      }
  
      if( pStmt && SQLITE_OK!=sqlite3_errcode(pDb->db) ){
        Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
        rc = TCL_ERROR;
        break;
      }

      pDb->nChange += sqlite3_changes(pDb->db);
      zSql = zLeft;
    }

    if( rc==TCL_OK ){
      Tcl_SetObjResult(interp, pRet);

    }





































    Tcl_DecrRefCount(pRet);
































    break;
  }


  /*
  **     $db function NAME SCRIPT
  **
  ** Create a new SQL function called NAME.  Whenever that function is
  ** called, invoke SCRIPT to evaluate the function.
  */
Changes to src/trigger.c.
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
59

60


61







62


63
64



65






66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91




92
93
94
95




96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
** structure is generated based on the information available and stored
** in pParse->pNewTrigger.  After the trigger actions have been parsed, the
** sqlite3FinishTrigger() function is called to complete the trigger
** construction process.
*/
void sqlite3BeginTrigger(
  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 */
){
  Trigger *nt;
  Table   *tab;
  char *zName = 0;        /* Name of the trigger */
  sqlite *db = pParse->db;
  int iDb;                /* When database to store the trigger in */

  DbFixer sFix;

  /* Check that: 

  ** 1. the trigger name does not already exist.


  ** 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( sqlite3_malloc_failed ) goto trigger_cleanup;
  assert( pTableName->nSrc==1 );
  if( db->init.busy
   && sqlite3FixInit(&sFix, pParse, db->init.iDb, "trigger", pName)
   && sqlite3FixSrcList(&sFix, pTableName)
  ){
    goto trigger_cleanup;
  }
  tab = sqlite3SrcListLookup(pParse, pTableName);
  if( !tab ){
    goto trigger_cleanup;
  }
  iDb = isTemp ? 1 : tab->iDb;
  if( iDb>=2 && !db->init.busy ){
    sqlite3ErrorMsg(pParse, "triggers may not be added to auxiliary "
       "database %s", db->aDb[tab->iDb].zName);
    goto trigger_cleanup;
  }


  zName = sqliteStrNDup(pName->z, pName->n);
  sqlite3Dequote(zName);
  if( sqlite3HashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
    sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
    goto trigger_cleanup;
  }
  if( sqlite3StrNICmp(tab->zName, "sqlite_", 7)==0 ){




    sqlite3ErrorMsg(pParse, "cannot create trigger on system table");
    pParse->nErr++;
    goto trigger_cleanup;
  }




  if( tab->pSelect && tr_tm != TK_INSTEAD ){
    sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", 
        (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
    goto trigger_cleanup;
  }
  if( !tab->pSelect && tr_tm == TK_INSTEAD ){
    sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
        " trigger on table: %S", pTableName, 0);
    goto trigger_cleanup;
  }

#ifndef SQLITE_OMIT_AUTHORIZATION
  {
    int code = SQLITE_CREATE_TRIGGER;
    const char *zDb = db->aDb[tab->iDb].zName;
    const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
    if( tab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
    if( sqlite3AuthCheck(pParse, code, zName, tab->zName, zDbTrig) ){
      goto trigger_cleanup;
    }
    if( sqlite3AuthCheck(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;
  nt->name = zName;
  zName = 0;
  nt->table = sqliteStrDup(pTableName->a[0].zName);
  if( sqlite3_malloc_failed ) goto trigger_cleanup;
  nt->iDb = iDb;
  nt->iTabDb = tab->iDb;
  nt->op = op;
  nt->tr_tm = tr_tm;
  nt->pWhen = sqlite3ExprDup(pWhen);
  nt->pColumns = sqlite3IdListDup(pColumns);
  nt->foreach = foreach;
  sqlite3TokenCopy(&nt->nameToken,pName);
  assert( pParse->pNewTrigger==0 );
  pParse->pNewTrigger = nt;

trigger_cleanup:
  sqliteFree(zName);
  sqlite3SrcListDelete(pTableName);
  sqlite3IdListDelete(pColumns);
  sqlite3ExprDelete(pWhen);
}







|
>








|
|


|
>


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

>
>
>
>
>
>


<
|
|
<


|
|
<
<
|
<
<
<



>






|
>
>
>
>




>
>
>
>
|




|




>



|

|
|


|





|









|
|
|

|

|
|
|
|
|
|
|
|

|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

90
91

92
93
94
95


96



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
** structure is generated based on the information available and stored
** in pParse->pNewTrigger.  After the trigger actions have been parsed, the
** sqlite3FinishTrigger() function is called to complete the trigger
** construction process.
*/
void sqlite3BeginTrigger(
  Parse *pParse,      /* The parse context of the CREATE TRIGGER statement */
  Token *pName1,      /* The name of the trigger */
  Token *pName2,      /* 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 */
){
  Trigger *pTrigger;
  Table *pTab;
  char *zName = 0;        /* Name of the trigger */
  sqlite *db = pParse->db;
  int iDb;                /* The database to store the trigger in */
  Token *pName;           /* The unqualified db name */
  DbFixer sFix;


  if( isTemp ){
    /* If TEMP was specified, then the trigger name may not be qualified. */
    if( pName2 && pName2->n>0 ){
      sqlite3ErrorMsg(pParse, "temporary trigger may not have qualified name");
      goto trigger_cleanup;
    }
    iDb = 1;
    pName = pName1;
  }else{
    /* Figure out the db that the the trigger will be created in */
    iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
    if( iDb<0 ){
      goto trigger_cleanup;
    }
  }

  /* If the trigger name was unqualified, and the table is a temp table,
  ** then set iDb to 1 to create the trigger in the temporary database.
  ** If sqlite3SrcListLookup() returns 0, indicating the table does not
  ** exist, the error is caught by the block below.
  */
  pTab = sqlite3SrcListLookup(pParse, pTableName);
  if( pName2->n==0 && pTab && pTab->iDb==1 ){
    iDb = 1;
  }

  /* Ensure the table name matches database name and that the table exists */
  if( sqlite3_malloc_failed ) goto trigger_cleanup;
  assert( pTableName->nSrc==1 );

  if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) && 
      sqlite3FixSrcList(&sFix, pTableName) ){

    goto trigger_cleanup;
  }
  pTab = sqlite3SrcListLookup(pParse, pTableName);
  if( !pTab ){


    /* The table does not exist. */



    goto trigger_cleanup;
  }

  /* Check that no trigger of the specified name exists */
  zName = sqliteStrNDup(pName->z, pName->n);
  sqlite3Dequote(zName);
  if( sqlite3HashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
    sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
    goto trigger_cleanup;
  }

  /* Do not create a trigger on a system table */
  if( (iDb!=1 && sqlite3StrICmp(pTab->zName, MASTER_NAME)==0) || 
      (iDb==1 && sqlite3StrICmp(pTab->zName, TEMP_MASTER_NAME)==0) 
  ){
    sqlite3ErrorMsg(pParse, "cannot create trigger on system table");
    pParse->nErr++;
    goto trigger_cleanup;
  }

  /* INSTEAD of triggers are only for views and views only support INSTEAD
  ** of triggers.
  */
  if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
    sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", 
        (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
    goto trigger_cleanup;
  }
  if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
    sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
        " trigger on table: %S", pTableName, 0);
    goto trigger_cleanup;
  }

#ifndef SQLITE_OMIT_AUTHORIZATION
  {
    int code = SQLITE_CREATE_TRIGGER;
    const char *zDb = db->aDb[pTab->iDb].zName;
    const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
    if( pTab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
    if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
      goto trigger_cleanup;
    }
    if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->iDb), 0, zDb)){
      goto trigger_cleanup;
    }
  }
#endif

  /* INSTEAD OF triggers can only appear on views and BEFORE 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 */
  pTrigger = (Trigger*)sqliteMalloc(sizeof(Trigger));
  if( pTrigger==0 ) goto trigger_cleanup;
  pTrigger->name = zName;
  zName = 0;
  pTrigger->table = sqliteStrDup(pTableName->a[0].zName);
  if( sqlite3_malloc_failed ) goto trigger_cleanup;
  pTrigger->iDb = iDb;
  pTrigger->iTabDb = pTab->iDb;
  pTrigger->op = op;
  pTrigger->tr_tm = tr_tm;
  pTrigger->pWhen = sqlite3ExprDup(pWhen);
  pTrigger->pColumns = sqlite3IdListDup(pColumns);
  pTrigger->foreach = foreach;
  sqlite3TokenCopy(&pTrigger->nameToken,pName);
  assert( pParse->pNewTrigger==0 );
  pParse->pNewTrigger = pTrigger;

trigger_cleanup:
  sqliteFree(zName);
  sqlite3SrcListDelete(pTableName);
  sqlite3IdListDelete(pColumns);
  sqlite3ExprDelete(pWhen);
}
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    };
    int addr;
    Vdbe *v;

    /* Make an entry in the sqlite_master table */
    v = sqlite3GetVdbe(pParse);
    if( v==0 ) goto triggerfinish_cleanup;
    sqlite3BeginWriteOperation(pParse, 0, 0);
    sqlite3OpenMasterTable(v, nt->iDb);
    addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
    sqlite3VdbeChangeP3(v, addr+2, nt->name, 0); 
    sqlite3VdbeChangeP3(v, addr+3, nt->table, 0); 
    sqlite3VdbeChangeP3(v, addr+5, pAll->z, pAll->n);
    if( nt->iDb==0 ){
      sqlite3ChangeCookie(db, v, 0);







|







219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
    };
    int addr;
    Vdbe *v;

    /* Make an entry in the sqlite_master table */
    v = sqlite3GetVdbe(pParse);
    if( v==0 ) goto triggerfinish_cleanup;
    sqlite3BeginWriteOperation(pParse, 0, nt->iDb);
    sqlite3OpenMasterTable(v, nt->iDb);
    addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
    sqlite3VdbeChangeP3(v, addr+2, nt->name, 0); 
    sqlite3VdbeChangeP3(v, addr+3, nt->table, 0); 
    sqlite3VdbeChangeP3(v, addr+5, pAll->z, pAll->n);
    if( nt->iDb==0 ){
      sqlite3ChangeCookie(db, v, 0);
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
*/
void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
  Table   *pTable;
  Vdbe *v;
  sqlite *db = pParse->db;

  assert( pTrigger->iDb<db->nDb );
  if( pTrigger->iDb>=2 ){
    sqlite3ErrorMsg(pParse, "triggers may not be removed from "
       "auxiliary database %s", db->aDb[pTrigger->iDb].zName);
    return;
  }
  pTable = sqlite3FindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName);
  assert(pTable);
  assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
  {
    int code = SQLITE_DROP_TRIGGER;
    const char *zDb = db->aDb[pTrigger->iDb].zName;
    const char *zTab = SCHEMA_TABLE(pTrigger->iDb);
    if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER;
    if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
      sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
      return;
    }
  }
#endif








<
<
<
<
<








|







446
447
448
449
450
451
452





453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
*/
void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
  Table   *pTable;
  Vdbe *v;
  sqlite *db = pParse->db;

  assert( pTrigger->iDb<db->nDb );





  pTable = sqlite3FindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName);
  assert(pTable);
  assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
  {
    int code = SQLITE_DROP_TRIGGER;
    const char *zDb = db->aDb[pTrigger->iDb].zName;
    const char *zTab = SCHEMA_TABLE(pTrigger->iDb);
    if( pTrigger->iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER;
    if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
      sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
      return;
    }
  }
#endif

458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
      { OP_String,     0, 0,        "trigger"},
      { OP_Column,     0, 0,        0},
      { OP_Ne,         0, ADDR(8),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(1),  0}, /* 8 */
    };

    sqlite3BeginWriteOperation(pParse, 0, 0);
    sqlite3OpenMasterTable(v, pTrigger->iDb);
    base = sqlite3VdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
    sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0);
    if( pTrigger->iDb==0 ){
      sqlite3ChangeCookie(db, v, 0);
    }
    sqlite3VdbeAddOp(v, OP_Close, 0, 0);







|







478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
      { OP_String,     0, 0,        "trigger"},
      { OP_Column,     0, 0,        0},
      { OP_Ne,         0, ADDR(8),  0},
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(1),  0}, /* 8 */
    };

    sqlite3BeginWriteOperation(pParse, 0, pTrigger->iDb);
    sqlite3OpenMasterTable(v, pTrigger->iDb);
    base = sqlite3VdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
    sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0);
    if( pTrigger->iDb==0 ){
      sqlite3ChangeCookie(db, v, 0);
    }
    sqlite3VdbeAddOp(v, OP_Close, 0, 0);
Changes to src/vdbe.c.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.345 2004/05/28 16:00:22 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.346 2004/05/29 02:37:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
  /* Pop nField entries from the stack and push the new entry on */
  if( addRowid || pOp->p2==0 ){
    popStack(&pTos, nField+addRowid);
  }
  pTos++;
  pTos->n = nByte;
  if( nByte<=sizeof(zTemp) ){
    assert( zNewRecord==zTemp );
    pTos->z = pTos->zShort;
    memcpy(pTos->zShort, zTemp, nByte);
    pTos->flags = MEM_Blob | MEM_Short;
  }else{
    assert( zNewRecord!=zTemp );
    pTos->z = zNewRecord;
    pTos->flags = MEM_Blob | MEM_Dyn;
  }

  /* If P2 is non-zero, and if the key contains a NULL value, and if this
  ** was an OP_MakeIdxKey instruction, not OP_MakeKey, jump to P2.
  */







|




|







2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
  /* Pop nField entries from the stack and push the new entry on */
  if( addRowid || pOp->p2==0 ){
    popStack(&pTos, nField+addRowid);
  }
  pTos++;
  pTos->n = nByte;
  if( nByte<=sizeof(zTemp) ){
    assert( zNewRecord==(unsigned char *)zTemp );
    pTos->z = pTos->zShort;
    memcpy(pTos->zShort, zTemp, nByte);
    pTos->flags = MEM_Blob | MEM_Short;
  }else{
    assert( zNewRecord!=(unsigned char *)zTemp );
    pTos->z = zNewRecord;
    pTos->flags = MEM_Blob | MEM_Dyn;
  }

  /* If P2 is non-zero, and if the key contains a NULL value, and if this
  ** was an OP_MakeIdxKey instruction, not OP_MakeKey, jump to P2.
  */
Changes to test/attach.test.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.16 2004/05/11 09:57:35 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







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach.test,v 1.17 2004/05/29 02:37:20 danielk1977 Exp $
#

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

for {set i 2} {$i<=15} {incr i} {
  file delete -force test$i.db
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  sqlite db2 test2.db
  catchsql {
    ATTACH DATABASE 'test.db' AS orig;
    CREATE TRIGGER r1 AFTER INSERT ON orig.t1 BEGIN;
      SELECT 'no-op';
    END;
  } db2
} {1 {triggers may not be added to auxiliary database orig}}
do_test attach-5.2 {
  catchsql {
    CREATE TABLE t5(x,y);
    CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
      SELECT 'no-op';
    END;
  } db2







|







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  sqlite db2 test2.db
  catchsql {
    ATTACH DATABASE 'test.db' AS orig;
    CREATE TRIGGER r1 AFTER INSERT ON orig.t1 BEGIN;
      SELECT 'no-op';
    END;
  } db2
} {1 {trigger r1 cannot reference objects in database orig}}
do_test attach-5.2 {
  catchsql {
    CREATE TABLE t5(x,y);
    CREATE TRIGGER r5 AFTER INSERT ON t5 BEGIN
      SELECT 'no-op';
    END;
  } db2
Changes to test/attach3.test.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and schema changes to attached databases.
#
# $Id: attach3.test,v 1.3 2004/05/28 12:33:32 danielk1977 Exp $
#


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

# Create tables t1 and t2 in the main database







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and schema changes to attached databases.
#
# $Id: attach3.test,v 1.4 2004/05/29 02:37:20 danielk1977 Exp $
#


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

# Create tables t1 and t2 in the main database
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154



































155
156
157
158
159
160
  execsql {
    INSERT INTO t3 VALUES(1, 2);
    SELECT * FROM t3;
  }
} {1 2}

# Create an index on the auxilary database table.
do_test attach4-2.1 {
  execsql {
    CREATE INDEX aux.i1 on t3(e);
  }
} {}
execsql {
  pragma vdbe_trace = off;
}
do_test attach4-2.2 {
  execsql {
    SELECT * FROM sqlite_master WHERE name = 'i1';
  }
} {}
do_test attach4-2.3 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {index i1 t3 5 {CREATE INDEX i1 on t3(e)}}

# Drop the index on the aux database table.
do_test attach4-3.1 {
  execsql {
    DROP INDEX aux.i1;
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {}
do_test attach4-3.2 {
  execsql {
    CREATE INDEX aux.i1 on t3(e);
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {index i1 t3 5 {CREATE INDEX i1 on t3(e)}}
do_test attach4-3.3 {
  execsql {
    DROP INDEX i1;
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {}

# Drop tables t1 and t2 in the auxilary database.
do_test attach4-4.1 {
  execsql {
    DROP TABLE aux.t1;
    SELECT name FROM aux.sqlite_master;
  }
} {t2 t3}
do_test attach4-4.2 {
  # This will drop main.t2
  execsql {
    DROP TABLE t2;
    SELECT name FROM aux.sqlite_master;
  }
} {t2 t3}
do_test attach4-4.3 {
  execsql {
    DROP TABLE t2;
    SELECT name FROM aux.sqlite_master;
  }
} {t3}

# Create a view in the auxilary database.
do_test attach4-5.1 {
  execsql {
    CREATE VIEW aux.v1 AS SELECT * FROM t3;
  }
} {}
do_test attach4-5.2 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'v1';
  }
} {view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t3}}
do_test attach4-5.3 {
  execsql {
    INSERT INTO aux.t3 VALUES('hello', 'world');
    SELECT * FROM v1;
  }
} {1 2 hello world}

# Drop the view 
do_test attach4-6.1 {
  execsql {
    DROP VIEW aux.v1;
  }
} {}
do_test attach4-5.2 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'v1';
  }
} {}




































finish_test











|







|




|






|





|





|







|





|






|







|




|




|







|




|




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






59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  execsql {
    INSERT INTO t3 VALUES(1, 2);
    SELECT * FROM t3;
  }
} {1 2}

# Create an index on the auxilary database table.
do_test attach3-2.1 {
  execsql {
    CREATE INDEX aux.i1 on t3(e);
  }
} {}
execsql {
  pragma vdbe_trace = off;
}
do_test attach3-2.2 {
  execsql {
    SELECT * FROM sqlite_master WHERE name = 'i1';
  }
} {}
do_test attach3-2.3 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {index i1 t3 5 {CREATE INDEX i1 on t3(e)}}

# Drop the index on the aux database table.
do_test attach3-3.1 {
  execsql {
    DROP INDEX aux.i1;
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {}
do_test attach3-3.2 {
  execsql {
    CREATE INDEX aux.i1 on t3(e);
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {index i1 t3 5 {CREATE INDEX i1 on t3(e)}}
do_test attach3-3.3 {
  execsql {
    DROP INDEX i1;
    SELECT * FROM aux.sqlite_master WHERE name = 'i1';
  }
} {}

# Drop tables t1 and t2 in the auxilary database.
do_test attach3-4.1 {
  execsql {
    DROP TABLE aux.t1;
    SELECT name FROM aux.sqlite_master;
  }
} {t2 t3}
do_test attach3-4.2 {
  # This will drop main.t2
  execsql {
    DROP TABLE t2;
    SELECT name FROM aux.sqlite_master;
  }
} {t2 t3}
do_test attach3-4.3 {
  execsql {
    DROP TABLE t2;
    SELECT name FROM aux.sqlite_master;
  }
} {t3}

# Create a view in the auxilary database.
do_test attach3-5.1 {
  execsql {
    CREATE VIEW aux.v1 AS SELECT * FROM t3;
  }
} {}
do_test attach3-5.2 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'v1';
  }
} {view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t3}}
do_test attach3-5.3 {
  execsql {
    INSERT INTO aux.t3 VALUES('hello', 'world');
    SELECT * FROM v1;
  }
} {1 2 hello world}

# Drop the view 
do_test attach3-6.1 {
  execsql {
    DROP VIEW aux.v1;
  }
} {}
do_test attach3-6.2 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'v1';
  }
} {}

# Create a trigger in the auxilary database.
do_test attach3-7.1 {
  execsql {
    CREATE TRIGGER aux.tr1 AFTER INSERT ON t3 BEGIN
      INSERT INTO t3 VALUES(new.e*2, new.f*2);
    END;
  }
} {}
do_test attach3-7.2 {
  execsql {
    DELETE FROM t3;
    INSERT INTO t3 VALUES(10, 20);
    SELECT * FROM t3;
  }
} {10 20 20 40}
do_test attach3-5.3 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'tr1';
  }
} {trigger tr1 t3 0 {CREATE TRIGGER aux.tr1 AFTER INSERT ON t3 BEGIN
      INSERT INTO t3 VALUES(new.e*2, new.f*2);
    END}}

# Drop the trigger 
do_test attach3-8.1 {
  execsql {
    DROP TRIGGER aux.tr1;
  }
} {}
do_test attach3-8.2 {
  execsql {
    SELECT * FROM aux.sqlite_master WHERE name = 'tr1';
  }
} {}

finish_test




Changes to test/trigger1.test.
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

do_test trigger1-1.1.2 {
   catchsql {
     CREATE TRIGGER trig UPDATE ON no_such_table BEGIN
       SELECT * from sqlite_master;
     END;
   } 
} {1 {no such table: no_such_table}}
do_test trigger1-1.1.2 {
   catchsql {
     CREATE TEMP TRIGGER trig UPDATE ON no_such_table BEGIN
       SELECT * from sqlite_master;
     END;
   } 
} {1 {no such table: no_such_table}}







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

do_test trigger1-1.1.2 {
   catchsql {
     CREATE TRIGGER trig UPDATE ON no_such_table BEGIN
       SELECT * from sqlite_master;
     END;
   } 
} {1 {no such table: main.no_such_table}}
do_test trigger1-1.1.2 {
   catchsql {
     CREATE TEMP TRIGGER trig UPDATE ON no_such_table BEGIN
       SELECT * from sqlite_master;
     END;
   } 
} {1 {no such table: no_such_table}}
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
do_test trigger1-1.12 {
  catchsql {
    create table t1(a,b);
    create trigger t1t instead of update on t1 for each row begin
      delete from t1 WHERE a=old.a+2;
    end;
  }
} {1 {cannot create INSTEAD OF trigger on table: t1}}
# Ensure that we cannot create BEFORE triggers on views
do_test trigger1-1.13 {
  catchsql {
    create view v1 as select * from t1;
    create trigger v1t before update on v1 for each row begin
      delete from t1 WHERE a=old.a+2;
    end;
  }
} {1 {cannot create BEFORE trigger on view: v1}}
# Ensure that we cannot create AFTER triggers on views
do_test trigger1-1.14 {
  catchsql {
    drop view v1;
    create view v1 as select * from t1;
    create trigger v1t AFTER update on v1 for each row begin
      delete from t1 WHERE a=old.a+2;
    end;
  }
} {1 {cannot create AFTER trigger on view: v1}}

# Check for memory leaks in the trigger parser
#
do_test trigger1-2.1 {
  catchsql {
    CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
      SELECT * FROM;  -- Syntax error







|








|









|







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
do_test trigger1-1.12 {
  catchsql {
    create table t1(a,b);
    create trigger t1t instead of update on t1 for each row begin
      delete from t1 WHERE a=old.a+2;
    end;
  }
} {1 {cannot create INSTEAD OF trigger on table: main.t1}}
# Ensure that we cannot create BEFORE triggers on views
do_test trigger1-1.13 {
  catchsql {
    create view v1 as select * from t1;
    create trigger v1t before update on v1 for each row begin
      delete from t1 WHERE a=old.a+2;
    end;
  }
} {1 {cannot create BEFORE trigger on view: main.v1}}
# Ensure that we cannot create AFTER triggers on views
do_test trigger1-1.14 {
  catchsql {
    drop view v1;
    create view v1 as select * from t1;
    create trigger v1t AFTER update on v1 for each row begin
      delete from t1 WHERE a=old.a+2;
    end;
  }
} {1 {cannot create AFTER trigger on view: main.v1}}

# Check for memory leaks in the trigger parser
#
do_test trigger1-2.1 {
  catchsql {
    CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
      SELECT * FROM;  -- Syntax error