Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix for ticket #41: Better handling of CREATE TRIGGER in the sqlite_complete() function. (CVS 567) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
f45c4b767a6b1451787836060235ff74 |
User & Date: | drh 2002-05-15 14:17:45.000 |
Context
2002-05-15
| ||
23:26 | Fix a typo in the c_interface.html documentation file. (CVS 568) (check-in: 454879fa40 user: drh tags: trunk) | |
14:17 | Fix for ticket #41: Better handling of CREATE TRIGGER in the sqlite_complete() function. (CVS 567) (check-in: f45c4b767a user: drh tags: trunk) | |
12:45 | Beginning to clean up the trigger code. Still lots of work to do. (CVS 566) (check-in: b10346818b user: drh tags: trunk) | |
Changes
Changes to src/main.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** | | > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** ** $Id: main.c,v 1.74 2002/05/15 14:17:45 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include <ctype.h> /* ** This is the callback routine for the code that initializes the ** database. See sqliteInit() below for additional information. ** ** Each callback contains the following information: ** |
︙ | ︙ | |||
472 473 474 475 476 477 478 479 480 | } sqliteHashClear(&db->aFunc); sqliteFree(db); } /* ** Return TRUE if the given SQL string ends in a semicolon. */ int sqlite_complete(const char *zSql){ | > > > > | > > > > > > > > < < < < < | | | > | > | > > > | > > > > | | > > | > > > > > > > > > > > | > > > > > > > | | > > > | | > | | > > > | | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | } sqliteHashClear(&db->aFunc); sqliteFree(db); } /* ** Return TRUE if the given SQL string ends in a semicolon. ** ** Special handling is require for CREATE TRIGGER statements. ** Whenever the CREATE TRIGGER keywords are seen, the statement ** must end with ";END;". */ int sqlite_complete(const char *zSql){ int isComplete = 1; int requireEnd = 0; int seenText = 0; int seenCreate = 0; while( *zSql ){ switch( *zSql ){ case ';': { isComplete = 1; seenText = 1; seenCreate = 0; break; } case ' ': case '\t': case '\n': case '\f': { break; } case '[': { isComplete = 0; seenText = 1; seenCreate = 0; zSql++; while( *zSql && *zSql!=']' ){ zSql++; } if( *zSql==0 ) return 0; break; } case '"': case '\'': { int c = *zSql; isComplete = 0; seenText = 1; seenCreate = 0; zSql++; while( *zSql && *zSql!=c ){ zSql++; } if( *zSql==0 ) return 0; break; } case '-': { if( zSql[1]!='-' ){ isComplete = 0; seenCreate = 0; break; } while( *zSql && *zSql!='\n' ){ zSql++; } if( *zSql==0 ) return seenText && isComplete && requireEnd==0; break; } case 'c': case 'C': { seenText = 1; if( !isComplete ) break; isComplete = 0; if( sqliteStrNICmp(zSql, "create", 6)!=0 ) break; if( !isspace(zSql[6]) ) break; zSql += 5; seenCreate = 1; while( isspace(zSql[1]) ) zSql++; if( sqliteStrNICmp(&zSql[1],"trigger", 7)!=0 ) break; zSql += 7; requireEnd++; break; } case 't': case 'T': { seenText = 1; if( !seenCreate ) break; seenCreate = 0; isComplete = 0; if( sqliteStrNICmp(zSql, "trigger", 7)!=0 ) break; if( !isspace(zSql[7]) ) break; zSql += 6; requireEnd++; break; } case 'e': case 'E': { seenCreate = 0; seenText = 1; if( !isComplete ) break; isComplete = 0; if( requireEnd==0 ) break; if( sqliteStrNICmp(zSql, "end", 3)!=0 ) break; zSql += 2; while( isspace(zSql[1]) ) zSql++; if( zSql[1]==';' ){ zSql++; isComplete = 1; requireEnd--; } break; } default: { seenCreate = 0; seenText = 1; isComplete = 0; break; } } zSql++; } return seenText && isComplete && requireEnd==0; } /* ** Execute SQL code. Return one of the SQLITE_ success/failure ** codes. Also write an error message into memory obtained from ** malloc() and make *pzErrMsg point to that message. ** |
︙ | ︙ |
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.65 2002/05/15 14:17:45 drh Exp $ */ %token_prefix TK_ %token_type {Token} %default_type {Token} %extra_argument {Parse *pParse} %syntax_error { sqliteSetString(&pParse->zErrMsg,"syntax error",0); |
︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 125 126 127 128 129 | id(A) ::= ID(X). {A = X;} id(A) ::= IGNORE(X). {A = X;} id(A) ::= KEY(X). {A = X;} id(A) ::= OFFSET(X). {A = X;} id(A) ::= PRAGMA(X). {A = X;} id(A) ::= REPLACE(X). {A = X;} id(A) ::= TEMP(X). {A = X;} id(A) ::= VACUUM(X). {A = X;} id(A) ::= VIEW(X). {A = X;} // And "ids" is an identifer-or-string. // %type ids {Token} ids(A) ::= id(X). {A = X;} | > | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | id(A) ::= ID(X). {A = X;} id(A) ::= IGNORE(X). {A = X;} id(A) ::= KEY(X). {A = X;} id(A) ::= OFFSET(X). {A = X;} id(A) ::= PRAGMA(X). {A = X;} id(A) ::= REPLACE(X). {A = X;} id(A) ::= TEMP(X). {A = X;} id(A) ::= TRIGGER(X). {A = X;} id(A) ::= VACUUM(X). {A = X;} id(A) ::= VIEW(X). {A = X;} // And "ids" is an identifer-or-string. // %type ids {Token} ids(A) ::= id(X). {A = X;} |
︙ | ︙ | |||
688 689 690 691 692 693 694 | // SELECT trigger_cmd(A) ::= select(X). {A = sqliteTriggerSelectStep(X); } //////////////////////// DROP TRIGGER statement ////////////////////////////// cmd ::= DROP TRIGGER ids(X). { sqliteDropTrigger(pParse,&X,0); } | < | 689 690 691 692 693 694 695 | // SELECT trigger_cmd(A) ::= select(X). {A = sqliteTriggerSelectStep(X); } //////////////////////// DROP TRIGGER statement ////////////////////////////// cmd ::= DROP TRIGGER ids(X). { sqliteDropTrigger(pParse,&X,0); } |
Changes to test/main.test.
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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is exercising the code in main.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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is exercising the code in main.c. # # $Id: main.test,v 1.11 2002/05/15 14:17:45 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Tests of the sqlite_complete() function. # do_test main-1.1 { |
︙ | ︙ | |||
66 67 68 69 70 71 72 73 74 75 76 77 78 79 | } {1} do_test main-1.14 { db complete {SELECT a-b FROM t1; } } {1} do_test main-1.15 { db complete {SELECT a-b FROM t1 } } {0} # Try to open a database with a corrupt database file. # do_test main-2.0 { catch {db close} file delete -force test.db set fd [open test.db w] | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {1} do_test main-1.14 { db complete {SELECT a-b FROM t1; } } {1} do_test main-1.15 { db complete {SELECT a-b FROM t1 } } {0} do_test main-1.16 { db complete { CREATE TABLE abc(x,y); } } {1} do_test main-1.17 { db complete { CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr; } } {0} do_test main-1.18 { db complete { CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr; END; } } {1} do_test main-1.19 { db complete { CREATE TRIGGER xyz AFTER DELETE abc BEGIN UPDATE pqr; unknown command; } } {0} do_test main-1.20 { db complete { CREATE TRIGGER xyz AFTER DELETE backend BEGIN UPDATE pqr; } } {0} do_test main-1.21 { db complete { CREATE TRIGGER xyz AFTER DELETE end BEGIN SELECT a, b FROM end; } } {0} do_test main-1.22 { db complete { CREATE TRIGGER xyz AFTER DELETE end BEGIN SELECT a, b FROM end; END; } } {1} do_test main-1.23 { db complete { CREATE TRIGGER xyz AFTER DELETE end BEGIN SELECT a, b FROM end; END; SELECT a, b FROM end; } } {1} do_test main-1.24 { db complete { CREATE TRIGGER xyz AFTER DELETE [;end;] BEGIN UPDATE pqr; } } {0} do_test main-1.25 { db complete { CREATE TRIGGER xyz AFTER DELETE backend BEGIN UPDATE pqr SET a=[;end;];;; } } {0} do_test main-1.26 { db complete { CREATE -- a comment TRIGGER xyz AFTER DELETE backend BEGIN UPDATE pqr SET a=5; } } {0} do_test main-1.27 { db complete { CREATE -- a comment TRIGGERX xyz AFTER DELETE backend BEGIN UPDATE pqr SET a=5; } } {1} do_test main-1.28 { db complete { CREATE TEMP TRIGGER xyz AFTER DELETE backend BEGIN UPDATE pqr SET a=5; } } {1} do_test main-1.29 { db complete { CREATE TRIGGER xyz AFTER DELETE backend BEGIN UPDATE pqr SET a=5; EXPLAIN select * from xyz; } } {0} # Try to open a database with a corrupt database file. # do_test main-2.0 { catch {db close} file delete -force test.db set fd [open test.db w] |
︙ | ︙ |