Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -228,10 +228,11 @@ $(TOP)/test/test_main.c \ $(TOP)/test/test_malloc.c \ $(TOP)/test/test_mem.c \ $(TOP)/test/test_misc1.c \ $(TOP)/test/test_mutex.c \ + $(TOP)/test/test_num.c \ $(TOP)/test/test_thread.c \ $(TOP)/test/test_thread0.c \ $(TOP)/test/test_utf.c \ $(TOP)/test/test_wsd.c Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -1901,17 +1901,19 @@ ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ - double value; - char *zV; - sqlite4AtoF(z, &value, sqlite4Strlen30(z), SQLITE4_UTF8); - assert( !sqlite4IsNaN(value) ); /* The new AtoF never returns NaN */ - if( negateFlag ) value = -value; - zV = dup8bytes(v, (char*)&value); - sqlite4VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); + int s = sizeof(sqlite4_num); + sqlite4_num *p = (sqlite4_num *)sqlite4DbMallocZero(sqlite4VdbeDb(v), s); + if( p ){ + *p = sqlite4_num_from_text(z, -1, 0, 0); + assert( p->sign==0 ); + assert( negateFlag==0 || negateFlag==1 ); + p->sign = negateFlag; + sqlite4VdbeAddOp4(v, OP_Num, 0, iMem, 0, (const char *)p, P4_NUM); + } } } #endif @@ -1927,26 +1929,20 @@ int i = pExpr->u.iValue; assert( i>=0 ); if( negFlag ) i = -i; sqlite4VdbeAddOp2(v, OP_Integer, i, iMem); }else{ + sqlite4_num *p; int c; i64 value; const char *z = pExpr->u.zToken; assert( z!=0 ); - c = sqlite4Atoi64(z, &value, sqlite4Strlen30(z), SQLITE4_UTF8); - if( c==0 || (c==2 && negFlag) ){ - char *zV; - if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; } - zV = dup8bytes(v, (char*)&value); - sqlite4VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); - }else{ -#ifdef SQLITE4_OMIT_FLOATING_POINT - sqlite4ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); -#else - codeReal(v, z, negFlag, iMem); -#endif + + p = (sqlite4_num *)sqlite4DbMallocRaw(pParse->db, sizeof(sqlite4_num)); + if( p ){ + *p = sqlite4_num_from_text(z, -1, (negFlag ? SQLITE4_NEGATIVE : 0), 0); + sqlite4VdbeAddOp4(v, OP_Num, p->e==0, iMem, 0, (const char *)p, P4_NUM); } } } /* Index: src/math.c ================================================================== --- src/math.c +++ src/math.c @@ -304,100 +304,153 @@ ** ** Conversion stops at the first \000 character. At most nIn bytes ** of zIn are examined. Or if nIn is negative, up to a billion bytes ** are scanned, which we assume is more than will be found in any valid ** numeric string. +** +** If the value does not contain a decimal point or exponent, and is +** within the range of a signed 64-bit integer, it is guaranteed that +** the exponent of the returned value is zero. */ -sqlite4_num sqlite4_num_from_text(const char *zIn, int nIn, unsigned flags){ - int incr = 1; - sqlite4_num r; +sqlite4_num sqlite4_num_from_text( + const char *zIn, /* Pointer to text to parse */ + int nIn, /* Size of zIn in bytes or (-ve) */ + unsigned flags, /* Conversion flags */ + int *pbReal /* OUT: True if text looks like a real */ +){ + /* Return this value (NaN) if a parse error occurs. */ + static const sqlite4_num error_value = {0, 0, SQLITE4_MX_EXP+1, 0}; + + static const i64 L10 = (LARGEST_INT64 / 10); + int aMaxFinal[2] = {7, 8}; + static int one = 1; /* Used to test machine endianness */ + int bRnd = 1; /* If mantissa overflows, round it */ + int bReal = 0; /* If text looks like a real */ + int seenRadix = 0; /* True after decimal point has been parsed */ + int seenDigit = 0; /* True after first non-zero digit parsed */ + int incr = 1; /* 1 for utf-8, 2 for utf-16 */ + sqlite4_num r; /* Value to return */ char c; int nDigit = 0; - int seenRadix = 0; int i; - static int one = 1; + + assert( L10==922337203685477580 ); memset(&r, 0, sizeof(r)); if( nIn<0 ) nIn = 1000000000; c = flags & 0xf; if( c==0 || c==SQLITE4_UTF8 ){ incr = 1; - }else if( c==SQLITE4_UTF16 ){ - incr = 2; - c = *(char*)&one; - zIn += c; - nIn -= c; - } - - if( nIn<=0 ) goto not_a_valid_number; - if( zIn[0]=='-' ){ - r.sign = 1; - i = incr; - }else if( zIn[0]=='+' ){ - i = incr; - }else{ - i = 0; - } - if( nIn<=0 ) goto not_a_valid_number; - if( nIn>=incr*3 + }else{ + if( c==SQLITE4_UTF16 ){ c = (3 - *(char*)&one); } + assert( c==SQLITE4_UTF16LE || c==SQLITE4_UTF16BE ); + incr = 2; + if( c==SQLITE4_UTF16BE ){ + zIn += 1; + nIn -= 1; + } + } + + /* If the IGNORE_WHITESPACE flag is set, ignore any leading whitespace. */ + i = 0; + if( flags & SQLITE4_IGNORE_WHITESPACE ){ + while( sqlite4Isspace(zIn[i]) && i=incr*3 && ((c=zIn[i])=='i' || c=='I') && ((c=zIn[i+incr])=='n' || c=='N') && ((c=zIn[i+incr*2])=='f' || c=='F') ){ r.e = SQLITE4_MX_EXP+1; - r.m = nIn<=i+incr*3 || zIn[i+incr*3]==0; - return r; + r.m = 1; + if( pbReal ) *pbReal = 1; + goto finished; } - while( i='0' && c<='9' ){ - if( c=='0' && nDigit==0 ){ - if( seenRadix && r.e > -(SQLITE4_MX_EXP+1000) ) r.e--; + int iDigit = (c - '0'); + + if( iDigit==0 && seenDigit==0 ){ + /* Handle leading zeroes. If they occur to the right of the decimal + ** point they can just be ignored. Otherwise, decrease the exponent + ** by one. */ + if( seenRadix ) r.e--; continue; } - nDigit++; - if( nDigit<=18 ){ - r.m = (r.m*10) + c - '0'; - if( seenRadix ) r.e--; - }else{ - if( c!='0' ) r.approx = 1; - if( !seenRadix ) r.e++; - } - }else if( c=='.' ){ - seenRadix = 1; - }else if( c=='e' || c=='E' ){ - int exp = 0; - int expsign = 0; - int nEDigit = 0; - if( zIn[i]=='-' ){ - expsign = 1; - i += incr; - }else if( zIn[i]=='+' ){ - i += incr; - } - if( i>=nIn ) goto not_a_valid_number; - while( i'9' ) goto not_a_valid_number; - if( c=='0' && nEDigit==0 ) continue; - nEDigit++; - if( nEDigit>3 ) goto not_a_valid_number; - exp = exp*10 + c - '0'; - } - if( expsign ) exp = -exp; - r.e += exp; - break; - }else{ - goto not_a_valid_number; - } - } - return r; - -not_a_valid_number: - r.e = SQLITE4_MX_EXP+1; - r.m = 0; - return r; + + seenDigit = 1; + if( r.e>0 || r.m>L10 || (r.m==L10 && iDigit>aMaxFinal[r.sign]) ){ + /* Mantissa overflow. */ + if( seenRadix==0 ) r.e++; + if( iDigit!=0 ){ r.approx = 1; } + if( bRnd ){ + if( iDigit>5 && r.m<((u64)LARGEST_INT64 + r.sign)) r.m++; + bRnd = 0; + } + bReal = 1; + }else{ + if( seenRadix ) r.e -= 1; + r.m = (r.m*10) + iDigit; + } + + }else{ + if( flags & SQLITE4_INTEGER_ONLY ) goto finished; + + if( c=='.' ){ + /* Permit only a single radix in each number */ + if( seenRadix ) goto finished; + seenRadix = 1; + bReal = 1; + }else if( c=='e' || c=='E' ){ + int f = (flags & (SQLITE4_PREFIX_ONLY|SQLITE4_IGNORE_WHITESPACE)); + sqlite4_num exp; + exp = sqlite4_num_from_text(&zIn[i+incr], nIn-i-incr, f, 0); + if( sqlite4_num_isnan(exp) ) goto finished; + if( exp.e || exp.m>999 ) goto finished; + bReal = 1; + r.e += (int)(exp.m) * (exp.sign ? -1 : 1); + i = nIn; + break; + }else{ + goto finished; + } + } + } + +finished: + + /* Check for a parse error. If one has occurred, set the return value + ** to NaN. */ + if( (flags & SQLITE4_PREFIX_ONLY)==0 && ilarge || (d>1.0 && d==(i64)d) ){ + d = d / 10.0; + x.e++; + } + + while( dL10 ) *pbLossy = 1; + iRet = iRet * 10; + } + for(i=num.e; i<0; i++){ + if( pbLossy && (iRet % 10) ) *pbLossy = 1; + iRet = iRet / 10; + } + + if( num.sign ) iRet = iRet*-1; + return iRet; +} + /* ** Convert an integer into text in the buffer supplied. The ** text is zero-terminated and right-justified in the buffer. ** A pointer to the first character of text is returned. @@ -446,116 +582,116 @@ /* ** Convert a number into text. Store the result in zOut[]. The ** zOut buffer must be at laest 30 characters in length. The output ** will be zero-terminated. */ -int sqlite4_num_to_text(sqlite4_num x, char *zOut){ +int sqlite4_num_to_text(sqlite4_num x, char *zOut, int bReal){ char zBuf[24]; - int nOut = 0; char *zNum; int n; static const char zeros[] = "0000000000000000000000000"; + + char *z = zOut; if( x.sign && x.m>0 ){ /* Add initial "-" for negative non-zero values */ - zOut[0] = '-'; - zOut++; - nOut++; + z[0] = '-'; + z++; } if( x.e>SQLITE4_MX_EXP ){ /* Handle NaN and infinite values */ if( x.m==0 ){ - memcpy(zOut, "NaN", 4); + memcpy(z, "NaN", 4); }else{ - memcpy(zOut, "inf", 4); + memcpy(z, "inf", 4); } - return nOut+3; + return (z - zOut)+3; } if( x.m==0 ){ - memcpy(zOut, "0", 2); - return 1; + memcpy(z, "0", 2); + return 1+(z-zOut); } zNum = renderInt(x.m, zBuf, sizeof(zBuf)); n = &zBuf[sizeof(zBuf)-1] - zNum; if( x.e>=0 && x.e+n<=25 ){ /* Integer values with up to 25 digits */ - memcpy(zOut, zNum, n+1); - nOut += n; + memcpy(z, zNum, n+1); + z += n; if( x.e>0 ){ - memcpy(&zOut[nOut], zeros, x.e); - zOut[nOut+x.e] = 0; - nOut += x.e; + memcpy(z, zeros, x.e); + z += x.e; + z[0] = 0; + } + if( bReal ){ + memcpy(z, ".0", 3); + z += 2; } - return nOut; + return (z - zOut); } if( x.e<0 && n+x.e > 0 ){ /* Fractional values where the decimal point occurs within the ** significant digits. ex: 12.345 */ int m = n+x.e; - memcpy(zOut, zNum, m); - nOut += m; - zOut += m; + memcpy(z, zNum, m); + z += m; zNum += m; n -= m; removeTrailingZeros(zNum, &n); if( n>0 ){ - zOut[0] = '.'; - memcpy(zOut+1, zNum, n); - nOut += n; - zOut[n+1] = 0; + z[0] = '.'; + z++; + memcpy(z, zNum, n); + z += n; + z[0] = 0; }else{ - zOut[0] = 0; + if( bReal ){ + memcpy(z, ".0", 3); + z += 2; + }else{ + z[0] = 0; + } } - return nOut; + return (z - zOut); } if( x.e<0 && x.e >= -n-5 ){ /* Values less than 1 and with no more than 5 subsequent zeros prior ** to the first significant digit. Ex: 0.0000012345 */ int j = -(n + x.e); - memcpy(zOut, "0.", 2); - nOut += 2; - zOut += 2; + memcpy(z, "0.", 2); + z += 2; if( j>0 ){ - memcpy(zOut, zeros, j); - nOut += j; - zOut += j; + memcpy(z, zeros, j); + z += j; } removeTrailingZeros(zNum, &n); - memcpy(zOut, zNum, n+1); - nOut += n; - zOut[n+1] = 0; - return nOut; + memcpy(z, zNum, n); + z += n; + z[0] = 0; + return (z - zOut); } /* Exponential notation from here to the end. ex: 1.234e-15 */ - zOut[0] = zNum[0]; + z[0] = zNum[0]; + z++; if( n>1 ){ int nOrig = n; removeTrailingZeros(zNum, &n); x.e += nOrig - n; } - if( n==1 ){ - /* Exactly one significant digit. ex: 8e12 */ - zOut++; - nOut++; - }else{ + if( n!=1 ){ /* Two or or more significant digits. ex: 1.23e17 */ - zOut[1] = '.'; - memcpy(zOut+2, zNum+1, n-1); - zOut += n+1; - nOut += n+1; + *z++ = '.'; + memcpy(z, zNum+1, n-1); + z += n-1; x.e += n-1; } - zOut[0] = 'e'; - zOut++; - nOut++; + *z++ = 'e'; if( x.e<0 ){ - zOut[0] = '-'; + *z++ = '-'; x.e = -x.e; }else{ - zOut[0] = '+'; + *z++ = '+'; } - zOut++; - nOut++; + z++; zNum = renderInt(x.e&0x7fff, zBuf, sizeof(zBuf)); - while( (zOut[0] = zNum[0])!=0 ){ zOut++; zNum++; nOut++; } - return nOut; + while( (z[0] = zNum[0])!=0 ){ z++; zNum++; } + return (z-zOut); } Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -45,15 +45,17 @@ ** Generate code to return a single integer value. */ static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){ Vdbe *v = sqlite4GetVdbe(pParse); int mem = ++pParse->nMem; - i64 *pI64 = sqlite4DbMallocRaw(pParse->db, sizeof(value)); - if( pI64 ){ - memcpy(pI64, &value, sizeof(value)); + sqlite4_num *pNum; + + pNum = sqlite4DbMallocRaw(pParse->db, sizeof(value)); + if( pNum ){ + *pNum = sqlite4_num_from_int64(value); } - sqlite4VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64); + sqlite4VdbeAddOp4(v, OP_Num, 1, mem, 0, (char *)pNum, P4_NUM); sqlite4VdbeSetNumCols(v, 1); sqlite4VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE4_STATIC); sqlite4VdbeAddOp2(v, OP_ResultRow, mem, 1); } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -4110,23 +4110,25 @@ sqlite4_num sqlite4_num_div(sqlite4_num, sqlite4_num); int sqlite4_num_isinf(sqlite4_num); int sqlite4_num_isnan(sqlite4_num); sqlite4_num sqlite4_num_round(sqlite4_num, int iDigit); int sqlite4_num_compare(sqlite4_num, sqlite4_num); -sqlite4_num sqlite4_num_from_text(const char*, int n, unsigned flags); +sqlite4_num sqlite4_num_from_text(const char*, int n, unsigned flags, int*); sqlite4_num sqlite4_num_from_int64(sqlite4_int64); sqlite4_num sqlite4_num_from_double(double); int sqlite4_num_to_int32(sqlite4_num, int*); -int sqlite4_num_to_int64(sqlite4_num, sqlite4_int64*); -double sqlite4_num_to_double(sqlite4_num); -int sqlite4_num_to_text(sqlite4_num, char*); +sqlite4_int64 sqlite4_num_to_int64(sqlite4_num, int *); +int sqlite4_num_to_double(sqlite4_num, double *); +int sqlite4_num_to_text(sqlite4_num, char*, int); /* ** CAPI4REF: Flags For Text-To-Numeric Conversion */ #define SQLITE4_PREFIX_ONLY 0x10 #define SQLITE4_IGNORE_WHITESPACE 0x20 +#define SQLITE4_NEGATIVE 0x40 +#define SQLITE4_INTEGER_ONLY 0x80 typedef struct sqlite4_tokenizer sqlite4_tokenizer; /* ** CAPI4REF: Register an FTS tokenizer implementation Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -227,22 +227,19 @@ ** do so without loss of information. In other words, if the string ** looks like a number, convert it into a number. If it does not ** look like a number, leave it alone. */ static void applyNumericAffinity(Mem *pRec){ - if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ - double rValue; - i64 iValue; - u8 enc = pRec->enc; - if( (pRec->flags&MEM_Str)==0 ) return; - if( sqlite4AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return; - if( 0==sqlite4Atoi64(pRec->z, &iValue, pRec->n, enc) ){ - pRec->u.i = iValue; - pRec->flags |= MEM_Int; - }else{ - pRec->r = rValue; - pRec->flags |= MEM_Real; + if( (pRec->flags & (MEM_Real|MEM_Int))==0 && (pRec->flags & MEM_Str) ){ + int flags = pRec->enc | SQLITE4_IGNORE_WHITESPACE; + int bReal = 0; + sqlite4_num num; + + num = sqlite4_num_from_text(pRec->z, pRec->n, flags, &bReal); + if( sqlite4_num_isnan(num)==0 ){ + pRec->u.num = num; + MemSetTypeFlag(pRec, (bReal ? MEM_Real : MEM_Int)); } } } /* @@ -392,18 +389,20 @@ ** Print the value of a register for tracing purposes: */ static void memTracePrint(FILE *out, Mem *p){ if( p->flags & MEM_Null ){ fprintf(out, " NULL"); - }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ - fprintf(out, " si:%lld", p->u.i); - }else if( p->flags & MEM_Int ){ - fprintf(out, " i:%lld", p->u.i); -#ifndef SQLITE4_OMIT_FLOATING_POINT - }else if( p->flags & MEM_Real ){ - fprintf(out, " r:%g", p->r); -#endif + }else if( p->flags & (MEM_Int|MEM_Real) ){ + char aNum[31]; + char *zFlags = "r"; + sqlite4_num_to_text(p->u.num, aNum, (p->flags & MEM_Real)); + if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ + zFlags = "si"; + }else if( p->flags & MEM_Int ){ + zFlags = "i"; + } + fprintf(out, " %s:%s", zFlags, aNum); }else if( p->flags & MEM_RowSet ){ fprintf(out, " (keyset)"); }else{ char zBuf[200]; sqlite4VdbeMemPrettyPrint(p, zBuf); @@ -417,13 +416,22 @@ fprintf(out, "\n"); } #endif #ifdef SQLITE4_DEBUG -# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M) +static int assertFlagsOk(Mem *p){ + u16 flags = p->flags; + assert( (flags&MEM_Int)==0 || (flags&MEM_Real)==0 ); + return 1; +} +#endif + +#ifdef SQLITE4_DEBUG +# define REGISTER_TRACE(R,M) \ + if(assertFlagsOk(M) && p->trace)registerTrace(p->trace,R,M) #else -# define REGISTER_TRACE(R,M) +# define REGISTER_TRACE(R,M) #endif #ifdef VDBE_PROFILE @@ -720,11 +728,11 @@ assert( pOp->p1>0 && pOp->p1<=p->nMem ); pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; - pIn1->u.i = pc; + pIn1->u.num = sqlite4_num_from_int64((i64)pc); REGISTER_TRACE(pOp->p1, pIn1); pc = pOp->p2 - 1; break; } @@ -733,11 +741,11 @@ ** Jump to the next instruction after the address in register P1. */ case OP_Return: { /* in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags & MEM_Int ); - pc = (int)pIn1->u.i; + sqlite4_num_to_int32(pIn1->u.num, &pc); break; } /* Opcode: Yield P1 * * * * ** @@ -746,12 +754,12 @@ case OP_Yield: { /* in1 */ int pcDest; pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); pIn1->flags = MEM_Int; - pcDest = (int)pIn1->u.i; - pIn1->u.i = pc; + sqlite4_num_to_int32(pIn1->u.num, &pcDest); + pIn1->u.num = sqlite4_num_from_int64(pc); REGISTER_TRACE(pOp->p1, pIn1); pc = pcDest; break; } @@ -836,38 +844,25 @@ /* Opcode: Integer P1 P2 * * * ** ** The 32-bit integer value P1 is written into register P2. */ case OP_Integer: { /* out2-prerelease */ - pOut->u.i = pOp->p1; - break; -} - -/* Opcode: Int64 * P2 * P4 * -** -** P4 is a pointer to a 64-bit integer value. -** Write that value into register P2. -*/ -case OP_Int64: { /* out2-prerelease */ - assert( pOp->p4.pI64!=0 ); - pOut->u.i = *pOp->p4.pI64; - break; -} - -#ifndef SQLITE4_OMIT_FLOATING_POINT -/* Opcode: Real * P2 * P4 * -** -** P4 is a pointer to a 64-bit floating point value. -** Write that value into register P2. -*/ -case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ - pOut->flags = MEM_Real; - assert( !sqlite4IsNaN(*pOp->p4.pReal) ); - pOut->r = *pOp->p4.pReal; - break; -} -#endif + pOut->u.num = sqlite4_num_from_int64((i64)pOp->p1); + break; +} + +/* Opcode: Num P1 P2 * P4 * +** +** P4 is a pointer to an sqlite4_num value. Write that value into +** register P2. Set the register flags to MEM_Int if P1 is non-zero, +** or MEM_Real otherwise. +*/ +case OP_Num: { /* out2-prerelease */ + pOut->flags = (pOp->p1 ? MEM_Int : MEM_Real); + pOut->u.num = *(pOp->p4.pNum); + break; +} /* Opcode: String8 * P2 * P4 * ** ** P4 points to a nul terminated UTF-8 string. This opcode is transformed ** into an OP_String before it is executed for the first time. @@ -1203,23 +1198,25 @@ case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ int flags; /* Combined MEM_* flags from both inputs */ i64 iA; /* Integer value of left operand */ i64 iB; /* Integer value of right operand */ - double rA; /* Real value of left operand */ - double rB; /* Real value of right operand */ + sqlite4_num num1; + sqlite4_num num2; pIn1 = &aMem[pOp->p1]; applyNumericAffinity(pIn1); pIn2 = &aMem[pOp->p2]; applyNumericAffinity(pIn2); pOut = &aMem[pOp->p3]; flags = pIn1->flags | pIn2->flags; if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; - if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ - iA = pIn1->u.i; - iB = pIn2->u.i; + + if( (pIn1->flags&MEM_Int) && (pIn2->flags&MEM_Int) ){ + iA = sqlite4_num_to_int64(pIn1->u.num, 0); + iB = sqlite4_num_to_int64(pIn2->u.num, 0); + switch( pOp->opcode ){ case OP_Add: if( sqlite4AddInt64(&iB,iA) ) goto fp_math; break; case OP_Subtract: if( sqlite4SubInt64(&iB,iA) ) goto fp_math; break; case OP_Multiply: if( sqlite4MulInt64(&iB,iA) ) goto fp_math; break; case OP_Divide: { @@ -1233,49 +1230,47 @@ if( iA==-1 ) iA = 1; iB %= iA; break; } } - pOut->u.i = iB; - MemSetTypeFlag(pOut, MEM_Int); - }else{ -fp_math: - rA = sqlite4VdbeRealValue(pIn1); - rB = sqlite4VdbeRealValue(pIn2); - switch( pOp->opcode ){ - case OP_Add: rB += rA; break; - case OP_Subtract: rB -= rA; break; - case OP_Multiply: rB *= rA; break; - case OP_Divide: { - /* (double)0 In case of SQLITE4_OMIT_FLOATING_POINT... */ - if( rA==(double)0 ) goto arithmetic_result_is_null; - rB /= rA; - break; - } - default: { - iA = (i64)rA; - iB = (i64)rB; - if( iA==0 ) goto arithmetic_result_is_null; - if( iA==-1 ) iA = 1; - rB = (double)(iB % iA); - break; - } - } -#ifdef SQLITE4_OMIT_FLOATING_POINT - pOut->u.i = rB; - MemSetTypeFlag(pOut, MEM_Int); -#else - if( sqlite4IsNaN(rB) ){ - goto arithmetic_result_is_null; - } - pOut->r = rB; - MemSetTypeFlag(pOut, MEM_Real); - if( (flags & MEM_Real)==0 ){ - sqlite4VdbeIntegerAffinity(pOut); - } -#endif - } + pOut->u.num = sqlite4_num_from_int64(iB); + MemSetTypeFlag(pOut, MEM_Int); + + break; + }else{ + + fp_math: + num1 = sqlite4VdbeNumValue(pIn1); + num2 = sqlite4VdbeNumValue(pIn2); + switch( pOp->opcode ){ + case OP_Add: + pOut->u.num = sqlite4_num_add(num1, num2); break; + case OP_Subtract: + pOut->u.num = sqlite4_num_sub(num2, num1); break; + case OP_Multiply: + pOut->u.num = sqlite4_num_mul(num1, num2); break; + case OP_Divide: + pOut->u.num = sqlite4_num_div(num2, num1); break; + default: { + iA = sqlite4_num_to_int64(num1, 0); + iB = sqlite4_num_to_int64(num2, 0); + if( iA==0 ) goto arithmetic_result_is_null; + pOut->u.num = sqlite4_num_from_int64(iB % iA); + break; + } + } + + if( sqlite4_num_isnan(pOut->u.num) ){ + goto arithmetic_result_is_null; + }else{ + MemSetTypeFlag(pOut, MEM_Real); + if( (flags & MEM_Real)==0 ){ + sqlite4VdbeIntegerAffinity(pOut); + } + } + } + break; arithmetic_result_is_null: sqlite4VdbeMemSetNull(pOut); break; @@ -1501,11 +1496,12 @@ if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB); } memcpy(&iA, &uA, sizeof(iA)); } } - pOut->u.i = iA; + + pOut->u.num = sqlite4_num_from_int64(iA); MemSetTypeFlag(pOut, MEM_Int); break; } /* Opcode: AddImm P1 P2 * * * @@ -1517,11 +1513,11 @@ */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite4VdbeMemIntegerify(pIn1); - pIn1->u.i += pOp->p2; + pIn1->u.num = sqlite4_num_add(pIn1->u.num, sqlite4_num_from_int64(pOp->p2)); break; } /* Opcode: MustBeInt P1 P2 * * * ** @@ -1557,11 +1553,11 @@ ** to have only a real value. */ case OP_RealAffinity: { /* in1 */ pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Int ){ - sqlite4VdbeMemRealify(pIn1); + MemSetTypeFlag(pIn1, MEM_Real); } break; } #endif @@ -1656,13 +1652,13 @@ ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); - if( (pIn1->flags & MEM_Null)==0 ){ - sqlite4VdbeMemRealify(pIn1); - } + sqlite4VdbeMemNumerify(pIn1); + pIn1->flags |= MEM_Real; + pIn1->flags &= ~MEM_Int; break; } #endif /* !defined(SQLITE4_OMIT_CAST) && !defined(SQLITE4_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 @@ -1798,11 +1794,11 @@ if( pOp->p5 & SQLITE4_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = res; + pOut->u.num = sqlite4_num_from_int64(res); REGISTER_TRACE(pOp->p2, pOut); }else if( res ){ pc = pOp->p2-1; } @@ -1949,11 +1945,11 @@ } pOut = &aMem[pOp->p3]; if( v1==2 ){ MemSetTypeFlag(pOut, MEM_Null); }else{ - pOut->u.i = v1; + pOut->u.num = sqlite4_num_from_int64(v1); MemSetTypeFlag(pOut, MEM_Int); } break; } @@ -2025,15 +2021,11 @@ int c; pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ){ c = pOp->p3; }else{ -#ifdef SQLITE4_OMIT_FLOATING_POINT - c = sqlite4VdbeIntValue(pIn1)!=0; -#else - c = sqlite4VdbeRealValue(pIn1)!=0.0; -#endif + c = sqlite4VdbeNumValue(pIn1).m!=0; if( pOp->opcode==OP_IfNot ) c = !c; } if( c ){ pc = pOp->p2-1; } @@ -2552,19 +2544,19 @@ ** ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { /* in3 */ Db *pDb; - u32 v; + i64 v; assert( pOp->p1>=0 && pOp->p1nDb ); pDb = &db->aDb[pOp->p1]; pIn3 = &aMem[pOp->p3]; sqlite4VdbeMemIntegerify(pIn3); - v = (u32)pIn3->u.i; - rc = sqlite4KVStorePutSchema(pDb->pKV, v); - pDb->pSchema->schema_cookie = (int)pIn3->u.i; + v = sqlite4_num_to_int64(pIn3->u.num, 0); + rc = sqlite4KVStorePutSchema(pDb->pKV, (u32)v); + pDb->pSchema->schema_cookie = (int)v; db->flags |= SQLITE4_InternChanges; if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ sqlite4ExpirePreparedStatements(db); @@ -2708,11 +2700,11 @@ assert( p2<=p->nMem ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite4VdbeMemIntegerify(pIn2); - p2 = (int)pIn2->u.i; + sqlite4_num_to_int32(pIn2->u.num, &p2); /* The p2 value always comes from a prior OP_NewIdxid opcode and ** that opcode will always set the p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ if( NEVER(p2<2) ) { @@ -3240,11 +3232,11 @@ ** instruction. */ case OP_Sequence: { /* out2-prerelease */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( p->apCsr[pOp->p1]!=0 ); - pOut->u.i = p->apCsr[pOp->p1]->seqCount++; + pOut->u.num = sqlite4_num_from_int64(p->apCsr[pOp->p1]->seqCount++); break; } /* Opcode: NewRowid P1 P2 P3 * * @@ -3260,10 +3252,11 @@ i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ const KVByteArray *aKey; /* Key of an existing row */ KVSize nKey; /* Size of the existing row key */ int n; /* Number of bytes decoded */ + i64 i3; /* Integer value from pIn3 */ v = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -3311,18 +3304,19 @@ pIn3 = sqlite4RegisterInRootFrame(p, pOp->p3); assert( memIsValid(pIn3) ); REGISTER_TRACE(pOp->p3, pIn3); sqlite4VdbeMemIntegerify(pIn3); assert( (pIn3->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( pIn3->u.i==MAX_ROWID ){ + i3 = sqlite4_num_to_int64(pIn3->u.num, 0); + if( i3==MAX_ROWID ){ rc = SQLITE4_FULL; } - if( vu.i ) v = pIn3->u.i; + if( vflags = MEM_Int; - pOut->u.i = v+1; + pOut->u.num = sqlite4_num_from_int64(v+1); break; } /* Opcode: NewIdxid P1 P2 * * * ** @@ -3334,10 +3328,11 @@ ** * the largest index number still visible in the database using the ** LEFAST query mode used by OP_NewRowid in database P2. */ case OP_NewIdxid: { /* in1 */ u64 iMax; + i64 i1; KVStore *pKV; KVCursor *pCsr; pKV = db->aDb[pOp->p2].pKV; pIn1 = &aMem[pOp->p1]; @@ -3359,15 +3354,17 @@ rc = SQLITE4_OK; } sqlite4KVCursorClose(pCsr); } - if( pIn1->u.i>=(i64)iMax ){ - pIn1->u.i++; + i1 = sqlite4_num_to_int64(pIn1->u.num, 0); + if( i1>=(i64)iMax ){ + i1++; }else{ - pIn1->u.i = iMax+1; + i1 = iMax+1; } + pIn1->u.num = sqlite4_num_from_int64(i1); break; } /* Opcode: Insert P1 P2 P3 P4 P5 @@ -3425,11 +3422,11 @@ if( pOp->opcode==OP_Insert ){ pKey = &aMem[pOp->p3]; assert( pKey->flags & MEM_Int ); assert( memIsValid(pKey) ); REGISTER_TRACE(pOp->p3, pKey); - iKey = pKey->u.i; + iKey = sqlite4_num_to_int64(pKey->u.num, 0); }else{ /* assert( pOp->opcode==OP_InsertInt ); */ iKey = pOp->p3; } @@ -3617,11 +3614,11 @@ n = sqlite4GetVarint64(aKey, nKey, (sqlite4_uint64*)&v); n = sqlite4VdbeDecodeIntKey(&aKey[n], nKey-n, &v); if( n==0 ) rc = SQLITE4_CORRUPT; } } - pOut->u.i = v; + pOut->u.num = sqlite4_num_from_int64(v); break; } /* Opcode: NullRow P1 * * * * ** @@ -4279,20 +4276,23 @@ ** ** This instruction throws an error if the memory cell is not initially ** an integer. */ case OP_MemMax: { /* in2 */ + i64 i1; + i64 i2; Mem *pIn1; - VdbeFrame *pFrame; pIn1 = sqlite4RegisterInRootFrame(p, pOp->p1); assert( memIsValid(pIn1) ); sqlite4VdbeMemIntegerify(pIn1); pIn2 = &aMem[pOp->p2]; REGISTER_TRACE(pOp->p1, pIn1); sqlite4VdbeMemIntegerify(pIn2); - if( pIn1->u.iu.i){ - pIn1->u.i = pIn2->u.i; + i1 = sqlite4_num_to_int64(pIn1->u.num, 0); + i2 = sqlite4_num_to_int64(pIn2->u.num, 0); + if( i1u.num = sqlite4_num_from_int64(i2); } REGISTER_TRACE(pOp->p1, pIn1); break; } #endif /* SQLITE4_OMIT_AUTOINCREMENT */ @@ -4303,13 +4303,15 @@ ** ** It is illegal to use this instruction on a register that does ** not contain an integer. An assertion fault will result if you try. */ case OP_IfPos: { /* jump, in1 */ + i64 i1; pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); - if( pIn1->u.i>0 ){ + i1 = sqlite4_num_to_int64(pIn1->u.num, 0); + if( i1>0 ){ pc = pOp->p2 - 1; } break; } @@ -4319,13 +4321,15 @@ ** ** It is illegal to use this instruction on a register that does ** not contain an integer. An assertion fault will result if you try. */ case OP_IfNeg: { /* jump, in1 */ + i64 i1; pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); - if( pIn1->u.i<0 ){ + i1 = sqlite4_num_to_int64(pIn1->u.num, 0); + if( i1<0 ){ pc = pOp->p2 - 1; } break; } @@ -4336,14 +4340,17 @@ ** ** It is illegal to use this instruction on a register that does ** not contain an integer. An assertion fault will result if you try. */ case OP_IfZero: { /* jump, in1 */ + i64 i1; pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); - pIn1->u.i += pOp->p3; - if( pIn1->u.i==0 ){ + i1 = sqlite4_num_to_int64(pIn1->u.num, 0); + i1 += pOp->p3; + pIn1->u.num = sqlite4_num_from_int64(i1); + if( i1==0 ){ pc = pOp->p2 - 1; } break; } @@ -4881,20 +4888,19 @@ */ case OP_FtsUpdate: { Fts5Info *pInfo; /* Description of fts5 index to update */ Mem *pKey; /* Primary key of indexed row */ Mem *aArg; /* Pointer to array of N arguments */ - Mem *pRoot; /* Root page number */ - int iRoot; + int iRoot; /* Root page number (or 0) */ assert( pOp->p4type==P4_FTS5INFO ); pInfo = pOp->p4.pFtsInfo; aArg = &aMem[pOp->p3]; pKey = &aMem[pOp->p1]; if( pOp->p2 ){ - iRoot = aMem[pOp->p2].u.i; + sqlite4_num_to_int32(aMem[pOp->p2].u.num, &iRoot); }else{ iRoot = 0; } rc = sqlite4Fts5Update(db, pInfo, iRoot, pKey, aArg, pOp->p5, &p->zErrMsg); @@ -4910,10 +4916,11 @@ case OP_FtsCksum: { Fts5Info *pInfo; /* Description of fts5 index to update */ Mem *pKey; /* Primary key of row */ Mem *aArg; /* Pointer to array of N values */ i64 cksum; /* Checksum for this row or index entry */ + i64 i1; assert( pOp->p4type==P4_FTS5INFO ); pInfo = pOp->p4.pFtsInfo; pOut = &aMem[pOp->p1]; @@ -4921,15 +4928,16 @@ aArg = &aMem[pOp->p3+1]; cksum = 0; if( pOp->p5 ){ sqlite4Fts5EntryCksum(db, pInfo, pKey, aArg, &cksum); - pOut->u.i = pOut->u.i ^ cksum; }else{ sqlite4Fts5RowCksum(db, pInfo, pKey, aArg, &cksum); - pOut->u.i = pOut->u.i ^ cksum; } + i1 = sqlite4_num_to_int64(pOut->u.num, 0); + pOut->u.num = sqlite4_num_from_int64(i1 ^ cksum); + break; } /* Opcode: FtsOpen P1 P2 P3 P4 P5 ** @@ -4943,11 +4951,10 @@ ** leave the cursor pointing at the first match and fall through to the ** next instruction. */ case OP_FtsOpen: { /* jump */ Fts5Info *pInfo; /* Description of fts5 index to update */ - char *zErr; VdbeCursor *pCur; char *zMatch; Mem *pMatch; pMatch = &aMem[pOp->p3]; Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -62,10 +62,11 @@ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Fts5Info *pFtsInfo; /* Used when p4type is P4_FTS5INDEXINFO */ int (*xAdvance)(VdbeCursor*); + sqlite4_num *pNum; /* Used when p4type is P4_NUM */ } p4; #ifdef SQLITE4_DEBUG char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE @@ -120,10 +121,11 @@ #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ #define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ #define P4_FTS5INFO (-20) /* P4 points to an Fts5Info structure */ +#define P4_NUM (-21) /* P4 points to an Fts5Info structure */ /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure ** is made. That copy is freed when the Vdbe is finalized. But if the ** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still ** gets freed when the Vdbe is finalized so it still should be obtained @@ -216,11 +218,10 @@ char *sqlite4VdbeExpandSql(Vdbe*, const char*); #endif sqlite4_value *sqlite4ColumnValue(sqlite4_stmt *pStmt, int iCol); void sqlite4VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); -int sqlite4VdbeRecordCompare(int,const void*,UnpackedRecord*); UnpackedRecord *sqlite4VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); #ifndef SQLITE4_OMIT_TRIGGER void sqlite4VdbeLinkSubProgram(Vdbe *, SubProgram *); #endif Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -128,20 +128,19 @@ ** integer etc.) of the same value. */ struct Mem { sqlite4 *db; /* The associated database connection */ char *z; /* String or BLOB value */ - double r; /* Real value */ union { - i64 i; /* Integer value used when MEM_Int is set in flags */ + sqlite4_num num; /* Numeric value used by MEM_Int and/or MEM_Real */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ } u; int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ - u8 type; /* One of SQLITE4_NULL, SQLITE4_TEXT, SQLITE4_INTEGER, etc */ + u8 type; /* One of SQLITE4_NULL, _TEXT, _INTEGER, etc */ u8 enc; /* SQLITE4_UTF8, SQLITE4_UTF16BE, SQLITE4_UTF16LE */ #ifdef SQLITE4_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ #endif @@ -408,12 +407,12 @@ int sqlite4VdbeMemMakeWriteable(Mem*); int sqlite4VdbeMemStringify(Mem*, int); i64 sqlite4VdbeIntValue(Mem*); int sqlite4VdbeMemIntegerify(Mem*); double sqlite4VdbeRealValue(Mem*); +sqlite4_num sqlite4VdbeNumValue(Mem *); void sqlite4VdbeIntegerAffinity(Mem*); -int sqlite4VdbeMemRealify(Mem*); int sqlite4VdbeMemNumerify(Mem*); void sqlite4VdbeMemSetRowSet(Mem *pMem); void sqlite4VdbeMemRelease(Mem *p); void sqlite4VdbeMemReleaseExternal(Mem *p); Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -670,11 +670,11 @@ ** __attribute__((aligned(8))) macro. */ static const Mem nullMem #if defined(SQLITE4_DEBUG) && defined(__GNUC__) __attribute__((aligned(8))) #endif - = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE4_NULL, 0, + = {0, "", {{0,0,0,0}}, 0, MEM_Null, SQLITE4_NULL, 0, #ifdef SQLITE4_DEBUG 0, 0, /* pScopyFrom, pFiller */ #endif 0, 0 }; @@ -1088,16 +1088,19 @@ } #endif /* SQLITE4_OMIT_UTF16 */ int sqlite4_bind_value(sqlite4_stmt *pStmt, int i, const sqlite4_value *pValue){ int rc; switch( pValue->type ){ - case SQLITE4_INTEGER: { - rc = sqlite4_bind_int64(pStmt, i, pValue->u.i); - break; - } + case SQLITE4_INTEGER: case SQLITE4_FLOAT: { - rc = sqlite4_bind_double(pStmt, i, pValue->r); + Mem *p = (Mem *)pValue; + Vdbe *v = (Vdbe *)pStmt; + vdbeUnbind(v, i); + v->aVar[i-1].u.num = p->u.num; + MemSetTypeFlag(&v->aVar[i-1], + (pValue->type==SQLITE4_FLOAT ? MEM_Real : MEM_Int) + ); break; } case SQLITE4_BLOB: { rc = sqlite4_bind_blob(pStmt, i, pValue->z, pValue->n, SQLITE4_TRANSIENT, 0); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -580,10 +580,11 @@ static void freeP4(sqlite4 *db, int p4type, void *p4){ if( p4 ){ assert( db ); switch( p4type ){ case P4_REAL: + case P4_NUM: case P4_INT64: case P4_DYNAMIC: case P4_KEYINFO: case P4_INTARRAY: case P4_KEYINFO_HANDOFF: { @@ -898,14 +899,14 @@ } case P4_MEM: { Mem *pMem = pOp->p4.pMem; if( pMem->flags & MEM_Str ){ zP4 = pMem->z; - }else if( pMem->flags & MEM_Int ){ - sqlite4_snprintf(zTemp, nTemp, "%lld", pMem->u.i); - }else if( pMem->flags & MEM_Real ){ - sqlite4_snprintf(zTemp, nTemp, "%.16g", pMem->r); + }else if( pMem->flags & (MEM_Int|MEM_Real) ){ + char aOut[30]; + sqlite4_num_to_text(pMem->u.num, aOut, (pMem->flags & MEM_Real)); + sqlite4_snprintf(zTemp, nTemp, "%s", aOut); }else if( pMem->flags & MEM_Null ){ sqlite4_snprintf(zTemp, nTemp, "NULL"); }else{ assert( pMem->flags & MEM_Blob ); zP4 = "(blob)"; @@ -1131,15 +1132,15 @@ pOp = &apSub[j]->aOp[i]; } if( p->explain==1 ){ pMem->flags = MEM_Int; pMem->type = SQLITE4_INTEGER; - pMem->u.i = i; /* Program counter */ + pMem->u.num = sqlite4_num_from_int64(i); /* Program counter */ pMem++; pMem->flags = MEM_Static|MEM_Str|MEM_Term; - pMem->z = (char*)sqlite4OpcodeName(pOp->opcode); /* Opcode */ + pMem->z = (char*)sqlite4OpcodeName(pOp->opcode); /* Opcode */ assert( pMem->z!=0 ); pMem->n = sqlite4Strlen30(pMem->z); pMem->type = SQLITE4_TEXT; pMem->enc = SQLITE4_UTF8; pMem++; @@ -1163,25 +1164,25 @@ } } } pMem->flags = MEM_Int; - pMem->u.i = pOp->p1; /* P1 */ + pMem->u.num = sqlite4_num_from_int64(pOp->p1); /* P1 */ pMem->type = SQLITE4_INTEGER; pMem++; pMem->flags = MEM_Int; - pMem->u.i = pOp->p2; /* P2 */ + pMem->u.num = sqlite4_num_from_int64(pOp->p2); /* P2 */ pMem->type = SQLITE4_INTEGER; pMem++; pMem->flags = MEM_Int; - pMem->u.i = pOp->p3; /* P3 */ + pMem->u.num = sqlite4_num_from_int64(pOp->p3); /* P3 */ pMem->type = SQLITE4_INTEGER; pMem++; - if( sqlite4VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ + if( sqlite4VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE4_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; z = displayP4(pOp, pMem->z, 32); @@ -2136,105 +2137,10 @@ p->magic = VDBE_MAGIC_DEAD; p->db = 0; sqlite4VdbeDeleteObject(db, p); } -/* -** The following functions: -** -** sqlite4VdbeSerialType() -** sqlite4VdbeSerialTypeLen() -** sqlite4VdbeSerialLen() -** sqlite4VdbeSerialPut() -** sqlite4VdbeSerialGet() -** -** encapsulate the code that serializes values for storage in SQLite -** data and index records. Each serialized value consists of a -** 'serial-type' and a blob of data. The serial type is an 8-byte unsigned -** integer, stored as a varint. -** -** In an SQLite index record, the serial type is stored directly before -** the blob of data that it corresponds to. In a table record, all serial -** types are stored at the start of the record, and the blobs of data at -** the end. Hence these functions allow the caller to handle the -** serial-type and data blob seperately. -** -** The following table describes the various storage classes for data: -** -** serial type bytes of data type -** -------------- --------------- --------------- -** 0 0 NULL -** 1 1 signed integer -** 2 2 signed integer -** 3 3 signed integer -** 4 4 signed integer -** 5 6 signed integer -** 6 8 signed integer -** 7 8 IEEE float -** 8 0 Integer constant 0 -** 9 0 Integer constant 1 -** 10,11 reserved for expansion -** N>=12 and even (N-12)/2 BLOB -** N>=13 and odd (N-13)/2 text -** -** The 8 and 9 types were added in 3.3.0, file format 4. Prior versions -** of SQLite will not understand those serial types. -*/ - -/* -** Return the serial-type for the value stored in pMem. -*/ -u32 sqlite4VdbeSerialType(Mem *pMem, int file_format){ - int flags = pMem->flags; - int n; - - if( flags&MEM_Null ){ - return 0; - } - if( flags&MEM_Int ){ - /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ -# define MAX_6BYTE ((((i64)0x00008000)<<32)-1) - i64 i = pMem->u.i; - u64 u; - if( file_format>=4 && (i&1)==i ){ - return 8+(u32)i; - } - if( i<0 ){ - if( i<(-MAX_6BYTE) ) return 6; - /* Previous test prevents: u = -(-9223372036854775808) */ - u = -i; - }else{ - u = i; - } - if( u<=127 ) return 1; - if( u<=32767 ) return 2; - if( u<=8388607 ) return 3; - if( u<=2147483647 ) return 4; - if( u<=MAX_6BYTE ) return 5; - return 6; - } - if( flags&MEM_Real ){ - return 7; - } - assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) ); - n = pMem->n; - assert( n>=0 ); - return ((n*2) + 12 + ((flags&MEM_Str)!=0)); -} - -/* -** Return the length of the data corresponding to the supplied serial-type. -*/ -u32 sqlite4VdbeSerialTypeLen(u32 serial_type){ - if( serial_type>=12 ){ - return (serial_type-12)/2; - }else{ - static const u8 aSize[] = { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0 }; - return aSize[serial_type]; - } -} - /* ** If we are on an architecture with mixed-endian floating ** points (ex: ARM7) then swap the lower 4 bytes with the ** upper 4 bytes. Return the result. ** @@ -2284,161 +2190,10 @@ # define swapMixedEndianFloat(X) X = floatSwap(X) #else # define swapMixedEndianFloat(X) #endif -/* -** Write the serialized data blob for the value stored in pMem into -** buf. It is assumed that the caller has allocated sufficient space. -** Return the number of bytes written. -** -** nBuf is the amount of space left in buf[]. nBuf must always be -** large enough to hold the entire field. Except, if the field is -** a blob with a zero-filled tail, then buf[] might be just the right -** size to hold everything except for the zero-filled tail. If buf[] -** is only big enough to hold the non-zero prefix, then only write that -** prefix into buf[]. But if buf[] is large enough to hold both the -** prefix and the tail then write the prefix and set the tail to all -** zeros. -** -** Return the number of bytes actually written into buf[]. The number -** of bytes in the zero-filled tail is included in the return value only -** if those bytes were zeroed in buf[]. -*/ -u32 sqlite4VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){ - u32 serial_type = sqlite4VdbeSerialType(pMem, file_format); - u32 len; - - /* Integer and Real */ - if( serial_type<=7 && serial_type>0 ){ - u64 v; - u32 i; - if( serial_type==7 ){ - assert( sizeof(v)==sizeof(pMem->r) ); - memcpy(&v, &pMem->r, sizeof(v)); - swapMixedEndianFloat(v); - }else{ - v = pMem->u.i; - } - len = i = sqlite4VdbeSerialTypeLen(serial_type); - assert( len<=(u32)nBuf ); - while( i-- ){ - buf[i] = (u8)(v&0xFF); - v >>= 8; - } - return len; - } - - /* String or blob */ - if( serial_type>=12 ){ - assert( pMem->n == (int)sqlite4VdbeSerialTypeLen(serial_type) ); - assert( pMem->n<=nBuf ); - len = pMem->n; - memcpy(buf, pMem->z, len); - return len; - } - - /* NULL or constants 0 or 1 */ - return 0; -} - -/* -** Deserialize the data blob pointed to by buf as serial type serial_type -** and store the result in pMem. Return the number of bytes read. -*/ -u32 sqlite4VdbeSerialGet( - const unsigned char *buf, /* Buffer to deserialize from */ - u32 serial_type, /* Serial type to deserialize */ - Mem *pMem /* Memory cell to write value into */ -){ - switch( serial_type ){ - case 10: /* Reserved for future use */ - case 11: /* Reserved for future use */ - case 0: { /* NULL */ - pMem->flags = MEM_Null; - break; - } - case 1: { /* 1-byte signed integer */ - pMem->u.i = (signed char)buf[0]; - pMem->flags = MEM_Int; - return 1; - } - case 2: { /* 2-byte signed integer */ - pMem->u.i = (((signed char)buf[0])<<8) | buf[1]; - pMem->flags = MEM_Int; - return 2; - } - case 3: { /* 3-byte signed integer */ - pMem->u.i = (((signed char)buf[0])<<16) | (buf[1]<<8) | buf[2]; - pMem->flags = MEM_Int; - return 3; - } - case 4: { /* 4-byte signed integer */ - pMem->u.i = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; - pMem->flags = MEM_Int; - return 4; - } - case 5: { /* 6-byte signed integer */ - u64 x = (((signed char)buf[0])<<8) | buf[1]; - u32 y = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]; - x = (x<<32) | y; - pMem->u.i = *(i64*)&x; - pMem->flags = MEM_Int; - return 6; - } - case 6: /* 8-byte signed integer */ - case 7: { /* IEEE floating point */ - u64 x; - u32 y; -#if !defined(NDEBUG) && !defined(SQLITE4_OMIT_FLOATING_POINT) - /* Verify that integers and floating point values use the same - ** byte order. Or, that if SQLITE4_MIXED_ENDIAN_64BIT_FLOAT is - ** defined that 64-bit floating point values really are mixed - ** endian. - */ - static const u64 t1 = ((u64)0x3ff00000)<<32; - static const double r1 = 1.0; - u64 t2 = t1; - swapMixedEndianFloat(t2); - assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); -#endif - - x = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; - y = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7]; - x = (x<<32) | y; - if( serial_type==6 ){ - pMem->u.i = *(i64*)&x; - pMem->flags = MEM_Int; - }else{ - assert( sizeof(x)==8 && sizeof(pMem->r)==8 ); - swapMixedEndianFloat(x); - memcpy(&pMem->r, &x, sizeof(x)); - pMem->flags = sqlite4IsNaN(pMem->r) ? MEM_Null : MEM_Real; - } - return 8; - } - case 8: /* Integer 0 */ - case 9: { /* Integer 1 */ - pMem->u.i = serial_type-8; - pMem->flags = MEM_Int; - return 0; - } - default: { - u32 len = (serial_type-12)/2; - pMem->z = (char *)buf; - pMem->n = len; - pMem->xDel = 0; - if( serial_type&0x01 ){ - pMem->flags = MEM_Str | MEM_Ephem; - }else{ - pMem->flags = MEM_Blob | MEM_Ephem; - } - return len; - } - } - return 0; -} /* ** This routine is used to allocate sufficient space for an UnpackedRecord ** structure large enough to be used with sqlite4VdbeRecordUnpack() if ** the first argument is a pointer to KeyInfo structure pKeyInfo. @@ -2481,163 +2236,10 @@ p->pKeyInfo = pKeyInfo; p->nField = pKeyInfo->nField + 1; return p; } -/* -** Given the nKey-byte encoding of a record in pKey[], populate the -** UnpackedRecord structure indicated by the fourth argument with the -** contents of the decoded record. -*/ -void sqlite4VdbeRecordUnpack( - KeyInfo *pKeyInfo, /* Information about the record format */ - int nKey, /* Size of the binary record */ - const void *pKey, /* The binary record */ - UnpackedRecord *p /* Populate this structure before returning. */ -){ - const unsigned char *aKey = (const unsigned char *)pKey; - int d; - u32 idx; /* Offset in aKey[] to read from */ - u16 u; /* Unsigned loop counter */ - u32 szHdr; - Mem *pMem = p->aMem; - - p->flags = 0; - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - idx = getVarint32(aKey, szHdr); - d = szHdr; - u = 0; - while( idxnField && d<=nKey ){ - u32 serial_type; - - idx += getVarint32(&aKey[idx], serial_type); - pMem->enc = pKeyInfo->enc; - pMem->db = pKeyInfo->db; - /* pMem->flags = 0; // sqlite4VdbeSerialGet() will set this for us */ - pMem->zMalloc = 0; - d += sqlite4VdbeSerialGet(&aKey[d], serial_type, pMem); - pMem++; - u++; - } - assert( u<=pKeyInfo->nField + 1 ); - p->nField = u; -} - -/* -** This function compares the two table rows or index records -** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero -** or positive integer if key1 is less than, equal to or -** greater than key2. The {nKey1, pKey1} key must be a blob -** created by th OP_MakeRecord opcode of the VDBE. The pPKey2 -** key must be a parsed key such as obtained from -** sqlite4VdbeParseRecord. -** -** Key1 and Key2 do not have to contain the same number of fields. -** The key with fewer fields is usually compares less than the -** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set -** and the common prefixes are equal, then key1 is less than key2. -** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are -** equal, then the keys are considered to be equal and -** the parts beyond the common prefix are ignored. -*/ -int sqlite4VdbeRecordCompare( - int nKey1, const void *pKey1, /* Left key */ - UnpackedRecord *pPKey2 /* Right key */ -){ - int d1; /* Offset into aKey[] of next data element */ - u32 idx1; /* Offset into aKey[] of next header element */ - u32 szHdr1; /* Number of bytes in header */ - int i = 0; - int nField; - int rc = 0; - const unsigned char *aKey1 = (const unsigned char *)pKey1; - KeyInfo *pKeyInfo; - Mem mem1; - - pKeyInfo = pPKey2->pKeyInfo; - mem1.enc = pKeyInfo->enc; - mem1.db = pKeyInfo->db; - /* mem1.flags = 0; // Will be initialized by sqlite4VdbeSerialGet() */ - VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */ - - /* Compilers may complain that mem1.u.i is potentially uninitialized. - ** We could initialize it, as shown here, to silence those complaints. - ** But in fact, mem1.u.i will never actually be used uninitialized, and doing - ** the unnecessary initialization has a measurable negative performance - ** impact, since this routine is a very high runner. And so, we choose - ** to ignore the compiler warnings and leave this variable uninitialized. - */ - /* mem1.u.i = 0; // not needed, here to silence compiler warning */ - - idx1 = getVarint32(aKey1, szHdr1); - d1 = szHdr1; - nField = pKeyInfo->nField; - while( idx1nField ){ - u32 serial_type1; - - /* Read the serial types for the next element in each key. */ - idx1 += getVarint32( aKey1+idx1, serial_type1 ); - if( d1>=nKey1 && sqlite4VdbeSerialTypeLen(serial_type1)>0 ) break; - - /* Extract the values to be compared. - */ - d1 += sqlite4VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); - - /* Do the comparison - */ - rc = sqlite4MemCompare(&mem1, &pPKey2->aMem[i], - iaColl[i] : 0); - if( rc!=0 ){ - assert( mem1.zMalloc==0 ); /* See comment below */ - - /* Invert the result if we are using DESC sort order. */ - if( pKeyInfo->aSortOrder && iaSortOrder[i] ){ - rc = -rc; - } - - /* If the PREFIX_SEARCH flag is set and all fields except the final - ** rowid field were equal, then clear the PREFIX_SEARCH flag and set - ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1). - ** This is used by the OP_IsUnique opcode. - */ - if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){ - assert( idx1==szHdr1 && rc ); - assert( mem1.flags & MEM_Int ); - pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH; - pPKey2->rowid = mem1.u.i; - } - - return rc; - } - i++; - } - - /* No memory allocation is ever used on mem1. Prove this using - ** the following assert(). If the assert() fails, it indicates a - ** memory leak and a need to call sqlite4VdbeMemRelease(&mem1). - */ - assert( mem1.zMalloc==0 ); - - /* rc==0 here means that one of the keys ran out of fields and - ** all the fields up to that point were equal. If the UNPACKED_INCRKEY - ** flag is set, then break the tie by treating key2 as larger. - ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes - ** are considered to be equal. Otherwise, the longer key is the - ** larger. As it happens, the pPKey2 will always be the longer - ** if there is a difference. - */ - assert( rc==0 ); - if( pPKey2->flags & UNPACKED_INCRKEY ){ - rc = -1; - }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){ - /* Leave rc==0 */ - }else if( idx1a[ofst+iByte]; } sqlite4VdbeMemSetInt64(pOut, v); }else if( type<=21 ){ + sqlite4_num num = {0, 0, 0, 0}; sqlite4_uint64 x; int e; - double r; + n = sqlite4GetVarint64(p->a+ofst, p->n-ofst, &x); e = (int)x; n += sqlite4GetVarint64(p->a+ofst+n, p->n-(ofst+n), &x); if( n!=size ) return SQLITE4_CORRUPT; - r = (double)x; - if( e&1 ) r = -r; - if( e&2 ){ - e = -(e>>2); - if( e==0 ){ - r *= 1e+300*1e+300; - }else{ - while( e<=-10 ){ r /= 1.0e10; e += 10; } - while( e<0 ){ r /= 10.0; e++; } - } - }else{ - e = e>>2; - while( e>=10 ){ r *= 1.0e10; e -= 10; } - while( e>0 ){ r *= 10.0; e--; } - } - sqlite4VdbeMemSetDouble(pOut, r); + + num.m = x; + num.e = (e >> 2); + if( e & 0x02 ) num.e = -1 * num.e; + if( e & 0x01 ) num.sign = 1; + pOut->u.num = num; + MemSetTypeFlag(pOut, MEM_Real); + }else if( cclass==0 ){ if( size==0 ){ sqlite4VdbeMemSetStr(pOut, "", 0, SQLITE4_UTF8, SQLITE4_TRANSIENT, 0); }else if( p->a[ofst]>0x02 ){ sqlite4VdbeMemSetStr(pOut, (char*)(p->a+ofst), size, @@ -227,44 +220,27 @@ for(i=0; i1.8e+19 ){ - e++; - r /= 10.0; - } - m = r; - if( e<0 ){ - e = (-e*4) + 2 + sign; - }else{ - e = e*4 + sign; - } + sqlite4_num *p = &aIn[i].u.num; + int e; + assert( p->sign==0 || p->sign==1 ); + if( p->e<0 ){ + e = (p->e*-4) + 2 + p->sign; + }else{ + e = (p->e*4) + p->sign; } n = sqlite4PutVarint64(aAux[i].z, (sqlite4_uint64)e); - n += sqlite4PutVarint64(aAux[i].z+n, m); + n += sqlite4PutVarint64(aAux[i].z+n, p->m); aAux[i].n = n; aOut[nOut++] = n+9; nPayload += n; }else if( flags & MEM_Str ){ n = aIn[i].n; @@ -287,11 +263,12 @@ for(i=0; i>= 8; aOut[nOut+(--n)] = v & 0xff; @@ -357,156 +334,113 @@ } return SQLITE4_OK; } /* -** Encode the positive integer m using the key encoding. -** -** To encode an integer, the integer value is represented as centimal -** (base-100) with E digits. Each centimal digit is stored in one byte -** with the most significant digits coming first. For each centimal -** digit X (with X>=0 and X<=99) the byte value will be 2*X+1 except -** for the last digit for which the value is 2*X. Trailing 0 digits are -** omitted, so that the encoding of the mantissa will never contain -** a zero byte. -** -** The key encoding consists of the E value (the number of -** centimal digits in the original number, before trailing zero digits -** are removed), followed by the mantissa encoding M. This routine -** only writes the mantissa. The E values will be embedded in the -** initial byte of the encoding by the calling function. This -** routine returns the value of E. E will always be at least 1 and -** no more than 10. -** -** Note that values encoded by this routine have exactly the same -** byte representation as the equivalent floating-point values encoded -** by the encodeLargeFloatKey() routine below. +** Write value v as a varint into buffer p. If parameter bInvert +** is non-zero, write the ones-complement of each byte instead of +** the usual value. +*/ +static void putVarint64(KeyEncoder *p, sqlite4_uint64 v, int bInvert){ + unsigned char *z = &p->aOut[p->nOut]; + int n = sqlite4PutVarint64(z, v); + if( bInvert ){ + int i; + for(i=0; inOut += n; +} + +/* +** Write value num into buffer p using the key encoding. */ -static int encodeIntKey(sqlite4_uint64 m, KeyEncoder *p){ - int i = 0; - int e; - unsigned char aDigits[20]; - assert( m>0 ); - do{ - aDigits[i++] = m%100; m /= 100; - }while( m ); - e = i; - while( i ) p->aOut[p->nOut++] = aDigits[--i]*2 + 1; - p->aOut[p->nOut-1] &= 0xfe; - return e; +static void encodeNumericKey(KeyEncoder *p, sqlite4_num num){ + if( num.m==0 ){ + p->aOut[p->nOut++] = 0x15; /* Numeric zero */ + }else if( sqlite4_num_isnan(num) ){ + p->aOut[p->nOut++] = 0x06; /* NaN */ + }else if( sqlite4_num_isinf(num) ){ + p->aOut[p->nOut++] = num.sign ? 0x07 : 0x23; /* Neg and Pos infinity */ + }else{ + int e; + u64 m; + int iDigit = 0; + u8 aDigit[12]; + + while( (num.m % 10)==0 ){ + num.e++; + num.m = num.m / 10; + } + m = num.m; + e = num.e; + + if( num.e % 2 ){ + aDigit[0] = 10 * (m % 10); + m = m / 10; + e--; + iDigit = 1; + }else{ + iDigit = 0; + } + + while( m ){ + aDigit[iDigit++] = (m % 100); + m = m / 100; + } + e = (iDigit + (e/2)); + + if( e>11 ){ /* Large value */ + if( num.sign==0 ){ + p->aOut[p->nOut++] = 0x22; + putVarint64(p, e, 0); + }else{ + p->aOut[p->nOut++] = 0x08; + putVarint64(p, e, 1); + } + } + else if( e>=0 ){ /* Medium value */ + if( num.sign==0 ){ + p->aOut[p->nOut++] = 0x17+e; + }else{ + p->aOut[p->nOut++] = 0x13-e; + } + } + else{ /* Small value */ + if( num.sign==0 ){ + p->aOut[p->nOut++] = 0x16; + putVarint64(p, -1*e, 1); + }else{ + p->aOut[p->nOut++] = 0x14; + putVarint64(p, -1*e, 0); + } + } + + /* Write M to the output. */ + while( (iDigit--)>0 ){ + u8 d = aDigit[iDigit]*2; + if( iDigit!=0 ) d |= 0x01; + if( num.sign ) d = ~d; + p->aOut[p->nOut++] = d; + } + } } /* ** Encode a single integer using the key encoding. The caller must ** ensure that sufficient space exits in a[] (at least 12 bytes). ** The return value is the number of bytes of a[] used. */ int sqlite4VdbeEncodeIntKey(u8 *a, sqlite4_int64 v){ - int i, e; KeyEncoder s; + sqlite4_num num; + + num = sqlite4_num_from_int64(v); + memset(&s, 0, sizeof(s)); s.aOut = a; - s.nOut = 1; - if( v<0 ){ - e = encodeIntKey((sqlite4_uint64)-v, &s); - assert( e<=10 ); - a[0] = 0x13-e; - for(i=1; i0 ){ - e = encodeIntKey((sqlite4_uint64)v, &s); - assert( e<=10 ); - a[0] = 0x17+e; - }else{ - a[0] = 0x15; - } + encodeNumericKey(&s, num); return s.nOut; } - -/* -** Encode the small positive floating point number r using the key -** encoding. The caller guarantees that r will be less than 1.0 and -** greater than 0.0. -** -** A floating point value is encoded as an integer exponent E and a -** mantissa M. The original value is equal to (M * 100^E). E is set -** to the smallest value possible without making M greater than or equal -** to 1.0. -** -** For this routine, E will always be zero or negative, since the original -** value is less than one. The encoding written by this routine is the -** ones-complement of the varint of the negative of E followed by the -** mantissa: -** -** Encoding: ~-E M -*/ -static void encodeSmallFloatKey(double r, KeyEncoder *p){ - int e = 0; - int i, n; - assert( r>0.0 && r<1.0 ); - while( r<1e-10 ){ r *= 1e8; e+=4; } - while( r<0.01 ){ r *= 100.0; e++; } - n = sqlite4PutVarint64(p->aOut+p->nOut, e); - for(i=0; iaOut[i+p->nOut] ^= 0xff; - p->nOut += n; - for(i=0; i<18 && r!=0.0; i++){ - r *= 100.0; - int d = r; - p->aOut[p->nOut++] = 2*d + 1; - r -= d; - } - p->aOut[p->nOut-1] &= 0xfe; -} - -/* -** Encode the large positive floating point number r using the key -** encoding. The caller guarantees that r will be finite and greater than -** or equal to 1.0. -** -** A floating point value is encoded as an integer exponent E and a -** mantissa M. The original value is equal to (M * 100^E). E is set to -** the smallest value possible without making M greater than or equal -** to 1.0. -** -** Each centimal digit of the mantissa is stored in a byte. If the value -** of the centimal digit is X (hence X>=0 and X<=99) then the byte value -** will be 2*X+1 for every byte of the mantissa, except for the last byte -** which will be 2*X+0. The mantissa must be the minimum number of bytes -** necessary to represent the value; trailing X==0 digits are omitted. -** This means that the mantissa will never contain a byte with the -** value 0x00. -** -** If E is greater than 10, then this routine writes of E as a varint -** followed by the mantissa as described above. Otherwise, if E is 10 or -** less, this routine only writes the mantissa and leaves the E value -** to be encoded as part of the opening byte of the field by the -** calling function. -** -** Encoding: M (if E<=10) -** E M (if E>10) -** -** This routine returns the value of E. -*/ -static int encodeLargeFloatKey(double r, KeyEncoder *p){ - int e = 0; - int i, n; - assert( r>=1.0 ); - while( r>=1e32 && e<=350 ){ r *= 1e-32; e+=16; } - while( r>=1e8 && e<=350 ){ r *= 1e-8; e+=4; } - while( r>=1.0 && e<=350 ){ r *= 0.01; e++; } - if( e>10 ){ - n = sqlite4PutVarint64(p->aOut+p->nOut, e); - p->nOut += n; - } - for(i=0; i<18 && r!=0.0; i++){ - r *= 100.0; - int d = r; - p->aOut[p->nOut++] = 2*d + 1; - r -= d; - } - p->aOut[p->nOut-1] &= 0xfe; - return e; -} - /* ** Encode a single column of the key */ static int encodeOneKeyValue( @@ -515,66 +449,21 @@ u8 sortOrder, /* Sort order for this value */ u8 isLastValue, /* True if this is the last value in the key */ CollSeq *pColl /* Collating sequence for the value */ ){ int flags = pMem->flags; - int i, e; + int i; int n; int iStart = p->nOut; if( flags & MEM_Null ){ if( enlargeEncoderAllocation(p, 1) ) return SQLITE4_NOMEM; p->aOut[p->nOut++] = 0x05; /* NULL */ }else - if( flags & MEM_Int ){ - sqlite4_int64 v = pMem->u.i; - if( enlargeEncoderAllocation(p, 11) ) return SQLITE4_NOMEM; - if( v==0 ){ - p->aOut[p->nOut++] = 0x15; /* Numeric zero */ - }else if( v<0 ){ - p->aOut[p->nOut++] = 0x08; /* Large negative number */ - i = p->nOut; - e = encodeIntKey((sqlite4_uint64)-v, p); - if( e<=10 ) p->aOut[i-1] = 0x13-e; - while( inOut ) p->aOut[i++] ^= 0xff; - }else{ - i = p->nOut; - p->aOut[p->nOut++] = 0x22; /* Large positive number */ - e = encodeIntKey((sqlite4_uint64)v, p); - if( e<=10 ) p->aOut[i] = 0x17+e; - } - }else - if( flags & MEM_Real ){ - double r = pMem->r; + if( flags & (MEM_Real|MEM_Int) ){ if( enlargeEncoderAllocation(p, 16) ) return SQLITE4_NOMEM; - if( r==0.0 ){ - p->aOut[p->nOut++] = 0x15; /* Numeric zero */ - }else if( sqlite4IsNaN(r) ){ - p->aOut[p->nOut++] = 0x06; /* NaN */ - }else if( (n = sqlite4IsInf(r))!=0 ){ - p->aOut[p->nOut++] = n<0 ? 0x07 : 0x23; /* Neg and Pos infinity */ - }else if( r<=-1.0 ){ - p->aOut[p->nOut++] = 0x08; /* Large negative values */ - i = p->nOut; - e = encodeLargeFloatKey(-r, p); - if( e<=10 ) p->aOut[i-1] = 0x13-e; - while( inOut ) p->aOut[i++] ^= 0xff; - }else if( r<0.0 ){ - p->aOut[p->nOut++] = 0x14; /* Small negative values */ - i = p->nOut; - encodeSmallFloatKey(-r, p); - while( inOut ) p->aOut[i++] ^= 0xff; - }else if( r<1.0 ){ - p->aOut[p->nOut++] = 0x16; /* Small positive values */ - encodeSmallFloatKey(r, p); - }else{ - i = p->nOut; - p->aOut[p->nOut++] = 0x22; /* Large positive values */ - e = encodeLargeFloatKey(r, p); - if( e<=10 ) p->aOut[i] = 0x17+e; - } - }else - if( flags & MEM_Str ){ + encodeNumericKey(p, pMem->u.num); + }else if( flags & MEM_Str ){ Mem *pEnc; /* Pointer to memory cell in correct enc. */ Mem sMem; /* Value converted to different encoding */ int enc; /* Required encoding */ /* Figure out the current encoding of pMem, and the encoding required @@ -822,12 +711,14 @@ i = 1; do{ m = m*100 + aKey[i]/2; e--; }while( aKey[i++] & 1 ); + while( (e--)>0 ){ m = m*100; } + if( isNeg ){ *pVal = -m; }else{ *pVal = m; } return m==0 ? 0 : i; } Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -174,11 +174,10 @@ assert( !(fg&(MEM_Str|MEM_Blob)) ); assert( fg&(MEM_Int|MEM_Real) ); assert( (pMem->flags&MEM_RowSet)==0 ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - if( sqlite4VdbeMemGrow(pMem, nByte, 0) ){ return SQLITE4_NOMEM; } /* For a Real or Integer, use sqlite4_mprintf() to produce the UTF-8 @@ -185,16 +184,12 @@ ** string representation of the value. Then, if the required encoding ** is UTF-16le or UTF-16be do a translation. ** ** FIX ME: It would be better if sqlite4_snprintf() could do UTF-16. */ - if( fg & MEM_Int ){ - sqlite4_snprintf(pMem->z, nByte, "%lld", pMem->u.i); - }else{ - assert( fg & MEM_Real ); - sqlite4_snprintf(pMem->z, nByte, "%!.15g", pMem->r); - } + sqlite4_num_to_text(pMem->u.num, pMem->z, (pMem->flags & MEM_Int)==0); + pMem->n = sqlite4Strlen30(pMem->z); pMem->enc = SQLITE4_UTF8; pMem->flags |= MEM_Str|MEM_Term; sqlite4VdbeChangeEncoding(pMem, enc); return rc; @@ -315,80 +310,62 @@ ** an SQL-NULL value, return 0. ** ** If pMem represents a string value, its encoding might be changed. */ i64 sqlite4VdbeIntValue(Mem *pMem){ - int flags; assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - flags = pMem->flags; - if( flags & MEM_Int ){ - return pMem->u.i; - }else if( flags & MEM_Real ){ - return doubleToInt64(pMem->r); - }else if( flags & (MEM_Str|MEM_Blob) ){ - i64 value = 0; - assert( pMem->z || pMem->n==0 ); - testcase( pMem->z==0 ); - sqlite4Atoi64(pMem->z, &value, pMem->n, pMem->enc); - return value; - }else{ - return 0; - } + return sqlite4_num_to_int64(sqlite4VdbeNumValue(pMem), 0); } /* ** Return the best representation of pMem that we can get into a ** double. If pMem is already a double or an integer, return its ** value. If it is a string or blob, try to convert it to a double. ** If it is a NULL, return 0.0. */ double sqlite4VdbeRealValue(Mem *pMem){ + double rVal = 0.0; assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - if( pMem->flags & MEM_Real ){ - return pMem->r; - }else if( pMem->flags & MEM_Int ){ - return (double)pMem->u.i; + sqlite4_num_to_double(sqlite4VdbeNumValue(pMem), &rVal); + return rVal; +} + +/* +** Extract and return a numeric value from memory cell pMem. This call +** does not modify the contents or flags of *pMem in any way. +*/ +sqlite4_num sqlite4VdbeNumValue(Mem *pMem){ + if( pMem->flags & (MEM_Real|MEM_Int) ){ + return pMem->u.num; }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ - /* (double)0 In case of SQLITE4_OMIT_FLOATING_POINT... */ - double val = (double)0; - sqlite4AtoF(pMem->z, &val, pMem->n, pMem->enc); - return val; + int flags = SQLITE4_PREFIX_ONLY | SQLITE4_IGNORE_WHITESPACE | pMem->enc; + return sqlite4_num_from_text(pMem->z, pMem->n, flags, 0); }else{ - /* (double)0 In case of SQLITE4_OMIT_FLOATING_POINT... */ - return (double)0; + sqlite4_num zero = {0,0,0,0}; + return zero; } } /* ** The MEM structure is already a MEM_Real. Try to also make it a ** MEM_Int if we can. */ void sqlite4VdbeIntegerAffinity(Mem *pMem){ + i64 i; + int bLossy; + assert( pMem->flags & MEM_Real ); assert( (pMem->flags & MEM_RowSet)==0 ); assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - pMem->u.i = doubleToInt64(pMem->r); - - /* Only mark the value as an integer if - ** - ** (1) the round-trip conversion real->int->real is a no-op, and - ** (2) The integer is neither the largest nor the smallest - ** possible integer (ticket #3922) - ** - ** The second and third terms in the following conditional enforces - ** the second condition under the assumption that addition overflow causes - ** values to wrap around. On x86 hardware, the third term is always - ** true and could be omitted. But we leave it in because other - ** architectures might behave differently. - */ - if( pMem->r==(double)pMem->u.i && pMem->u.i>SMALLEST_INT64 - && ALWAYS(pMem->u.iflags |= MEM_Int; + i = sqlite4_num_to_int64(pMem->u.num, &bLossy); + if( bLossy==0 ){ + MemSetTypeFlag(pMem, MEM_Int); + pMem->u.num = sqlite4_num_from_int64(i); } } /* ** Convert pMem to type integer. Invalidate any prior representations. @@ -396,25 +373,14 @@ int sqlite4VdbeMemIntegerify(Mem *pMem){ assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( (pMem->flags & MEM_RowSet)==0 ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - pMem->u.i = sqlite4VdbeIntValue(pMem); - MemSetTypeFlag(pMem, MEM_Int); - return SQLITE4_OK; -} - -/* -** Convert pMem so that it is of type MEM_Real. -** Invalidate any prior representations. -*/ -int sqlite4VdbeMemRealify(Mem *pMem){ - assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - - pMem->r = sqlite4VdbeRealValue(pMem); - MemSetTypeFlag(pMem, MEM_Real); + if( (pMem->flags & MEM_Int)==0 ){ + pMem->u.num = sqlite4_num_from_int64(sqlite4VdbeIntValue(pMem)); + MemSetTypeFlag(pMem, MEM_Int); + } return SQLITE4_OK; } /* ** Convert pMem so that it has types MEM_Real or MEM_Int or both. @@ -424,19 +390,17 @@ ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ int sqlite4VdbeMemNumerify(Mem *pMem){ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ + int bReal = 0; + int flags = (pMem->enc | SQLITE4_PREFIX_ONLY | SQLITE4_IGNORE_WHITESPACE); + assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); - if( 0==sqlite4Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){ - MemSetTypeFlag(pMem, MEM_Int); - }else{ - pMem->r = sqlite4VdbeRealValue(pMem); - MemSetTypeFlag(pMem, MEM_Real); - sqlite4VdbeIntegerAffinity(pMem); - } + pMem->u.num = sqlite4_num_from_text(pMem->z, pMem->n, flags, &bReal); + MemSetTypeFlag(pMem, (bReal ? MEM_Real : MEM_Int)); } assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); pMem->flags &= ~(MEM_Str|MEM_Blob); return SQLITE4_OK; } @@ -460,11 +424,11 @@ ** Delete any previous value and set the value stored in *pMem to val, ** manifest type INTEGER. */ void sqlite4VdbeMemSetInt64(Mem *pMem, i64 val){ sqlite4VdbeMemRelease(pMem); - pMem->u.i = val; + pMem->u.num = sqlite4_num_from_int64(val); pMem->flags = MEM_Int; pMem->type = SQLITE4_INTEGER; } #ifndef SQLITE4_OMIT_FLOATING_POINT @@ -475,11 +439,11 @@ void sqlite4VdbeMemSetDouble(Mem *pMem, double val){ if( sqlite4IsNaN(val) ){ sqlite4VdbeMemSetNull(pMem); }else{ sqlite4VdbeMemRelease(pMem); - pMem->r = val; + pMem->u.num = sqlite4_num_from_double(val); pMem->flags = MEM_Real; pMem->type = SQLITE4_FLOAT; } } #endif @@ -728,38 +692,13 @@ /* If one value is a number and the other is not, the number is less. ** If both are numbers, compare as reals if one is a real, or as integers ** if both values are integers. */ if( combined_flags&(MEM_Int|MEM_Real) ){ - if( !(f1&(MEM_Int|MEM_Real)) ){ - return 1; - } - if( !(f2&(MEM_Int|MEM_Real)) ){ - return -1; - } - if( (f1 & f2 & MEM_Int)==0 ){ - double r1, r2; - if( (f1&MEM_Real)==0 ){ - r1 = (double)pMem1->u.i; - }else{ - r1 = pMem1->r; - } - if( (f2&MEM_Real)==0 ){ - r2 = (double)pMem2->u.i; - }else{ - r2 = pMem2->r; - } - if( r1r2 ) return 1; - return 0; - }else{ - assert( f1&MEM_Int ); - assert( f2&MEM_Int ); - if( pMem1->u.i < pMem2->u.i ) return -1; - if( pMem1->u.i > pMem2->u.i ) return 1; - return 0; - } + if( !(f1&(MEM_Int|MEM_Real)) ) return 1; + if( !(f2&(MEM_Int|MEM_Real)) ) return -1; + return (sqlite4_num_compare(pMem1->u.num, pMem2->u.num) - 2); } /* If one value is a string and the other is a blob, the string is less. ** If both are strings, compare using the collating functions. */ @@ -947,18 +886,11 @@ } }else if( op==TK_UMINUS ) { /* This branch happens for multiple negative signs. Ex: -(-5) */ if( SQLITE4_OK==sqlite4ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ sqlite4VdbeMemNumerify(pVal); - if( pVal->u.i==SMALLEST_INT64 ){ - pVal->flags &= MEM_Int; - pVal->flags |= MEM_Real; - pVal->r = (double)LARGEST_INT64; - }else{ - pVal->u.i = -pVal->u.i; - } - pVal->r = -pVal->r; + pVal->u.num = sqlite4_num_mul(pVal->u.num, sqlite4_num_from_int64(-1)); sqlite4ValueApplyAffinity(pVal, affinity, enc); } }else if( op==TK_NULL ){ pVal = sqlite4ValueNew(db); if( pVal==0 ) goto no_mem; Index: src/vdbetrace.c ================================================================== --- src/vdbetrace.c +++ src/vdbetrace.c @@ -117,14 +117,14 @@ nextIndex = idx + 1; assert( idx>0 && idx<=p->nVar ); pVar = &p->aVar[idx-1]; if( pVar->flags & MEM_Null ){ sqlite4StrAccumAppend(&out, "NULL", 4); - }else if( pVar->flags & MEM_Int ){ - sqlite4XPrintf(&out, "%lld", pVar->u.i); - }else if( pVar->flags & MEM_Real ){ - sqlite4XPrintf(&out, "%!.16g", pVar->r); + }else if( pVar->flags & (MEM_Int|MEM_Real) ){ + char aOut[30]; + sqlite4_num_to_text(pVar->u.num, aOut, (pVar->flags & MEM_Real)); + sqlite4XPrintf(&out, "%s", aOut); }else if( pVar->flags & MEM_Str ){ #ifndef SQLITE4_OMIT_UTF16 u8 enc = ENC(db); if( enc!=SQLITE4_UTF8 ){ Mem utf8; ADDED test/auth4.test Index: test/auth4.test ================================================================== --- /dev/null +++ test/auth4.test @@ -0,0 +1,131 @@ +# 2013 May 8 +# +# 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 contains tests for the sqlite4_authorizer_push() and +# sqlite4_authorizer_pop() API functions. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix auth4 + +ifcapable !auth { finish_test ; return } + +#-------------------------------------------------------------------- +# Test cases auth4-1.* test that when there are multiple authorizers +# on the stack, they are invoked in order from most to least recently +# added until all have been invoked or one of them returns other than +# SQLITE4_OK. +# +do_execsql_test 1.0 { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); +} + +proc auth_callback {id code z1 z2 z3 z4} { + if {$code == "SQLITE4_READ" && $z1=="t1" && $z2=="y"} { + incr ::NAUTH + return [lindex $::AUTH $id] + } + return SQLITE4_OK +} + +sqlite4_authorizer_push db {auth_callback 2} +sqlite4_authorizer_push db {auth_callback 1} +sqlite4_authorizer_push db {auth_callback 0} + +foreach {tn codes ncall res} { + 1 {SQLITE4_OK SQLITE4_OK SQLITE4_OK} 3 {0 {1 one 2 two}} + + 2 {SQLITE4_OK SQLITE4_OK SQLITE4_DENY} 3 {1 {access to t1.y is prohibited}} + 3 {SQLITE4_DENY SQLITE4_OK SQLITE4_OK} 1 {1 {access to t1.y is prohibited}} + 4 {SQLITE4_OK SQLITE4_DENY SQLITE4_OK} 2 {1 {access to t1.y is prohibited}} + + 5 {SQLITE4_OK SQLITE4_OK SQLITE4_IGNORE} 3 {0 {1 {} 2 {}}} + 6 {SQLITE4_IGNORE SQLITE4_OK SQLITE4_OK} 1 {0 {1 {} 2 {}}} + 7 {SQLITE4_OK SQLITE4_IGNORE SQLITE4_OK} 2 {0 {1 {} 2 {}}} + + 8 {SQLITE4_OK SQLITE4_OK SQLITE4_ALLOW} 3 {0 {1 one 2 two}} + 9 {SQLITE4_ALLOW SQLITE4_OK SQLITE4_OK} 1 {0 {1 one 2 two}} + 10 {SQLITE4_OK SQLITE4_ALLOW SQLITE4_OK} 2 {0 {1 one 2 two}} + +} { + db cache flush + + set ::AUTH $codes + set ::NAUTH 0 + + do_catchsql_test 1.$tn.1 { SELECT * FROM t1; } $res + do_test 1.$tn.2 { set ::NAUTH } $ncall +} + +sqlite4_authorizer_pop db +sqlite4_authorizer_pop db +sqlite4_authorizer_pop db + +#-------------------------------------------------------------------- +# Test cases auth4-2.* test that the push and pop operations seem to +# work correctly. +# +set ::STACK [list] +proc auth_callback {id code z1 z2 z3 z4} { + if {$code == "SQLITE4_READ" && $z1=="t1" && $z2=="y"} { + lappend ::AUTH $id + } + return SQLITE4_OK +} +proc test_stack {} { + set ::AUTH [list] + db eval { SELECT * FROM t1 } + set ::AUTH +} +proc push {id} { + set ::STACK [concat $id $::STACK] + sqlite4_authorizer_push db [list auth_callback $id] +} +proc pop {} { + set ::STACK [lrange $::STACK 1 end] + sqlite4_authorizer_pop db +} + +do_execsql_test 2.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 'one'); +} + +for {set i 1} {$i <= 100} {incr i} { + if { int(rand()*2.0) } { + pop + } else { + push [expr int(rand() * 500.0)] + } + do_test 2.$i { test_stack } $::STACK +} + +#-------------------------------------------------------------------- +# Test that sqlite4_authorizer_pop() returns an error if the stack is +# empty when it is called. +# +db close +sqlite4 db test.db + +do_test 3.1 { + sqlite4_authorizer_pop db +} {SQLITE4_ERROR} +do_test 3.2 { + sqlite4_authorizer_push db xyz + sqlite4_authorizer_pop db +} {SQLITE4_OK} + +finish_test + Index: test/cast.test ================================================================== --- test/cast.test +++ test/cast.test @@ -237,11 +237,11 @@ do_realnum_test cast-3.3 { execsql {SELECT CAST(9223372036854774800 AS real)} } 9.22337203685477e+18 do_test cast-3.4 { execsql {SELECT CAST(CAST(9223372036854774800 AS real) AS integer)} -} 9223372036854774784 +} 9223372036854774800 do_test cast-3.5 { execsql {SELECT CAST(-9223372036854774800 AS integer)} } -9223372036854774800 do_test cast-3.6 { execsql {SELECT CAST(-9223372036854774800 AS numeric)} @@ -249,11 +249,11 @@ do_realnum_test cast-3.7 { execsql {SELECT CAST(-9223372036854774800 AS real)} } -9.22337203685477e+18 do_test cast-3.8 { execsql {SELECT CAST(CAST(-9223372036854774800 AS real) AS integer)} -} -9223372036854774784 +} -9223372036854774800 do_test cast-3.11 { execsql {SELECT CAST('9223372036854774800' AS integer)} } 9223372036854774800 do_test cast-3.12 { execsql {SELECT CAST('9223372036854774800' AS numeric)} @@ -262,11 +262,11 @@ execsql {SELECT CAST('9223372036854774800' AS real)} } 9.22337203685477e+18 ifcapable long_double { do_test cast-3.14 { execsql {SELECT CAST(CAST('9223372036854774800' AS real) AS integer)} - } 9223372036854774784 + } 9223372036854774800 } do_test cast-3.15 { execsql {SELECT CAST('-9223372036854774800' AS integer)} } -9223372036854774800 do_test cast-3.16 { @@ -276,11 +276,11 @@ execsql {SELECT CAST('-9223372036854774800' AS real)} } -9.22337203685477e+18 ifcapable long_double { do_test cast-3.18 { execsql {SELECT CAST(CAST('-9223372036854774800' AS real) AS integer)} - } -9223372036854774784 + } -9223372036854774800 } if {[db eval {PRAGMA encoding}]=="UTF-8"} { do_test cast-3.21 { execsql {SELECT CAST(x'39323233333732303336383534373734383030' AS integer)} } 9223372036854774800 @@ -294,11 +294,11 @@ do_test cast-3.24 { execsql { SELECT CAST(CAST(x'39323233333732303336383534373734383030' AS real) AS integer) } - } 9223372036854774784 + } 9223372036854774800 } } do_test case-3.31 { execsql {SELECT CAST(NULL AS numeric)} } {{}} Index: test/num.test ================================================================== --- test/num.test +++ test/num.test @@ -86,6 +86,81 @@ sqlite4_num_to_text [sqlite4_num_div 2 1] } {2} do_test num-6.1.4 { sqlite4_num_to_text [sqlite4_num_div 22 10] } {2.2} + +#------------------------------------------------------------------------- +# The following test cases - num-7.* - test the sqlite4_num_from_double() +# API function. +# +foreach {tn in out} { + 1 1.0 {sign:0 e:0 m:1} + 2 -1.0 {sign:1 e:0 m:1} + 3 1.5 {sign:0 e:-1 m:15} + 4 -1.5 {sign:1 e:-1 m:15} + 5 0.15 {sign:0 e:-2 m:15} + 6 -0.15 {sign:1 e:-2 m:15} + 7 45.345687 {sign:0 e:-6 m:45345687} + 8 1000000000000000000 {sign:0 e:18 m:1} +} { + do_test num-7.1.$tn { + set res [sqlite4_num_from_double $in] + list [lindex $res 0] [lindex $res 2] [lindex $res 3] + } [list [lindex $out 0] [lindex $out 1] [lindex $out 2]] +} + +#------------------------------------------------------------------------- +# Test the boundary conditions in sqlite4_num_from_text() for parsing +# values that can fit in a signed 64-bit integer variable. And others. +# +foreach {tn in out} { + 0 9223372036854775806 {sign:0 approx:0 e:0 m:9223372036854775806} + 1 9223372036854775807 {sign:0 approx:0 e:0 m:9223372036854775807} + 2 -9223372036854775808 {sign:1 approx:0 e:0 m:9223372036854775808} + 3 -9223372036854775807 {sign:1 approx:0 e:0 m:9223372036854775807} + 4 -9223372036854775806 {sign:1 approx:0 e:0 m:9223372036854775806} +} { + do_test num-8.1.$tn { sqlite4_num_from_text $in } $out +} + +foreach {tn in out} { + 0 9223372036854775808 {sign:0 approx:1 e:1 m:922337203685477581} + 1 9223372036854775809 {sign:0 approx:1 e:1 m:922337203685477581} + 2 9223372036854775810 {sign:0 approx:0 e:1 m:922337203685477581} + 3 9223372036854775811 {sign:0 approx:1 e:1 m:922337203685477581} + + 4 -9223372036854775809 {sign:1 approx:1 e:1 m:922337203685477581} + 5 -9223372036854775810 {sign:1 approx:0 e:1 m:922337203685477581} + 6 -9223372036854775811 {sign:1 approx:1 e:1 m:922337203685477581} +} { + do_test num-8.2.$tn { sqlite4_num_from_text $in } $out +} + +foreach {tn in out} { + 0 2147483648 {sign:0 approx:0 e:0 m:2147483648} + 1 -2147483648 {sign:1 approx:0 e:0 m:2147483648} +} { + do_test num-8.3.$tn { sqlite4_num_from_text $in } $out +} + +#------------------------------------------------------------------------- +# Test parsing of values with decimal points. +# +foreach {tn in out} { + 0 1.5 {sign:0 approx:0 e:-1 m:15} + 1 1.005 {sign:0 approx:0 e:-3 m:1005} + 2 00000 {sign:0 approx:0 e:0 m:0} + 3 00.000 {sign:0 approx:0 e:-3 m:0} + 4 -1.005 {sign:1 approx:0 e:-3 m:1005} + 5.1 1 {sign:0 approx:0 e:0 m:1} + 5.2 1.0 {sign:0 approx:0 e:-1 m:10} + 5.3 1. {sign:0 approx:0 e:0 m:1} + 5.4 1e0 {sign:0 approx:0 e:0 m:1} +} { + do_test num-9.1.$tn { sqlite4_num_from_text $in } [list {*}$out] +} + +#------------------------------------------------------------------------- finish_test + + ADDED test/num2.test Index: test/num2.test ================================================================== --- /dev/null +++ test/num2.test @@ -0,0 +1,31 @@ +# 2013 May 29 +# +# 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. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix num2 + +do_execsql_test 1.1 { SELECT 1.0 } {1.0} +do_execsql_test 1.2 { SELECT typeof(1.0) } {real} +do_execsql_test 1.3 { SELECT cast(1.0 AS TEXT) } {1.0} +do_execsql_test 1.4 { SELECT cast((1.0+1.0) AS TEXT) } {2.0} + +do_execsql_test 1.5 { SELECT typeof(1.0+1.0) } {real} +do_execsql_test 1.6 { SELECT typeof(1.0*1.0) } {real} +do_execsql_test 1.7 { SELECT typeof(1.0/1.0) } {real} +do_execsql_test 1.8 { SELECT typeof(1.0-1.0) } {real} +do_execsql_test 1.8 { SELECT typeof(1.0%1.0) } {real} + +finish_test + + Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -183,11 +183,11 @@ notnull.test null.test printf.test quote.test - savepoint.test savepoint2.test savepoint5.test + savepoint.test savepoint5.test select1.test select2.test select3.test select4.test select5.test select6.test select7.test select8.test select9.test selectA.test selectB.test selectC.test Index: test/savepoint2.test ================================================================== --- test/savepoint2.test +++ test/savepoint2.test @@ -47,11 +47,11 @@ wal_check_journal_mode savepoint2-1.1 unset -nocomplain ::sig unset -nocomplain SQL -set iterations 20 +set iterations 2 set SQL(1) { DELETE FROM t3 WHERE random()%10!=0; INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; @@ -148,7 +148,5 @@ wal_check_journal_mode savepoint2-$ii.7 } unset -nocomplain ::sig unset -nocomplain SQL - -finish_test Index: test/simple.test ================================================================== --- test/simple.test +++ test/simple.test @@ -98,11 +98,10 @@ #------------------------------------------------------------------------- reset_db -breakpoint do_execsql_test 4.1 { CREATE TABLE t1(k PRIMARY KEY, v) } do_execsql_test 4.2 { CREATE INDEX i1 ON t1(v) } do_execsql_test 4.3 { SELECT * FROM sqlite_master @@ -1478,7 +1477,60 @@ } 1 do_execsql_test 75.3 { SELECT count(*) FROM t1 WHERE b = x'12345678' } 1 + +#------------------------------------------------------------------------- +# Real vs. integer values. +# +reset_db +do_execsql_test 76.1 { + CREATE TABLE t1(a REAL); + CREATE TABLE log(x); + CREATE TRIGGER BEFORE INSERT ON t1 BEGIN + INSERT INTO log VALUES('value = ' || new.a); + END; +} + +do_execsql_test 76.2 { INSERT INTO t1 VALUES(-23) } +do_execsql_test 76.3 { + SELECT * FROM log; +} {{value = -23.0}} + +do_execsql_test 76.4 { + CREATE TABLE t2(a REAL, str); +} +do_execsql_test 76.5 { + INSERT INTO t2 VALUES(0.0012345, ''); +} +do_execsql_test 76.6 { SELECT cast(a AS TEXT) FROM t2 } {0.0012345} + +#------------------------------------------------------------------------- +# Integer keys. +# +reset_db +do_execsql_test 77.1 { CREATE TABLE t1(x) } +do_test 77.2 { + for {set i 0} {$i < 99} {incr i} { + execsql { INSERT INTO t1 VALUES(NULL) } + } +} {} +do_execsql_test 77.3 { INSERT INTO t1 VALUES(NULL) } +do_execsql_test 77.4 { INSERT INTO t1 VALUES(NULL) } + +#------------------------------------------------------------------------- +# +reset_db +do_test 78.1 { + execsql { + CREATE TABLE t1 (id INTEGER PRIMARY KEY, v); + INSERT INTO t1 VALUES(42, 3); + } +} {} + +do_execsql_test 78.2 { + SELECT id, v FROM t1 WHERE id>1.5; +} {42 3} finish_test + Index: test/testInt.h ================================================================== --- test/testInt.h +++ test/testInt.h @@ -64,7 +64,10 @@ #define TESTMEM_CTRL_FAULTREPORT 62930003 sqlite4_mm *test_mm_debug(sqlite4_mm *p); sqlite4_mm *test_mm_faultsim(sqlite4_mm *p); +/* test_num.c */ +int Sqlitetest_num_init(Tcl_Interp *interp); + #endif Index: test/test_main.c ================================================================== --- test/test_main.c +++ test/test_main.c @@ -4138,211 +4138,13 @@ } sqlite4_test_control(SQLITE4_TESTCTRL_OPTIMIZATIONS, db, mask); return TCL_OK; } -#define NUM_FORMAT "sign:%d approx:%d e:%d m:%lld" - -/* Append a return value representing a sqlite4_num. -*/ -static void append_num_result( Tcl_Interp *interp, sqlite4_num A ){ - char buf[100]; - sprintf( buf, NUM_FORMAT, A.sign, A.approx, A.e, A.m ); - Tcl_AppendResult(interp, buf, 0); -} - -/* Convert a string either representing a sqlite4_num (listing its fields as -** returned by append_num_result) or that can be parsed as one. Invalid -** strings become NaN. -*/ -static sqlite4_num test_parse_num( char *arg ){ - sqlite4_num A; - int sign, approx, e; - if( sscanf( arg, NUM_FORMAT, &sign, &approx, &e, &A.m)==4 ){ - A.sign = sign; - A.approx = approx; - A.e = e; - return A; - } else { - return sqlite4_num_from_text(arg, -1, 0); - } -} - -/* Convert return values of sqlite4_num to strings that will be readable in -** the tests. -*/ -static char *describe_num_comparison( int code ){ - switch( code ){ - case 0: return "incomparable"; - case 1: return "lesser"; - case 2: return "equal"; - case 3: return "greater"; - default: return "error"; - } -} - -/* Compare two numbers A and B. Returns "incomparable", "lesser", "equal", -** "greater", or "error". -*/ -static int test_num_compare( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - sqlite4_num A, B; - int cmp; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " NUM NUM\"", 0); - return TCL_ERROR; - } - - A = test_parse_num( argv[1] ); - B = test_parse_num( argv[2] ); - cmp = sqlite4_num_compare(A, B); - Tcl_AppendResult( interp, describe_num_comparison( cmp ), 0); - return TCL_OK; -} - -/* Create a sqlite4_num from a string. The optional second argument specifies -** how many bytes may be read. -*/ -static int test_num_from_text( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - sqlite4_num A; - int len; - if( argc!=2 && argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " STRING\" or \"", argv[0], " STRING INTEGER\"", 0); - return TCL_ERROR; - } - - if( argc==3 ){ - if ( Tcl_GetInt(interp, argv[2], &len) ) return TCL_ERROR; - }else{ - len = -1; - } - - A = sqlite4_num_from_text( argv[1], len, 0 ); - append_num_result(interp, A); - return TCL_OK; -} - -static int test_num_to_text( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - char text[30]; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " NUM\"", 0); - return TCL_ERROR; - } - sqlite4_num_to_text( test_parse_num( argv[1] ), text ); - Tcl_AppendResult( interp, text, 0 ); - return TCL_OK; -} - -static int test_num_binary_op( - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv, /* Text of each argument */ - sqlite4_num (*op) (sqlite4_num, sqlite4_num) -){ - sqlite4_num A, B, R; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " NUM NUM\"", 0); - return TCL_ERROR; - } - A = test_parse_num(argv[1]); - B = test_parse_num(argv[2]); - R = op(A, B); - append_num_result(interp, R); - return TCL_OK; -} - -static int test_num_add( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - return test_num_binary_op( interp, argc, argv, sqlite4_num_add ); -} - -static int test_num_sub( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - return test_num_binary_op( interp, argc, argv, sqlite4_num_sub ); -} - -static int test_num_mul( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - return test_num_binary_op( interp, argc, argv, sqlite4_num_mul ); -} - -static int test_num_div( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - return test_num_binary_op( interp, argc, argv, sqlite4_num_div ); -} - -static int test_num_predicate( - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv, /* Text of each argument */ - int (*pred) (sqlite4_num) -){ - sqlite4_num A; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " NUM\"", 0); - return TCL_ERROR; - } - A = test_parse_num(argv[1]); - Tcl_AppendResult(interp, pred(A) ? "true" : "false", 0); - return TCL_OK; -} - -static int test_num_isinf( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - return test_num_predicate( interp, argc, argv, sqlite4_num_isinf ); -} - -static int test_num_isnan( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - char **argv /* Text of each argument */ -){ - return test_num_predicate( interp, argc, argv, sqlite4_num_isnan ); -} - void sqlite4TestInit(Tcl_Interp *interp){ Sqlitetest_auth_init(interp); + Sqlitetest_num_init(interp); } /* ** Register commands with the TCL interpreter. */ @@ -4393,19 +4195,10 @@ { "sqlite_delete_collation", (Tcl_CmdProc*)delete_collation }, { "sqlite4_get_autocommit", (Tcl_CmdProc*)get_autocommit }, { "sqlite4_stack_used", (Tcl_CmdProc*)test_stack_used }, { "printf", (Tcl_CmdProc*)test_printf }, { "sqlite4IoTrace", (Tcl_CmdProc*)test_io_trace }, - { "sqlite4_num_compare", (Tcl_CmdProc*)test_num_compare }, - { "sqlite4_num_from_text", (Tcl_CmdProc*)test_num_from_text }, - { "sqlite4_num_to_text", (Tcl_CmdProc*)test_num_to_text }, - { "sqlite4_num_add", (Tcl_CmdProc*)test_num_add }, - { "sqlite4_num_sub", (Tcl_CmdProc*)test_num_sub }, - { "sqlite4_num_mul", (Tcl_CmdProc*)test_num_mul }, - { "sqlite4_num_div", (Tcl_CmdProc*)test_num_div }, - { "sqlite4_num_isinf", (Tcl_CmdProc*)test_num_isinf }, - { "sqlite4_num_isnan", (Tcl_CmdProc*)test_num_isnan }, }; static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData;