Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ let package = Package(
"OMIT_DEPRECATED",
"OMIT_PROGRESS_CALLBACK",
"OMIT_SHARED_CACHE",
"USE_ALLOCA",
"OMIT_AUTOINIT",
"STRICT_SUBTYPE_1",
"ENABLE_CARRAY",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The following traits are used to specify the build configuration and the default
| OMIT_DEPRECATED | Y | [SQLITE_OMIT_DEPRECATED](https://sqlite.org/compile.html#omit_deprecated) |
| OMIT_PROGRESS_CALLBACK | Y | [SQLITE_OMIT_PROGRESS_CALLBACK](https://sqlite.org/compile.html#omit_progress_callback) |
| OMIT_SHARED_CACHE | Y | [SQLITE_OMIT_SHARED_CACHE](https://sqlite.org/compile.html#omit_shared_cache) |
| USE_ALLOCA | Y | [SQLITE_USE_ALLOCA](https://sqlite.org/compile.html#use_alloca) |
| USE_ALLOCA | | [SQLITE_USE_ALLOCA](https://sqlite.org/compile.html#use_alloca) |
| OMIT_AUTOINIT | Y | [SQLITE_OMIT_AUTOINIT](https://sqlite.org/compile.html#omit_autoinit) |
| STRICT_SUBTYPE_1 | Y | [SQLITE_STRICT_SUBTYPE=1](https://sqlite.org/compile.html#strict_subtype) |

Expand Down
5 changes: 5 additions & 0 deletions Sources/CSQLite/csqlite_shims.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,11 @@ int csqlite_sqlite3_db_config_enable_comments(sqlite3 *db, int x, int *y)
return sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_COMMENTS, x, y);
}

int csqlite_sqlite3_db_config_fp_digits(sqlite3 *db, int x, int *y)
{
return sqlite3_db_config(db, SQLITE_DBCONFIG_FP_DIGITS, x, y);
}

// MARK: - Virtual table configuration

int csqlite_sqlite3_vtab_config_constraint_support(sqlite3 *db, int x)
Expand Down
80 changes: 65 additions & 15 deletions Sources/CSQLite/decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ SQLITE_EXTENSION_INIT1
#define IsSpace(X) isspace((unsigned char)X)
#endif

#ifndef SQLITE_DECIMAL_MAX_DIGIT
# define SQLITE_DECIMAL_MAX_DIGIT 10000000
#endif

/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
Expand Down Expand Up @@ -69,7 +73,8 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
int i;
int iExp = 0;

p = sqlite3_malloc( sizeof(*p) );
if( zIn==0 ) goto new_from_text_failed;
p = sqlite3_malloc64( sizeof(*p) );
if( p==0 ) goto new_from_text_failed;
p->sign = 0;
p->oom = 0;
Expand Down Expand Up @@ -128,9 +133,10 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( p->a==0 ) goto new_from_text_failed;
if( a==0 ) goto new_from_text_failed;
p->a = a;
memset(p->a+p->nDigit, 0, iExp);
p->nDigit += iExp;
}
Expand All @@ -148,9 +154,10 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( p->a==0 ) goto new_from_text_failed;
if( a==0 ) goto new_from_text_failed;
p->a = a;
memmove(p->a+iExp, p->a, p->nDigit);
memset(p->a, 0, iExp);
p->nDigit += iExp;
Expand All @@ -161,6 +168,7 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
for(i=0; i<p->nDigit && p->a[i]==0; i++){}
if( i>=p->nDigit ) p->sign = 0;
}
if( p->nDigit>SQLITE_DECIMAL_MAX_DIGIT ) goto new_from_text_failed;
return p;

new_from_text_failed:
Expand Down Expand Up @@ -291,12 +299,38 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
sqlite3_result_text(pCtx, z, i, sqlite3_free);
}

/*
** Round a decimal value to N significant digits. N must be positive.
*/
static void decimal_round(Decimal *p, int N){
int i;
int nZero;
if( N<1 ) return;
if( p==0 ) return;
if( p->nDigit<=N ) return;
for(nZero=0; nZero<p->nDigit && p->a[nZero]==0; nZero++){}
N += nZero;
if( p->nDigit<=N ) return;
if( p->a[N]>4 ){
p->a[N-1]++;
for(i=N-1; i>0 && p->a[i]>9; i--){
p->a[i] = 0;
p->a[i-1]++;
}
if( p->a[0]>9 ){
p->a[0] = 1;
p->nFrac--;
}
}
memset(&p->a[N], 0, p->nDigit - N);
}

/*
** 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){
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
char *z; /* The output buffer */
int i; /* Loop counter */
int nZero; /* Number of leading zeros */
Expand All @@ -314,7 +348,8 @@ static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
sqlite3_result_null(pCtx);
return;
}
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
if( N<1 ) N = 0;
for(nDigit=p->nDigit; nDigit>N && p->a[nDigit-1]==0; nDigit--){}
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
nFrac = p->nFrac + (nDigit - p->nDigit);
nDigit -= nZero;
Expand Down Expand Up @@ -430,15 +465,18 @@ static void decimalCmpFunc(
static void decimal_expand(Decimal *p, int nDigit, int nFrac){
int nAddSig;
int nAddFrac;
signed char *a;
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 ){
if( nDigit+1>SQLITE_DECIMAL_MAX_DIGIT ){ p->oom = 1; return; }
a = sqlite3_realloc64(p->a, nDigit+1);
if( a==0 ){
p->oom = 1;
return;
}
p->a = a;
if( nAddSig ){
memmove(p->a+nAddSig, p->a, p->nDigit);
memset(p->a, 0, nAddSig);
Expand Down Expand Up @@ -533,14 +571,18 @@ static void decimalMul(Decimal *pA, Decimal *pB){
signed char *acc = 0;
int i, j, k;
int minFrac;
sqlite3_int64 sumDigit;

if( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
acc = sqlite3_malloc64( (sqlite3_int64)pA->nDigit +
(sqlite3_int64)pB->nDigit + 2 );
sumDigit = pA->nDigit;
sumDigit += pB->nDigit;
sumDigit += 2;
if( sumDigit>SQLITE_DECIMAL_MAX_DIGIT ){ pA->oom = 1; return; }
acc = sqlite3_malloc64( sumDigit );
if( acc==0 ){
pA->oom = 1;
goto mul_end;
Expand Down Expand Up @@ -677,10 +719,16 @@ static void decimalFunc(
sqlite3_value **argv
){
Decimal *p = decimal_new(context, argv[0], 0);
UNUSED_PARAMETER(argc);
int N;
if( argc==2 ){
N = sqlite3_value_int(argv[1]);
if( N>0 ) decimal_round(p, N);
}else{
N = 0;
}
if( p ){
if( sqlite3_user_data(context)!=0 ){
decimal_result_sci(context, p);
decimal_result_sci(context, p, N);
}else{
decimal_result(context, p);
}
Expand Down Expand Up @@ -766,7 +814,7 @@ static void decimalSumStep(
if( p==0 ) return;
if( !p->isInit ){
p->isInit = 1;
p->a = sqlite3_malloc(2);
p->a = sqlite3_malloc64(2);
if( p->a==0 ){
p->oom = 1;
}else{
Expand Down Expand Up @@ -850,7 +898,7 @@ static void decimalPow2Func(
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
Decimal *pA = decimalPow2(sqlite3_value_int(argv[0]));
decimal_result_sci(context, pA);
decimal_result_sci(context, pA, 0);
decimal_free(pA);
}
}
Expand All @@ -871,7 +919,9 @@ int sqlite3_decimal_init(
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "decimal", 1, 0, decimalFunc },
{ "decimal", 2, 0, decimalFunc },
{ "decimal_exp", 1, 1, decimalFunc },
{ "decimal_exp", 2, 1, decimalFunc },
{ "decimal_cmp", 2, 0, decimalCmpFunc },
{ "decimal_add", 2, 0, decimalAddFunc },
{ "decimal_sub", 2, 0, decimalSubFunc },
Expand Down
36 changes: 35 additions & 1 deletion Sources/CSQLite/ieee754.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ static void ieee754func(
}

if( m<0 ){
if( m<(-9223372036854775807LL) ) return;
isNeg = 1;
m = -m;
if( m<0 ) return;
}else if( m==0 && e>-1000 && e<1000 ){
sqlite3_result_double(context, 0.0);
return;
Expand Down Expand Up @@ -259,6 +259,38 @@ static void ieee754func_to_blob(
}
}

/*
** Functions to convert between 64-bit integers and floats.
**
** The bit patterns are copied. The numeric values are different.
*/
static void ieee754func_from_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
double r;
sqlite3_int64 v = sqlite3_value_int64(argv[0]);
memcpy(&r, &v, sizeof(r));
sqlite3_result_double(context, r);
}
}
static void ieee754func_to_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[0]);
sqlite3_uint64 v;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ieee754func_to_int() copies the double bit-pattern into a sqlite3_uint64 and then passes it to sqlite3_result_int64(). Converting an out-of-range unsigned 64-bit value to signed 64-bit is implementation-defined and can yield inconsistent results across platforms. Use a sqlite3_int64 destination (or memcpy into a sqlite3_int64) so the result is well-defined while preserving the bit pattern.

Suggested change
sqlite3_uint64 v;
sqlite3_int64 v;

Copilot uses AI. Check for mistakes.
memcpy(&v, &r, sizeof(v));
sqlite3_result_int64(context, v);
}
}

/*
** SQL Function: ieee754_inc(r,N)
**
Expand Down Expand Up @@ -311,6 +343,8 @@ int sqlite3_ieee_init(
{ "ieee754_exponent", 1, 2, ieee754func },
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
{ "ieee754_to_int", 1, 0, ieee754func_to_int },
{ "ieee754_from_int", 1, 0, ieee754func_from_int },
{ "ieee754_inc", 2, 0, ieee754inc },
};
unsigned int i;
Expand Down
2 changes: 2 additions & 0 deletions Sources/CSQLite/include/csqlite_shims.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ int csqlite_sqlite3_db_config_enable_attach_create(sqlite3 *db, int x, int *y);
int csqlite_sqlite3_db_config_enable_attach_write(sqlite3 *db, int x, int *y);
/// Equivalent to `sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_COMMENTS, x, y)`
int csqlite_sqlite3_db_config_enable_comments(sqlite3 *db, int x, int *y);
/// Equivalent to `sqlite3_db_config(db, SQLITE_DBCONFIG_FP_DIGITS, x, y)`
int csqlite_sqlite3_db_config_fp_digits(sqlite3 *db, int x, int *y);

// MARK: - Virtual table configuration
// See https://sqlite.org/c3ref/vtab_config.html
Expand Down
Loading