Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -7037,10 +7037,11 @@ { "sqlite3_stmt_scanstatus_reset", test_stmt_scanstatus_reset, 0 }, #endif }; static int bitmask_size = sizeof(Bitmask)*8; + static int longdouble_size = sizeof(LONGDOUBLE_TYPE); int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; @@ -7137,10 +7138,12 @@ (char*)&sqlite3_temp_directory, TCL_LINK_STRING); Tcl_LinkVar(interp, "sqlite_data_directory", (char*)&sqlite3_data_directory, TCL_LINK_STRING); Tcl_LinkVar(interp, "bitmask_size", (char*)&bitmask_size, TCL_LINK_INT|TCL_LINK_READ_ONLY); + Tcl_LinkVar(interp, "longdouble_size", + (char*)&longdouble_size, TCL_LINK_INT|TCL_LINK_READ_ONLY); Tcl_LinkVar(interp, "sqlite_sync_count", (char*)&sqlite3_sync_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_fullsync_count", (char*)&sqlite3_fullsync_count, TCL_LINK_INT); #if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_TEST) Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -3632,10 +3632,38 @@ int c = memcmp(pB1->z, pB2->z, pB1->n>pB2->n ? pB2->n : pB1->n); if( c ) return c; return pB1->n - pB2->n; } +/* +** Do a comparison between a 64-bit signed integer and a 64-bit floating-point +** number. Return negative, zero, or positive if the first (i64) is less than, +** equal to, or greater than the second (double). +*/ +static int sqlite3IntFloatCompare(i64 i, double r){ + if( sizeof(LONGDOUBLE_TYPE)>8 ){ + LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; + if( xr ) return +1; + return 0; + }else{ + i64 y; + double s; + if( r<-9223372036854775808.0 ) return +1; + if( r>9223372036854775807.0 ) return -1; + y = (i64)r; + if( iy ){ + if( y==SMALLEST_INT64 && r>0.0 ) return -1; + return +1; + } + s = (double)i; + if( sr ) return +1; + return 0; + } +} /* ** Compare the values contained by the two memory cells, returning ** negative, zero or positive if pMem1 is less than, equal to, or greater ** than pMem2. Sorting order is NULL's first, followed by numbers (integers @@ -3658,38 +3686,38 @@ */ if( combined_flags&MEM_Null ){ return (f2&MEM_Null) - (f1&MEM_Null); } - /* 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) ){ - double r1, r2; - if( (f1 & f2 & MEM_Int)!=0 ){ - if( pMem1->u.i < pMem2->u.i ) return -1; - if( pMem1->u.i > pMem2->u.i ) return 1; - return 0; - } - if( (f1&MEM_Real)!=0 ){ - r1 = pMem1->u.r; - }else if( (f1&MEM_Int)!=0 ){ - r1 = (double)pMem1->u.i; - }else{ - return 1; - } - if( (f2&MEM_Real)!=0 ){ - r2 = pMem2->u.r; - }else if( (f2&MEM_Int)!=0 ){ - r2 = (double)pMem2->u.i; - }else{ - return -1; - } - if( r1r2 ) return 1; - return 0; + /* At least one of the two values is a number + */ + if( combined_flags&(MEM_Int|MEM_Real) ){ + if( (f1 & f2 & MEM_Int)!=0 ){ + if( pMem1->u.i < pMem2->u.i ) return -1; + if( pMem1->u.i > pMem2->u.i ) return +1; + return 0; + } + if( (f1 & f2 & MEM_Real)!=0 ){ + if( pMem1->u.r < pMem2->u.r ) return -1; + if( pMem1->u.r > pMem2->u.r ) return +1; + return 0; + } + if( (f1&MEM_Int)!=0 ){ + if( (f2&MEM_Real)!=0 ){ + return sqlite3IntFloatCompare(pMem1->u.i, pMem2->u.r); + }else{ + return -1; + } + } + if( (f1&MEM_Real)!=0 ){ + if( (f2&MEM_Int)!=0 ){ + return -sqlite3IntFloatCompare(pMem2->u.i, pMem1->u.r); + }else{ + return -1; + } + } + return +1; } /* 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. */ @@ -3836,17 +3864,12 @@ if( serial_type>=10 ){ rc = +1; }else if( serial_type==0 ){ rc = -1; }else if( serial_type==7 ){ - double rhs = (double)pRhs->u.i; sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); - if( mem1.u.rrhs ){ - rc = +1; - } + rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); }else{ i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); i64 rhs = pRhs->u.i; if( lhsu.r; - double lhs; sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); if( serial_type==7 ){ - lhs = mem1.u.r; + if( mem1.u.ru.r ){ + rc = -1; + }else if( mem1.u.r>pRhs->u.r ){ + rc = +1; + } }else{ - lhs = (double)mem1.u.i; - } - if( lhsrhs ){ - rc = +1; + rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); } } } /* RHS is a string */ @@ -4086,10 +4106,11 @@ ){ const u8 *aKey1 = (const u8*)pKey1; int serial_type; int res; + assert( pPKey2->aMem[0].flags & MEM_Str ); vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); getVarint32(&aKey1[1], serial_type); if( serial_type<12 ){ res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ }else if( !(serial_type & 0x01) ){ Index: test/atof1.test ================================================================== --- test/atof1.test +++ test/atof1.test @@ -13,11 +13,11 @@ # set testdir [file dirname $argv0] source $testdir/tester.tcl -if {![info exists __GNUC__] || [regexp arm $tcl_platform(machine)]} { +if {$::longdouble_size<=8} { finish_test return } expr srand(1) ADDED test/numindex1.test Index: test/numindex1.test ================================================================== --- /dev/null +++ test/numindex1.test @@ -0,0 +1,79 @@ +# 2015-11-05 +# +# 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 tests for indexes on large numeric values. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + + +# Test cases from Zsbán Ambrus: +# +do_execsql_test numindex1-1.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE INDEX t1b ON t1(b); + INSERT INTO t1(a,b) VALUES(100, 356282677878746339); + INSERT INTO t1(a,b) VALUES(50, 356282677878746339.0); + INSERT INTO t1(a,b) VALUES(0, 356282677878746340); + DELETE FROM t1 WHERE a=50; + PRAGMA integrity_check; +} {ok} + +do_execsql_test numindex1-1.2 { + CREATE TABLE t2(a,b); + INSERT INTO t2(a,b) VALUES('b', 1<<58), + ('c', (1<<58)+1e-7), ('d', (1<<58)+1); + SELECT a, b, typeof(b), '|' FROM t2 ORDER BY +a; +} {b 288230376151711744 integer | c 2.88230376151712e+17 real | d 288230376151711745 integer |} + +do_execsql_test numindex1-1.3 { + SELECT x.a || CASE WHEN x.b==y.b THEN '==' ELSE '<>' END || y.a + FROM t2 AS x, t2 AS y + ORDER BY +x.a, +x.b; +} {b==b b==c b<>d c==b c==c c<>d d<>b d<>c d==d} + +# New test cases +# +do_execsql_test numindex1-2.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY,b); + CREATE INDEX t1b ON t1(b); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) + INSERT INTO t1(a,b) SELECT x, 10000000000000004.0 FROM c + WHERE x NOT IN (23,37); + INSERT INTO t1(a,b) VALUES(23,10000000000000005); + INSERT INTO t1(a,b) VALUES(37,10000000000000003); + DELETE FROM t1 WHERE a NOT IN (23,37); + PRAGMA integrity_check; +} {ok} + +do_execsql_test numindex1-3.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY,b); + CREATE INDEX t1b ON t1(b); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<20) + INSERT INTO t1(a,b) SELECT x, 100000000000000005.0 + FROM c WHERE x NOT IN (3,5,7,11,13,17,19); + INSERT INTO t1(a,b) VALUES(3,100000000000000005); + INSERT INTO t1(a,b) VALUES(5,100000000000000000); + INSERT INTO t1(a,b) VALUES(7,100000000000000008); + INSERT INTO t1(a,b) VALUES(11,100000000000000006); + INSERT INTO t1(a,b) VALUES(13,100000000000000001); + INSERT INTO t1(a,b) VALUES(17,100000000000000004); + INSERT INTO t1(a,b) VALUES(19,100000000000000003); + PRAGMA integrity_check; +} {ok} + +do_execsql_test numindex1-3.2 { + SELECT a FROM t1 ORDER BY b; +} {1 2 4 5 6 8 9 10 12 14 15 16 18 20 13 19 17 3 11 7} + +finish_test