Skip to content
Merged
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
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
43 changes: 38 additions & 5 deletions Sources/CSQLite/decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,36 @@ 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;
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 +338,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 @@ -677,10 +702,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;
}
Comment on lines +708 to +711
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

decimalFunc calls decimal_round(p, N) before checking whether decimal_new(...) returned NULL. decimal_new can legitimately return NULL (e.g., SQLITE_NULL input, invalid blob length, NaN/Infinity, OOM), which makes this a NULL dereference crash when argc==2 and N>0. Move the rounding call behind the if (p) check (and ideally also skip rounding for p->isNull/p->oom).

Suggested change
if( N>0 ) decimal_round(p, N);
}else{
N = 0;
}
}else{
N = 0;
}
if( p && N>0 && !p->isNull && !p->oom ){
decimal_round(p, N);
}

Copilot uses AI. Check for mistakes.
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 @@ -850,7 +881,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 +902,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 },
Comment on lines 904 to +907
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

New 2-argument overloads for decimal/decimal_exp (significant-digits rounding) are introduced here, but the existing Swift extension tests only cover the 1-argument behavior. Please add tests that exercise rounding (e.g., decimal('1.2345', 3) and decimal_exp(..., N)) including edge cases like N<=0 and NULL input.

Copilot uses AI. Check for mistakes.
{ "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 Mar 6, 2026

Choose a reason for hiding this comment

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

ieee754func_to_int stores the copied bit-pattern in a sqlite3_uint64 and then passes it to sqlite3_result_int64. If the high bit is set, converting an out-of-range unsigned 64-bit value to signed 64-bit is implementation-defined and may not preserve the intended bit pattern across platforms/compilers. To guarantee a bitwise reinterpretation, copy into a sqlite3_int64 (or use an explicit memcpy into a signed variable) before returning it.

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 },
Comment on lines +346 to +347
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

New SQL functions ieee754_to_int/ieee754_from_int are registered here, but the existing Swift extension tests only cover ieee754(...). Please add coverage that validates round-trip behavior and bit-pattern preservation (including NaN payloads / sign-bit cases if applicable).

Copilot uses AI. Check for mistakes.
{ "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