Move the tointeger() and toreal() functions out of core and make them into

a run-time loadable extension.

FossilOrigin-Name: 9f66dd7e3790c04f0ab724419f5381bd21f9ebad
This commit is contained in:
drh 2013-10-14 21:14:42 +00:00
parent 362d21614e
commit 5f8cdac620
9 changed files with 525 additions and 156 deletions

View File

@ -396,6 +396,7 @@ TESTSRC += \
$(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/wholenumber.c
# Source code to the library files needed by the test fixture

View File

@ -782,6 +782,7 @@ TESTEXT = \
$(TOP)\ext\misc\percentile.c \
$(TOP)\ext\misc\regexp.c \
$(TOP)\ext\misc\spellfix.c \
$(TOP)\ext\misc\totype.c \
$(TOP)\ext\misc\wholenumber.c

503
ext/misc/totype.c Normal file
View File

@ -0,0 +1,503 @@
/*
** 2013-10-14
**
** 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 SQLite extension implements functions tointeger(X) and toreal(X).
**
** If X is an integer, real, or string value that can be
** losslessly represented as an integer, then tointeger(X)
** returns the corresponding integer value.
** If X is an 8-byte BLOB then that blob is interpreted as
** a signed two-compliment little-endian encoding of an integer
** and tointeger(X) returns the corresponding integer value.
** Otherwise tointeger(X) return NULL.
**
** If X is an integer, real, or string value that can be
** convert into a real number, preserving at least 15 digits
** of precision, then toreal(X) returns the corresponding real value.
** If X is an 8-byte BLOB then that blob is interpreted as
** a 64-bit IEEE754 big-endian floating point value
** and toreal(X) returns the corresponding real value.
** Otherwise toreal(X) return NULL.
**
** Note that tointeger(X) of an 8-byte BLOB assumes a little-endian
** encoding whereas toreal(X) of an 8-byte BLOB assumes a big-endian
** encoding.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
/*
** Determine if this is running on a big-endian or little-endian
** processor
*/
#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
|| defined(__x86_64) || defined(__x86_64__)
# define SQLITE_BIGENDIAN 0
# define SQLITE_LITTLEENDIAN 1
#else
const int totype_one = 1;
# define SQLITE_BIGENDIAN (*(char *)(&totype_one)==0)
# define SQLITE_LITTLEENDIAN (*(char *)(&totype_one)==1)
#endif
/*
** Constants for the largest and smallest possible 64-bit signed integers.
** These macros are designed to work correctly on both 32-bit and 64-bit
** compilers.
*/
#define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
/*
** Return TRUE if character c is a whitespace character
*/
static int totypeIsspace(unsigned char c){
return c==' ' || c=='\t' || c=='\n' || c=='\r';
}
/*
** Return TRUE if character c is a digit
*/
static int totypeIsdigit(unsigned char c){
return c>='0' && c<='9';
}
/*
** Compare the 19-character string zNum against the text representation
** value 2^63: 9223372036854775808. Return negative, zero, or positive
** if zNum is less than, equal to, or greater than the string.
** Note that zNum must contain exactly 19 characters.
**
** Unlike memcmp() this routine is guaranteed to return the difference
** in the values of the last digit if the only difference is in the
** last digit. So, for example,
**
** totypeCompare2pow63("9223372036854775800", 1)
**
** will return -8.
*/
static int totypeCompare2pow63(const char *zNum){
int c = 0;
int i;
/* 012345678901234567 */
const char *pow63 = "922337203685477580";
for(i=0; c==0 && i<18; i++){
c = (zNum[i]-pow63[i])*10;
}
if( c==0 ){
c = zNum[18] - '8';
}
return c;
}
/*
** Convert zNum to a 64-bit signed integer.
**
** If the zNum value is representable as a 64-bit twos-complement
** integer, then write that value into *pNum and return 0.
**
** If zNum is exactly 9223372036854665808, return 2. This special
** case is broken out because while 9223372036854665808 cannot be a
** signed 64-bit integer, its negative -9223372036854665808 can be.
**
** If zNum is too big for a 64-bit integer and is not
** 9223372036854665808 or if zNum contains any non-numeric text,
** then return 1.
*/
static int totypeAtoi64(const char *zNum, sqlite3_int64 *pNum, int length){
sqlite3_uint64 u = 0;
int neg = 0; /* assume positive */
int i;
int c = 0;
int nonNum = 0;
const char *zStart;
const char *zEnd = zNum + length;
while( zNum<zEnd && totypeIsspace(*zNum) ) zNum++;
if( zNum<zEnd ){
if( *zNum=='-' ){
neg = 1;
zNum++;
}else if( *zNum=='+' ){
zNum++;
}
}
zStart = zNum;
while( zNum<zEnd && zNum[0]=='0' ){ zNum++; } /* Skip leading zeros. */
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i++){
u = u*10 + c - '0';
}
if( u>LARGEST_INT64 ){
*pNum = SMALLEST_INT64;
}else if( neg ){
*pNum = -(sqlite3_int64)u;
}else{
*pNum = (sqlite3_int64)u;
}
if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19 || nonNum ){
/* zNum is empty or contains non-numeric text or is longer
** than 19 digits (thus guaranteeing that it is too large) */
return 1;
}else if( i<19 ){
/* Less than 19 digits, so we know that it fits in 64 bits */
assert( u<=LARGEST_INT64 );
return 0;
}else{
/* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
c = totypeCompare2pow63(zNum);
if( c<0 ){
/* zNum is less than 9223372036854775808 so it fits */
assert( u<=LARGEST_INT64 );
return 0;
}else if( c>0 ){
/* zNum is greater than 9223372036854775808 so it overflows */
return 1;
}else{
/* zNum is exactly 9223372036854775808. Fits if negative. The
** special case 2 overflow if positive */
assert( u-1==LARGEST_INT64 );
assert( (*pNum)==SMALLEST_INT64 );
return neg ? 0 : 2;
}
}
}
/*
** The string z[] is an text representation of a real number.
** Convert this string to a double and write it into *pResult.
**
** The string z[] is length bytes in length (bytes, not characters) and
** uses the encoding enc. The string is not necessarily zero-terminated.
**
** Return TRUE if the result is a valid real number (or integer) and FALSE
** if the string is empty or contains extraneous text. Valid numbers
** are in one of these formats:
**
** [+-]digits[E[+-]digits]
** [+-]digits.[digits][E[+-]digits]
** [+-].digits[E[+-]digits]
**
** Leading and trailing whitespace is ignored for the purpose of determining
** validity.
**
** If some prefix of the input string is a valid number, this routine
** returns FALSE but it still converts the prefix and writes the result
** into *pResult.
*/
static int totypeAtoF(const char *z, double *pResult, int length){
const char *zEnd = z + length;
/* sign * significand * (10 ^ (esign * exponent)) */
int sign = 1; /* sign of significand */
sqlite3_int64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int eValid = 1; /* True exponent is either not used or is well-formed */
double result;
int nDigits = 0;
int nonNum = 0;
*pResult = 0.0; /* Default return value, in case of an error */
/* skip leading spaces */
while( z<zEnd && totypeIsspace(*z) ) z++;
if( z>=zEnd ) return 0;
/* get sign of significand */
if( *z=='-' ){
sign = -1;
z++;
}else if( *z=='+' ){
z++;
}
/* skip leading zeroes */
while( z<zEnd && z[0]=='0' ) z++, nDigits++;
/* copy max significant digits to significand */
while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
z++, nDigits++;
}
/* skip non-significant significand digits
** (increase exponent by d to shift decimal left) */
while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++, d++;
if( z>=zEnd ) goto totype_atof_calc;
/* if decimal point is present */
if( *z=='.' ){
z++;
/* copy digits from after decimal to significand
** (decrease exponent by d to shift decimal right) */
while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
z++, nDigits++, d--;
}
/* skip non-significant digits */
while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++;
}
if( z>=zEnd ) goto totype_atof_calc;
/* if exponent is present */
if( *z=='e' || *z=='E' ){
z++;
eValid = 0;
if( z>=zEnd ) goto totype_atof_calc;
/* get sign of exponent */
if( *z=='-' ){
esign = -1;
z++;
}else if( *z=='+' ){
z++;
}
/* copy digits to exponent */
while( z<zEnd && totypeIsdigit(*z) ){
e = e<10000 ? (e*10 + (*z - '0')) : 10000;
z++;
eValid = 1;
}
}
/* skip trailing spaces */
if( nDigits && eValid ){
while( z<zEnd && totypeIsspace(*z) ) z++;
}
totype_atof_calc:
/* adjust exponent by d, and update sign */
e = (e*esign) + d;
if( e<0 ) {
esign = -1;
e *= -1;
} else {
esign = 1;
}
/* if 0 significand */
if( !s ) {
/* In the IEEE 754 standard, zero is signed.
** Add the sign if we've seen at least one digit */
result = (sign<0 && nDigits) ? -(double)0 : (double)0;
} else {
/* attempt to reduce exponent */
if( esign>0 ){
while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
}else{
while( !(s%10) && e>0 ) e--,s/=10;
}
/* adjust the sign of significand */
s = sign<0 ? -s : s;
/* if exponent, scale significand as appropriate
** and store in result. */
if( e ){
double scale = 1.0;
/* attempt to handle extremely small/large numbers better */
if( e>307 && e<342 ){
while( e%308 ) { scale *= 1.0e+1; e -= 1; }
if( esign<0 ){
result = s / scale;
result /= 1.0e+308;
}else{
result = s * scale;
result *= 1.0e+308;
}
}else if( e>=342 ){
if( esign<0 ){
result = 0.0*s;
}else{
result = 1e308*1e308*s; /* Infinity */
}
}else{
/* 1.0e+22 is the largest power of 10 than can be
** represented exactly. */
while( e%22 ) { scale *= 1.0e+1; e -= 1; }
while( e>0 ) { scale *= 1.0e+22; e -= 22; }
if( esign<0 ){
result = s / scale;
}else{
result = s * scale;
}
}
} else {
result = (double)s;
}
}
/* store the result */
*pResult = result;
/* return true if number and no extra non-whitespace chracters after */
return z>=zEnd && nDigits>0 && eValid && nonNum==0;
}
/*
** tointeger(X): If X is any value (integer, double, blob, or string) that
** can be losslessly converted into an integer, then make the conversion and
** return the result. Otherwise, return NULL.
*/
static void tointegerFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( argc==1 );
(void)argc;
switch( sqlite3_value_type(argv[0]) ){
case SQLITE_FLOAT: {
double rVal = sqlite3_value_double(argv[0]);
sqlite3_int64 iVal = (sqlite3_int64)rVal;
if( rVal==(double)iVal ){
sqlite3_result_int64(context, iVal);
}
break;
}
case SQLITE_INTEGER: {
sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
break;
}
case SQLITE_BLOB: {
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
if( zBlob ){
int nBlob = sqlite3_value_bytes(argv[0]);
if( nBlob==sizeof(sqlite3_int64) ){
sqlite3_int64 iVal;
if( SQLITE_BIGENDIAN ){
int i;
unsigned char zBlobRev[sizeof(sqlite3_int64)];
for(i=0; i<sizeof(sqlite3_int64); i++){
zBlobRev[i] = zBlob[sizeof(sqlite3_int64)-1-i];
}
memcpy(&iVal, zBlobRev, sizeof(sqlite3_int64));
}else{
memcpy(&iVal, zBlob, sizeof(sqlite3_int64));
}
sqlite3_result_int64(context, iVal);
}
}
break;
}
case SQLITE_TEXT: {
const unsigned char *zStr = sqlite3_value_text(argv[0]);
if( zStr ){
int nStr = sqlite3_value_bytes(argv[0]);
if( nStr && !totypeIsspace(zStr[0]) ){
sqlite3_int64 iVal;
if( !totypeAtoi64((const char*)zStr, &iVal, nStr) ){
sqlite3_result_int64(context, iVal);
}
}
}
break;
}
default: {
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
break;
}
}
}
/*
** toreal(X): If X is any value (integer, double, blob, or string) that can
** be losslessly converted into a real number, then do so and return that
** real number. Otherwise return NULL.
*/
#if defined(_MSC_VER)
#pragma optimize("", off)
#endif
static void torealFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( argc==1 );
(void)argc;
switch( sqlite3_value_type(argv[0]) ){
case SQLITE_FLOAT: {
sqlite3_result_double(context, sqlite3_value_double(argv[0]));
break;
}
case SQLITE_INTEGER: {
sqlite3_int64 iVal = sqlite3_value_int64(argv[0]);
double rVal = (double)iVal;
if( iVal==(sqlite3_int64)rVal ){
sqlite3_result_double(context, rVal);
}
break;
}
case SQLITE_BLOB: {
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
if( zBlob ){
int nBlob = sqlite3_value_bytes(argv[0]);
if( nBlob==sizeof(double) ){
double rVal;
if( SQLITE_LITTLEENDIAN ){
int i;
unsigned char zBlobRev[sizeof(double)];
for(i=0; i<sizeof(double); i++){
zBlobRev[i] = zBlob[sizeof(double)-1-i];
}
memcpy(&rVal, zBlobRev, sizeof(double));
}else{
memcpy(&rVal, zBlob, sizeof(double));
}
sqlite3_result_double(context, rVal);
}
}
break;
}
case SQLITE_TEXT: {
const unsigned char *zStr = sqlite3_value_text(argv[0]);
if( zStr ){
int nStr = sqlite3_value_bytes(argv[0]);
if( nStr && !totypeIsspace(zStr[0]) && !totypeIsspace(zStr[nStr-1]) ){
double rVal;
if( totypeAtoF((const char*)zStr, &rVal, nStr) ){
sqlite3_result_double(context, rVal);
return;
}
}
}
break;
}
default: {
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
break;
}
}
}
#if defined(_MSC_VER)
#pragma optimize("", on)
#endif
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_totype_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
tointegerFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
torealFunc, 0, 0);
}
return rc;
}

View File

@ -279,6 +279,7 @@ TESTSRC += \
$(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/wholenumber.c \
$(TOP)/ext/misc/vfslog.c

View File

@ -1,9 +1,9 @@
C Fix\sa\scrash\sin\sFTS\sincremental\sphrase\sprocessing\sthat\scan\soccur\sif\sthe\ssecond\sor\ssubsequent\stoken\sis\smuch\smore\scommon\sin\sthe\sdataset\sthan\sthe\sfirst.
D 2013-10-14T20:30:51.215
C Move\sthe\stointeger()\sand\storeal()\sfunctions\sout\sof\score\sand\smake\sthem\sinto\na\srun-time\sloadable\sextension.
D 2013-10-14T21:14:42.543
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in e2d28ec95bd17ab4f3b6ee40b7102e9d7a0857b9
F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc d72be28ba13ec3986ac5791a6549036a84dc5903
F Makefile.msc ec5d662ed5a15ff819928c0495017af13910d7b6
F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION a8d1f6839521130dc73c5408cdd24bcfd791df34
@ -115,6 +115,7 @@ F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
F ext/misc/spellfix.c 5e1d547e9a2aed13897fa91bac924333f62fd2d9
F ext/misc/totype.c 368c089adfeabfe3de5fd4b8c581df53743cf6c0
F ext/misc/vfslog.c 1abb192d8d4bd323adbddec0c024580496b51b7a
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
@ -140,7 +141,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 814d3de5ec227817ff2ad26cbc73159c968a2a58
F main.mk 2e01504061f618db804812143a9e9ec45a66ae70
F main.mk c6a433cb334bbb019625c137ab5d5e7568b47cff
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh d2a981497b404d6498f5ff3e3b1f3816bdfcb338
@ -176,7 +177,7 @@ F src/delete.c 45788c5e48734f2af4acd75a876466e5b9838e34
F src/expr.c e7338ccffdc391c53ba2d51c5eb6a2f5299e040e
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 5dc10cbaa355753903cd2a64da040f948997ebf8
F src/func.c 5fb4103cc5fd2920696e0a263e6a56a2844ab35d
F src/func.c 2c47b65e6e00e3e9374942f28254faf8adafe398
F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
@ -227,7 +228,7 @@ F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c 3b5f3716e320480659239abe887164521c575d83
F src/test1.c 26226cfd2b6dc3f77d2eb27f07ffcf236b4e728b
F src/test1.c 5757066e503a8ed51313cb3a5d9bcdcced2991a9
F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
@ -569,7 +570,7 @@ F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test c7e80a44eebac8604397eb2ad83d0d5d9d541237
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
F test/func3.test dbccee9133cfef1473c59ec07b5f0262b9d72f9a
F test/func4.test 142490571f2e7ee6c3c5a65f24cad3f8342c02a2
F test/func4.test cb3f888a1cafad195a1f8d396875bdb11dc4faf9
F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74
F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6
F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
@ -1124,7 +1125,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
P a0f7cbc068416cf55b86056f2ce7ee505c6cc3ea
R 5f9ee33bea159851b0bd3b4df54def2b
U dan
Z 1fee4cc09a93b480a53b5d3c883e606f
P 0bf438fc30582a08fddfc3cec49366ee17ae2abe
R ade7979aa97b3511bc21cfd9a8979ec2
U drh
Z 602799c8fb404ec8c5a4601c8b4e8f8d

View File

@ -1 +1 @@
0bf438fc30582a08fddfc3cec49366ee17ae2abe
9f66dd7e3790c04f0ab724419f5381bd21f9ebad

View File

@ -965,145 +965,6 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}
}
/*
** tointeger(X): If X is any value (integer, double, blob, or string) that
** can be losslessly converted into an integer, then make the conversion and
** return the result. Otherwise, return NULL.
*/
static void tointegerFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( argc==1 );
UNUSED_PARAMETER(argc);
switch( sqlite3_value_type(argv[0]) ){
case SQLITE_FLOAT: {
double rVal = sqlite3_value_double(argv[0]);
i64 iVal = (i64)rVal;
if( rVal==(double)iVal ){
sqlite3_result_int64(context, iVal);
}
break;
}
case SQLITE_INTEGER: {
sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
break;
}
case SQLITE_BLOB: {
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
if( zBlob ){
int nBlob = sqlite3_value_bytes(argv[0]);
if( nBlob==sizeof(i64) ){
i64 iVal;
if( SQLITE_BIGENDIAN ){
int i;
unsigned char *zBlobRev = contextMalloc(context, nBlob);
if( !zBlobRev ) break;
for(i=0; i<nBlob; i++) zBlobRev[i] = zBlob[nBlob-1-i];
memcpy(&iVal, zBlobRev, sizeof(i64));
sqlite3_free(zBlobRev);
}else{
memcpy(&iVal, zBlob, sizeof(i64));
}
sqlite3_result_int64(context, iVal);
}
}
break;
}
case SQLITE_TEXT: {
const unsigned char *zStr = sqlite3_value_text(argv[0]);
if( zStr ){
int nStr = sqlite3_value_bytes(argv[0]);
if( nStr && !sqlite3Isspace(zStr[0]) ){
i64 iVal;
if( !sqlite3Atoi64((const char*)zStr, &iVal, nStr, SQLITE_UTF8) ){
sqlite3_result_int64(context, iVal);
}
}
}
break;
}
default: {
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
break;
}
}
}
/*
** toreal(X): If X is any value (integer, double, blob, or string) that can
** be losslessly converted into a real number, then do so and return that
** real number. Otherwise return NULL.
*/
#if defined(_MSC_VER)
#pragma optimize("", off)
#endif
static void torealFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( argc==1 );
UNUSED_PARAMETER(argc);
switch( sqlite3_value_type(argv[0]) ){
case SQLITE_FLOAT: {
sqlite3_result_double(context, sqlite3_value_double(argv[0]));
break;
}
case SQLITE_INTEGER: {
i64 iVal = sqlite3_value_int64(argv[0]);
double rVal = (double)iVal;
if( iVal==(i64)rVal ){
sqlite3_result_double(context, rVal);
}
break;
}
case SQLITE_BLOB: {
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
if( zBlob ){
int nBlob = sqlite3_value_bytes(argv[0]);
if( nBlob==sizeof(double) ){
double rVal;
if( SQLITE_LITTLEENDIAN ){
int i;
unsigned char *zBlobRev = contextMalloc(context, nBlob);
if( !zBlobRev ) break;
for(i=0; i<nBlob; i++) zBlobRev[i] = zBlob[nBlob-1-i];
memcpy(&rVal, zBlobRev, sizeof(double));
sqlite3_free(zBlobRev);
}else{
memcpy(&rVal, zBlob, sizeof(double));
}
sqlite3_result_double(context, rVal);
}
}
break;
}
case SQLITE_TEXT: {
const unsigned char *zStr = sqlite3_value_text(argv[0]);
if( zStr ){
int nStr = sqlite3_value_bytes(argv[0]);
if( nStr && !sqlite3Isspace(zStr[0]) && !sqlite3Isspace(zStr[nStr-1]) ){
double rVal;
if( sqlite3AtoF((const char*)zStr, &rVal, nStr, SQLITE_UTF8) ){
sqlite3_result_double(context, rVal);
return;
}
}
}
break;
}
default: {
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
break;
}
}
}
#if defined(_MSC_VER)
#pragma optimize("", on)
#endif
/*
** The unicode() function. Return the integer unicode code-point value
** for the first character of the input string.
@ -1814,8 +1675,6 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
FUNCTION(quote, 1, 0, 0, quoteFunc ),
FUNCTION(tointeger, 1, 0, 0, tointegerFunc ),
FUNCTION(toreal, 1, 0, 0, torealFunc ),
FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
FUNCTION(changes, 0, 0, 0, changes ),
FUNCTION(total_changes, 0, 0, 0, total_changes ),

View File

@ -6160,6 +6160,7 @@ static int tclLoadStaticExtensionCmd(
extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
static const struct {
const char *zExtName;
@ -6173,6 +6174,7 @@ static int tclLoadStaticExtensionCmd(
{ "percentile", sqlite3_percentile_init },
{ "regexp", sqlite3_regexp_init },
{ "spellfix", sqlite3_spellfix_init },
{ "totype", sqlite3_totype_init },
{ "wholenumber", sqlite3_wholenumber_init },
};
sqlite3 *db;

View File

@ -19,9 +19,10 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
set saved_tcl_precision $tcl_precision
set tcl_precision 0
load_static_extension db totype
set highPrecision(1) [expr \
{[memdbsql {SELECT tointeger(9223372036854775807 + 1);}] eq {{}}}]
{[db eval {SELECT tointeger(9223372036854775807 + 1);}] eq {{}}}]
do_execsql_test func4-1.1 {
SELECT tointeger(NULL);
@ -195,7 +196,7 @@ do_execsql_test func4-1.55 {
ifcapable floatingpoint {
set highPrecision(2) [expr \
{[memdbsql {SELECT toreal(-9223372036854775808 + 1);}] eq {{}}}]
{[db eval {SELECT toreal(-9223372036854775808 + 1);}] eq {{}}}]
do_execsql_test func4-2.1 {
SELECT toreal(NULL);