diff options
Diffstat (limited to 'src/vdbe.c')
-rw-r--r-- | src/vdbe.c | 257 |
1 files changed, 204 insertions, 53 deletions
diff --git a/src/vdbe.c b/src/vdbe.c index af42f9b52..fa558eb7c 100644 --- a/src/vdbe.c +++ b/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. +*/ +/* 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_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; } } |