/* Native BoxDB decimals retain SQLite's BLOB record encoding and C type ABI. */
#include <float.h>
typedef struct BoxdbWireBlob { const char *data; int length; } BoxdbWireBlob;
#define BOXDB_DECIMAL_PREFIX "d"
#define BOXDB_DECIMAL_PREFIX_LEN 1
#define BOXDB_BLOB_PREFIX "b"
/* The spare Mem bit records an API text view without changing the wire value.
** u.nZero caches its byte length (negative for UTF-16); MEM_Zero is absent. */
#define MEM_BoxdbText 0x0080
static int boxdbNativeKind(const Mem*);
static int boxdbKind(const Mem*);
static int boxdbIsUnsigned(const Mem*);
static u64 boxdbUnsignedValue(const Mem*);
extern int boxdb_native_text(int,const char*,int,char**,int*);
extern int boxdb_decimal_eval(int,int,const char*,int,int,const char*,int,char**,int*);
extern int boxdb_decimal_compare(int,const char*,int,int,const char*,int);
static int boxdbIsDecimal(const Mem *p){
int i, digits=0, fraction=0, dot=0, first=-1, last=-1, scale;
if( (p->flags & (MEM_Blob|MEM_Str))!=MEM_Blob || p->n<=1 || p->n>8195 || !p->z
|| p->z[0]!='d' ) return 0;
i=1;
if( p->z[i]=='-' || p->z[i]=='+' ) i++;
for(; i<p->n; i++){
char c=p->z[i];
if( c=='.' && !dot ){ dot=1; continue; }
if( c<'0' || c>'9' ) return 0;
if( c!='0' ){ if( first<0 ) first=digits; last=digits; }
digits++;
fraction+=dot;
}
if( !digits ) return 0;
if( first<0 ) return 1;
scale=fraction-(digits-last-1);
return last-first+1<=4096 && scale>=-4096 && scale<=4096;
}
static int boxdbIsBytea(const Mem *p){
return (p->flags & (MEM_Blob|MEM_Str))==MEM_Blob && p->n>=1 && p->z!=0 && p->z[0]=='b';
}
static i64 boxdbByteaInt(const Mem *p){
Mem text=*p;
if( p->flags & MEM_Zero ) return 0;
text.z++; text.n--; text.flags=MEM_Str|MEM_Ephem;
return sqlite3VdbeIntValue(&text);
}
static double boxdbByteaReal(Mem *p){
Mem text=*p;
if( p->flags & MEM_Zero ) return 0.0;
text.z++; text.n--; text.flags=MEM_Str|MEM_Ephem;
return sqlite3VdbeRealValue(&text);
}
static int boxdbEncodeBytea(Mem *p){
char *bytes;
int n = p->n;
bytes = sqlite3_malloc64((sqlite3_uint64)n+1);
if( !bytes ) return SQLITE_NOMEM;
bytes[0]='b';
if( n ) memcpy(bytes+1,p->z,n);
return sqlite3VdbeMemSetStr(p,bytes,n+1,0,sqlite3_free);
}
static const void *boxdbValueText(Mem *p, u8 enc){
Mem text;
u8 requested = enc & ~SQLITE_UTF16_ALIGNED;
i64 offset, size;
int n;
memset(&text,0,sizeof(text));
text.db=p->db;
if( (p->flags & MEM_Zero) && sqlite3VdbeMemExpandBlob(p) ) return 0;
if( boxdbNativeKind(p) ){
char *formatted=0; int length=0;
if( boxdb_native_text(boxdbNativeKind(p),p->z+1,p->n-1,&formatted,&length) ){
sqlite3_free(formatted); return 0;
}
sqlite3VdbeMemSetStr(&text,formatted,length,SQLITE_UTF8,sqlite3_free);
}else{
text.z=p->z+1; text.n=p->n-1;
text.enc=boxdbIsDecimal(p) ? SQLITE_UTF8 : p->enc;
text.flags=MEM_Str|MEM_Ephem;
}
if( sqlite3VdbeMemMakeWriteable(&text) ) return 0;
if( sqlite3VdbeChangeEncoding(&text,requested) ){
sqlite3VdbeMemRelease(&text);
return 0;
}
n=text.n;
offset=((i64)p->n+3)&~(i64)1;
size=offset+n+2;
if( size>0x7fffffff || sqlite3VdbeMemGrow(p,(int)size,1) ){
sqlite3VdbeMemRelease(&text);
return 0;
}
p->z[p->n]=p->z[p->n+1]=0;
memcpy(p->z+offset,text.z,n);
p->z[offset+n]=p->z[offset+n+1]=0;
p->flags |= MEM_BoxdbText|MEM_Term;
p->u.nZero=requested==SQLITE_UTF8 ? n : -n-1;
sqlite3VdbeMemRelease(&text);
return p->z+offset;
}
static int boxdbDecimalOperand(const Mem *p, const char **data, int *len, char *buf){
if( boxdbIsUnsigned(p) ){
sqlite3_snprintf(32,buf,"%llu",boxdbUnsignedValue(p));
*data=buf; *len=(int)strlen(buf); return 2;
}
if( boxdbIsDecimal(p) ){
*data = p->z + BOXDB_DECIMAL_PREFIX_LEN;
*len = p->n - BOXDB_DECIMAL_PREFIX_LEN;
return 1;
}
if( p->flags & (MEM_Int|MEM_IntReal) ){
sqlite3_snprintf(32, buf, "%lld", p->u.i);
*data = buf;
*len = (int)strlen(buf);
return 2;
}
if( p->flags & MEM_Real ){
*data = (const char*)&p->u.r;
*len = sizeof(double);
return 3;
}
if( p->flags & MEM_Str ){
*data = (const char*)sqlite3_value_text((Mem*)p);
*len = sqlite3_value_bytes((Mem*)p);
return 1;
}
*data = "";
*len = 0;
return 1;
}
static int boxdbDecimalOp(Mem *out, int op, const Mem *a, const Mem *b, char **error){
const char *za, *zb;
char ba[32], bb[32], *result = 0;
int na, nb, ka, kb, nresult = 0, rc;
ka = boxdbDecimalOperand(a, &za, &na, ba);
kb = boxdbDecimalOperand(b ? b : a, &zb, &nb, bb);
if( !za || !zb ) return SQLITE_NOMEM;
rc = boxdb_decimal_eval(op, ka, za, na, kb, zb, nb, &result, &nresult);
if( rc==SQLITE_OK ){
return sqlite3VdbeMemSetStr(out, result, nresult, 0, sqlite3_free);
}
if( error ) *error = result; else sqlite3_free(result);
return rc;
}
static int boxdbDecimalCoerce(Mem *p){
if( p->flags & MEM_Null || boxdbIsDecimal(p) ) return SQLITE_OK;
if( p->flags & MEM_Str ){
int rc = sqlite3VdbeChangeEncoding(p, SQLITE_UTF8);
if( rc ) return rc;
}
return boxdbDecimalOp(p, 0, p, 0, 0);
}
static int boxdbDecimalText(Mem *p){
char *text = sqlite3_mprintf("%.*s", p->n-BOXDB_DECIMAL_PREFIX_LEN,
p->z+BOXDB_DECIMAL_PREFIX_LEN);
if( !text ) return SQLITE_NOMEM;
return sqlite3VdbeMemSetStr(p, text, -1, SQLITE_UTF8, sqlite3_free);
}
static int boxdbBlobText(Mem *p, u8 enc){
int rc;
char *text=sqlite3_malloc64((sqlite3_uint64)p->n-1+2);
if( !text ) return SQLITE_NOMEM;
memcpy(text,p->z+1,p->n-1);
text[p->n-1]=text[p->n]=0;
rc=sqlite3VdbeMemSetStr(p,text,p->n-1,p->enc,sqlite3_free);
if( rc==SQLITE_OK ) rc=sqlite3VdbeChangeEncoding(p,enc);
return rc;
}
static int boxdbDecimalCompare(const Mem *a, const Mem *b){
const char *za, *zb;
char ba[32], bb[32];
int na, nb, ka, kb;
ka = boxdbDecimalOperand(a, &za, &na, ba);
kb = boxdbDecimalOperand(b, &zb, &nb, bb);
if( ka==3 && (a->u.r>DBL_MAX || a->u.r<-DBL_MAX) ) return a->u.r>0 ? 1 : -1;
if( kb==3 && (b->u.r>DBL_MAX || b->u.r<-DBL_MAX) ) return b->u.r>0 ? -1 : 1;
return boxdb_decimal_compare(ka, za, na, kb, zb, nb);
}
static int boxdbDecimalAffinityCompare(Mem *left, Mem *right, CollSeq *coll, char affinity){
Mem a,b;
int result;
memset(&a,0,sizeof(a));
memset(&b,0,sizeof(b));
a.db=left->db;
b.db=right->db;
sqlite3VdbeMemShallowCopy(&a,left,MEM_Ephem);
sqlite3VdbeMemShallowCopy(&b,right,MEM_Ephem);
if( affinity==SQLITE_AFF_DECIMAL ){
boxdbDecimalCoerce(&a);
boxdbDecimalCoerce(&b);
}else{
if( boxdbIsDecimal(&a) && boxdbDecimalText(&a)==SQLITE_OK ) sqlite3VdbeChangeEncoding(&a,ENC(a.db));
if( boxdbIsDecimal(&b) && boxdbDecimalText(&b)==SQLITE_OK ) sqlite3VdbeChangeEncoding(&b,ENC(b.db));
}
result=sqlite3MemCompare(&a,&b,coll);
sqlite3VdbeMemRelease(&a);
sqlite3VdbeMemRelease(&b);
return result;
}
static void boxdbDecimalAffinity(Mem *p, char affinity, u8 enc){
Mem candidate;
if( affinity==SQLITE_AFF_TEXT ){
if( boxdbDecimalText(p)==SQLITE_OK ) sqlite3VdbeChangeEncoding(p,enc);
return;
}
if( affinity<SQLITE_AFF_NUMERIC ) return;
memset(&candidate,0,sizeof(candidate));
candidate.flags=MEM_Int;
candidate.u.i=boxdbDecimalInt(p);
if( boxdbDecimalCompare(p,&candidate)==0 ){
sqlite3VdbeMemSetInt64(p,candidate.u.i);
return;
}
candidate.flags=MEM_Real;
candidate.u.r=boxdbDecimalReal(p);
if( boxdbDecimalCompare(p,&candidate)==0 ) sqlite3VdbeMemSetDouble(p,candidate.u.r);
}
static i64 boxdbDecimalInt(const Mem *p){
Mem text = *p;
text.z += BOXDB_DECIMAL_PREFIX_LEN;
text.n -= BOXDB_DECIMAL_PREFIX_LEN;
text.flags = MEM_Str|MEM_Ephem;
text.enc = SQLITE_UTF8;
return sqlite3VdbeIntValue(&text);
}
static double boxdbDecimalReal(Mem *p){
Mem text = *p;
text.z += BOXDB_DECIMAL_PREFIX_LEN;
text.n -= BOXDB_DECIMAL_PREFIX_LEN;
text.flags = MEM_Str|MEM_Ephem;
text.enc = SQLITE_UTF8;
return sqlite3VdbeRealValue(&text);
}
static int boxdbDecimalBoolean(const Mem *p){
int i;
for(i=BOXDB_DECIMAL_PREFIX_LEN; i<p->n; i++){
if( p->z[i]>='1' && p->z[i]<='9' ) return 1;
}
return 0;
}
static void boxdbDecimalFunction(sqlite3_context *ctx, int op, Mem *a, Mem *b){
Mem result;
char *error = 0;
memset(&result, 0, sizeof(result));
result.db = sqlite3_context_db_handle(ctx);
if( boxdbDecimalOp(&result, op, a, b, &error) ){
sqlite3_result_error(ctx, error ? error : "DECIMAL allocation failed", -1);
sqlite3_free(error);
}else{
sqlite3_result_value(ctx, &result);
}
sqlite3VdbeMemRelease(&result);
}
/* Stored DECIMAL precision checks belong to the engine, including SQL imports. */
static void boxdbDecimalValidFunc(sqlite3_context *ctx, int argc, sqlite3_value **argv){
Mem *value=(Mem*)argv[0];
i64 precision, scale;
int i, whole=0, fraction=0, significantFraction=0, dot=0, leading=1;
UNUSED_PARAMETER(argc);
if( value->flags & MEM_Null ){ sqlite3_result_int(ctx,1); return; }
precision=sqlite3_value_int64(argv[1]);
scale=sqlite3_value_int64(argv[2]);
if( sqlite3_value_type(argv[1])!=SQLITE_INTEGER || sqlite3_value_type(argv[2])!=SQLITE_INTEGER
|| precision<1 || precision>1000 || scale<0 || scale>precision ){
sqlite3_result_error(ctx,"invalid DECIMAL precision/scale",-1);
return;
}
if( !boxdbIsDecimal(value) ){
sqlite3_result_error(ctx,"invalid DECIMAL value",-1);
return;
}
i=1;
if( value->z[i]=='-' || value->z[i]=='+' ) i++;
for(; i<value->n; i++){
char c=value->z[i];
if( c=='.' ){ dot=1; continue; }
if( dot ){
fraction++;
if( c!='0' ) significantFraction=fraction;
}else{
if( c!='0' ) leading=0;
if( !leading ) whole++;
}
}
if( whole>precision-scale || significantFraction>scale ){
sqlite3_result_error(ctx,"DECIMAL precision or scale overflow",-1);
}else sqlite3_result_int(ctx,1);
}
static int boxdbConcat(Mem *out, Mem *a, Mem *b, u8 enc){
const char *za=(const char*)sqlite3_value_text(a);
const char *zb=(const char*)sqlite3_value_text(b);
int na=sqlite3_value_bytes(a), nb=sqlite3_value_bytes(b);
char *text;
int rc;
if( !za || !zb ) return SQLITE_NOMEM;
if( (i64)na+nb>out->db->aLimit[SQLITE_LIMIT_LENGTH] ) return SQLITE_TOOBIG;
text=sqlite3_malloc64((sqlite3_uint64)na+nb+1);
if( !text ) return SQLITE_NOMEM;
memcpy(text,za,na);
memcpy(text+na,zb,nb);
text[na+nb]=0;
rc=sqlite3VdbeMemSetStr(out,text,na+nb,SQLITE_UTF8,sqlite3_free);
if( rc==SQLITE_OK ) rc=sqlite3VdbeChangeEncoding(out,enc);
return rc;
}