diff options
author | drh <drh@noemail.net> | 2013-10-14 21:14:42 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2013-10-14 21:14:42 +0000 |
commit | 5f8cdac620fbf61fbdebd173f18b06b7f6f1906f (patch) | |
tree | e6a4f36ada821bb2629913b96f706dbe866ffdf8 | |
parent | 362d21614e22ed56cbffd4f786965aac8ddabdcd (diff) | |
download | sqlite-5f8cdac620fbf61fbdebd173f18b06b7f6f1906f.tar.gz sqlite-5f8cdac620fbf61fbdebd173f18b06b7f6f1906f.zip |
Move the tointeger() and toreal() functions out of core and make them into
a run-time loadable extension.
FossilOrigin-Name: 9f66dd7e3790c04f0ab724419f5381bd21f9ebad
-rw-r--r-- | Makefile.in | 1 | ||||
-rw-r--r-- | Makefile.msc | 1 | ||||
-rw-r--r-- | ext/misc/totype.c | 503 | ||||
-rw-r--r-- | main.mk | 1 | ||||
-rw-r--r-- | manifest | 25 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/func.c | 141 | ||||
-rw-r--r-- | src/test1.c | 2 | ||||
-rw-r--r-- | test/func4.test | 5 |
9 files changed, 525 insertions, 156 deletions
diff --git a/Makefile.in b/Makefile.in index 91cac6ed3..f01cae05e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/Makefile.msc b/Makefile.msc index f50b31b41..b9aeea334 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -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 diff --git a/ext/misc/totype.c b/ext/misc/totype.c new file mode 100644 index 000000000..e7288d56b --- /dev/null +++ b/ext/misc/totype.c @@ -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; +} @@ -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 @@ -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 diff --git a/manifest.uuid b/manifest.uuid index f84250b29..a52f12edb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0bf438fc30582a08fddfc3cec49366ee17ae2abe
\ No newline at end of file +9f66dd7e3790c04f0ab724419f5381bd21f9ebad
\ No newline at end of file diff --git a/src/func.c b/src/func.c index 73b6c3bb2..e2ab68f03 100644 --- a/src/func.c +++ b/src/func.c @@ -966,145 +966,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 ), diff --git a/src/test1.c b/src/test1.c index b99efa710..d8a9e52d2 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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; diff --git a/test/func4.test b/test/func4.test index 70acc655a..34a8be884 100644 --- a/test/func4.test +++ b/test/func4.test @@ -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); |