/* Shared native-value dispatch using SQLite's standard record storage classes. */
/* Reject unsupported declarations and CAST targets before affinity erases the
** spelling. BoxDB translates logical ENUM declarations to TEXT before prepare.
** Whitespace, comments and parameters do not make these engine types valid. */
static int boxdbUnsupportedType(const char *z, int length){
char name[24]; int i,j,n=0,size,token;
for(i=0;i<length;i+=size){
size=(int)sqlite3GetToken((const unsigned char*)z+i,&token);
if( size<=0 || size>length-i ) return 0;
if( token==TK_LP ) break;
if( token==TK_SPACE || token==TK_COMMENT ) continue;
for(j=0;j<size;j++){
if( n>=(int)sizeof(name)-1 ) return 0;
name[n++]=sqlite3Toupper(z[i+j]);
}
}
name[n]=0;
return strcmp(name,"DOUBLEPRECISION")==0
|| strcmp(name,"CHARACTERVARYING")==0
|| strcmp(name,"JSONB")==0
|| strcmp(name,"FLOAT4")==0
|| strcmp(name,"FLOAT")==0
|| strcmp(name,"FLOAT8")==0
|| strcmp(name,"ENUM")==0;
}
typedef struct BoxdbType { int kind; u8 affinity; char tag; int size; const char *name; } BoxdbType;
#include "boxdb_types.inc"
extern int boxdb_native_text(int,const char*,int,char**,int*);
extern int boxdb_native_eval(int,int,int,const char*,int,int,const char*,int,char**,int*,int*);
extern int boxdb_native_compare(int,const char*,int,int,const char*,int);
extern sqlite3_uint64 boxdb_native_hash(int,const char*,int);
static const BoxdbType *boxdbType(int kind){
unsigned i;
for(i=0; i<ArraySize(boxdbTypes); i++) if( boxdbTypes[i].kind==kind ) return &boxdbTypes[i];
return 0;
}
static int boxdbNativeKind(const Mem *p){
unsigned i;
if( (p->flags&(MEM_Blob|MEM_Str))!=MEM_Blob || p->n<1 || !p->z ) return 0;
for(i=1; i<ArraySize(boxdbTypes); i++){
if( p->z[0]==boxdbTypes[i].tag && p->n==1+boxdbTypes[i].size ) return boxdbTypes[i].kind;
}
return 0;
}
static int boxdbKind(const Mem *p){
int kind;
if( !(p->flags&MEM_Blob) ) return 0;
kind=boxdbNativeKind(p);
return kind ? kind : boxdbIsDecimal(p);
}
static int boxdbNativeAffinityKind(u8 affinity){
unsigned i;
for(i=1;i<ArraySize(boxdbTypes);i++) if(boxdbTypes[i].affinity==affinity) return boxdbTypes[i].kind;
return 0;
}
static int boxdbNativeText(Mem *p){
char *text=0; int length=0;
int rc=boxdb_native_text(boxdbNativeKind(p),p->z+1,p->n-1,&text,&length);
if( rc ){ sqlite3_free(text); return rc; }
return sqlite3VdbeMemSetStr(p,text,length,SQLITE_UTF8,sqlite3_free);
}
static int boxdbNativeOperand(Mem *p, const char **data, int *length){
int kind=boxdbKind(p);
if( kind ){ *data=p->z+1; *length=p->n-1; return kind; }
if( p->flags&(MEM_Int|MEM_IntReal) ){ *data=(const char*)&p->u.i; *length=8; return -1; }
if( p->flags&MEM_Real ){ *data=(const char*)&p->u.r; *length=8; return -2; }
if( p->flags&MEM_Str ){ *data=(const char*)sqlite3_value_text(p); *length=sqlite3_value_bytes(p); return 0; }
*data=""; *length=0; return -3;
}
typedef struct BoxdbFunctionArgument { int kind; const char *data; int length; } BoxdbFunctionArgument;
extern int boxdb_native_function(int,int,const BoxdbFunctionArgument*,char**,int*);
static void boxdbTemporalFunction(sqlite3_context *context, int argc, sqlite3_value **argv){
BoxdbFunctionArgument args[7];
char *result=0; int i,length=0,rc,function=(int)(intptr_t)sqlite3_user_data(context);
i64 currentSeconds;
if( argc>7 ){ sqlite3_result_error(context,"temporal function accepts at most seven arguments",-1); return; }
for(i=0;i<argc;i++){
if( sqlite3_value_type(argv[i])==SQLITE_NULL ) return;
args[i].kind=boxdbNativeOperand(argv[i],&args[i].data,&args[i].length);
if( !args[i].data ){ sqlite3_result_error_nomem(context); return; }
}
if( function==5 ){
i64 current=sqlite3StmtCurrentTime(context);
if( current<=0 ){ sqlite3_result_error(context,"cannot read current UTC date",-1); return; }
currentSeconds=(current-210866760000000LL)/1000;
args[argc].kind=-1; args[argc].data=(const char*)¤tSeconds; args[argc].length=8;
argc++;
}
rc=boxdb_native_function(function,argc,args,&result,&length);
if( rc==SQLITE_OK ){
rc=sqlite3VdbeMemSetStr(context->pOut,result,length,0,sqlite3_free);
if( rc ) sqlite3_result_error_nomem(context);
}else{
if( rc==SQLITE_NOMEM ) sqlite3_result_error_nomem(context);
else sqlite3_result_error(context,result ? result : "temporal function failed",length);
sqlite3_free(result);
}
}
static int boxdbNativeOp(Mem *out, int operation, int target, Mem *a, Mem *b, char **error){
const char *za,*zb=""; int na,nb=0,ka,kb=-3,nresult=0,rc,resultKind=0; char *result=0;
if( !target && b && (boxdbIsUnsigned(a) || boxdbIsUnsigned(b)) && (boxdbIsDecimal(a) || boxdbIsDecimal(b)) ) return boxdbDecimalOp(out,operation,a,b,error);
ka=boxdbNativeOperand(a,&za,&na);
if( b ) kb=boxdbNativeOperand(b,&zb,&nb);
if( !za || !zb ) return SQLITE_NOMEM;
rc=boxdb_native_eval(operation,target,ka,za,na,kb,zb,nb,&result,&nresult,&resultKind);
if( rc==SQLITE_OK && resultKind==-1 ){ i64 value; if( nresult!=8 ){ sqlite3_free(result); return SQLITE_ERROR; } memcpy(&value,result,8); sqlite3_free(result); sqlite3VdbeMemSetInt64(out,value); return SQLITE_OK; }
if( rc==SQLITE_OK ) return sqlite3VdbeMemSetStr(out,result,nresult,0,sqlite3_free);
if( error ) *error=result; else sqlite3_free(result);
return rc;
}
static int boxdbNativeCoerce(Mem *p, int kind){
if( (p->flags&MEM_Null) || boxdbNativeKind(p)==kind ) return SQLITE_OK;
return boxdbNativeOp(p,8,kind,p,0,0);
}
static int boxdbNativeCompare(const Mem *a, const Mem *b){
return boxdb_native_compare(boxdbNativeKind(a),a->z+1,a->n-1,boxdbNativeKind(b),b->z+1,b->n-1);
}
static int boxdbRank(const Mem *p){
int kind=boxdbKind(p);
if( kind ) return (kind==1 || kind>=7) ? 1 : (kind==5 ? 5 : kind+1);
if( p->flags&(MEM_Int|MEM_IntReal|MEM_Real) ) return 1;
return p->flags&MEM_Str ? 100 : 101;
}
static int boxdbNativeAffinityCompare(Mem *left, Mem *right, CollSeq *coll, u8 affinity){
Mem a,b; int rc,kind=boxdbNativeAffinityKind(affinity);
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( kind ){
boxdbNativeCoerce(&a,kind); boxdbNativeCoerce(&b,kind);
}else if( affinity==SQLITE_AFF_TEXT ){
if( boxdbNativeKind(&a) ) boxdbNativeText(&a);
if( boxdbNativeKind(&b) ) boxdbNativeText(&b);
}
rc=sqlite3MemCompare(&a,&b,coll);
sqlite3VdbeMemRelease(&a); sqlite3VdbeMemRelease(&b);
return rc;
}
static int boxdbIsUnsigned(const Mem *p){ return boxdbNativeKind(p)>=7; }
static int boxdbNumeric(const Mem *p){
return boxdbIsUnsigned(p) || boxdbIsDecimal(p) || (p->flags&(MEM_Int|MEM_IntReal|MEM_Real));
}
static int boxdbUnsignedCompare(const Mem *left, const Mem *right){
Mem a,b; int rc;
if(!boxdbNumeric(left) || !boxdbNumeric(right)) return boxdbNumeric(left) ? -1 : 1;
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(boxdbIsUnsigned(&a)){boxdbNativeText(&a);boxdbDecimalCoerce(&a);}
if(boxdbIsUnsigned(&b)){boxdbNativeText(&b);boxdbDecimalCoerce(&b);}
rc=boxdbDecimalCompare(&a,&b);
sqlite3VdbeMemRelease(&a);sqlite3VdbeMemRelease(&b);return rc;
}
static u64 boxdbUnsignedValue(const Mem *p){
u64 n=0; int i; for(i=1;i<p->n;i++) n=(n<<8)|(unsigned char)p->z[i]; return n;
}