NULL values are distinct. A comparison involving a NULL is always false.
Operations on a NULL value yield a NULL result. This change makes SQLite operate more like the SQL spec, but it may break existing applications that assumed the old behavior. All the old tests pass but we still need to add new tests to better verify the new behavior. Fix for ticket #44. (CVS 589) FossilOrigin-Name: 9051173742f1b0e15a809d12a0c9c98fd2c4614d
This commit is contained in:
parent
195e6967fb
commit
f5905aa7be
36
manifest
36
manifest
@ -1,5 +1,5 @@
|
||||
C Additional\stesting\sof\sLEFT\sOUTER\sJOIN.\s(CVS\s588)
|
||||
D 2002-05-25T00:18:21
|
||||
C NULL\svalues\sare\sdistinct.\s\sA\scomparison\sinvolving\sa\sNULL\sis\salways\sfalse.\nOperations\son\sa\sNULL\svalue\syield\sa\sNULL\sresult.\s\sThis\schange\smakes\sSQLite\noperate\smore\slike\sthe\sSQL\sspec,\sbut\sit\smay\sbreak\sexisting\sapplications\sthat\nassumed\sthe\sold\sbehavior.\s\sAll\sthe\sold\stests\spass\sbut\swe\sstill\sneed\sto\sadd\nnew\stests\sto\sbetter\sverify\sthe\snew\sbehavior.\s\sFix\sfor\sticket\s#44.\s(CVS\s589)
|
||||
D 2002-05-26T20:54:33
|
||||
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
|
||||
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
|
||||
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
|
||||
@ -23,11 +23,11 @@ F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
|
||||
F src/build.c 36e42718a7a94f554ea39508993378482f5335c7
|
||||
F src/delete.c a2b098cbbf518e6b641847e26de85827793bc523
|
||||
F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
|
||||
F src/expr.c 818a702ba93e444813b8935a7ab509f6e3352b49
|
||||
F src/expr.c 1a7a2f5f2b7dd37659783cdb6efaac74a092ba71
|
||||
F src/func.c a31dcba85bc2ecb9b752980289cf7e6cd0cafbce
|
||||
F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
|
||||
F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
|
||||
F src/insert.c bbbd803da8b125ec5a5f881f4d559887eb922c57
|
||||
F src/insert.c 24b4e146319bada6f82a1d5eae6b38b3065d132f
|
||||
F src/main.c 6e53c49a390fabd5fecce9e3b128c61c85208000
|
||||
F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
|
||||
F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc
|
||||
@ -37,11 +37,11 @@ F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
|
||||
F src/parse.y c681da701bf142967325b8791f22418e2d81552d
|
||||
F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd
|
||||
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
|
||||
F src/select.c bbf00ee2b4412c7249baf0ba737ba6a93fe82e78
|
||||
F src/select.c 9b404b6eeb3428896fdbddc6e1ace68ee931ec17
|
||||
F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
|
||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||
F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
|
||||
F src/sqliteInt.h 9d565908e2ca53c54d1a0ae445cba039ee018aba
|
||||
F src/sqliteInt.h 9de24fb527b3e472be19600e7c22534b981c8e61
|
||||
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
||||
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
|
||||
F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
|
||||
@ -49,12 +49,12 @@ F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730
|
||||
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
|
||||
F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
|
||||
F src/tokenize.c facec7dc0b4a13e17ad67702f548dac2f7c6a732
|
||||
F src/trigger.c 75dd64808c56ff1b20ee6c6620f5d61487712d74
|
||||
F src/trigger.c f9adb404ea355a8be2c9cd9740794a898cf1096c
|
||||
F src/update.c f68375173bf5338cae3e97012708e10f206aedd9
|
||||
F src/util.c 707c30f8c13cddace7c08556ac450c0b786660b3
|
||||
F src/vdbe.c bde1dad84ea4b0de4ac590d0d29522e45bfd1470
|
||||
F src/vdbe.h def669b9f2728589aabcb5db756429db02465c9a
|
||||
F src/where.c 9030d188139f4de73c4b238706afeae8bc4e2f26
|
||||
F src/vdbe.c caa269517b2392986c8f55401f272e76b9de82d9
|
||||
F src/vdbe.h b8706429131c14b307a07aab7e47f95a9da53610
|
||||
F src/where.c b054f2f23127bd57eb5f973bcd38764b875d73fe
|
||||
F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
|
||||
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
|
||||
F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
|
||||
@ -63,7 +63,7 @@ F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895
|
||||
F test/conflict.test 5149646703d3930c9111068b5cda7e2e938476e3
|
||||
F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a
|
||||
F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
|
||||
F test/expr.test 846795016b5993a7411f772eebe82ab67bd7230a
|
||||
F test/expr.test bfb773721e69566d2a7565c7f03b466e1f33a53f
|
||||
F test/func.test d34e461f0acb0cf2978a4b3a3e098460f2ea8fbc
|
||||
F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
|
||||
F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
|
||||
@ -89,11 +89,11 @@ F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
|
||||
F test/select1.test 6ba20b52d563b7fb917d8a61a7560d02f90a1a52
|
||||
F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4
|
||||
F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
|
||||
F test/select4.test c2313f8c16ca298b0b1ce9cc3c0cfed0939ffea9
|
||||
F test/select4.test 0bc0065102573c6b71003292fd4a5def63923c61
|
||||
F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac
|
||||
F test/select6.test efb8d0c07a440441db87db2c4ade6904e1407e85
|
||||
F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b
|
||||
F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78
|
||||
F test/subselect.test f3bc1dcbddddcea08d818fcff75228ad3464fc83
|
||||
F test/table.test d9fd161dc9a2dbe0795d836336019ea6d0952ef8
|
||||
F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
|
||||
F test/tclsqlite.test 79deeffd7cd637ca0f06c5dbbf2f44d272079533
|
||||
@ -101,8 +101,8 @@ F test/temptable.test daa83489eea2e9aaeeece09675c28be84c72cb67
|
||||
F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6
|
||||
F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee
|
||||
F test/trigger1.test a0550c5cce97170dbebb88eee09506d9ad1174e9
|
||||
F test/trigger2.test 2d23ad8a7e74c64018b6c7dff79bbf2574ffd776
|
||||
F test/unique.test 07776624b82221a80c8b4138ce0dd8b0853bb3ea
|
||||
F test/trigger2.test 7f2b0a9b20004449c78b834c2f22494db3b2e63a
|
||||
F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44
|
||||
F test/update.test a0aa0bf83e6fad8407d0e4ad25ebb09b513f5bf4
|
||||
F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
|
||||
F test/view.test b9851e9142de5e5831fdf18f125cbe1256cb550a
|
||||
@ -135,7 +135,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
|
||||
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
|
||||
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
||||
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
||||
P 99bd1f5b9a1a20bfeefe15c00d96a34a5f40923e
|
||||
R 289d0473700b92bde61e98c6617d8be0
|
||||
P d8d04c14f18d1feba89ccea0be70530a18248c51
|
||||
R 2ee233b2a9ebb3e4a8fe3e93b3747a0c
|
||||
U drh
|
||||
Z f910247eaeb3d662acb5a7b6aeac9b33
|
||||
Z f507bf5d6b83b961339e312ca54783e8
|
||||
|
@ -1 +1 @@
|
||||
d8d04c14f18d1feba89ccea0be70530a18248c51
|
||||
9051173742f1b0e15a809d12a0c9c98fd2c4614d
|
164
src/expr.c
164
src/expr.c
@ -12,7 +12,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.63 2002/05/24 02:04:33 drh Exp $
|
||||
** $Id: expr.c,v 1.64 2002/05/26 20:54:33 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -818,7 +818,13 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
case TK_REM:
|
||||
case TK_BITAND:
|
||||
case TK_BITOR:
|
||||
case TK_SLASH: {
|
||||
case TK_SLASH:
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
case TK_GE:
|
||||
case TK_NE:
|
||||
case TK_EQ: {
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteExprCode(pParse, pExpr->pRight);
|
||||
sqliteVdbeAddOp(v, op, 0, 0);
|
||||
@ -837,21 +843,6 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqliteVdbeAddOp(v, OP_Concat, 2, 0);
|
||||
break;
|
||||
}
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
case TK_GE:
|
||||
case TK_NE:
|
||||
case TK_EQ: {
|
||||
int dest;
|
||||
sqliteVdbeAddOp(v, OP_Integer, 1, 0);
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteExprCode(pParse, pExpr->pRight);
|
||||
dest = sqliteVdbeCurrentAddr(v) + 2;
|
||||
sqliteVdbeAddOp(v, op, 0, dest);
|
||||
sqliteVdbeAddOp(v, OP_AddImm, -1, 0);
|
||||
break;
|
||||
}
|
||||
case TK_UMINUS: {
|
||||
assert( pExpr->pLeft );
|
||||
if( pExpr->pLeft->op==TK_FLOAT || pExpr->pLeft->op==TK_INTEGER ){
|
||||
@ -881,7 +872,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqliteVdbeAddOp(v, OP_Integer, 1, 0);
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
dest = sqliteVdbeCurrentAddr(v) + 2;
|
||||
sqliteVdbeAddOp(v, op, 0, dest);
|
||||
sqliteVdbeAddOp(v, op, 1, dest);
|
||||
sqliteVdbeAddOp(v, OP_AddImm, -1, 0);
|
||||
break;
|
||||
}
|
||||
@ -913,20 +904,27 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqliteVdbeAddOp(v, OP_Integer, 1, 0);
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
addr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, addr+4);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr+6);
|
||||
if( pExpr->pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+2);
|
||||
sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+6);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+2);
|
||||
sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+6);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_AddImm, -1, 0);
|
||||
break;
|
||||
}
|
||||
case TK_BETWEEN: {
|
||||
int lbl = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
sqliteExprIfFalse(pParse, pExpr, lbl);
|
||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
sqliteVdbeResolveLabel(v, lbl);
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_Ge, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Pull, 1, 0);
|
||||
sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_Le, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_And, 0, 0);
|
||||
break;
|
||||
}
|
||||
case TK_AS: {
|
||||
@ -935,44 +933,54 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
}
|
||||
case TK_CASE: {
|
||||
int expr_end_label;
|
||||
int next_when_label;
|
||||
int null_result_label;
|
||||
int jumpInst;
|
||||
int addr;
|
||||
int nExpr;
|
||||
int i;
|
||||
|
||||
assert(pExpr->pList);
|
||||
assert((pExpr->pList->nExpr % 2) == 0);
|
||||
assert(pExpr->pList->nExpr > 0);
|
||||
expr_end_label = sqliteVdbeMakeLabel(pParse->pVdbe);
|
||||
nExpr = pExpr->pList->nExpr;
|
||||
expr_end_label = sqliteVdbeMakeLabel(v);
|
||||
null_result_label = sqliteVdbeMakeLabel(v);
|
||||
if( pExpr->pLeft ){
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteVdbeAddOp(v, OP_IsNull, -1, expr_end_label);
|
||||
}
|
||||
for(i=0; i<pExpr->pList->nExpr; i=i+2){
|
||||
if( i!=0 ){
|
||||
sqliteVdbeResolveLabel(pParse->pVdbe, next_when_label);
|
||||
}
|
||||
next_when_label = sqliteVdbeMakeLabel(pParse->pVdbe);
|
||||
for(i=0; i<nExpr; i=i+2){
|
||||
sqliteExprCode(pParse, pExpr->pList->a[i].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_IsNull, -1, null_result_label);
|
||||
if( pExpr->pLeft ){
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_Dup, 0, 1);
|
||||
sqliteExprCode(pParse, pExpr->pList->a[i].pExpr);
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_Ne, 0, next_when_label);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 1, 1);
|
||||
jumpInst = sqliteVdbeAddOp(v, OP_Ne, 0, 0);
|
||||
}else{
|
||||
sqliteExprIfFalse(pParse, pExpr->pList->a[i].pExpr, next_when_label);
|
||||
}
|
||||
if( pExpr->pLeft ){
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_Pop, 1, 0);
|
||||
jumpInst = sqliteVdbeAddOp(v, OP_IfNot, 0, 0);
|
||||
}
|
||||
sqliteExprCode(pParse, pExpr->pList->a[i+1].pExpr);
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_Goto, 0, expr_end_label);
|
||||
}
|
||||
sqliteVdbeResolveLabel(pParse->pVdbe, next_when_label);
|
||||
if( pExpr->pLeft ){
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, expr_end_label);
|
||||
if( i>=nExpr-2 ){
|
||||
sqliteVdbeResolveLabel(v, null_result_label);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
if( pExpr->pRight!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, expr_end_label);
|
||||
}
|
||||
}
|
||||
addr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeChangeP2(v, jumpInst, addr);
|
||||
}
|
||||
if( pExpr->pRight ){
|
||||
sqliteExprCode(pParse, pExpr->pRight);
|
||||
}else{
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
}
|
||||
sqliteVdbeResolveLabel(v, expr_end_label);
|
||||
if( pExpr->pLeft ){
|
||||
sqliteVdbeAddOp(v, OP_Pull, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
}
|
||||
sqliteVdbeResolveLabel(pParse->pVdbe, expr_end_label);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -982,8 +990,11 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
** Generate code for a boolean expression such that a jump is made
|
||||
** to the label "dest" if the expression is true but execution
|
||||
** continues straight thru if the expression is false.
|
||||
**
|
||||
** If the expression evaluates to NULL (neither true nor false), then
|
||||
** take the jump if the jumpIfNull flag is true.
|
||||
*/
|
||||
void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
|
||||
void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int op = 0;
|
||||
if( v==0 || pExpr==0 ) return;
|
||||
@ -1001,18 +1012,18 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
|
||||
switch( pExpr->op ){
|
||||
case TK_AND: {
|
||||
int d2 = sqliteVdbeMakeLabel(v);
|
||||
sqliteExprIfFalse(pParse, pExpr->pLeft, d2);
|
||||
sqliteExprIfTrue(pParse, pExpr->pRight, dest);
|
||||
sqliteExprIfFalse(pParse, pExpr->pLeft, d2, !jumpIfNull);
|
||||
sqliteExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
sqliteVdbeResolveLabel(v, d2);
|
||||
break;
|
||||
}
|
||||
case TK_OR: {
|
||||
sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
|
||||
sqliteExprIfTrue(pParse, pExpr->pRight, dest);
|
||||
sqliteExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
sqliteExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
break;
|
||||
}
|
||||
case TK_NOT: {
|
||||
sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
|
||||
sqliteExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
break;
|
||||
}
|
||||
case TK_LT:
|
||||
@ -1023,17 +1034,22 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
|
||||
case TK_EQ: {
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteExprCode(pParse, pExpr->pRight);
|
||||
sqliteVdbeAddOp(v, op, 0, dest);
|
||||
sqliteVdbeAddOp(v, op, jumpIfNull, dest);
|
||||
break;
|
||||
}
|
||||
case TK_ISNULL:
|
||||
case TK_NOTNULL: {
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteVdbeAddOp(v, op, 0, dest);
|
||||
sqliteVdbeAddOp(v, op, 1, dest);
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
int addr;
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
addr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, addr+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, jumpIfNull ? dest : addr+4);
|
||||
if( pExpr->pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, dest);
|
||||
}else{
|
||||
@ -1042,21 +1058,21 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
|
||||
break;
|
||||
}
|
||||
case TK_BETWEEN: {
|
||||
int lbl = sqliteVdbeMakeLabel(v);
|
||||
int addr;
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_Lt, 0, lbl);
|
||||
addr = sqliteVdbeAddOp(v, OP_Lt, !jumpIfNull, 0);
|
||||
sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_Le, 0, dest);
|
||||
sqliteVdbeAddOp(v, OP_Le, jumpIfNull, dest);
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
sqliteVdbeResolveLabel(v, lbl);
|
||||
sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sqliteExprCode(pParse, pExpr);
|
||||
sqliteVdbeAddOp(v, OP_If, 0, dest);
|
||||
sqliteVdbeAddOp(v, OP_If, jumpIfNull, dest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1066,8 +1082,11 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
|
||||
** Generate code for a boolean expression such that a jump is made
|
||||
** to the label "dest" if the expression is false but execution
|
||||
** continues straight thru if the expression is true.
|
||||
**
|
||||
** If the expression evaluates to NULL (neither true nor false) then
|
||||
** jump if jumpIfNull is true or fall through if jumpIfNull is false.
|
||||
*/
|
||||
void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
|
||||
void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int op = 0;
|
||||
if( v==0 || pExpr==0 ) return;
|
||||
@ -1084,19 +1103,19 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
|
||||
}
|
||||
switch( pExpr->op ){
|
||||
case TK_AND: {
|
||||
sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
|
||||
sqliteExprIfFalse(pParse, pExpr->pRight, dest);
|
||||
sqliteExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
sqliteExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
break;
|
||||
}
|
||||
case TK_OR: {
|
||||
int d2 = sqliteVdbeMakeLabel(v);
|
||||
sqliteExprIfTrue(pParse, pExpr->pLeft, d2);
|
||||
sqliteExprIfFalse(pParse, pExpr->pRight, dest);
|
||||
sqliteExprIfTrue(pParse, pExpr->pLeft, d2, !jumpIfNull);
|
||||
sqliteExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
sqliteVdbeResolveLabel(v, d2);
|
||||
break;
|
||||
}
|
||||
case TK_NOT: {
|
||||
sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
|
||||
sqliteExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
break;
|
||||
}
|
||||
case TK_LT:
|
||||
@ -1107,17 +1126,22 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
|
||||
case TK_EQ: {
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteExprCode(pParse, pExpr->pRight);
|
||||
sqliteVdbeAddOp(v, op, 0, dest);
|
||||
sqliteVdbeAddOp(v, op, jumpIfNull, dest);
|
||||
break;
|
||||
}
|
||||
case TK_ISNULL:
|
||||
case TK_NOTNULL: {
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
sqliteVdbeAddOp(v, op, 0, dest);
|
||||
sqliteVdbeAddOp(v, op, 1, dest);
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
int addr;
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
addr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, addr+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, jumpIfNull ? dest : addr+4);
|
||||
if( pExpr->pSelect ){
|
||||
sqliteVdbeAddOp(v, OP_NotFound, pExpr->iTable, dest);
|
||||
}else{
|
||||
@ -1131,17 +1155,17 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
|
||||
addr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeAddOp(v, OP_Ge, 0, addr+3);
|
||||
sqliteVdbeAddOp(v, OP_Ge, !jumpIfNull, addr+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, dest);
|
||||
sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
|
||||
sqliteVdbeAddOp(v, OP_Gt, 0, dest);
|
||||
sqliteVdbeAddOp(v, OP_Gt, jumpIfNull, dest);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sqliteExprCode(pParse, pExpr);
|
||||
sqliteVdbeAddOp(v, OP_Not, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_If, 0, dest);
|
||||
sqliteVdbeAddOp(v, OP_If, jumpIfNull, dest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
26
src/insert.c
26
src/insert.c
@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.58 2002/05/24 02:04:33 drh Exp $
|
||||
** $Id: insert.c,v 1.59 2002/05/26 20:54:33 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -290,14 +290,12 @@ void sqliteInsert(
|
||||
if( srcTab>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
|
||||
}else{
|
||||
int addr;
|
||||
sqliteExprCode(pParse, pList->a[keyColumn].pExpr);
|
||||
|
||||
/* If the PRIMARY KEY expression is NULL, then use OP_NewRecno
|
||||
** to generate a unique primary key value.
|
||||
*/
|
||||
addr = sqliteVdbeAddOp(v, OP_Dup, 0, 1);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, 0, addr+4);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_NewRecno, base, 0);
|
||||
}
|
||||
@ -499,7 +497,7 @@ void sqliteGenerateConstraintChecks(
|
||||
int iCur;
|
||||
Index *pIdx;
|
||||
int seenReplace = 0;
|
||||
int jumpInst;
|
||||
int jumpInst1, jumpInst2;
|
||||
int contAddr;
|
||||
int hasTwoRecnos = (isUpdate && recnoChng);
|
||||
|
||||
@ -527,7 +525,7 @@ void sqliteGenerateConstraintChecks(
|
||||
onError = OE_Abort;
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol-1-i, 1);
|
||||
addr = sqliteVdbeAddOp(v, OP_NotNull, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_NotNull, 1, 0);
|
||||
switch( onError ){
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
@ -565,14 +563,13 @@ void sqliteGenerateConstraintChecks(
|
||||
if( onError==OE_Default ) onError = OE_Abort;
|
||||
}
|
||||
if( onError!=OE_Replace ){
|
||||
int jumpInst2;
|
||||
if( isUpdate ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
jumpInst2 = sqliteVdbeAddOp(v, OP_Eq, 0, 0);
|
||||
jumpInst1 = sqliteVdbeAddOp(v, OP_Eq, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
|
||||
jumpInst = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
|
||||
jumpInst2 = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
|
||||
switch( onError ){
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
@ -588,9 +585,9 @@ void sqliteGenerateConstraintChecks(
|
||||
default: assert(0);
|
||||
}
|
||||
contAddr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeChangeP2(v, jumpInst, contAddr);
|
||||
sqliteVdbeChangeP2(v, jumpInst2, contAddr);
|
||||
if( isUpdate ){
|
||||
sqliteVdbeChangeP2(v, jumpInst2, contAddr);
|
||||
sqliteVdbeChangeP2(v, jumpInst1, contAddr);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||
}
|
||||
@ -609,7 +606,7 @@ void sqliteGenerateConstraintChecks(
|
||||
sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
|
||||
jumpInst1 = sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
|
||||
onError = pIdx->onError;
|
||||
if( onError==OE_None ) continue;
|
||||
if( overrideError!=OE_Default ){
|
||||
@ -619,7 +616,7 @@ void sqliteGenerateConstraintChecks(
|
||||
if( onError==OE_Default ) onError = OE_Abort;
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1);
|
||||
jumpInst = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
|
||||
jumpInst2 = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
|
||||
switch( onError ){
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
@ -645,7 +642,8 @@ void sqliteGenerateConstraintChecks(
|
||||
default: assert(0);
|
||||
}
|
||||
contAddr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeChangeP2(v, jumpInst, contAddr);
|
||||
sqliteVdbeChangeP2(v, jumpInst1, contAddr);
|
||||
sqliteVdbeChangeP2(v, jumpInst2, contAddr);
|
||||
}
|
||||
}
|
||||
|
||||
|
18
src/select.c
18
src/select.c
@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.85 2002/05/25 00:18:21 drh Exp $
|
||||
** $Id: select.c,v 1.86 2002/05/26 20:54:34 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -325,13 +325,12 @@ static int selectInnerLoop(
|
||||
** and this row has been seen before, then do not make this row
|
||||
** part of the result.
|
||||
*/
|
||||
if( distinct>=0 ){
|
||||
int lbl = sqliteVdbeMakeLabel(v);
|
||||
if( distinct>=0 && pEList && pEList->nExpr>0 ){
|
||||
sqliteVdbeAddOp(v, OP_IsNull, -pEList->nExpr, sqliteVdbeCurrentAddr(v)+7);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1);
|
||||
sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl);
|
||||
sqliteVdbeAddOp(v, OP_Distinct, distinct, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, iContinue);
|
||||
sqliteVdbeResolveLabel(v, lbl);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutStrKey, distinct, 0);
|
||||
}
|
||||
@ -359,8 +358,8 @@ static int selectInnerLoop(
|
||||
** table iParm.
|
||||
*/
|
||||
if( eDest==SRT_Union ){
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, iParm, 0);
|
||||
sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 1);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0);
|
||||
}else
|
||||
|
||||
@ -378,7 +377,7 @@ static int selectInnerLoop(
|
||||
** the temporary table iParm.
|
||||
*/
|
||||
if( eDest==SRT_Except ){
|
||||
int addr = sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0);
|
||||
int addr = sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 1);
|
||||
sqliteVdbeAddOp(v, OP_NotFound, iParm, addr+3);
|
||||
sqliteVdbeAddOp(v, OP_Delete, iParm, 0);
|
||||
}else
|
||||
@ -389,6 +388,7 @@ static int selectInnerLoop(
|
||||
*/
|
||||
if( eDest==SRT_Set ){
|
||||
assert( nColumn==1 );
|
||||
sqliteVdbeAddOp(v, OP_IsNull, -1, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0);
|
||||
}else
|
||||
@ -1738,7 +1738,7 @@ int sqliteSelect(
|
||||
startagg = sqliteVdbeAddOp(v, OP_AggNext, 0, endagg);
|
||||
pParse->useAgg = 1;
|
||||
if( pHaving ){
|
||||
sqliteExprIfFalse(pParse, pHaving, startagg);
|
||||
sqliteExprIfFalse(pParse, pHaving, startagg, 1);
|
||||
}
|
||||
if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
|
||||
startagg, endagg) ){
|
||||
|
@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.116 2002/05/25 00:18:21 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.117 2002/05/26 20:54:34 drh Exp $
|
||||
*/
|
||||
#include "sqlite.h"
|
||||
#include "hash.h"
|
||||
@ -817,8 +817,8 @@ void sqliteUpdate(Parse*, Token*, ExprList*, Expr*, int);
|
||||
WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int);
|
||||
void sqliteWhereEnd(WhereInfo*);
|
||||
void sqliteExprCode(Parse*, Expr*);
|
||||
void sqliteExprIfTrue(Parse*, Expr*, int);
|
||||
void sqliteExprIfFalse(Parse*, Expr*, int);
|
||||
void sqliteExprIfTrue(Parse*, Expr*, int, int);
|
||||
void sqliteExprIfFalse(Parse*, Expr*, int, int);
|
||||
Table *sqliteFindTable(sqlite*,const char*);
|
||||
Index *sqliteFindIndex(sqlite*,const char*);
|
||||
void sqliteUnlinkAndDeleteIndex(sqlite*,Index*);
|
||||
|
@ -587,7 +587,7 @@ int sqliteCodeRowTrigger(
|
||||
sqliteExprDelete(whenExpr);
|
||||
return 1;
|
||||
}
|
||||
sqliteExprIfFalse(pParse, whenExpr, endTrigger);
|
||||
sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
|
||||
sqliteExprDelete(whenExpr);
|
||||
|
||||
codeTriggerProgram(pParse, pTrigger->step_list, orconf);
|
||||
|
257
src/vdbe.c
257
src/vdbe.c
@ -30,7 +30,7 @@
|
||||
** But other routines are also provided to help in building up
|
||||
** a program instruction by instruction.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.148 2002/05/24 20:31:37 drh Exp $
|
||||
** $Id: vdbe.c,v 1.149 2002/05/26 20:54:34 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@ -1068,17 +1068,17 @@ static char *zOpName[] = { 0,
|
||||
"AggFunc", "AggInit", "AggPush", "AggPop",
|
||||
"SetInsert", "SetFound", "SetNotFound", "MakeRecord",
|
||||
"MakeKey", "MakeIdxKey", "IncrKey", "Goto",
|
||||
"If", "Halt", "ColumnCount", "ColumnName",
|
||||
"Callback", "NullCallback", "Integer", "String",
|
||||
"Pop", "Dup", "Pull", "Push",
|
||||
"MustBeInt", "Add", "AddImm", "Subtract",
|
||||
"Multiply", "Divide", "Remainder", "BitAnd",
|
||||
"BitOr", "BitNot", "ShiftLeft", "ShiftRight",
|
||||
"AbsValue", "Eq", "Ne", "Lt",
|
||||
"Le", "Gt", "Ge", "IsNull",
|
||||
"NotNull", "Negative", "And", "Or",
|
||||
"Not", "Concat", "Noop", "Function",
|
||||
"Limit",
|
||||
"If", "IfNot", "Halt", "ColumnCount",
|
||||
"ColumnName", "Callback", "NullCallback", "Integer",
|
||||
"String", "Pop", "Dup", "Pull",
|
||||
"Push", "MustBeInt", "Add", "AddImm",
|
||||
"Subtract", "Multiply", "Divide", "Remainder",
|
||||
"BitAnd", "BitOr", "BitNot", "ShiftLeft",
|
||||
"ShiftRight", "AbsValue", "Eq", "Ne",
|
||||
"Lt", "Le", "Gt", "Ge",
|
||||
"IsNull", "NotNull", "Negative", "And",
|
||||
"Or", "Not", "Concat", "Noop",
|
||||
"Function", "Limit",
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1280,6 +1280,7 @@ int sqliteVdbeExec(
|
||||
sqlite *db = p->db; /* The database */
|
||||
char **zStack; /* Text stack */
|
||||
Stack *aStack; /* Additional stack information */
|
||||
unsigned uniqueCnt = 0; /* Used by OP_MakeRecord when P2!=0 */
|
||||
int errorAction = OE_Abort; /* Recovery action to do in case of an error */
|
||||
int undoTransOnError = 0; /* If error, either ROLLBACK or COMMIT */
|
||||
char zBuf[100]; /* Space to sprintf() an integer */
|
||||
@ -1719,6 +1720,7 @@ case OP_Concat: {
|
||||
** and push the result back onto the stack. If either element
|
||||
** is a string then it is converted to a double using the atof()
|
||||
** function before the addition.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Multiply * * *
|
||||
**
|
||||
@ -1726,6 +1728,7 @@ case OP_Concat: {
|
||||
** and push the result back onto the stack. If either element
|
||||
** is a string then it is converted to a double using the atof()
|
||||
** function before the multiplication.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Subtract * * *
|
||||
**
|
||||
@ -1735,6 +1738,7 @@ case OP_Concat: {
|
||||
** and push the result back onto the stack. If either element
|
||||
** is a string then it is converted to a double using the atof()
|
||||
** function before the subtraction.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Divide * * *
|
||||
**
|
||||
@ -1744,6 +1748,7 @@ case OP_Concat: {
|
||||
** and push the result back onto the stack. If either element
|
||||
** is a string then it is converted to a double using the atof()
|
||||
** function before the division. Division by zero returns NULL.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Remainder * * *
|
||||
**
|
||||
@ -1753,6 +1758,7 @@ case OP_Concat: {
|
||||
** and push the remainder after division onto the stack. If either element
|
||||
** is a string then it is converted to a double using the atof()
|
||||
** function before the division. Division by zero returns NULL.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
case OP_Add:
|
||||
case OP_Subtract:
|
||||
@ -1762,7 +1768,11 @@ case OP_Remainder: {
|
||||
int tos = p->tos;
|
||||
int nos = tos - 1;
|
||||
VERIFY( if( nos<0 ) goto not_enough_stack; )
|
||||
if( (aStack[tos].flags & aStack[nos].flags & STK_Int)==STK_Int ){
|
||||
if( ((aStack[tos].flags | aStack[nos].flags) & STK_Null)!=0 ){
|
||||
POPSTACK;
|
||||
Release(p, nos);
|
||||
aStack[nos].flags = STK_Null;
|
||||
}else if( (aStack[tos].flags & aStack[nos].flags & STK_Int)==STK_Int ){
|
||||
int a, b;
|
||||
a = aStack[tos].i;
|
||||
b = aStack[nos].i;
|
||||
@ -1838,7 +1848,9 @@ case OP_Function: {
|
||||
VERIFY( if( n<0 ) goto bad_instruction; )
|
||||
VERIFY( if( p->tos+1<n ) goto not_enough_stack; )
|
||||
for(i=p->tos-n+1; i<=p->tos; i++){
|
||||
if( (aStack[i].flags & STK_Null)==0 ){
|
||||
if( aStack[i].flags & STK_Null ){
|
||||
zStack[i] = 0;
|
||||
}else{
|
||||
if( Stringify(p, i) ) goto no_mem;
|
||||
}
|
||||
}
|
||||
@ -1872,24 +1884,28 @@ case OP_Function: {
|
||||
** Pop the top two elements from the stack. Convert both elements
|
||||
** to integers. Push back onto the stack the bit-wise AND of the
|
||||
** two elements.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: BitOr * * *
|
||||
**
|
||||
** Pop the top two elements from the stack. Convert both elements
|
||||
** to integers. Push back onto the stack the bit-wise OR of the
|
||||
** two elements.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: ShiftLeft * * *
|
||||
**
|
||||
** Pop the top two elements from the stack. Convert both elements
|
||||
** to integers. Push back onto the stack the top element shifted
|
||||
** left by N bits where N is the second element on the stack.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: ShiftRight * * *
|
||||
**
|
||||
** Pop the top two elements from the stack. Convert both elements
|
||||
** to integers. Push back onto the stack the top element shifted
|
||||
** right by N bits where N is the second element on the stack.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
case OP_BitAnd:
|
||||
case OP_BitOr:
|
||||
@ -1899,6 +1915,12 @@ case OP_ShiftRight: {
|
||||
int nos = tos - 1;
|
||||
int a, b;
|
||||
VERIFY( if( nos<0 ) goto not_enough_stack; )
|
||||
if( (aStack[tos].flags | aStack[nos].flags) & STK_Null ){
|
||||
POPSTACK;
|
||||
Release(p,nos);
|
||||
aStack[nos].flags = STK_Null;
|
||||
break;
|
||||
}
|
||||
Integerify(p, tos);
|
||||
Integerify(p, nos);
|
||||
a = aStack[tos].i;
|
||||
@ -1973,40 +1995,82 @@ mismatch:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Eq * P2 *
|
||||
/* Opcode: Eq P1 P2 *
|
||||
**
|
||||
** Pop the top two elements from the stack. If they are equal, then
|
||||
** jump to instruction P2. Otherwise, continue to the next instruction.
|
||||
**
|
||||
** If either operand is NULL (and thus if the result is unknown) then
|
||||
** take the jump if P1 is true.
|
||||
**
|
||||
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
|
||||
** stack if the jump would have been taken, or a 0 if not. Push a
|
||||
** NULL if either operand was NULL.
|
||||
*/
|
||||
/* Opcode: Ne * P2 *
|
||||
/* Opcode: Ne P1 P2 *
|
||||
**
|
||||
** Pop the top two elements from the stack. If they are not equal, then
|
||||
** jump to instruction P2. Otherwise, continue to the next instruction.
|
||||
**
|
||||
** If either operand is NULL (and thus if the result is unknown) then
|
||||
** take the jump if P1 is true.
|
||||
**
|
||||
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
|
||||
** stack if the jump would have been taken, or a 0 if not. Push a
|
||||
** NULL if either operand was NULL.
|
||||
*/
|
||||
/* Opcode: Lt * P2 *
|
||||
/* Opcode: Lt P1 P2 *
|
||||
**
|
||||
** Pop the top two elements from the stack. If second element (the
|
||||
** next on stack) is less than the first (the top of stack), then
|
||||
** jump to instruction P2. Otherwise, continue to the next instruction.
|
||||
** In other words, jump if NOS<TOS.
|
||||
**
|
||||
** If either operand is NULL (and thus if the result is unknown) then
|
||||
** take the jump if P1 is true.
|
||||
**
|
||||
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
|
||||
** stack if the jump would have been taken, or a 0 if not. Push a
|
||||
** NULL if either operand was NULL.
|
||||
*/
|
||||
/* Opcode: Le * P2 *
|
||||
/* Opcode: Le P1 P2 *
|
||||
**
|
||||
** Pop the top two elements from the stack. If second element (the
|
||||
** next on stack) is less than or equal to the first (the top of stack),
|
||||
** then jump to instruction P2. In other words, jump if NOS<=TOS.
|
||||
**
|
||||
** If either operand is NULL (and thus if the result is unknown) then
|
||||
** take the jump if P1 is true.
|
||||
**
|
||||
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
|
||||
** stack if the jump would have been taken, or a 0 if not. Push a
|
||||
** NULL if either operand was NULL.
|
||||
*/
|
||||
/* Opcode: Gt * P2 *
|
||||
/* Opcode: Gt P1 P2 *
|
||||
**
|
||||
** Pop the top two elements from the stack. If second element (the
|
||||
** next on stack) is greater than the first (the top of stack),
|
||||
** then jump to instruction P2. In other words, jump if NOS>TOS.
|
||||
**
|
||||
** If either operand is NULL (and thus if the result is unknown) then
|
||||
** take the jump if P1 is true.
|
||||
**
|
||||
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
|
||||
** stack if the jump would have been taken, or a 0 if not. Push a
|
||||
** NULL if either operand was NULL.
|
||||
*/
|
||||
/* Opcode: Ge * P2 *
|
||||
/* Opcode: Ge P1 P2 *
|
||||
**
|
||||
** Pop the top two elements from the stack. If second element (the next
|
||||
** on stack) is greater than or equal to the first (the top of stack),
|
||||
** then jump to instruction P2. In other words, jump if NOS>=TOS.
|
||||
**
|
||||
** If either operand is NULL (and thus if the result is unknown) then
|
||||
** take the jump if P1 is true.
|
||||
**
|
||||
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
|
||||
** stack if the jump would have been taken, or a 0 if not. Push a
|
||||
** NULL if either operand was NULL.
|
||||
*/
|
||||
case OP_Eq:
|
||||
case OP_Ne:
|
||||
@ -2021,7 +2085,17 @@ case OP_Ge: {
|
||||
VERIFY( if( nos<0 ) goto not_enough_stack; )
|
||||
ft = aStack[tos].flags;
|
||||
fn = aStack[nos].flags;
|
||||
if( (ft & fn & STK_Int)==STK_Int ){
|
||||
if( (ft | fn) & STK_Null ){
|
||||
POPSTACK;
|
||||
POPSTACK;
|
||||
if( pOp->p2 ){
|
||||
if( pOp->p1 ) pc = pOp->p2-1;
|
||||
}else{
|
||||
p->tos++;
|
||||
aStack[nos].flags = STK_Null;
|
||||
}
|
||||
break;
|
||||
}else if( (ft & fn & STK_Int)==STK_Int ){
|
||||
c = aStack[nos].i - aStack[tos].i;
|
||||
}else if( (ft & STK_Int)!=0 && (fn & STK_Str)!=0 && isInteger(zStack[nos]) ){
|
||||
Integerify(p, nos);
|
||||
@ -2043,7 +2117,13 @@ case OP_Ge: {
|
||||
}
|
||||
POPSTACK;
|
||||
POPSTACK;
|
||||
if( c ) pc = pOp->p2-1;
|
||||
if( pOp->p2 ){
|
||||
if( c ) pc = pOp->p2-1;
|
||||
}else{
|
||||
p->tos++;
|
||||
aStack[nos].flags = STK_Int;
|
||||
aStack[nos].i = c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2052,12 +2132,14 @@ case OP_Ge: {
|
||||
** Pop two values off the stack. Take the logical AND of the
|
||||
** two values and push the resulting boolean value back onto the
|
||||
** stack.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
/* Opcode: Or * * *
|
||||
**
|
||||
** Pop two values off the stack. Take the logical OR of the
|
||||
** two values and push the resulting boolean value back onto the
|
||||
** stack.
|
||||
** If either operand is NULL, the result is NULL.
|
||||
*/
|
||||
case OP_And:
|
||||
case OP_Or: {
|
||||
@ -2065,6 +2147,12 @@ case OP_Or: {
|
||||
int nos = tos - 1;
|
||||
int c;
|
||||
VERIFY( if( nos<0 ) goto not_enough_stack; )
|
||||
if( (aStack[tos].flags | aStack[nos].flags) & STK_Null ){
|
||||
POPSTACK;
|
||||
Release(p, nos);
|
||||
aStack[nos].flags = STK_Null;
|
||||
break;
|
||||
}
|
||||
Integerify(p, tos);
|
||||
Integerify(p, nos);
|
||||
if( pOp->opcode==OP_And ){
|
||||
@ -2082,12 +2170,14 @@ case OP_Or: {
|
||||
/* Opcode: Negative * * *
|
||||
**
|
||||
** Treat the top of the stack as a numeric quantity. Replace it
|
||||
** with its additive inverse.
|
||||
** with its additive inverse. If the top of the stack is NULL
|
||||
** its value is unchanged.
|
||||
*/
|
||||
/* Opcode: AbsValue * * *
|
||||
**
|
||||
** Treat the top of the stack as a numeric quantity. Replace it
|
||||
** with its absolute value.
|
||||
** with its absolute value. If the top of the stack is NULL
|
||||
** its value is unchanged.
|
||||
*/
|
||||
case OP_Negative:
|
||||
case OP_AbsValue: {
|
||||
@ -2105,6 +2195,8 @@ case OP_AbsValue: {
|
||||
aStack[tos].i = -aStack[tos].i;
|
||||
}
|
||||
aStack[tos].flags = STK_Int;
|
||||
}else if( aStack[tos].flags & STK_Null ){
|
||||
/* Do nothing */
|
||||
}else{
|
||||
Realify(p, tos);
|
||||
Release(p, tos);
|
||||
@ -2119,11 +2211,13 @@ case OP_AbsValue: {
|
||||
/* Opcode: Not * * *
|
||||
**
|
||||
** Interpret the top of the stack as a boolean value. Replace it
|
||||
** with its complement.
|
||||
** with its complement. If the top of the stack is NULL its value
|
||||
** is unchanged.
|
||||
*/
|
||||
case OP_Not: {
|
||||
int tos = p->tos;
|
||||
VERIFY( if( p->tos<0 ) goto not_enough_stack; )
|
||||
if( aStack[tos].flags & STK_Null ) break; /* Do nothing to NULLs */
|
||||
Integerify(p, tos);
|
||||
Release(p, tos);
|
||||
aStack[tos].i = !aStack[tos].i;
|
||||
@ -2134,11 +2228,13 @@ case OP_Not: {
|
||||
/* Opcode: BitNot * * *
|
||||
**
|
||||
** Interpret the top of the stack as an value. Replace it
|
||||
** with its ones-complement.
|
||||
** with its ones-complement. If the top of the stack is NULL its
|
||||
** value is unchanged.
|
||||
*/
|
||||
case OP_BitNot: {
|
||||
int tos = p->tos;
|
||||
VERIFY( if( p->tos<0 ) goto not_enough_stack; )
|
||||
if( aStack[tos].flags & STK_Null ) break; /* Do nothing to NULLs */
|
||||
Integerify(p, tos);
|
||||
Release(p, tos);
|
||||
aStack[tos].i = ~aStack[tos].i;
|
||||
@ -2155,60 +2251,92 @@ case OP_Noop: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: If * P2 *
|
||||
/* Opcode: If P1 P2 *
|
||||
**
|
||||
** Pop a single boolean from the stack. If the boolean popped is
|
||||
** true, then jump to p2. Otherwise continue to the next instruction.
|
||||
** An integer is false if zero and true otherwise. A string is
|
||||
** false if it has zero length and true otherwise.
|
||||
**
|
||||
** If the value popped of the stack is NULL, then take the jump if P1
|
||||
** is true and fall through if P1 is false.
|
||||
*/
|
||||
case OP_If: {
|
||||
/* Opcode: IfNot P1 P2 *
|
||||
**
|
||||
** Pop a single boolean from the stack. If the boolean popped is
|
||||
** false, then jump to p2. Otherwise continue to the next instruction.
|
||||
** An integer is false if zero and true otherwise. A string is
|
||||
** false if it has zero length and true otherwise.
|
||||
**
|
||||
** If the value popped of the stack is NULL, then take the jump if P1
|
||||
** is true and fall through if P1 is false.
|
||||
*/
|
||||
case OP_If:
|
||||
case OP_IfNot: {
|
||||
int c;
|
||||
VERIFY( if( p->tos<0 ) goto not_enough_stack; )
|
||||
Integerify(p, p->tos);
|
||||
c = aStack[p->tos].i;
|
||||
if( aStack[p->tos].flags & STK_Null ){
|
||||
c = pOp->p1;
|
||||
}else{
|
||||
Integerify(p, p->tos);
|
||||
c = aStack[p->tos].i;
|
||||
if( pOp->opcode==OP_IfNot ) c = !c;
|
||||
}
|
||||
POPSTACK;
|
||||
if( c ) pc = pOp->p2-1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IsNull * P2 *
|
||||
/* Opcode: IsNull P1 P2 *
|
||||
**
|
||||
** Pop a single value from the stack. If the value popped is NULL
|
||||
** then jump to p2. Otherwise continue to the next
|
||||
** instruction.
|
||||
** If any of the top abs(P1) values on the stack are NULL, then jump
|
||||
** to P2. The stack is popped P1 times if P1>0. If P1<0 then all values
|
||||
** are left unchanged on the stack.
|
||||
*/
|
||||
case OP_IsNull: {
|
||||
int c;
|
||||
VERIFY( if( p->tos<0 ) goto not_enough_stack; )
|
||||
c = (aStack[p->tos].flags & STK_Null)!=0;
|
||||
POPSTACK;
|
||||
if( c ) pc = pOp->p2-1;
|
||||
int i, cnt;
|
||||
cnt = pOp->p1;
|
||||
if( cnt<0 ) cnt = -cnt;
|
||||
VERIFY( if( p->tos+1-cnt<0 ) goto not_enough_stack; )
|
||||
for(i=0; i<cnt; i++){
|
||||
if( aStack[p->tos-i].flags & STK_Null ){
|
||||
pc = pOp->p2-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pOp->p1>0 ) PopStack(p, cnt);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: NotNull * P2 *
|
||||
/* Opcode: NotNull P1 P2 *
|
||||
**
|
||||
** Pop a single value from the stack. If the value popped is not
|
||||
** NULL, then jump to p2. Otherwise continue to the next
|
||||
** instruction.
|
||||
** Jump to P2 if the top value on the stack is not NULL. Pop the
|
||||
** stack if P1 is greater than zero. If P1 is less than or equal to
|
||||
** zero then leave the value on the stack.
|
||||
*/
|
||||
case OP_NotNull: {
|
||||
int c;
|
||||
VERIFY( if( p->tos<0 ) goto not_enough_stack; )
|
||||
c = (aStack[p->tos].flags & STK_Null)==0;
|
||||
POPSTACK;
|
||||
if( c ) pc = pOp->p2-1;
|
||||
if( (aStack[p->tos].flags & STK_Null)==0 ) pc = pOp->p2-1;
|
||||
if( pOp->p1>0 ){ POPSTACK; }
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: MakeRecord P1 * *
|
||||
/* Opcode: MakeRecord P1 P2 *
|
||||
**
|
||||
** Convert the top P1 entries of the stack into a single entry
|
||||
** suitable for use as a data record in a database table. The
|
||||
** details of the format are irrelavant as long as the OP_Column
|
||||
** opcode can decode the record later. Refer to source code
|
||||
** comments for the details of the record format.
|
||||
**
|
||||
** If P2 is true (non-zero) and one or more of the P1 entries
|
||||
** that go into building the record is NULL, then add some extra
|
||||
** bytes to the record to make it distinct for other entries created
|
||||
** during the same run of the VDBE. The extra bytes added are a
|
||||
** counter that is reset with each run of the VDBE, so records
|
||||
** created this way will not necessarily be distinct across runs.
|
||||
** But they should be distinct for transient tables (created using
|
||||
** OP_OpenTemp) which is what they are intended for.
|
||||
*/
|
||||
case OP_MakeRecord: {
|
||||
char *zNewRecord;
|
||||
@ -2217,6 +2345,8 @@ case OP_MakeRecord: {
|
||||
int i, j;
|
||||
int idxWidth;
|
||||
u32 addr;
|
||||
int addUnique = 0; /* True to cause bytes to be added to make the
|
||||
** generated record distinct */
|
||||
|
||||
/* Assuming the record contains N fields, the record format looks
|
||||
** like this:
|
||||
@ -2240,11 +2370,14 @@ case OP_MakeRecord: {
|
||||
VERIFY( if( p->tos+1<nField ) goto not_enough_stack; )
|
||||
nByte = 0;
|
||||
for(i=p->tos-nField+1; i<=p->tos; i++){
|
||||
if( (aStack[i].flags & STK_Null)==0 ){
|
||||
if( (aStack[i].flags & STK_Null) ){
|
||||
addUnique = pOp->p2;
|
||||
}else{
|
||||
if( Stringify(p, i) ) goto no_mem;
|
||||
nByte += aStack[i].n;
|
||||
}
|
||||
}
|
||||
if( addUnique ) nByte += sizeof(uniqueCnt);
|
||||
if( nByte + nField + 1 < 256 ){
|
||||
idxWidth = 1;
|
||||
}else if( nByte + 2*nField + 2 < 65536 ){
|
||||
@ -2260,7 +2393,7 @@ case OP_MakeRecord: {
|
||||
zNewRecord = sqliteMalloc( nByte );
|
||||
if( zNewRecord==0 ) goto no_mem;
|
||||
j = 0;
|
||||
addr = idxWidth*(nField+1);
|
||||
addr = idxWidth*(nField+1) + addUnique*sizeof(uniqueCnt);
|
||||
for(i=p->tos-nField+1; i<=p->tos; i++){
|
||||
zNewRecord[j++] = addr & 0xff;
|
||||
if( idxWidth>1 ){
|
||||
@ -2280,6 +2413,11 @@ case OP_MakeRecord: {
|
||||
zNewRecord[j++] = (addr>>16)&0xff;
|
||||
}
|
||||
}
|
||||
if( addUnique ){
|
||||
memcpy(&zNewRecord[j], &uniqueCnt, sizeof(uniqueCnt));
|
||||
uniqueCnt++;
|
||||
j += sizeof(uniqueCnt);
|
||||
}
|
||||
for(i=p->tos-nField+1; i<=p->tos; i++){
|
||||
if( (aStack[i].flags & STK_Null)==0 ){
|
||||
memcpy(&zNewRecord[j], zStack[i], aStack[i].n);
|
||||
@ -2311,7 +2449,7 @@ case OP_MakeRecord: {
|
||||
**
|
||||
** See also: MakeIdxKey, SortMakeKey
|
||||
*/
|
||||
/* Opcode: MakeIdxKey P1 * *
|
||||
/* Opcode: MakeIdxKey P1 P2 *
|
||||
**
|
||||
** Convert the top P1 entries of the stack into a single entry suitable
|
||||
** for use as the key in an index. In addition, take one additional integer
|
||||
@ -2327,6 +2465,12 @@ case OP_MakeRecord: {
|
||||
** in the stack is the first field and the top of the stack becomes the
|
||||
** last.
|
||||
**
|
||||
** If P2 is not zero and one or more of the P1 entries that go into the
|
||||
** generated key is NULL, then jump to P2 after the new key has been
|
||||
** pushed on the stack. In other words, jump to P2 if the key is
|
||||
** guaranteed to be unique. This jump can be used to skip a subsequent
|
||||
** uniqueness test.
|
||||
**
|
||||
** See also: MakeKey, SortMakeKey
|
||||
*/
|
||||
case OP_MakeIdxKey:
|
||||
@ -2336,6 +2480,7 @@ case OP_MakeKey: {
|
||||
int nField;
|
||||
int addRowid;
|
||||
int i, j;
|
||||
int containsNull = 0;
|
||||
|
||||
addRowid = pOp->opcode==OP_MakeIdxKey;
|
||||
nField = pOp->p1;
|
||||
@ -2347,6 +2492,7 @@ case OP_MakeKey: {
|
||||
char *z;
|
||||
if( flags & STK_Null ){
|
||||
nByte += 2;
|
||||
containsNull = 1;
|
||||
}else if( flags & STK_Real ){
|
||||
z = aStack[i].z;
|
||||
sqliteRealToSortable(aStack[i].r, &z[1]);
|
||||
@ -2406,8 +2552,11 @@ case OP_MakeKey: {
|
||||
Integerify(p, p->tos-nField);
|
||||
iKey = intToKey(aStack[p->tos-nField].i);
|
||||
memcpy(&zNewKey[j], &iKey, sizeof(u32));
|
||||
PopStack(p, nField+1);
|
||||
if( pOp->p2 && containsNull ) pc = pOp->p2 - 1;
|
||||
}else{
|
||||
if( pOp->p2==0 ) PopStack(p, nField+addRowid);
|
||||
}
|
||||
if( pOp->p2==0 ) PopStack(p, nField+addRowid);
|
||||
VERIFY( NeedStack(p, p->tos+1); )
|
||||
p->tos++;
|
||||
aStack[p->tos].n = nByte;
|
||||
@ -4373,7 +4522,9 @@ case OP_AggFunc: {
|
||||
VERIFY( if( p->tos+1<n ) goto not_enough_stack; )
|
||||
VERIFY( if( aStack[p->tos].flags!=STK_Int ) goto bad_instruction; )
|
||||
for(i=p->tos-n; i<p->tos; i++){
|
||||
if( (aStack[i].flags & STK_Null)==0 ){
|
||||
if( aStack[i].flags & STK_Null ){
|
||||
zStack[i] = 0;
|
||||
}else{
|
||||
if( Stringify(p, i) ) goto no_mem;
|
||||
}
|
||||
}
|
||||
|
85
src/vdbe.h
85
src/vdbe.h
@ -15,7 +15,7 @@
|
||||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** simple program to access and modify the underlying database.
|
||||
**
|
||||
** $Id: vdbe.h,v 1.52 2002/05/23 22:07:03 drh Exp $
|
||||
** $Id: vdbe.h,v 1.53 2002/05/26 20:54:34 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_VDBE_H_
|
||||
#define _SQLITE_VDBE_H_
|
||||
@ -157,53 +157,54 @@ typedef struct VdbeOp VdbeOp;
|
||||
|
||||
#define OP_Goto 76
|
||||
#define OP_If 77
|
||||
#define OP_Halt 78
|
||||
#define OP_IfNot 78
|
||||
#define OP_Halt 79
|
||||
|
||||
#define OP_ColumnCount 79
|
||||
#define OP_ColumnName 80
|
||||
#define OP_Callback 81
|
||||
#define OP_NullCallback 82
|
||||
#define OP_ColumnCount 80
|
||||
#define OP_ColumnName 81
|
||||
#define OP_Callback 82
|
||||
#define OP_NullCallback 83
|
||||
|
||||
#define OP_Integer 83
|
||||
#define OP_String 84
|
||||
#define OP_Pop 85
|
||||
#define OP_Dup 86
|
||||
#define OP_Pull 87
|
||||
#define OP_Push 88
|
||||
#define OP_MustBeInt 89
|
||||
#define OP_Integer 84
|
||||
#define OP_String 85
|
||||
#define OP_Pop 86
|
||||
#define OP_Dup 87
|
||||
#define OP_Pull 88
|
||||
#define OP_Push 89
|
||||
#define OP_MustBeInt 90
|
||||
|
||||
#define OP_Add 90
|
||||
#define OP_AddImm 91
|
||||
#define OP_Subtract 92
|
||||
#define OP_Multiply 93
|
||||
#define OP_Divide 94
|
||||
#define OP_Remainder 95
|
||||
#define OP_BitAnd 96
|
||||
#define OP_BitOr 97
|
||||
#define OP_BitNot 98
|
||||
#define OP_ShiftLeft 99
|
||||
#define OP_ShiftRight 100
|
||||
#define OP_AbsValue 101
|
||||
#define OP_Eq 102
|
||||
#define OP_Ne 103
|
||||
#define OP_Lt 104
|
||||
#define OP_Le 105
|
||||
#define OP_Gt 106
|
||||
#define OP_Ge 107
|
||||
#define OP_IsNull 108
|
||||
#define OP_NotNull 109
|
||||
#define OP_Negative 110
|
||||
#define OP_And 111
|
||||
#define OP_Or 112
|
||||
#define OP_Not 113
|
||||
#define OP_Concat 114
|
||||
#define OP_Noop 115
|
||||
#define OP_Function 116
|
||||
#define OP_Add 91
|
||||
#define OP_AddImm 92
|
||||
#define OP_Subtract 93
|
||||
#define OP_Multiply 94
|
||||
#define OP_Divide 95
|
||||
#define OP_Remainder 96
|
||||
#define OP_BitAnd 97
|
||||
#define OP_BitOr 98
|
||||
#define OP_BitNot 99
|
||||
#define OP_ShiftLeft 100
|
||||
#define OP_ShiftRight 101
|
||||
#define OP_AbsValue 102
|
||||
#define OP_Eq 103
|
||||
#define OP_Ne 104
|
||||
#define OP_Lt 105
|
||||
#define OP_Le 106
|
||||
#define OP_Gt 107
|
||||
#define OP_Ge 108
|
||||
#define OP_IsNull 109
|
||||
#define OP_NotNull 110
|
||||
#define OP_Negative 111
|
||||
#define OP_And 112
|
||||
#define OP_Or 113
|
||||
#define OP_Not 114
|
||||
#define OP_Concat 115
|
||||
#define OP_Noop 116
|
||||
#define OP_Function 117
|
||||
|
||||
#define OP_Limit 117
|
||||
#define OP_Limit 118
|
||||
|
||||
|
||||
#define OP_MAX 117
|
||||
#define OP_MAX 118
|
||||
|
||||
/*
|
||||
** Prototypes for the VDBE interface. See comments on the implementation
|
||||
|
@ -13,7 +13,7 @@
|
||||
** the WHERE clause of SQL statements. Also found here are subroutines
|
||||
** to generate VDBE code to evaluate expressions.
|
||||
**
|
||||
** $Id: where.c,v 1.47 2002/05/24 20:31:38 drh Exp $
|
||||
** $Id: where.c,v 1.48 2002/05/26 20:54:34 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -199,7 +199,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
** expression and either jump over all of the code or fall thru.
|
||||
*/
|
||||
if( pWhere && sqliteExprIsConstant(pWhere) ){
|
||||
sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak);
|
||||
sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1);
|
||||
}
|
||||
|
||||
/* Split the WHERE clause into as many as 32 separate subexpressions
|
||||
@ -795,7 +795,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
haveKey = 0;
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base+idx, 0);
|
||||
}
|
||||
sqliteExprIfFalse(pParse, aExpr[j].p, cont);
|
||||
sqliteExprIfFalse(pParse, aExpr[j].p, cont, 1);
|
||||
aExpr[j].p = 0;
|
||||
}
|
||||
brk = cont;
|
||||
|
@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing expressions.
|
||||
#
|
||||
# $Id: expr.test,v 1.19 2002/03/24 13:13:29 drh Exp $
|
||||
# $Id: expr.test,v 1.20 2002/05/26 20:54:34 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -22,8 +22,7 @@ execsql {CREATE TABLE test1(i1 int, i2 int, r1 real, r2 real, t1 text, t2 text)}
|
||||
execsql {INSERT INTO test1 VALUES(1,2,1.1,2.2,'hello','world')}
|
||||
proc test_expr {name settings expr result} {
|
||||
do_test $name [format {
|
||||
execsql {UPDATE test1 SET %s}
|
||||
execsql {SELECT %s FROM test1}
|
||||
execsql {BEGIN; UPDATE test1 SET %s; SELECT %s FROM test1; ROLLBACK;}
|
||||
} $settings $expr] $result
|
||||
}
|
||||
|
||||
@ -63,7 +62,7 @@ test_expr expr-1.33 {i1=1, i2=2} {i1=1 OR i2=1} {1}
|
||||
test_expr expr-1.34 {i1=1, i2=2} {i1=2 OR i2=2} {1}
|
||||
test_expr expr-1.35 {i1=1, i2=2} {i1-i2=-1} {1}
|
||||
test_expr expr-1.36 {i1=1, i2=0} {not i1} {0}
|
||||
test_expr expr-1.37 {i1=1, i2=NULL} {not i2} {1}
|
||||
test_expr expr-1.37 {i1=1, i2=0} {not i2} {1}
|
||||
test_expr expr-1.38 {i1=1} {-i1} {-1}
|
||||
test_expr expr-1.39 {i1=1} {+i1} {1}
|
||||
test_expr expr-1.40 {i1=1, i2=2} {+(i2+i1)} {3}
|
||||
@ -320,8 +319,8 @@ proc test_expr2 {name expr result} {
|
||||
test_expr2 expr-7.2 {a<10 AND a>8} {9}
|
||||
test_expr2 expr-7.3 {a<=10 AND a>=8} {8 9 10}
|
||||
test_expr2 expr-7.4 {a>=8 AND a<=10} {8 9 10}
|
||||
test_expr2 expr-7.5 {a>=20 OR a<=1} {{} 1 20}
|
||||
test_expr2 expr-7.6 {b!=4 AND a<=3} {{} 1 3}
|
||||
test_expr2 expr-7.5 {a>=20 OR a<=1} {1 20}
|
||||
test_expr2 expr-7.6 {b!=4 AND a<=3} {1 3}
|
||||
test_expr2 expr-7.7 {b==8 OR b==16 OR b==32} {3 4 5}
|
||||
test_expr2 expr-7.8 {NOT b<>8 OR b==1024} {3 10}
|
||||
test_expr2 expr-7.9 {b LIKE '10%'} {10 20}
|
||||
@ -332,13 +331,13 @@ test_expr2 expr-7.13 {b GLOB '*1[456]'} {4}
|
||||
test_expr2 expr-7.14 {a ISNULL} {{}}
|
||||
test_expr2 expr-7.15 {a NOTNULL AND a<3} {1 2}
|
||||
test_expr2 expr-7.16 {a AND a<3} {1 2}
|
||||
test_expr2 expr-7.17 {NOT a} {{}}
|
||||
test_expr2 expr-7.17 {NOT a} {}
|
||||
test_expr2 expr-7.18 {a==11 OR (b>1000 AND b<2000)} {10 11}
|
||||
test_expr2 expr-7.19 {a<=1 OR a>=20} {{} 1 20}
|
||||
test_expr2 expr-7.20 {a<1 OR a>20} {{}}
|
||||
test_expr2 expr-7.21 {a>19 OR a<1} {{} 20}
|
||||
test_expr2 expr-7.19 {a<=1 OR a>=20} {1 20}
|
||||
test_expr2 expr-7.20 {a<1 OR a>20} {}
|
||||
test_expr2 expr-7.21 {a>19 OR a<1} {20}
|
||||
test_expr2 expr-7.22 {a!=1 OR a=100} \
|
||||
{{} 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
|
||||
{2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
|
||||
test_expr2 expr-7.23 {(a notnull AND a<4) OR a==8} {1 2 3 8}
|
||||
test_expr2 expr-7.24 {a LIKE '2_' OR a==8} {8 20}
|
||||
test_expr2 expr-7.25 {a GLOB '2?' OR a==8} {8 20}
|
||||
|
@ -12,7 +12,7 @@
|
||||
# focus of this file is testing UNION, INTERSECT and EXCEPT operators
|
||||
# in SELECT statements.
|
||||
#
|
||||
# $Id: select4.test,v 1.6 2002/05/24 16:14:16 drh Exp $
|
||||
# $Id: select4.test,v 1.7 2002/05/26 20:54:35 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -249,6 +249,48 @@ do_test select4-6.2 {
|
||||
}
|
||||
} {0 1 1 1 2 2 3 4 3 7 4 8 5 15}
|
||||
|
||||
# NULLs are distinct. Make sure the UNION operator recognizes this
|
||||
#
|
||||
do_test select4-6.3 {
|
||||
execsql {
|
||||
SELECT NULL UNION SELECT NULL UNION
|
||||
SELECT 1 UNION SELECT 2 AS 'x'
|
||||
ORDER BY x;
|
||||
}
|
||||
} {{} {} 1 2}
|
||||
do_test select4-6.3 {
|
||||
execsql {
|
||||
SELECT NULL UNION ALL SELECT NULL UNION ALL
|
||||
SELECT 1 UNION ALL SELECT 2 AS 'x'
|
||||
ORDER BY x;
|
||||
}
|
||||
} {{} {} 1 2}
|
||||
|
||||
# Make sure the DISTINCT keyword treats NULLs as DISTINCT
|
||||
#
|
||||
do_test select4-6.4 {
|
||||
execsql {
|
||||
SELECT * FROM (
|
||||
SELECT NULL, 1 UNION ALL SELECT NULL, 1
|
||||
);
|
||||
}
|
||||
} {{} 1 {} 1}
|
||||
do_test select4-6.5 {
|
||||
execsql {
|
||||
SELECT DISTINCT * FROM (
|
||||
SELECT NULL, 1 UNION ALL SELECT NULL, 1
|
||||
);
|
||||
}
|
||||
} {{} 1 {} 1}
|
||||
do_test select4-6.6 {
|
||||
execsql {
|
||||
SELECT DISTINCT * FROM (
|
||||
SELECT 1,2 UNION ALL SELECT 1,2
|
||||
);
|
||||
}
|
||||
} {1 2}
|
||||
|
||||
|
||||
# Make sure column names are correct when a compound select appears as
|
||||
# an expression in the WHERE clause.
|
||||
#
|
||||
|
@ -12,7 +12,7 @@
|
||||
# focus of this file is testing SELECT statements that are part of
|
||||
# expressions.
|
||||
#
|
||||
# $Id: subselect.test,v 1.4 2001/09/16 00:13:28 drh Exp $
|
||||
# $Id: subselect.test,v 1.5 2002/05/26 20:54:35 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -55,9 +55,8 @@ do_test subselect-1.3c {
|
||||
# NULL as the result. Check it out.
|
||||
#
|
||||
do_test subselect-1.4 {
|
||||
execsql {INSERT INTO t1 VALUES(NULL,8)}
|
||||
execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=5)}
|
||||
} {8}
|
||||
execsql {SELECT b from t1 where a = coalesce((SELECT a FROM t1 WHERE b=5),1)}
|
||||
} {2}
|
||||
|
||||
# Try multiple subselects within a single expression.
|
||||
#
|
||||
|
@ -77,7 +77,7 @@ foreach tbl_defn [ list \
|
||||
|
||||
CREATE TRIGGER before_update_row BEFORE UPDATE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog),
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl),
|
||||
new.a, new.b);
|
||||
@ -85,7 +85,7 @@ foreach tbl_defn [ list \
|
||||
|
||||
CREATE TRIGGER after_update_row AFTER UPDATE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog),
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl),
|
||||
new.a, new.b);
|
||||
@ -94,7 +94,7 @@ foreach tbl_defn [ list \
|
||||
CREATE TRIGGER conditional_update_row AFTER UPDATE ON tbl FOR EACH ROW
|
||||
WHEN old.a = 1
|
||||
BEGIN
|
||||
INSERT INTO clog VALUES ( (SELECT max(idx) + 1 FROM clog),
|
||||
INSERT INTO clog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM clog),
|
||||
old.a, old.b,
|
||||
(SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl),
|
||||
new.a, new.b);
|
||||
@ -120,7 +120,7 @@ foreach tbl_defn [ list \
|
||||
INSERT INTO tbl VALUES (300, 200);
|
||||
CREATE TRIGGER delete_before_row BEFORE DELETE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog),
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl),
|
||||
0, 0);
|
||||
@ -128,7 +128,7 @@ foreach tbl_defn [ list \
|
||||
|
||||
CREATE TRIGGER delete_after_row AFTER DELETE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog),
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl),
|
||||
0, 0);
|
||||
@ -148,7 +148,7 @@ foreach tbl_defn [ list \
|
||||
DELETE FROM rlog;
|
||||
CREATE TRIGGER insert_before_row BEFORE INSERT ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog),
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
0, 0,
|
||||
(SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl),
|
||||
new.a, new.b);
|
||||
@ -156,7 +156,7 @@ foreach tbl_defn [ list \
|
||||
|
||||
CREATE TRIGGER insert_after_row AFTER INSERT ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog),
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
0, 0,
|
||||
(SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl),
|
||||
new.a, new.b);
|
||||
|
@ -12,7 +12,7 @@
|
||||
# focus of this file is testing the CREATE UNIQUE INDEX statement,
|
||||
# and primary keys, and the UNIQUE constraint on table columns
|
||||
#
|
||||
# $Id: unique.test,v 1.3 2001/12/21 14:30:44 drh Exp $
|
||||
# $Id: unique.test,v 1.4 2002/05/26 20:54:35 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -162,4 +162,37 @@ do_test unique-3.4 {
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
|
||||
# Make sure NULLs are distinct as far as the UNIQUE tests are
|
||||
# concerned.
|
||||
#
|
||||
do_test unique-4.1 {
|
||||
execsql {
|
||||
CREATE TABLE t4(a UNIQUE, b, c, UNIQUE(b,c));
|
||||
INSERT INTO t4 VALUES(1,2,3);
|
||||
INSERT INTO t4 VALUES(NULL, 2, NULL);
|
||||
SELECT * FROM t4;
|
||||
}
|
||||
} {1 2 3 {} 2 {}}
|
||||
do_test unique-4.2 {
|
||||
catchsql {
|
||||
INSERT INTO t4 VALUES(NULL, 3, 4);
|
||||
}
|
||||
} {0 {}}
|
||||
do_test unique-4.3 {
|
||||
execsql {
|
||||
SELECT * FROM t4
|
||||
}
|
||||
} {1 2 3 {} 2 {} {} 3 4}
|
||||
do_test unique-4.4 {
|
||||
catchsql {
|
||||
INSERT INTO t4 VALUES(2, 2, NULL);
|
||||
}
|
||||
} {0 {}}
|
||||
do_test unique-4.5 {
|
||||
execsql {
|
||||
SELECT * FROM t4
|
||||
}
|
||||
} {1 2 3 {} 2 {} {} 3 4 2 2 {}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user