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: |
4060a37d0baaa60c50f2dde4a1ab3441 |
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
Changes to src/build.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | ** DROP INDEX ** creating ID lists ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** PRAGMA ** | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** DROP INDEX ** creating ID lists ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK ** PRAGMA ** ** $Id: build.c,v 1.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 | 0==sqlite3StrNICmp(db->aDb[i].zName, pName->z, pName->n) ){ return i; } } return -1; } | | | 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 | ** 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. */ | | | 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 | sqlite3StartTable(pParse, pBegin, pName1, pName2, isTemp, 1); p = pParse->pNewTable; if( p==0 || pParse->nErr ){ 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 | ** 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 */ | | < < < < < < < < < | < | | | | | | < | < | | | > > < | < | | < < < < < < | 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 | ** ************************************************************************* ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** ************************************************************************* ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** ** @(#) $Id: parse.y,v 1.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 | 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) | | | > | 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 | 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); } | | | | 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 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** ** @(#) $Id: sqliteInt.h,v 1.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 | ** The root-page of the master database table. */ #define MASTER_ROOT 1 /* ** The name of the schema table. */ | | | 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 | 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*); | | > | 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 | 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); | | > | 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 | /* ** 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 ** | | | 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 | 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 */ }; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | ** 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; | > | > | | | | < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < | < | 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 | ** 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 */ | | > | | | > < > | > > | > > > > > > > | > > | | > > > > > > > > > < | | < | | < < | < < < > | > > > > > > > > | | > | | | | | | | | | | | | | | | | | | | 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 | }; int addr; Vdbe *v; /* Make an entry in the sqlite_master table */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; | | | 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 | */ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){ Table *pTable; Vdbe *v; sqlite *db = pParse->db; assert( pTrigger->iDb<db->nDb ); | < < < < < | | 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 | { 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 */ }; | | | 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 | ** ** 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. ** | | | 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 | /* 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) ){ | | | | 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 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is testing the ATTACH and DETACH commands # and related functionality. # | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is testing the ATTACH and DETACH commands # and related functionality. # # $Id: attach.test,v 1.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 | 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 | | | 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 | # 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. # | | | 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 | execsql { INSERT INTO t3 VALUES(1, 2); SELECT * FROM t3; } } {1 2} # Create an index on the auxilary database table. | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test trigger1-1.1.2 { catchsql { CREATE TRIGGER trig UPDATE ON no_such_table BEGIN SELECT * from sqlite_master; END; } | | | 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 | 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; } | | | | | 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 |
︙ | ︙ |