2020-06-22 19:12:23 +00:00
/*
* * 2020 - 06 - 22
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * Routines to implement arbitrary - precision decimal math .
* *
* * The focus here is on simplicity and correctness , not performance .
*/
# include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
# include <assert.h>
# include <string.h>
# include <ctype.h>
# include <stdlib.h>
2020-07-18 18:44:59 +00:00
/* Mark a function parameter as unused, to suppress nuisance compiler
* * warnings . */
# ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X) (void)(X)
# endif
2020-06-22 19:12:23 +00:00
/* A decimal object */
typedef struct Decimal Decimal ;
struct Decimal {
char sign ; /* 0 for positive, 1 for negative */
char oom ; /* True if an OOM is encountered */
char isNull ; /* True if holds a NULL rather than a number */
char isInit ; /* True upon initialization */
int nDigit ; /* Total number of digits */
int nFrac ; /* Number of digits to the right of the decimal point */
signed char * a ; /* Array of digits. Most significant first. */
} ;
/*
* * Release memory held by a Decimal , but do not free the object itself .
*/
static void decimal_clear ( Decimal * p ) {
sqlite3_free ( p - > a ) ;
}
/*
* * Destroy a Decimal object
*/
static void decimal_free ( Decimal * p ) {
if ( p ) {
decimal_clear ( p ) ;
sqlite3_free ( p ) ;
}
}
/*
2023-06-29 23:03:30 +00:00
* * Allocate a new Decimal object initialized to the text in zIn [ ] .
* * Return NULL if any kind of error occurs .
2020-06-22 19:12:23 +00:00
*/
2023-06-29 23:03:30 +00:00
static Decimal * decimalNewFromText ( const char * zIn , int n ) {
Decimal * p = 0 ;
int i ;
2020-06-22 19:12:23 +00:00
int iExp = 0 ;
2023-06-29 23:03:30 +00:00
2020-06-22 19:12:23 +00:00
p = sqlite3_malloc ( sizeof ( * p ) ) ;
2023-06-29 23:03:30 +00:00
if ( p = = 0 ) goto new_from_text_failed ;
2020-06-22 19:12:23 +00:00
p - > sign = 0 ;
p - > oom = 0 ;
p - > isInit = 1 ;
p - > isNull = 0 ;
p - > nDigit = 0 ;
p - > nFrac = 0 ;
p - > a = sqlite3_malloc64 ( n + 1 ) ;
2023-06-29 23:03:30 +00:00
if ( p - > a = = 0 ) goto new_from_text_failed ;
2020-06-22 19:12:23 +00:00
for ( i = 0 ; isspace ( zIn [ i ] ) ; i + + ) { }
if ( zIn [ i ] = = ' - ' ) {
p - > sign = 1 ;
i + + ;
} else if ( zIn [ i ] = = ' + ' ) {
i + + ;
}
while ( i < n & & zIn [ i ] = = ' 0 ' ) i + + ;
while ( i < n ) {
char c = zIn [ i ] ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
p - > a [ p - > nDigit + + ] = c - ' 0 ' ;
} else if ( c = = ' . ' ) {
p - > nFrac = p - > nDigit + 1 ;
} else if ( c = = ' e ' | | c = = ' E ' ) {
int j = i + 1 ;
int neg = 0 ;
if ( j > = n ) break ;
if ( zIn [ j ] = = ' - ' ) {
neg = 1 ;
j + + ;
} else if ( zIn [ j ] = = ' + ' ) {
j + + ;
}
while ( j < n & & iExp < 1000000 ) {
if ( zIn [ j ] > = ' 0 ' & & zIn [ j ] < = ' 9 ' ) {
iExp = iExp * 10 + zIn [ j ] - ' 0 ' ;
}
j + + ;
}
if ( neg ) iExp = - iExp ;
break ;
}
i + + ;
}
if ( p - > nFrac ) {
p - > nFrac = p - > nDigit - ( p - > nFrac - 1 ) ;
}
if ( iExp > 0 ) {
if ( p - > nFrac > 0 ) {
if ( iExp < = p - > nFrac ) {
p - > nFrac - = iExp ;
iExp = 0 ;
} else {
iExp - = p - > nFrac ;
p - > nFrac = 0 ;
}
}
if ( iExp > 0 ) {
p - > a = sqlite3_realloc64 ( p - > a , p - > nDigit + iExp + 1 ) ;
2023-06-29 23:03:30 +00:00
if ( p - > a = = 0 ) goto new_from_text_failed ;
2020-06-22 19:12:23 +00:00
memset ( p - > a + p - > nDigit , 0 , iExp ) ;
p - > nDigit + = iExp ;
}
} else if ( iExp < 0 ) {
int nExtra ;
iExp = - iExp ;
nExtra = p - > nDigit - p - > nFrac - 1 ;
if ( nExtra ) {
if ( nExtra > = iExp ) {
p - > nFrac + = iExp ;
iExp = 0 ;
} else {
iExp - = nExtra ;
p - > nFrac = p - > nDigit - 1 ;
}
}
if ( iExp > 0 ) {
p - > a = sqlite3_realloc64 ( p - > a , p - > nDigit + iExp + 1 ) ;
2023-06-29 23:03:30 +00:00
if ( p - > a = = 0 ) goto new_from_text_failed ;
2020-06-22 19:12:23 +00:00
memmove ( p - > a + iExp , p - > a , p - > nDigit ) ;
memset ( p - > a , 0 , iExp ) ;
p - > nDigit + = iExp ;
p - > nFrac + = iExp ;
}
}
return p ;
2023-06-29 23:03:30 +00:00
new_from_text_failed :
if ( p ) {
if ( p - > a ) sqlite3_free ( p - > a ) ;
sqlite3_free ( p ) ;
}
return 0 ;
}
/* Forward reference */
static Decimal * decimalFromDouble ( double ) ;
/*
* * Allocate a new Decimal object from an sqlite3_value . Return a pointer
* * to the new object , or NULL if there is an error . If the pCtx argument
* * is not NULL , then errors are reported on it as well .
* *
* * If the pIn argument is SQLITE_TEXT or SQLITE_INTEGER , it is converted
* * directly into a Decimal . For SQLITE_FLOAT or for SQLITE_BLOB of length
* * 8 bytes , the resulting double value is expanded into its decimal equivalent .
* * If pIn is NULL or if it is a BLOB that is not exactly 8 bytes in length ,
* * then NULL is returned .
*/
static Decimal * decimal_new (
sqlite3_context * pCtx , /* Report error here, if not null */
sqlite3_value * pIn , /* Construct the decimal object from this */
int bTextOnly /* Always interpret pIn as text if true */
) {
Decimal * p = 0 ;
int eType = sqlite3_value_type ( pIn ) ;
if ( bTextOnly & & ( eType = = SQLITE_FLOAT | | eType = = SQLITE_BLOB ) ) {
eType = SQLITE_TEXT ;
}
switch ( eType ) {
case SQLITE_TEXT :
case SQLITE_INTEGER : {
const char * zIn = ( const char * ) sqlite3_value_text ( pIn ) ;
int n = sqlite3_value_bytes ( pIn ) ;
p = decimalNewFromText ( zIn , n ) ;
if ( p = = 0 ) goto new_failed ;
break ;
}
case SQLITE_FLOAT : {
p = decimalFromDouble ( sqlite3_value_double ( pIn ) ) ;
break ;
}
case SQLITE_BLOB : {
const unsigned char * x ;
unsigned int i ;
sqlite3_uint64 v = 0 ;
double r ;
if ( sqlite3_value_bytes ( pIn ) ! = sizeof ( r ) ) break ;
x = sqlite3_value_blob ( pIn ) ;
for ( i = 0 ; i < sizeof ( r ) ; i + + ) {
v = ( v < < 8 ) | x [ i ] ;
}
memcpy ( & r , & v , sizeof ( r ) ) ;
p = decimalFromDouble ( r ) ;
break ;
}
case SQLITE_NULL : {
break ;
}
}
return p ;
new_failed :
2020-06-22 19:12:23 +00:00
if ( pCtx ) sqlite3_result_error_nomem ( pCtx ) ;
sqlite3_free ( p ) ;
return 0 ;
}
/*
* * Make the given Decimal the result .
*/
static void decimal_result ( sqlite3_context * pCtx , Decimal * p ) {
char * z ;
int i , j ;
int n ;
if ( p = = 0 | | p - > oom ) {
sqlite3_result_error_nomem ( pCtx ) ;
return ;
}
if ( p - > isNull ) {
sqlite3_result_null ( pCtx ) ;
return ;
}
z = sqlite3_malloc ( p - > nDigit + 4 ) ;
if ( z = = 0 ) {
sqlite3_result_error_nomem ( pCtx ) ;
return ;
}
i = 0 ;
if ( p - > nDigit = = 0 | | ( p - > nDigit = = 1 & & p - > a [ 0 ] = = 0 ) ) {
p - > sign = 0 ;
}
if ( p - > sign ) {
z [ 0 ] = ' - ' ;
i = 1 ;
}
n = p - > nDigit - p - > nFrac ;
if ( n < = 0 ) {
z [ i + + ] = ' 0 ' ;
}
j = 0 ;
2020-06-23 14:44:57 +00:00
while ( n > 1 & & p - > a [ j ] = = 0 ) {
j + + ;
n - - ;
}
2020-06-22 19:12:23 +00:00
while ( n > 0 ) {
z [ i + + ] = p - > a [ j ] + ' 0 ' ;
j + + ;
n - - ;
}
if ( p - > nFrac ) {
z [ i + + ] = ' . ' ;
do {
z [ i + + ] = p - > a [ j ] + ' 0 ' ;
j + + ;
} while ( j < p - > nDigit ) ;
}
z [ i ] = 0 ;
sqlite3_result_text ( pCtx , z , i , sqlite3_free ) ;
}
2023-06-29 14:49:23 +00:00
/*
* * Make the given Decimal the result in an format similar to ' % + # e ' .
* * In other words , show exponential notation with leading and trailing
* * zeros omitted .
*/
static void decimal_result_sci ( sqlite3_context * pCtx , Decimal * p ) {
char * z ; /* The output buffer */
int i ; /* Loop counter */
int nZero ; /* Number of leading zeros */
int nDigit ; /* Number of digits not counting trailing zeros */
int nFrac ; /* Digits to the right of the decimal point */
int exp ; /* Exponent value */
signed char zero ; /* Zero value */
signed char * a ; /* Array of digits */
if ( p = = 0 | | p - > oom ) {
sqlite3_result_error_nomem ( pCtx ) ;
return ;
}
if ( p - > isNull ) {
sqlite3_result_null ( pCtx ) ;
return ;
}
for ( nDigit = p - > nDigit ; nDigit > 0 & & p - > a [ nDigit - 1 ] = = 0 ; nDigit - - ) { }
for ( nZero = 0 ; nZero < nDigit & & p - > a [ nZero ] = = 0 ; nZero + + ) { }
nFrac = p - > nFrac + ( nDigit - p - > nDigit ) ;
nDigit - = nZero ;
z = sqlite3_malloc ( nDigit + 20 ) ;
if ( z = = 0 ) {
sqlite3_result_error_nomem ( pCtx ) ;
return ;
}
if ( nDigit = = 0 ) {
zero = 0 ;
a = & zero ;
nDigit = 1 ;
nFrac = 0 ;
} else {
a = & p - > a [ nZero ] ;
}
if ( p - > sign & & nDigit > 0 ) {
z [ 0 ] = ' - ' ;
} else {
z [ 0 ] = ' + ' ;
}
z [ 1 ] = a [ 0 ] + ' 0 ' ;
z [ 2 ] = ' . ' ;
if ( nDigit = = 1 ) {
z [ 3 ] = ' 0 ' ;
i = 4 ;
} else {
for ( i = 1 ; i < nDigit ; i + + ) {
z [ 2 + i ] = a [ i ] + ' 0 ' ;
}
i = nDigit + 2 ;
}
exp = nDigit - nFrac - 1 ;
sqlite3_snprintf ( nDigit + 20 - i , & z [ i ] , " e%+03d " , exp ) ;
sqlite3_result_text ( pCtx , z , - 1 , sqlite3_free ) ;
}
2020-06-22 19:12:23 +00:00
/*
* * Compare to Decimal objects . Return negative , 0 , or positive if the
* * first object is less than , equal to , or greater than the second .
* *
* * Preconditions for this routine :
* *
* * pA ! = 0
* * pA - > isNull = = 0
* * pB ! = 0
* * pB - > isNull = = 0
*/
static int decimal_cmp ( const Decimal * pA , const Decimal * pB ) {
int nASig , nBSig , rc , n ;
if ( pA - > sign ! = pB - > sign ) {
return pA - > sign ? - 1 : + 1 ;
}
if ( pA - > sign ) {
const Decimal * pTemp = pA ;
pA = pB ;
pB = pTemp ;
}
nASig = pA - > nDigit - pA - > nFrac ;
nBSig = pB - > nDigit - pB - > nFrac ;
if ( nASig ! = nBSig ) {
return nASig - nBSig ;
}
n = pA - > nDigit ;
if ( n > pB - > nDigit ) n = pB - > nDigit ;
rc = memcmp ( pA - > a , pB - > a , n ) ;
if ( rc = = 0 ) {
rc = pA - > nDigit - pB - > nDigit ;
}
return rc ;
}
/*
* * SQL Function : decimal_cmp ( X , Y )
* *
* * Return negative , zero , or positive if X is less then , equal to , or
* * greater than Y .
*/
static void decimalCmpFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
Decimal * pA = 0 , * pB = 0 ;
int rc ;
2020-07-18 18:44:59 +00:00
UNUSED_PARAMETER ( argc ) ;
2023-06-29 23:03:30 +00:00
pA = decimal_new ( context , argv [ 0 ] , 1 ) ;
2020-06-22 19:12:23 +00:00
if ( pA = = 0 | | pA - > isNull ) goto cmp_done ;
2023-06-29 23:03:30 +00:00
pB = decimal_new ( context , argv [ 1 ] , 1 ) ;
2020-06-22 19:12:23 +00:00
if ( pB = = 0 | | pB - > isNull ) goto cmp_done ;
rc = decimal_cmp ( pA , pB ) ;
if ( rc < 0 ) rc = - 1 ;
else if ( rc > 0 ) rc = + 1 ;
sqlite3_result_int ( context , rc ) ;
cmp_done :
decimal_free ( pA ) ;
decimal_free ( pB ) ;
}
/*
* * Expand the Decimal so that it has a least nDigit digits and nFrac
* * digits to the right of the decimal point .
*/
static void decimal_expand ( Decimal * p , int nDigit , int nFrac ) {
int nAddSig ;
int nAddFrac ;
if ( p = = 0 ) return ;
nAddFrac = nFrac - p - > nFrac ;
nAddSig = ( nDigit - p - > nDigit ) - nAddFrac ;
if ( nAddFrac = = 0 & & nAddSig = = 0 ) return ;
p - > a = sqlite3_realloc64 ( p - > a , nDigit + 1 ) ;
if ( p - > a = = 0 ) {
p - > oom = 1 ;
return ;
}
if ( nAddSig ) {
memmove ( p - > a + nAddSig , p - > a , p - > nDigit ) ;
memset ( p - > a , 0 , nAddSig ) ;
p - > nDigit + = nAddSig ;
}
if ( nAddFrac ) {
memset ( p - > a + p - > nDigit , 0 , nAddFrac ) ;
p - > nDigit + = nAddFrac ;
p - > nFrac + = nAddFrac ;
}
}
/*
2023-06-29 20:28:03 +00:00
* * Add the value pB into pA . A : = A + B .
2020-06-22 19:12:23 +00:00
* *
2020-06-23 14:44:57 +00:00
* * Both pA and pB might become denormalized by this routine .
2020-06-22 19:12:23 +00:00
*/
static void decimal_add ( Decimal * pA , Decimal * pB ) {
int nSig , nFrac , nDigit ;
int i , rc ;
if ( pA = = 0 ) {
return ;
}
if ( pA - > oom | | pB = = 0 | | pB - > oom ) {
pA - > oom = 1 ;
return ;
}
if ( pA - > isNull | | pB - > isNull ) {
pA - > isNull = 1 ;
return ;
}
nSig = pA - > nDigit - pA - > nFrac ;
2020-06-23 14:44:57 +00:00
if ( nSig & & pA - > a [ 0 ] = = 0 ) nSig - - ;
if ( nSig < pB - > nDigit - pB - > nFrac ) {
nSig = pB - > nDigit - pB - > nFrac ;
}
2020-06-22 19:12:23 +00:00
nFrac = pA - > nFrac ;
if ( nFrac < pB - > nFrac ) nFrac = pB - > nFrac ;
nDigit = nSig + nFrac + 1 ;
decimal_expand ( pA , nDigit , nFrac ) ;
decimal_expand ( pB , nDigit , nFrac ) ;
if ( pA - > oom | | pB - > oom ) {
pA - > oom = 1 ;
} else {
if ( pA - > sign = = pB - > sign ) {
int carry = 0 ;
for ( i = nDigit - 1 ; i > = 0 ; i - - ) {
int x = pA - > a [ i ] + pB - > a [ i ] + carry ;
if ( x > = 10 ) {
carry = 1 ;
pA - > a [ i ] = x - 10 ;
} else {
carry = 0 ;
pA - > a [ i ] = x ;
}
}
} else {
signed char * aA , * aB ;
int borrow = 0 ;
rc = memcmp ( pA - > a , pB - > a , nDigit ) ;
if ( rc < 0 ) {
aA = pB - > a ;
aB = pA - > a ;
pA - > sign = ! pA - > sign ;
} else {
aA = pA - > a ;
aB = pB - > a ;
}
for ( i = nDigit - 1 ; i > = 0 ; i - - ) {
int x = aA [ i ] - aB [ i ] - borrow ;
if ( x < 0 ) {
pA - > a [ i ] = x + 10 ;
borrow = 1 ;
} else {
pA - > a [ i ] = x ;
borrow = 0 ;
}
}
}
}
}
2023-06-29 20:28:03 +00:00
/*
* * Multiply A by B . A : = A * B
* *
* * All significant digits after the decimal point are retained .
* * Trailing zeros after the decimal point are omitted as long as
* * the number of digits after the decimal point is no less than
* * either the number of digits in either input .
*/
static void decimalMul ( Decimal * pA , Decimal * pB ) {
signed char * acc = 0 ;
int i , j , k ;
int minFrac ;
if ( pA = = 0 | | pA - > oom | | pA - > isNull
| | pB = = 0 | | pB - > oom | | pB - > isNull
) {
goto mul_end ;
}
acc = sqlite3_malloc64 ( pA - > nDigit + pB - > nDigit + 2 ) ;
if ( acc = = 0 ) {
pA - > oom = 1 ;
goto mul_end ;
}
memset ( acc , 0 , pA - > nDigit + pB - > nDigit + 2 ) ;
minFrac = pA - > nFrac ;
if ( pB - > nFrac < minFrac ) minFrac = pB - > nFrac ;
for ( i = pA - > nDigit - 1 ; i > = 0 ; i - - ) {
signed char f = pA - > a [ i ] ;
int carry = 0 , x ;
for ( j = pB - > nDigit - 1 , k = i + j + 3 ; j > = 0 ; j - - , k - - ) {
x = acc [ k ] + f * pB - > a [ j ] + carry ;
acc [ k ] = x % 10 ;
carry = x / 10 ;
}
x = acc [ k ] + carry ;
acc [ k ] = x % 10 ;
acc [ k - 1 ] + = x / 10 ;
}
sqlite3_free ( pA - > a ) ;
pA - > a = acc ;
acc = 0 ;
pA - > nDigit + = pB - > nDigit + 2 ;
pA - > nFrac + = pB - > nFrac ;
pA - > sign ^ = pB - > sign ;
while ( pA - > nFrac > minFrac & & pA - > a [ pA - > nDigit - 1 ] = = 0 ) {
pA - > nFrac - - ;
pA - > nDigit - - ;
}
mul_end :
sqlite3_free ( acc ) ;
}
/*
* * Create a new Decimal object that contains an integer power of 2.
*/
static Decimal * decimalPow2 ( int N ) {
Decimal * pA = 0 ; /* The result to be returned */
Decimal * pX = 0 ; /* Multiplier */
if ( N < - 20000 | | N > 20000 ) goto pow2_fault ;
2023-06-29 23:03:30 +00:00
pA = decimalNewFromText ( " 1.0 " , 3 ) ;
2023-06-29 20:28:03 +00:00
if ( pA = = 0 | | pA - > oom ) goto pow2_fault ;
if ( N = = 0 ) return pA ;
if ( N > 0 ) {
2023-06-29 23:03:30 +00:00
pX = decimalNewFromText ( " 2.0 " , 3 ) ;
2023-06-29 20:28:03 +00:00
} else {
N = - N ;
2023-06-29 23:03:30 +00:00
pX = decimalNewFromText ( " 0.5 " , 3 ) ;
2023-06-29 20:28:03 +00:00
}
if ( pX = = 0 | | pX - > oom ) goto pow2_fault ;
while ( 1 /* Exit by break */ ) {
if ( N & 1 ) {
decimalMul ( pA , pX ) ;
if ( pA - > oom ) goto pow2_fault ;
}
N > > = 1 ;
if ( N = = 0 ) break ;
decimalMul ( pX , pX ) ;
}
decimal_free ( pX ) ;
return pA ;
pow2_fault :
decimal_free ( pA ) ;
decimal_free ( pX ) ;
return 0 ;
}
/*
* * Use an IEEE754 binary64 ( " double " ) to generate a new Decimal object .
*/
static Decimal * decimalFromDouble ( double r ) {
sqlite3_int64 m , a ;
int e ;
int isNeg ;
Decimal * pA ;
Decimal * pX ;
char zNum [ 100 ] ;
if ( r < 0.0 ) {
isNeg = 1 ;
r = - r ;
} else {
isNeg = 0 ;
}
memcpy ( & a , & r , sizeof ( a ) ) ;
if ( a = = 0 ) {
e = 0 ;
m = 0 ;
} else {
e = a > > 52 ;
m = a & ( ( ( ( sqlite3_int64 ) 1 ) < < 52 ) - 1 ) ;
if ( e = = 0 ) {
m < < = 1 ;
} else {
m | = ( ( sqlite3_int64 ) 1 ) < < 52 ;
}
while ( e < 1075 & & m > 0 & & ( m & 1 ) = = 0 ) {
m > > = 1 ;
e + + ;
}
if ( isNeg ) m = - m ;
e = e - 1075 ;
if ( e > 971 ) {
return 0 ; /* A NaN or an Infinity */
}
}
/* At this point m is the integer significand and e is the exponent */
sqlite3_snprintf ( sizeof ( zNum ) , zNum , " %lld " , m ) ;
2023-06-29 23:03:30 +00:00
pA = decimalNewFromText ( zNum , ( int ) strlen ( zNum ) ) ;
2023-06-29 20:28:03 +00:00
pX = decimalPow2 ( e ) ;
decimalMul ( pA , pX ) ;
decimal_free ( pX ) ;
return pA ;
}
/*
* * SQL Function : decimal ( X )
2023-08-18 15:39:38 +00:00
* * OR : decimal_exp ( X )
2023-06-29 20:28:03 +00:00
* *
* * Convert input X into decimal and then back into text .
* *
2023-06-29 23:03:30 +00:00
* * If X is originally a float , then a full decimal expansion of that floating
2023-06-29 20:28:03 +00:00
* * point value is done . Or if X is an 8 - byte blob , it is interpreted
* * as a float and similarly expanded .
2023-06-29 23:03:30 +00:00
* *
2023-08-18 15:39:38 +00:00
* * The decimal_exp ( X ) function returns the result in exponential notation .
2023-06-29 23:03:30 +00:00
* * decimal ( X ) returns a complete decimal , without the e + NNN at the end .
2023-06-29 20:28:03 +00:00
*/
static void decimalFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
2023-06-29 23:03:30 +00:00
Decimal * p = decimal_new ( context , argv [ 0 ] , 0 ) ;
2023-06-29 20:28:03 +00:00
UNUSED_PARAMETER ( argc ) ;
if ( p ) {
if ( sqlite3_user_data ( context ) ! = 0 ) {
decimal_result_sci ( context , p ) ;
} else {
decimal_result ( context , p ) ;
}
decimal_free ( p ) ;
}
}
2020-06-22 19:12:23 +00:00
/*
* * Compare text in decimal order .
*/
static int decimalCollFunc (
void * notUsed ,
int nKey1 , const void * pKey1 ,
int nKey2 , const void * pKey2
) {
const unsigned char * zA = ( const unsigned char * ) pKey1 ;
const unsigned char * zB = ( const unsigned char * ) pKey2 ;
2023-06-29 23:03:30 +00:00
Decimal * pA = decimalNewFromText ( ( const char * ) zA , nKey1 ) ;
Decimal * pB = decimalNewFromText ( ( const char * ) zB , nKey2 ) ;
2020-06-22 19:12:23 +00:00
int rc ;
2020-07-18 18:44:59 +00:00
UNUSED_PARAMETER ( notUsed ) ;
2020-06-22 19:12:23 +00:00
if ( pA = = 0 | | pB = = 0 ) {
rc = 0 ;
} else {
rc = decimal_cmp ( pA , pB ) ;
}
decimal_free ( pA ) ;
decimal_free ( pB ) ;
return rc ;
}
/*
* * SQL Function : decimal_add ( X , Y )
* * decimal_sub ( X , Y )
* *
* * Return the sum or difference of X and Y .
*/
static void decimalAddFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
2023-06-29 23:03:30 +00:00
Decimal * pA = decimal_new ( context , argv [ 0 ] , 1 ) ;
Decimal * pB = decimal_new ( context , argv [ 1 ] , 1 ) ;
2020-07-18 18:44:59 +00:00
UNUSED_PARAMETER ( argc ) ;
2020-06-22 19:12:23 +00:00
decimal_add ( pA , pB ) ;
decimal_result ( context , pA ) ;
decimal_free ( pA ) ;
decimal_free ( pB ) ;
}
static void decimalSubFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
2023-06-29 23:03:30 +00:00
Decimal * pA = decimal_new ( context , argv [ 0 ] , 1 ) ;
Decimal * pB = decimal_new ( context , argv [ 1 ] , 1 ) ;
2020-07-18 18:44:59 +00:00
UNUSED_PARAMETER ( argc ) ;
2021-05-03 13:24:30 +00:00
if ( pB ) {
pB - > sign = ! pB - > sign ;
decimal_add ( pA , pB ) ;
decimal_result ( context , pA ) ;
}
2020-06-22 19:12:23 +00:00
decimal_free ( pA ) ;
decimal_free ( pB ) ;
}
/* Aggregate funcion: decimal_sum(X)
* *
* * Works like sum ( ) except that it uses decimal arithmetic for unlimited
* * precision .
*/
static void decimalSumStep (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
Decimal * p ;
Decimal * pArg ;
2020-07-18 18:44:59 +00:00
UNUSED_PARAMETER ( argc ) ;
2020-06-22 19:12:23 +00:00
p = sqlite3_aggregate_context ( context , sizeof ( * p ) ) ;
if ( p = = 0 ) return ;
if ( ! p - > isInit ) {
p - > isInit = 1 ;
p - > a = sqlite3_malloc ( 2 ) ;
if ( p - > a = = 0 ) {
p - > oom = 1 ;
} else {
p - > a [ 0 ] = 0 ;
}
p - > nDigit = 1 ;
p - > nFrac = 0 ;
}
if ( sqlite3_value_type ( argv [ 0 ] ) = = SQLITE_NULL ) return ;
2023-06-29 23:03:30 +00:00
pArg = decimal_new ( context , argv [ 0 ] , 1 ) ;
2020-06-22 19:12:23 +00:00
decimal_add ( p , pArg ) ;
decimal_free ( pArg ) ;
}
static void decimalSumInverse (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
Decimal * p ;
Decimal * pArg ;
2020-07-18 18:44:59 +00:00
UNUSED_PARAMETER ( argc ) ;
2020-06-22 19:12:23 +00:00
p = sqlite3_aggregate_context ( context , sizeof ( * p ) ) ;
if ( p = = 0 ) return ;
if ( sqlite3_value_type ( argv [ 0 ] ) = = SQLITE_NULL ) return ;
2023-06-29 23:03:30 +00:00
pArg = decimal_new ( context , argv [ 0 ] , 1 ) ;
2020-06-22 19:12:23 +00:00
if ( pArg ) pArg - > sign = ! pArg - > sign ;
decimal_add ( p , pArg ) ;
decimal_free ( pArg ) ;
}
static void decimalSumValue ( sqlite3_context * context ) {
Decimal * p = sqlite3_aggregate_context ( context , 0 ) ;
if ( p = = 0 ) return ;
decimal_result ( context , p ) ;
}
static void decimalSumFinalize ( sqlite3_context * context ) {
Decimal * p = sqlite3_aggregate_context ( context , 0 ) ;
if ( p = = 0 ) return ;
decimal_result ( context , p ) ;
decimal_clear ( p ) ;
}
2020-06-22 21:25:37 +00:00
/*
* * SQL Function : decimal_mul ( X , Y )
* *
* * Return the product of X and Y .
*/
static void decimalMulFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
2023-06-29 23:03:30 +00:00
Decimal * pA = decimal_new ( context , argv [ 0 ] , 1 ) ;
Decimal * pB = decimal_new ( context , argv [ 1 ] , 1 ) ;
2020-07-18 18:44:59 +00:00
UNUSED_PARAMETER ( argc ) ;
2020-06-22 21:25:37 +00:00
if ( pA = = 0 | | pA - > oom | | pA - > isNull
| | pB = = 0 | | pB - > oom | | pB - > isNull
) {
goto mul_end ;
}
2023-06-29 20:28:03 +00:00
decimalMul ( pA , pB ) ;
if ( pA - > oom ) {
2020-06-22 21:25:37 +00:00
goto mul_end ;
}
decimal_result ( context , pA ) ;
mul_end :
decimal_free ( pA ) ;
decimal_free ( pB ) ;
}
2020-06-22 19:12:23 +00:00
2023-06-29 14:49:23 +00:00
/*
2023-06-29 20:28:03 +00:00
* * SQL Function : decimal_pow2 ( N )
2023-06-29 14:49:23 +00:00
* *
2023-06-29 20:28:03 +00:00
* * Return the N - th power of 2. N must be an integer .
2023-06-29 14:49:23 +00:00
*/
2023-06-29 20:28:03 +00:00
static void decimalPow2Func (
2023-06-29 14:49:23 +00:00
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
2023-06-29 17:48:32 +00:00
UNUSED_PARAMETER ( argc ) ;
2023-06-29 20:28:03 +00:00
if ( sqlite3_value_type ( argv [ 0 ] ) = = SQLITE_INTEGER ) {
Decimal * pA = decimalPow2 ( sqlite3_value_int ( argv [ 0 ] ) ) ;
decimal_result_sci ( context , pA ) ;
decimal_free ( pA ) ;
}
2023-06-29 14:49:23 +00:00
}
2020-06-22 19:12:23 +00:00
# ifdef _WIN32
__declspec ( dllexport )
# endif
int sqlite3_decimal_init (
sqlite3 * db ,
char * * pzErrMsg ,
const sqlite3_api_routines * pApi
) {
int rc = SQLITE_OK ;
static const struct {
const char * zFuncName ;
int nArg ;
2023-06-29 20:28:03 +00:00
int iArg ;
2020-06-22 19:12:23 +00:00
void ( * xFunc ) ( sqlite3_context * , int , sqlite3_value * * ) ;
} aFunc [ ] = {
2023-06-29 20:28:03 +00:00
{ " decimal " , 1 , 0 , decimalFunc } ,
2023-08-18 15:39:38 +00:00
{ " decimal_exp " , 1 , 1 , decimalFunc } ,
2023-06-29 20:28:03 +00:00
{ " decimal_cmp " , 2 , 0 , decimalCmpFunc } ,
{ " decimal_add " , 2 , 0 , decimalAddFunc } ,
{ " decimal_sub " , 2 , 0 , decimalSubFunc } ,
{ " decimal_mul " , 2 , 0 , decimalMulFunc } ,
{ " decimal_pow2 " , 1 , 0 , decimalPow2Func } ,
2020-06-22 19:12:23 +00:00
} ;
2020-07-18 18:44:59 +00:00
unsigned int i ;
2020-06-22 19:12:23 +00:00
( void ) pzErrMsg ; /* Unused parameter */
2020-08-08 00:44:45 +00:00
SQLITE_EXTENSION_INIT2 ( pApi ) ;
2022-12-23 14:49:24 +00:00
for ( i = 0 ; i < ( int ) ( sizeof ( aFunc ) / sizeof ( aFunc [ 0 ] ) ) & & rc = = SQLITE_OK ; i + + ) {
2020-06-22 19:12:23 +00:00
rc = sqlite3_create_function ( db , aFunc [ i ] . zFuncName , aFunc [ i ] . nArg ,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC ,
2023-06-29 20:28:03 +00:00
aFunc [ i ] . iArg ? db : 0 , aFunc [ i ] . xFunc , 0 , 0 ) ;
2020-06-22 19:12:23 +00:00
}
if ( rc = = SQLITE_OK ) {
rc = sqlite3_create_window_function ( db , " decimal_sum " , 1 ,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC , 0 ,
decimalSumStep , decimalSumFinalize ,
decimalSumValue , decimalSumInverse , 0 ) ;
}
if ( rc = = SQLITE_OK ) {
rc = sqlite3_create_collation ( db , " decimal " , SQLITE_UTF8 ,
0 , decimalCollFunc ) ;
}
return rc ;
}