Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad9533679a | |||
| 37fd50df1f | |||
| 9246c85b61 | |||
| 6a9be2cbc7 | |||
| a5b1f34125 | |||
| 263a8e8c0a | |||
| a02d6d8237 | |||
| 8ba619b5f5 | |||
| f4449dc0ba | |||
| d655290c99 | |||
| bb9549e0be | |||
| d51cefd1bb | |||
| bc6d949aa3 | |||
| cc336321f8 | |||
| d41d454fc1 | |||
| 0f0e5700ba | |||
| 01bdff7a1c | |||
| 81602595a0 | |||
| 5da49ff562 | |||
| a5512fb179 | |||
| b3b64179df | |||
| a17b04f840 | |||
| 8a1104940d | |||
| 4c40b7b819 | |||
| bc532ae15f | |||
| 50ba4e3efd | |||
| afef7fc136 | |||
| bae91082dd | |||
| 7d0103bb06 | |||
| 68c88ea04f | |||
| 3e1e7aad3d | |||
| 59f13119cf | |||
| 26cd8bc1af | |||
| 45d7562843 | |||
| 3748b7329f | |||
| efd0cf87df | |||
| 728650ecb3 | |||
| e68899f1e4 | |||
| 85ca6d7ace | |||
| 1790ccb98b | |||
| 6aa5a42beb | |||
| 7623795018 | |||
| aa4356da68 | |||
| 930e4167bd | |||
| 521f20e3d3 | |||
| c9f240cea4 | |||
| 1b3c6e727e | |||
| 0478d1d88a | |||
| b884158434 | |||
| 841e596ad0 | |||
| bc2b65832e | |||
| 5270d7424d | |||
| 37b188fbca | |||
| c8885d42fe | |||
| c27bda0b79 | |||
| fecdff4354 | |||
| 453be55b85 | |||
| 17c20bb15e | |||
| 9ee9444a0a | |||
| 42d042e602 | |||
| 3f073f9ef8 | |||
| aebeabacff | |||
| 002330dc48 | |||
| a1b0ff1735 | |||
| 24b368da8d | |||
| 4cd3a59a79 | |||
| 07d76f7b95 | |||
| 1bfe92a691 | |||
| 48114d08cf | |||
| dd8b12a6c5 | |||
| 60f41362cf | |||
| 2ddfa6a360 | |||
| ec3e57fa92 | |||
| 500ca334bd | |||
| 0b1a1e8c37 | |||
| a75011a891 | |||
| cd88a74115 | |||
| 9ea7632cca | |||
| 7f5fe1faf3 | |||
| 8fbb335d9f | |||
| 60783f47b2 | |||
| d847c73153 | |||
| 12b198f1a2 | |||
| 502c618f01 | |||
| b9561384d0 | |||
| d2b9cdd592 | |||
| cc19bed8b1 | |||
| 077efc2730 | |||
| 7f9dcd664d | |||
| d6c671e626 | |||
| d4170ac0eb | |||
| 71aac8763f | |||
| b3d7f1c2db |
@@ -348,7 +348,7 @@ Or on windows:
|
||||
> nmake /f Makefile.msc verify-source
|
||||
|
||||
Using the makefile to verify source integrity is good for detecting
|
||||
accidental changes to the source tree, but malecious changes could be
|
||||
accidental changes to the source tree, but malicious changes could be
|
||||
hidden by also modifying the makefiles.
|
||||
|
||||
## Contacts
|
||||
|
||||
@@ -4655,9 +4655,13 @@ static void fts5SecureDeleteOverflow(
|
||||
int i1 = pLeaf->szLeaf;
|
||||
int i2 = 0;
|
||||
|
||||
i1 += fts5GetVarint32(&aPg[i1], iFirst);
|
||||
if( iFirst<iNext ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
break;
|
||||
}
|
||||
aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2);
|
||||
if( aIdx==0 ) break;
|
||||
i1 += fts5GetVarint32(&aPg[i1], iFirst);
|
||||
i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift);
|
||||
if( i1<pLeaf->nn ){
|
||||
memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1);
|
||||
|
||||
+345
-96
@@ -58,41 +58,24 @@ static void decimal_free(Decimal *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a new Decimal object. Initialize it to the number given
|
||||
** by the input string.
|
||||
** Allocate a new Decimal object initialized to the text in zIn[].
|
||||
** Return NULL if any kind of error occurs.
|
||||
*/
|
||||
static Decimal *decimal_new(
|
||||
sqlite3_context *pCtx,
|
||||
sqlite3_value *pIn,
|
||||
int nAlt,
|
||||
const unsigned char *zAlt
|
||||
){
|
||||
Decimal *p;
|
||||
int n, i;
|
||||
const unsigned char *zIn;
|
||||
static Decimal *decimalNewFromText(const char *zIn, int n){
|
||||
Decimal *p = 0;
|
||||
int i;
|
||||
int iExp = 0;
|
||||
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ) goto new_no_mem;
|
||||
if( p==0 ) goto new_from_text_failed;
|
||||
p->sign = 0;
|
||||
p->oom = 0;
|
||||
p->isInit = 1;
|
||||
p->isNull = 0;
|
||||
p->nDigit = 0;
|
||||
p->nFrac = 0;
|
||||
if( zAlt ){
|
||||
n = nAlt,
|
||||
zIn = zAlt;
|
||||
}else{
|
||||
if( sqlite3_value_type(pIn)==SQLITE_NULL ){
|
||||
p->a = 0;
|
||||
p->isNull = 1;
|
||||
return p;
|
||||
}
|
||||
n = sqlite3_value_bytes(pIn);
|
||||
zIn = sqlite3_value_text(pIn);
|
||||
}
|
||||
p->a = sqlite3_malloc64( n+1 );
|
||||
if( p->a==0 ) goto new_no_mem;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
for(i=0; isspace(zIn[i]); i++){}
|
||||
if( zIn[i]=='-' ){
|
||||
p->sign = 1;
|
||||
@@ -143,7 +126,7 @@ static Decimal *decimal_new(
|
||||
}
|
||||
if( iExp>0 ){
|
||||
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
|
||||
if( p->a==0 ) goto new_no_mem;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
memset(p->a+p->nDigit, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
}
|
||||
@@ -162,7 +145,7 @@ static Decimal *decimal_new(
|
||||
}
|
||||
if( iExp>0 ){
|
||||
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
|
||||
if( p->a==0 ) goto new_no_mem;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
memmove(p->a+iExp, p->a, p->nDigit);
|
||||
memset(p->a, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
@@ -171,7 +154,76 @@ static Decimal *decimal_new(
|
||||
}
|
||||
return p;
|
||||
|
||||
new_no_mem:
|
||||
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:
|
||||
if( pCtx ) sqlite3_result_error_nomem(pCtx);
|
||||
sqlite3_free(p);
|
||||
return 0;
|
||||
@@ -231,19 +283,64 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: decimal(X)
|
||||
**
|
||||
** Convert input X into decimal and then back into text
|
||||
** 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 decimalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *p = decimal_new(context, argv[0], 0, 0);
|
||||
UNUSED_PARAMETER(argc);
|
||||
decimal_result(context, p);
|
||||
decimal_free(p);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -296,9 +393,9 @@ static void decimalCmpFunc(
|
||||
int rc;
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
pA = decimal_new(context, argv[0], 0, 0);
|
||||
pA = decimal_new(context, argv[0], 1);
|
||||
if( pA==0 || pA->isNull ) goto cmp_done;
|
||||
pB = decimal_new(context, argv[1], 0, 0);
|
||||
pB = decimal_new(context, argv[1], 1);
|
||||
if( pB==0 || pB->isNull ) goto cmp_done;
|
||||
rc = decimal_cmp(pA, pB);
|
||||
if( rc<0 ) rc = -1;
|
||||
@@ -338,7 +435,7 @@ static void decimal_expand(Decimal *p, int nDigit, int nFrac){
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the value pB into pA.
|
||||
** Add the value pB into pA. A := A + B.
|
||||
**
|
||||
** Both pA and pB might become denormalized by this routine.
|
||||
*/
|
||||
@@ -407,6 +504,172 @@ static void decimal_add(Decimal *pA, Decimal *pB){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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;
|
||||
pA = decimalNewFromText("1.0", 3);
|
||||
if( pA==0 || pA->oom ) goto pow2_fault;
|
||||
if( N==0 ) return pA;
|
||||
if( N>0 ){
|
||||
pX = decimalNewFromText("2.0", 3);
|
||||
}else{
|
||||
N = -N;
|
||||
pX = decimalNewFromText("0.5", 3);
|
||||
}
|
||||
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);
|
||||
pA = decimalNewFromText(zNum, (int)strlen(zNum));
|
||||
pX = decimalPow2(e);
|
||||
decimalMul(pA, pX);
|
||||
decimal_free(pX);
|
||||
return pA;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: decimal(X)
|
||||
** OR: decimal_sci(X)
|
||||
**
|
||||
** Convert input X into decimal and then back into text.
|
||||
**
|
||||
** If X is originally a float, then a full decimal expansion of that floating
|
||||
** point value is done. Or if X is an 8-byte blob, it is interpreted
|
||||
** as a float and similarly expanded.
|
||||
**
|
||||
** The decimal_sci(X) function returns the result in scientific notation.
|
||||
** decimal(X) returns a complete decimal, without the e+NNN at the end.
|
||||
*/
|
||||
static void decimalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *p = decimal_new(context, argv[0], 0);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( p ){
|
||||
if( sqlite3_user_data(context)!=0 ){
|
||||
decimal_result_sci(context, p);
|
||||
}else{
|
||||
decimal_result(context, p);
|
||||
}
|
||||
decimal_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare text in decimal order.
|
||||
*/
|
||||
@@ -417,8 +680,8 @@ static int decimalCollFunc(
|
||||
){
|
||||
const unsigned char *zA = (const unsigned char*)pKey1;
|
||||
const unsigned char *zB = (const unsigned char*)pKey2;
|
||||
Decimal *pA = decimal_new(0, 0, nKey1, zA);
|
||||
Decimal *pB = decimal_new(0, 0, nKey2, zB);
|
||||
Decimal *pA = decimalNewFromText((const char*)zA, nKey1);
|
||||
Decimal *pB = decimalNewFromText((const char*)zB, nKey2);
|
||||
int rc;
|
||||
UNUSED_PARAMETER(notUsed);
|
||||
if( pA==0 || pB==0 ){
|
||||
@@ -443,8 +706,8 @@ static void decimalAddFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *pA = decimal_new(context, argv[0], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
Decimal *pA = decimal_new(context, argv[0], 1);
|
||||
Decimal *pB = decimal_new(context, argv[1], 1);
|
||||
UNUSED_PARAMETER(argc);
|
||||
decimal_add(pA, pB);
|
||||
decimal_result(context, pA);
|
||||
@@ -456,8 +719,8 @@ static void decimalSubFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *pA = decimal_new(context, argv[0], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
Decimal *pA = decimal_new(context, argv[0], 1);
|
||||
Decimal *pB = decimal_new(context, argv[1], 1);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( pB ){
|
||||
pB->sign = !pB->sign;
|
||||
@@ -495,7 +758,7 @@ static void decimalSumStep(
|
||||
p->nFrac = 0;
|
||||
}
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pArg = decimal_new(context, argv[0], 0, 0);
|
||||
pArg = decimal_new(context, argv[0], 1);
|
||||
decimal_add(p, pArg);
|
||||
decimal_free(pArg);
|
||||
}
|
||||
@@ -510,7 +773,7 @@ static void decimalSumInverse(
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pArg = decimal_new(context, argv[0], 0, 0);
|
||||
pArg = decimal_new(context, argv[0], 1);
|
||||
if( pArg ) pArg->sign = !pArg->sign;
|
||||
decimal_add(p, pArg);
|
||||
decimal_free(pArg);
|
||||
@@ -531,66 +794,49 @@ static void decimalSumFinalize(sqlite3_context *context){
|
||||
** SQL Function: decimal_mul(X, Y)
|
||||
**
|
||||
** Return the product of X and Y.
|
||||
**
|
||||
** 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 decimalMulFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *pA = decimal_new(context, argv[0], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
signed char *acc = 0;
|
||||
int i, j, k;
|
||||
int minFrac;
|
||||
Decimal *pA = decimal_new(context, argv[0], 1);
|
||||
Decimal *pB = decimal_new(context, argv[1], 1);
|
||||
UNUSED_PARAMETER(argc);
|
||||
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 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
decimalMul(pA, pB);
|
||||
if( pA->oom ){
|
||||
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--;
|
||||
}
|
||||
decimal_result(context, pA);
|
||||
|
||||
mul_end:
|
||||
sqlite3_free(acc);
|
||||
decimal_free(pA);
|
||||
decimal_free(pB);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: decimal_pow2(N)
|
||||
**
|
||||
** Return the N-th power of 2. N must be an integer.
|
||||
*/
|
||||
static void decimalPow2Func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
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_free(pA);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
@@ -603,13 +849,16 @@ int sqlite3_decimal_init(
|
||||
static const struct {
|
||||
const char *zFuncName;
|
||||
int nArg;
|
||||
int iArg;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "decimal", 1, decimalFunc },
|
||||
{ "decimal_cmp", 2, decimalCmpFunc },
|
||||
{ "decimal_add", 2, decimalAddFunc },
|
||||
{ "decimal_sub", 2, decimalSubFunc },
|
||||
{ "decimal_mul", 2, decimalMulFunc },
|
||||
{ "decimal", 1, 0, decimalFunc },
|
||||
{ "decimal_sci", 1, 1, decimalFunc },
|
||||
{ "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 },
|
||||
};
|
||||
unsigned int i;
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
@@ -619,7 +868,7 @@ int sqlite3_decimal_init(
|
||||
for(i=0; i<(int)(sizeof(aFunc)/sizeof(aFunc[0])) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, aFunc[i].xFunc, 0, 0);
|
||||
aFunc[i].iArg ? db : 0, aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_window_function(db, "decimal_sum", 1,
|
||||
|
||||
+32
-1
@@ -256,6 +256,37 @@ static void ieee754func_to_blob(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: ieee754_inc(r,N)
|
||||
**
|
||||
** Move the floating point value r by N quantums and return the new
|
||||
** values.
|
||||
**
|
||||
** Behind the scenes: this routine merely casts r into a 64-bit unsigned
|
||||
** integer, adds N, then casts the value back into float.
|
||||
**
|
||||
** Example: To find the smallest positive number:
|
||||
**
|
||||
** SELECT ieee754_inc(0.0,+1);
|
||||
*/
|
||||
static void ieee754inc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
double r;
|
||||
sqlite3_int64 N;
|
||||
sqlite3_uint64 m1, m2;
|
||||
double r2;
|
||||
UNUSED_PARAMETER(argc);
|
||||
r = sqlite3_value_double(argv[0]);
|
||||
N = sqlite3_value_int64(argv[1]);
|
||||
memcpy(&m1, &r, 8);
|
||||
m2 = m1 + N;
|
||||
memcpy(&r2, &m2, 8);
|
||||
sqlite3_result_double(context, r2);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
@@ -277,7 +308,7 @@ 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_inc", 2, 0, ieee754inc },
|
||||
};
|
||||
unsigned int i;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
+115
-94
@@ -68,13 +68,6 @@ ifeq (,$(emcc.version))
|
||||
else
|
||||
$(info using emcc version [$(emcc.version)])
|
||||
endif
|
||||
emcc.version := $(shell "$(emcc.bin)" --version | sed -n 1p \
|
||||
| sed -e 's/^.* \([3-9][^ ]*\) .*$$/\1/;')
|
||||
ifeq (,$(emcc.version))
|
||||
$(warning Cannot determine emcc version. This might unduly impact build flags.)
|
||||
else
|
||||
$(info using emcc version [$(emcc.version)])
|
||||
endif
|
||||
|
||||
wasm-strip ?= $(shell which wasm-strip 2>/dev/null)
|
||||
ifeq (,$(filter clean,$(MAKECMDGOALS)))
|
||||
@@ -159,6 +152,9 @@ endif
|
||||
# bundle.
|
||||
#
|
||||
# A custom sqlite3.c must not have any spaces in its name.
|
||||
# $(sqlite3.canonical.c) must point to the sqlite3.c in
|
||||
# the sqlite3 canonical source tree, as that source file
|
||||
# is required for certain utility and test code.
|
||||
sqlite3.canonical.c := $(dir.top)/sqlite3.c
|
||||
sqlite3.c ?= $(firstword $(wildcard $(dir.top)/sqlite3-see.c) $(sqlite3.canonical.c))
|
||||
sqlite3.h := $(dir.top)/sqlite3.h
|
||||
@@ -381,18 +377,14 @@ sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||
|
||||
# "External" API files which are part of our distribution
|
||||
# SOAP.js is an external API file which is part of our distribution
|
||||
# but not part of the sqlite3-api.js amalgamation.
|
||||
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
|
||||
# COPY_XAPI = a $(call)able function to copy $1 to $(dir.dout), where
|
||||
# $1 must be one of the "external" JS API files.
|
||||
define COPY_XAPI
|
||||
sqlite3-api.ext.jses += $$(dir.dout)/$$(notdir $(1))
|
||||
$$(dir.dout)/$$(notdir $(1)): $(1) $$(MAKEFILE)
|
||||
cp $$< $$@
|
||||
endef
|
||||
$(foreach X,$(SOAP.js),\
|
||||
$(eval $(call COPY_XAPI,$(X))))
|
||||
SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
|
||||
sqlite3-api.ext.jses += $(SOAP.js.bld)
|
||||
$(SOAP.js.bld): $(SOAP.js)
|
||||
cp $< $@
|
||||
|
||||
all quick: $(sqlite3-api.ext.jses)
|
||||
q: quick
|
||||
|
||||
@@ -615,26 +607,40 @@ $(post-js.js.in): $(post-jses.js) $(MAKEFILE)
|
||||
|
||||
########################################################################
|
||||
# call-make-pre-post is a $(call)able which creates rules for
|
||||
# pre-js-$(1).js. $1 = the base name of the JS file on whose behalf
|
||||
# this pre-js is for (one of: sqlite3, sqlite3-wasmfs). $2 is the
|
||||
# build mode: one of $(JS_BUILD_MODES). This
|
||||
# sets up --[extern-][pre/post]-js flags in
|
||||
# $(pre-post-$(1).flags.$(2)) and dependencies in
|
||||
# $(pre-post-$(1).deps.$(2)).
|
||||
# pre-js-$(1)-$(2).js. $1 = the base name of the JS file on whose
|
||||
# behalf this pre-js is for (one of: sqlite3, sqlite3-wasmfs). $2 is
|
||||
# the build mode: one of $(JS_BUILD_MODES). This sets up
|
||||
# --[extern-][pre/post]-js flags in $(pre-post-$(1)-$(2).flags) and
|
||||
# dependencies in $(pre-post-$(1)-$(2).deps). The resulting files get
|
||||
# filtered using $(C-PP.FILTER). Any flags necessary for such
|
||||
# filtering need to be set in $(c-pp.D.$(1)-$(2)) before $(call)ing
|
||||
# this.
|
||||
define call-make-pre-post
|
||||
pre-post-$(1).flags.$(2) ?=
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(2)) $$(MAKEFILE)
|
||||
cp $$(pre-js.js.$(2)) $$@
|
||||
pre-post-$(1)-$(2).flags ?=
|
||||
pre-js.js.$(1)-$(2) := $$(dir.tmp)/pre-js.$(1)-$(2).intermediary.js
|
||||
$$(eval $$(call C-PP.FILTER,$$(pre-js.js.in),$$(pre-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
post-js.js.$(1)-$(2) := $$(dir.tmp)/post-js.$(1)-$(2).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(post-js.js.in),$$(post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
extern-post-js.js.$(1)-$(2) := $$(dir.tmp)/extern-post-js.$(1)-$(2).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(extern-post-js.js.in),$$(extern-post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
pre-post-common.flags.$(1)-$(2) := \
|
||||
$$(pre-post-common.flags) \
|
||||
--post-js=$$(post-js.js.$(1)-$(2)) \
|
||||
--extern-post-js=$$(extern-post-js.js.$(1)-$(2))
|
||||
pre-post-jses.$(1)-$(2).deps := $$(pre-post-jses.deps.common) \
|
||||
$$(post-js.js.$(1)-$(2)) $$(extern-post-js.js.$(1)-$(2))
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(1)-$(2)) $$(MAKEFILE)
|
||||
cp $$(pre-js.js.$(1)-$(2)) $$@
|
||||
@if [ sqlite3-wasmfs = $(1) ]; then \
|
||||
echo "delete Module[xNameOfInstantiateWasm] /*for WASMFS build*/;"; \
|
||||
elif [ sqlite3 != $(1) ]; then \
|
||||
echo "Module[xNameOfInstantiateWasm].uri = '$(1).wasm';"; \
|
||||
fi >> $$@
|
||||
pre-post-$(1).deps.$(2) := \
|
||||
$$(pre-post-jses.deps.$(2)) \
|
||||
pre-post-$(1)-$(2).deps := \
|
||||
$$(pre-post-jses.$(1)-$(2).deps) \
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
pre-post-$(1).flags.$(2) += \
|
||||
$$(pre-post-common.flags.$(2)) \
|
||||
pre-post-$(1)-$(2).flags += \
|
||||
$$(pre-post-common.flags.$(1)-$(2)) \
|
||||
--pre-js=$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
endef
|
||||
# /post-js and pre-js
|
||||
@@ -645,7 +651,8 @@ endef
|
||||
# https://github.com/emscripten-core/emscripten/issues/14383
|
||||
sqlite3.wasm := $(dir.dout)/sqlite3.wasm
|
||||
sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
|
||||
sqlite3-wasm.cses := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
sqlite3-wasm.cfiles := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
|
||||
# sqlite3-wasm.o vs sqlite3-wasm.c: building against the latter
|
||||
# (predictably) results in a slightly faster binary. We're close
|
||||
# enough to the target speed requirements that the 500ms makes a
|
||||
@@ -655,7 +662,8 @@ sqlite3-wasm.cses := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
# SQLITE3.xJS.EXPORT-DEFAULT is part of SQLITE3-WASMFS.xJS.RECIPE and
|
||||
# SETUP_LIB_BUILD_MODE, factored into a separate piece to avoid code
|
||||
# duplication. $1 is 1 if the build mode needs this workaround (esm,
|
||||
# bundler-friendly) and 0 if not (vanilla).
|
||||
# bundler-friendly, node) and 0 if not (vanilla). $2 must be empty for
|
||||
# all builds except sqlite3-wasmfs.mjs, in which case it must be 1.
|
||||
#
|
||||
# Reminder for ESM builds: even if we use -sEXPORT_ES6=0, emcc _still_
|
||||
# adds:
|
||||
@@ -671,11 +679,13 @@ sqlite3-wasm.cses := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
# https://github.com/emscripten-core/emscripten/issues/18237
|
||||
define SQLITE3.xJS.ESM-EXPORT-DEFAULT
|
||||
if [ x1 = x$(1) ]; then \
|
||||
echo "Fragile workaround for an Emscripten annoyance. See SQLITE3.xJS.RECIPE."; \
|
||||
echo "Fragile workaround for emscripten/issues/18237. See SQLITE3.xJS.RECIPE."; \
|
||||
sed -i -e '0,/^export default/{/^export default/d;}' $@ || exit $$?; \
|
||||
if ! grep -q '^export default' $@; then \
|
||||
echo "Cannot find export default." 1>&2; \
|
||||
exit 1; \
|
||||
if [ x != x$(2) ]; then \
|
||||
if ! grep -q '^export default' $@; then \
|
||||
echo "Cannot find export default." 1>&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
fi
|
||||
endef
|
||||
@@ -695,53 +705,57 @@ pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)
|
||||
# SETUP_LIB_BUILD_MODE is a $(call)'able which sets up numerous pieces
|
||||
# for one of the build modes.
|
||||
#
|
||||
# $1 = build mode name: one of $(JS_BUILD_MODES)
|
||||
# $2 = 1 for ESM build mode, else 0
|
||||
# $3 = resulting sqlite-api JS/MJS file
|
||||
# $4 = resulting JS/MJS file
|
||||
# $5 = -D... flags for $(bin.c-pp)
|
||||
# $6 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
|
||||
# $1 = one of: sqlite3, sqlite3-wasmfs
|
||||
# $2 = build mode name: one of $(JS_BUILD_MODES)
|
||||
# $3 = 1 for ESM build mode, else 0
|
||||
# $4 = resulting sqlite-api JS/MJS file
|
||||
# $5 = resulting JS/MJS file
|
||||
# $6 = -D... flags for $(bin.c-pp)
|
||||
# $7 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
|
||||
#
|
||||
# emcc.environment.$(1) must be set to a value for the -sENVIRONMENT flag.
|
||||
# Maintenance reminder: be careful not to introduce spaces around args
|
||||
# ($1, $2), otherwise string concatenation will malfunction.
|
||||
#
|
||||
# emcc.environment.$(2) must be set to a value for the -sENVIRONMENT flag.
|
||||
#
|
||||
# $(cflags.$(1)) and $(cflags.$(1).$(2)) may be defined to append
|
||||
# CFLAGS to a given build mode.
|
||||
#
|
||||
# $(emcc.flags.$(1)) and $(emcc.flags.$(1).$(2)) may be defined to
|
||||
# append emcc-specific flags to a given build mode.
|
||||
define SETUP_LIB_BUILD_MODE
|
||||
$(info Setting up build [$(1)]: $(4))
|
||||
c-pp.D.$(1) := $(5)
|
||||
pre-js.js.$(1) := $$(dir.tmp)/pre-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(pre-js.js.in),$$(pre-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
post-js.js.$(1) := $$(dir.tmp)/post-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(post-js.js.in),$$(post-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
extern-post-js.js.$(1) := $$(dir.tmp)/extern-post-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(extern-post-js.js.in),$$(extern-post-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
pre-post-common.flags.$(1) := \
|
||||
$$(pre-post-common.flags) \
|
||||
--post-js=$$(post-js.js.$(1)) \
|
||||
--extern-post-js=$$(extern-post-js.js.$(1))
|
||||
pre-post-jses.deps.$(1) := $$(pre-post-jses.deps.common) \
|
||||
$$(post-js.js.$(1)) $$(extern-post-js.js.$(1))
|
||||
$$(eval $$(call call-make-pre-post,sqlite3,$(1)))
|
||||
emcc.flags.sqlite3.$(1) := $(6)
|
||||
$$(eval $$(call C-PP.FILTER, $$(sqlite3-api.js.in), $(3), $(5)))
|
||||
$(4): $(3) $$(MAKEFILE) $$(sqlite3-wasm.cses) $$(EXPORTED_FUNCTIONS.api) $$(pre-post-sqlite3.deps.$(1))
|
||||
$(info Setting up build [$(1)-$(2)]: $(5))
|
||||
c-pp.D.$(1)-$(2) := $(6)
|
||||
$$(eval $$(call call-make-pre-post,$(1),$(2)))
|
||||
emcc.flags.$(1).$(2) ?=
|
||||
emcc.flags.$(1).$(2) += $(7)
|
||||
$$(eval $$(call C-PP.FILTER, $$(sqlite3-api.js.in), $(4), $(6)))
|
||||
$(5): $(4) $$(MAKEFILE) $$(sqlite3-wasm.cfiles) $$(EXPORTED_FUNCTIONS.api) $$(pre-post-$(1)-$(2).deps)
|
||||
@echo "Building $$@ ..."
|
||||
$$(emcc.bin) -o $$@ $$(emcc_opt_full) $$(emcc.flags) \
|
||||
$$(emcc.jsflags) \
|
||||
-sENVIRONMENT=$$(emcc.environment.$(1)) \
|
||||
$$(pre-post-sqlite3.flags.$(1)) $$(emcc.flags.sqlite3.$(1)) \
|
||||
$$(cflags.common) $$(SQLITE_OPT) $$(cflags.wasm_extra_init) $$(sqlite3-wasm.cses)
|
||||
@$$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,$(2))
|
||||
@case $(1) in \
|
||||
-sENVIRONMENT=$$(emcc.environment.$(2)) \
|
||||
$$(pre-post-$(1)-$(2).flags) \
|
||||
$$(emcc.flags.$(1)) $$(emcc.flags.$(1).$(2)) \
|
||||
$$(cflags.common) $$(SQLITE_OPT) \
|
||||
$$(cflags.$(1)) $$(cflags.$(1).$(2)) \
|
||||
$$(cflags.wasm_extra_init) $$(sqlite3-wasm.cfiles)
|
||||
@$$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,$(3))
|
||||
@dotwasm=$$(basename $$@).wasm; \
|
||||
chmod -x $$$$dotwasm; \
|
||||
$(maybe-wasm-strip) $$$$dotwasm; \
|
||||
case $(2) in \
|
||||
bundler-friendly|node) \
|
||||
echo "Patching $(3) for sqlite3.wasm..."; \
|
||||
rm -f $$(dir.dout)/sqlite3-$(1).wasm; \
|
||||
sed -i -e 's/sqlite3-$(1).wasm/sqlite3.wasm/g' $$@ || exit $$$$?; \
|
||||
echo "Patching $$@ for $(1).wasm..."; \
|
||||
rm -f $$$$dotwasm; \
|
||||
dotwasm=; \
|
||||
sed -i -e 's/$(1)-$(2).wasm/$(1).wasm/g' $$@ || exit $$$$?; \
|
||||
;; \
|
||||
esac
|
||||
chmod -x $$(sqlite3.wasm)
|
||||
$$(maybe-wasm-strip) $$(sqlite3.wasm)
|
||||
@ls -la $@ $$(sqlite3.wasm)
|
||||
all: $(4)
|
||||
quick: $(4)
|
||||
CLEAN_FILES += $(3) $(4)
|
||||
esac; \
|
||||
ls -la $$$$dotwasm $$@
|
||||
all: $(5)
|
||||
quick: $(5)
|
||||
CLEAN_FILES += $(4) $(5)
|
||||
endef
|
||||
# ^^^ /SETUP_LIB_BUILD_MODE
|
||||
########################################################################
|
||||
@@ -753,21 +767,24 @@ sqlite3-api-bundler-friendly.mjs := $(dir.dout)/sqlite3-api-bundler-friendly.mjs
|
||||
sqlite3-bundler-friendly.mjs := $(dir.dout)/sqlite3-bundler-friendly.mjs
|
||||
sqlite3-api-node.mjs := $(dir.dout)/sqlite3-api-node.mjs
|
||||
sqlite3-node.mjs := $(dir.dout)/sqlite3-node.mjs
|
||||
# Maintenance reminder: careful not to introduce spaces around args $1, $2
|
||||
#$(info $(call SETUP_LIB_BUILD_MODE,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,esm,1, $(sqlite3-api.mjs), $(sqlite3.mjs), \
|
||||
#$(info $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0,\
|
||||
$(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,esm,1,\
|
||||
$(sqlite3-api.mjs), $(sqlite3.mjs), \
|
||||
-Dtarget=es6-module, -sEXPORT_ES6 -sUSE_ES6_IMPORT_META))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,bundler-friendly,1,\
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,bundler-friendly,1,\
|
||||
$(sqlite3-api-bundler-friendly.mjs),$(sqlite3-bundler-friendly.mjs),\
|
||||
$(c-pp.D.esm) -Dtarget=es6-bundler-friendly))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,node,1,\
|
||||
$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,node,1,\
|
||||
$(sqlite3-api-node.mjs),$(sqlite3-node.mjs),\
|
||||
$(c-pp.D.bundler-friendly) -Dtarget=node))
|
||||
$(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node))
|
||||
# The various -D... values used by *.c-pp.js include:
|
||||
#
|
||||
# -Dtarget=es6-module: for all ESM module builds
|
||||
#
|
||||
# -Dtarget=node: for node.js builds
|
||||
#
|
||||
# -Dtarget=es6-module -Dtarget=es6-bundler-friendly: intended for
|
||||
# "bundler-friendly" ESM module build. These have some restrictions
|
||||
# on how URL() objects are constructed in some contexts: URLs which
|
||||
@@ -849,7 +866,7 @@ all: batch
|
||||
# emcc.speedtest1.common = emcc flags used by multiple builds of speedtest1
|
||||
# emcc.speedtest1 = emcc flags used by main build of speedtest1
|
||||
emcc.speedtest1.common := $(emcc_opt_full)
|
||||
emcc.speedtest1 :=
|
||||
emcc.speedtest1 := -I. -I$(dir $(sqlite3.canonical.c))
|
||||
emcc.speedtest1 += -sENVIRONMENT=web
|
||||
emcc.speedtest1 += -sALLOW_MEMORY_GROWTH
|
||||
emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
|
||||
@@ -892,22 +909,24 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api.main)
|
||||
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.main); } > $@
|
||||
speedtest1.js := $(dir.dout)/speedtest1.js
|
||||
speedtest1.wasm := $(dir.dout)/speedtest1.wasm
|
||||
cflags.speedtest1 := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
speedtest1.cses := $(speedtest1.c) $(sqlite3-wasm.c)
|
||||
emcc.flags.speedtest1-vanilla := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
|
||||
speedtest1.cfiles := $(speedtest1.c) $(sqlite3-wasm.c)
|
||||
$(eval $(call call-make-pre-post,speedtest1,vanilla))
|
||||
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cses) \
|
||||
$(pre-post-speedtest1.deps.vanilla) \
|
||||
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
|
||||
$(pre-post-speedtest1-vanilla.deps) \
|
||||
$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) \
|
||||
$(emcc.speedtest1) -I$(dir $(sqlite3.canonical.c)) \
|
||||
$(emcc.speedtest1) \
|
||||
$(emcc.speedtest1.common) \
|
||||
$(cflags.speedtest1) $(pre-post-speedtest1.flags.vanilla) \
|
||||
$(emcc.flags.speedtest1-vanilla) $(pre-post-speedtest1-vanilla.flags) \
|
||||
$(SQLITE_OPT) \
|
||||
-USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c) \
|
||||
$(speedtest1.exit-runtime0) \
|
||||
-o $@ $(speedtest1.cses) -lm
|
||||
-o $@ $(speedtest1.cfiles) -lm
|
||||
$(maybe-wasm-strip) $(speedtest1.wasm)
|
||||
chmod -x $(speedtest1.wasm)
|
||||
ls -la $@ $(speedtest1.wasm)
|
||||
|
||||
speedtest1: $(speedtest1.js)
|
||||
@@ -933,9 +952,9 @@ CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
|
||||
#
|
||||
# To create those, we filter tester1.c-pp.js with $(bin.c-pp)...
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.js))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm)))
|
||||
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
|
||||
# Note that we do not include $(sqlite3-bundler-friendly.mjs) in this
|
||||
# because bundlers are client-specific.
|
||||
@@ -981,6 +1000,8 @@ include fiddle.make
|
||||
ifneq (,$(filter wasmfs,$(MAKECMDGOALS)))
|
||||
wasmfs.enable ?= 1
|
||||
else
|
||||
# Unconditionally enable wasmfs for [dist]clean so that the wasmfs
|
||||
# sub-make can clean up.
|
||||
wasmfs.enable ?= $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
|
||||
endif
|
||||
ifeq (1,$(wasmfs.enable))
|
||||
|
||||
@@ -23,10 +23,7 @@ const toExportForESM =
|
||||
impls which Emscripten installs at some point in the file above
|
||||
this.
|
||||
*/
|
||||
const originalInit =
|
||||
/* Maintenance reminder: DO NOT use `self.` here. It's correct
|
||||
for non-ES6 Module cases but wrong for ES6 modules because those
|
||||
resolve this symbol differently. */ sqlite3InitModule;
|
||||
const originalInit = sqlite3InitModule;
|
||||
if(!originalInit){
|
||||
throw new Error("Expecting globalThis.sqlite3InitModule to be defined by the Emscripten build.");
|
||||
}
|
||||
@@ -66,18 +63,16 @@ const toExportForESM =
|
||||
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
|
||||
return originalInit(...args).then((EmscriptenModule)=>{
|
||||
if('undefined'!==typeof WorkerGlobalScope &&
|
||||
(EmscriptenModule['ENVIRONMENT_IS_PTHREAD']
|
||||
|| EmscriptenModule['_pthread_self']
|
||||
|| 'function'===typeof threadAlert
|
||||
|| globalThis?.location?.pathname?.endsWith?.('.worker.js')
|
||||
)){
|
||||
EmscriptenModule['ENVIRONMENT_IS_PTHREAD']){
|
||||
/** Workaround for wasmfs-generated worker, which calls this
|
||||
routine from each individual thread and requires that its
|
||||
argument be returned. All of the criteria above are fragile,
|
||||
based solely on inspection of the offending code, not public
|
||||
Emscripten details. */
|
||||
argument be returned. The conditional criteria above are
|
||||
fragile, based solely on inspection of the offending code,
|
||||
not public Emscripten details. */
|
||||
//console.warn("sqlite3InitModule() returning E-module.",EmscriptenModule);
|
||||
return EmscriptenModule;
|
||||
}
|
||||
//console.warn("sqlite3InitModule() returning sqlite3 object.");
|
||||
const s = EmscriptenModule.sqlite3;
|
||||
s.scriptInfo = initModuleState;
|
||||
//console.warn("sqlite3.scriptInfo =",s.scriptInfo);
|
||||
@@ -124,5 +119,5 @@ const toExportForESM =
|
||||
return globalThis.sqlite3InitModule /* required for ESM */;
|
||||
})();
|
||||
//#if target=es6-module
|
||||
export default toExportForESM;
|
||||
export { toExportForESM as default, toExportForESM as sqlite3InitModule }
|
||||
//#endif
|
||||
|
||||
@@ -23,7 +23,7 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
|
||||
const SABC = Object.assign(
|
||||
Object.create(null), {
|
||||
exports: Module['asm'],
|
||||
memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */
|
||||
memory: Module.wasmMemory /* gets set if built with -sIMPORTED_MEMORY */
|
||||
},
|
||||
globalThis.sqlite3ApiConfig || {}
|
||||
);
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
- `memory`[^1]: optional WebAssembly.Memory object, defaulting to
|
||||
`exports.memory`. In Emscripten environments this should be set
|
||||
to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be
|
||||
to `Module.wasmMemory` if the build uses `-sIMPORTED_MEMORY`, or be
|
||||
left undefined/falsy to default to `exports.memory` when using
|
||||
WASM-exported memory.
|
||||
|
||||
@@ -92,8 +92,8 @@
|
||||
currently work due to incompatible Emscripten-side changes made
|
||||
in the WASMFS+OPFS combination. This option is currently ignored.
|
||||
|
||||
[^1] = This property may optionally be a function, in which case this
|
||||
function re-assigns calls that function to fetch the value,
|
||||
[^1] = This property may optionally be a function, in which case
|
||||
this function calls that function to fetch the value,
|
||||
enabling delayed evaluation.
|
||||
|
||||
The returned object is the top-level sqlite3 namespace object.
|
||||
@@ -149,11 +149,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
config[k] = config[k]();
|
||||
}
|
||||
});
|
||||
config.wasmOpfsDir =
|
||||
/* 2022-12-17: WASMFS+OPFS can no longer be activated from the
|
||||
main thread (aborts via a failed assert() if it's attempted),
|
||||
which eliminates any(?) benefit to supporting it. */ false;
|
||||
|
||||
/**
|
||||
The main sqlite3 binding API gets installed into this object,
|
||||
mimicking the C API as closely as we can. The numerous members
|
||||
@@ -809,7 +804,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
|| toss3("Missing API config.exports (WASM module exports)."),
|
||||
|
||||
/**
|
||||
When Emscripten compiles with `-sIMPORT_MEMORY`, it
|
||||
When Emscripten compiles with `-sIMPORTED_MEMORY`, it
|
||||
initalizes the heap and imports it into wasm, as opposed to
|
||||
the other way around. In this case, the memory is not
|
||||
available via this.exports.memory.
|
||||
@@ -1177,31 +1172,31 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
/** State for sqlite3_wasmfs_opfs_dir(). */
|
||||
let __wasmfsOpfsDir = undefined;
|
||||
/**
|
||||
2022-12-17: incompatible WASMFS changes have made WASMFS+OPFS
|
||||
unavailable from the main thread, which eliminates the most
|
||||
significant benefit of supporting WASMFS. This function is now a
|
||||
no-op which always returns a falsy value. Before that change,
|
||||
this function behaved as documented below (and how it will again
|
||||
if we can find a compelling reason to support it).
|
||||
|
||||
If the wasm environment has a WASMFS/OPFS-backed persistent
|
||||
storage directory, its path is returned by this function. If it
|
||||
does not then it returns "" (noting that "" is a falsy value).
|
||||
|
||||
The first time this is called, this function inspects the current
|
||||
environment to determine whether persistence support is available
|
||||
and, if it is, enables it (if needed).
|
||||
and, if it is, enables it (if needed). After the first call it
|
||||
always returns the cached result.
|
||||
|
||||
This function currently only recognizes the WASMFS/OPFS storage
|
||||
combination and its path refers to storage rooted in the
|
||||
Emscripten-managed virtual filesystem.
|
||||
If the returned string is not empty, any files stored under the
|
||||
given path (recursively) are housed in OPFS storage. If the
|
||||
returned string is empty, this particular persistent storage
|
||||
option is not available on the client.
|
||||
|
||||
Though the mount point name returned by this function is intended
|
||||
to remain stable, clients should not hard-coded it anywhere. Always call this function to get the path.
|
||||
|
||||
Note that this function is a no-op in must builds of this
|
||||
library, as the WASMFS capability requires a custom
|
||||
build.
|
||||
*/
|
||||
capi.sqlite3_wasmfs_opfs_dir = function(){
|
||||
if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir;
|
||||
// If we have no OPFS, there is no persistent dir
|
||||
const pdir = config.wasmfsOpfsDir;
|
||||
console.error("sqlite3_wasmfs_opfs_dir() can no longer work due "+
|
||||
"to incompatible WASMFS changes. It will be removed.");
|
||||
if(!pdir
|
||||
|| !globalThis.FileSystemHandle
|
||||
|| !globalThis.FileSystemDirectoryHandle
|
||||
@@ -1223,8 +1218,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
Experimental and subject to change or removal.
|
||||
|
||||
Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a
|
||||
non-empty string and the given name starts with (that string +
|
||||
'/'), else returns false.
|
||||
@@ -1234,13 +1227,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
return (p && name) ? name.startsWith(p+'/') : false;
|
||||
};
|
||||
|
||||
// This bit is highly arguable and is incompatible with the fiddle shell.
|
||||
if(false && 0===wasm.exports.sqlite3_vfs_find(0)){
|
||||
/* Assume that sqlite3_initialize() has not yet been called.
|
||||
This will be the case in an SQLITE_OS_KV build. */
|
||||
wasm.exports.sqlite3_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name
|
||||
(defaulting to "main"), returns a truthy value (see below) if
|
||||
|
||||
@@ -118,11 +118,11 @@ const installOpfsVfs = function callee(options){
|
||||
options.proxyUri = options.proxyUri();
|
||||
}
|
||||
const thePromise = new Promise(function(promiseResolve_, promiseReject_){
|
||||
const loggers = {
|
||||
0:sqlite3.config.error,
|
||||
1:sqlite3.config.warn,
|
||||
2:sqlite3.config.log
|
||||
};
|
||||
const loggers = [
|
||||
sqlite3.config.error,
|
||||
sqlite3.config.warn,
|
||||
sqlite3.config.log
|
||||
];
|
||||
const logImpl = (level,...args)=>{
|
||||
if(options.verbose>level) loggers[level]("OPFS syncer:",...args);
|
||||
};
|
||||
@@ -191,8 +191,9 @@ const installOpfsVfs = function callee(options){
|
||||
s.count = s.time = 0;
|
||||
}
|
||||
}/*metrics*/;
|
||||
const opfsVfs = new sqlite3_vfs();
|
||||
const opfsIoMethods = new sqlite3_io_methods();
|
||||
const opfsVfs = new sqlite3_vfs()
|
||||
.addOnDispose( ()=>opfsIoMethods.dispose());
|
||||
let promiseWasRejected = undefined;
|
||||
const promiseReject = (err)=>{
|
||||
promiseWasRejected = true;
|
||||
@@ -241,11 +242,10 @@ const installOpfsVfs = function callee(options){
|
||||
opfsVfs.$zName = wasm.allocCString("opfs");
|
||||
// All C-side memory of opfsVfs is zeroed out, but just to be explicit:
|
||||
opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null;
|
||||
opfsVfs.ondispose = [
|
||||
opfsVfs.addOnDispose(
|
||||
'$zName', opfsVfs.$zName,
|
||||
'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null),
|
||||
'cleanup opfsIoMethods', ()=>opfsIoMethods.dispose()
|
||||
];
|
||||
'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null)
|
||||
);
|
||||
/**
|
||||
Pedantic sidebar about opfsVfs.ondispose: the entries in that array
|
||||
are items to clean up when opfsVfs.dispose() is called, but in this
|
||||
@@ -298,6 +298,7 @@ const installOpfsVfs = function callee(options){
|
||||
lock contention to free up.
|
||||
*/
|
||||
state.asyncIdleWaitTime = 150;
|
||||
|
||||
/**
|
||||
Whether the async counterpart should log exceptions to
|
||||
the serialization channel. That produces a great deal of
|
||||
@@ -832,22 +833,19 @@ const installOpfsVfs = function callee(options){
|
||||
/* If it turns out that we need to adjust for timezone, see:
|
||||
https://stackoverflow.com/a/11760121/1458521 */
|
||||
wasm.poke(pOut, 2440587.5 + (new Date().getTime()/86400000),
|
||||
'double');
|
||||
'double');
|
||||
return 0;
|
||||
},
|
||||
xCurrentTimeInt64: function(pVfs,pOut){
|
||||
// TODO: confirm that this calculation is correct
|
||||
wasm.poke(pOut, (2440587.5 * 86400000) + new Date().getTime(),
|
||||
'i64');
|
||||
'i64');
|
||||
return 0;
|
||||
},
|
||||
xDelete: function(pVfs, zName, doSyncDir){
|
||||
mTimeStart('xDelete');
|
||||
opRun('xDelete', wasm.cstrToJs(zName), doSyncDir, false);
|
||||
/* We're ignoring errors because we cannot yet differentiate
|
||||
between harmless and non-harmless failures. */
|
||||
const rc = opRun('xDelete', wasm.cstrToJs(zName), doSyncDir, false);
|
||||
mTimeEnd();
|
||||
return 0;
|
||||
return rc;
|
||||
},
|
||||
xFullPathname: function(pVfs,zName,nOut,pOut){
|
||||
/* Until/unless we have some notion of "current dir"
|
||||
@@ -1089,7 +1087,7 @@ const installOpfsVfs = function callee(options){
|
||||
propagate any exception on error, rather than returning false.
|
||||
*/
|
||||
opfsUtil.unlink = async function(fsEntryName, recursive = false,
|
||||
throwOnError = false){
|
||||
throwOnError = false){
|
||||
try {
|
||||
const [hDir, filenamePart] =
|
||||
await opfsUtil.getDirForFilename(fsEntryName, false);
|
||||
@@ -1186,19 +1184,23 @@ const installOpfsVfs = function callee(options){
|
||||
contention. */
|
||||
sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000);
|
||||
sqlite3.capi.sqlite3_exec(oo1Db, [
|
||||
/* Truncate journal mode is faster than delete for
|
||||
this vfs, per speedtest1. That gap seems to have closed with
|
||||
Chrome version 108 or 109, but "persist" is very roughly 5-6%
|
||||
faster than truncate in initial tests.
|
||||
/* As of July 2023, the PERSIST journal mode on OPFS is
|
||||
somewhat slower than DELETE or TRUNCATE (it was faster
|
||||
before Chrome version 108 or 109). TRUNCATE and DELETE
|
||||
have very similar performance on OPFS.
|
||||
|
||||
For later analysis: Roy Hashimoto notes that TRUNCATE
|
||||
and PERSIST modes may decrease OPFS concurrency because
|
||||
multiple connections can open the journal file in those
|
||||
modes:
|
||||
Roy Hashimoto notes that TRUNCATE and PERSIST modes may
|
||||
decrease OPFS concurrency because multiple connections
|
||||
can open the journal file in those modes:
|
||||
|
||||
https://github.com/rhashimoto/wa-sqlite/issues/68
|
||||
|
||||
Given that, and the fact that testing has not revealed
|
||||
any appreciable difference between performance of
|
||||
TRUNCATE and DELETE modes on OPFS, we currently (as of
|
||||
2023-07-13) default to DELETE mode.
|
||||
*/
|
||||
"pragma journal_mode=persist;",
|
||||
"pragma journal_mode=DELETE;",
|
||||
/*
|
||||
This vfs benefits hugely from cache on moderate/large
|
||||
speedtest1 --size 50 and --size 100 workloads. We
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
Dummy function stubs to get sqlite3.c compiling with
|
||||
wasi-sdk. This requires, in addition:
|
||||
|
||||
-D_WASI_EMULATED_MMAN -D_WASI_EMULATED_GETPID
|
||||
|
||||
-lwasi-emulated-getpid
|
||||
*/
|
||||
typedef unsigned mode_t;
|
||||
int fchmod(int fd, mode_t mode);
|
||||
int fchmod(int fd, mode_t mode){
|
||||
return (fd && mode) ? 0 : 0;
|
||||
}
|
||||
typedef unsigned uid_t;
|
||||
typedef uid_t gid_t;
|
||||
int fchown(int fd, uid_t owner, gid_t group);
|
||||
int fchown(int fd, uid_t owner, gid_t group){
|
||||
return (fd && owner && group) ? 0 : 0;
|
||||
}
|
||||
uid_t geteuid(void);
|
||||
uid_t geteuid(void){return 0;}
|
||||
#if !defined(F_WRLCK)
|
||||
enum {
|
||||
F_WRLCK,
|
||||
F_RDLCK,
|
||||
F_GETLK,
|
||||
F_SETLK,
|
||||
F_UNLCK
|
||||
};
|
||||
#endif
|
||||
|
||||
#undef HAVE_PREAD
|
||||
|
||||
#include <wasi/api.h>
|
||||
#define WASM__KEEP __attribute__((used))
|
||||
|
||||
#if 0
|
||||
/**
|
||||
wasi-sdk cannot build sqlite3's default VFS without at least the following
|
||||
functions. They are apparently syscalls which clients have to implement or
|
||||
otherwise obtain.
|
||||
|
||||
https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md
|
||||
*/
|
||||
environ_get
|
||||
environ_sizes_get
|
||||
clock_time_get
|
||||
fd_close
|
||||
fd_fdstat_get
|
||||
fd_fdstat_set_flags
|
||||
fd_filestat_get
|
||||
fd_filestat_set_size
|
||||
fd_pread
|
||||
fd_prestat_get
|
||||
fd_prestat_dir_name
|
||||
fd_read
|
||||
fd_seek
|
||||
fd_sync
|
||||
fd_write
|
||||
path_create_directory
|
||||
path_filestat_get
|
||||
path_filestat_set_times
|
||||
path_open
|
||||
path_readlink
|
||||
path_remove_directory
|
||||
path_unlink_file
|
||||
poll_oneoff
|
||||
proc_exit
|
||||
#endif
|
||||
@@ -174,7 +174,7 @@
|
||||
globalThis.WhWasmUtilInstaller = function(target){
|
||||
'use strict';
|
||||
if(undefined===target.bigIntEnabled){
|
||||
target.bigIntEnabled = !!self['BigInt64Array'];
|
||||
target.bigIntEnabled = !!globalThis['BigInt64Array'];
|
||||
}
|
||||
|
||||
/** Throws a new Error, the message of which is the concatenation of
|
||||
@@ -355,8 +355,8 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
break;
|
||||
default:
|
||||
if(target.bigIntEnabled){
|
||||
if(n===self['BigUint64Array']) return c.HEAP64U;
|
||||
else if(n===self['BigInt64Array']) return c.HEAP64;
|
||||
if(n===globalThis['BigUint64Array']) return c.HEAP64U;
|
||||
else if(n===globalThis['BigInt64Array']) return c.HEAP64;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1656,25 +1656,11 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
? opt.callProxy : undefined;
|
||||
}
|
||||
|
||||
/** If true, the constructor emits a warning. The intent is that
|
||||
this be set to true after bootstrapping of the higher-level
|
||||
client library is complete, to warn downstream clients that
|
||||
they shouldn't be relying on this implemenation detail which
|
||||
does not have a stable interface. */
|
||||
static warnOnUse = false;
|
||||
|
||||
/** If true, convertArg() will FuncPtrAdapter.debugOut() when it
|
||||
(un)installs a function binding to/from WASM. Note that
|
||||
deinstallation of bindScope=transient bindings happens
|
||||
via scopedAllocPop() so will not be output. */
|
||||
static debugFuncInstall = false;
|
||||
|
||||
/** Function used for debug output. */
|
||||
static debugOut = console.debug.bind(console);
|
||||
|
||||
static bindScopes = [
|
||||
'transient', 'context', 'singleton', 'permanent'
|
||||
];
|
||||
/**
|
||||
Note that static class members are defined outside of the class
|
||||
to work around an emcc toolchain build problem: one of the
|
||||
tools in emsdk v3.1.42 does not support the static keyword.
|
||||
*/
|
||||
|
||||
/* Dummy impl. Overwritten per-instance as needed. */
|
||||
contextKey(argv,argIndex){
|
||||
@@ -1761,6 +1747,26 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
}/*convertArg()*/
|
||||
}/*FuncPtrAdapter*/;
|
||||
|
||||
/** If true, the constructor emits a warning. The intent is that
|
||||
this be set to true after bootstrapping of the higher-level
|
||||
client library is complete, to warn downstream clients that
|
||||
they shouldn't be relying on this implemenation detail which
|
||||
does not have a stable interface. */
|
||||
xArg.FuncPtrAdapter.warnOnUse = false;
|
||||
|
||||
/** If true, convertArg() will FuncPtrAdapter.debugOut() when it
|
||||
(un)installs a function binding to/from WASM. Note that
|
||||
deinstallation of bindScope=transient bindings happens
|
||||
via scopedAllocPop() so will not be output. */
|
||||
xArg.FuncPtrAdapter.debugFuncInstall = false;
|
||||
|
||||
/** Function used for debug output. */
|
||||
xArg.FuncPtrAdapter.debugOut = console.debug.bind(console);
|
||||
|
||||
xArg.FuncPtrAdapter.bindScopes = [
|
||||
'transient', 'context', 'singleton', 'permanent'
|
||||
];
|
||||
|
||||
const __xArgAdapterCheck =
|
||||
(t)=>xArg.get(t) || toss("Argument adapter not found:",t);
|
||||
|
||||
|
||||
+12
-11
@@ -93,8 +93,8 @@
|
||||
<ul>
|
||||
<li><a href='speedtest1.html'>speedtest1</a>: a main-thread WASM build of speedtest1.</li>
|
||||
<li><a href='speedtest1.html?vfs=kvvfs'>speedtest1?vfs=kvvfs</a>: speedtest1 with the kvvfs.</li>
|
||||
<li><a href='speedtest1-worker.html?size=25'>speedtest1-worker</a>: an interactive Worker-thread variant of speedtest1.</li>
|
||||
<li><a href='speedtest1-worker.html?vfs=opfs&size=25'>speedtest1-worker?vfs=opfs</a>: speedtest1-worker with the
|
||||
<li><a href='speedtest1-worker.html?size=15'>speedtest1-worker</a>: an interactive Worker-thread variant of speedtest1.</li>
|
||||
<li><a href='speedtest1-worker.html?vfs=opfs&size=10'>speedtest1-worker?vfs=opfs</a>: speedtest1-worker with the
|
||||
OPFS VFS preselected and configured for a moderate workload.</li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -117,18 +117,19 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!--li>WASMFS-specific tests which currently do not work due to incompatible changes
|
||||
made to the WASMFS+OPFS combination.
|
||||
<li><strong>WASMFS</strong>-specific tests which require that
|
||||
the WASMFS build is available on this server (it is not by
|
||||
default) and that this server emits the COOP/COEP headers.
|
||||
<ul>
|
||||
<li><a href='speedtest1-wasmfs.html?flags=--size,25'>speedtest1-wasmfs</a>:
|
||||
<li><a href='scratchpad-wasmfs.html'>scratchpad-wasmfs</a>:
|
||||
experimenting with WASMFS/OPFS-based persistence.
|
||||
</li>
|
||||
<li><a href='speedtest1-wasmfs.html?flags=--size,15'>speedtest1-wasmfs</a>:
|
||||
a variant of speedtest1 built solely for the wasmfs/opfs
|
||||
feature.</li>
|
||||
<li><a href='scratchpad-wasmfs-main.html'>scratchpad-wasmfs-main</a>:
|
||||
experimenting with WASMFS/OPFS-based persistence. Maintenance
|
||||
reminder: we cannot currently (2022-09-15) load WASMFS in a
|
||||
worker due to an Emscripten wasm loader limitation.</li>
|
||||
feature.
|
||||
</li>
|
||||
</ul>
|
||||
</li-->
|
||||
</li>
|
||||
<!--li><a href='x.html'></a></li-->
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -60,9 +60,9 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
|
||||
memberPrefix = (config.memberPrefix || ""),
|
||||
memberSuffix = (config.memberSuffix || ""),
|
||||
bigIntEnabled = (undefined===config.bigIntEnabled
|
||||
? !!self['BigInt64Array'] : !!config.bigIntEnabled),
|
||||
BigInt = self['BigInt'],
|
||||
BigInt64Array = self['BigInt64Array'],
|
||||
? !!globalThis['BigInt64Array'] : !!config.bigIntEnabled),
|
||||
BigInt = globalThis['BigInt'],
|
||||
BigInt64Array = globalThis['BigInt64Array'],
|
||||
/* Undocumented (on purpose) config options: */
|
||||
ptrSizeof = config.ptrSizeof || 4,
|
||||
ptrIR = config.ptrIR || 'i32'
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
2022-05-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.
|
||||
|
||||
***********************************************************************
|
||||
|
||||
A basic test script for sqlite3-api.js. This file must be run in
|
||||
main JS thread and sqlite3.js must have been loaded before it.
|
||||
*/
|
||||
'use strict';
|
||||
(function(){
|
||||
const toss = function(...args){throw new Error(args.join(' '))};
|
||||
const log = console.log.bind(console),
|
||||
warn = console.warn.bind(console),
|
||||
error = console.error.bind(console);
|
||||
|
||||
const stdout = log;
|
||||
const stderr = error;
|
||||
|
||||
const test1 = function(db){
|
||||
db.exec("create table if not exists t(a);")
|
||||
.transaction(function(db){
|
||||
db.prepare("insert into t(a) values(?)")
|
||||
.bind(new Date().getTime())
|
||||
.stepFinalize();
|
||||
stdout("Number of values in table t:",
|
||||
db.selectValue("select count(*) from t"));
|
||||
});
|
||||
};
|
||||
|
||||
const runTests = function(sqlite3){
|
||||
const capi = sqlite3.capi,
|
||||
oo = sqlite3.oo1,
|
||||
wasm = sqlite3.wasm;
|
||||
stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
|
||||
const persistentDir = capi.sqlite3_wasmfs_opfs_dir();
|
||||
if(persistentDir){
|
||||
stdout("Persistent storage dir:",persistentDir);
|
||||
}else{
|
||||
stderr("No persistent storage available.");
|
||||
}
|
||||
const startTime = performance.now();
|
||||
let db;
|
||||
try {
|
||||
db = new oo.DB(persistentDir+'/foo.db');
|
||||
stdout("DB filename:",db.filename);
|
||||
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
|
||||
banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
|
||||
[
|
||||
test1
|
||||
].forEach((f)=>{
|
||||
const n = performance.now();
|
||||
stdout(banner1,"Running",f.name+"()...");
|
||||
f(db, sqlite3);
|
||||
stdout(banner2,f.name+"() took ",(performance.now() - n),"ms");
|
||||
});
|
||||
}finally{
|
||||
if(db) db.close();
|
||||
}
|
||||
stdout("Total test time:",(performance.now() - startTime),"ms");
|
||||
};
|
||||
|
||||
sqlite3InitModule(self.sqlite3TestModule).then(runTests);
|
||||
})();
|
||||
@@ -10,20 +10,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<header id='titlebar'><span>sqlite3 WASMFS/OPFS Main-thread Scratchpad</span></header>
|
||||
<!-- emscripten bits -->
|
||||
<figure id="module-spinner">
|
||||
<div class="spinner"></div>
|
||||
<div class='center'><strong>Initializing app...</strong></div>
|
||||
<div class='center'>
|
||||
On a slow internet connection this may take a moment. If this
|
||||
message displays for "a long time", intialization may have
|
||||
failed and the JavaScript console may contain clues as to why.
|
||||
</div>
|
||||
</figure>
|
||||
<div class="emscripten" id="module-status">Downloading...</div>
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
|
||||
</div><!-- /emscripten bits -->
|
||||
<p>Scratchpad/test app for the WASMF/OPFS integration in the
|
||||
main window thread. This page requires that the sqlite3 API have
|
||||
been built with WASMFS support. If OPFS support is available then
|
||||
@@ -33,8 +19,12 @@
|
||||
<p>All stuff on this page happens in the dev console.</p>
|
||||
<hr>
|
||||
<div id='test-output'></div>
|
||||
<script src="sqlite3-wasmfs.js"></script>
|
||||
<script src="common/SqliteTestUtil.js"></script>
|
||||
<script src="scratchpad-wasmfs-main.js"></script>
|
||||
<script>
|
||||
(function(){
|
||||
const W = new Worker('scratchpad-wasmfs.mjs',{
|
||||
type: 'module'
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
2022-05-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.
|
||||
|
||||
***********************************************************************
|
||||
|
||||
A basic test script for sqlite3-api.js. This file must be run in
|
||||
main JS thread and sqlite3.js must have been loaded before it.
|
||||
*/
|
||||
import sqlite3InitModule from './jswasm/sqlite3-wasmfs.mjs';
|
||||
//console.log('sqlite3InitModule =',sqlite3InitModule);
|
||||
const toss = function(...args){throw new Error(args.join(' '))};
|
||||
const log = console.log.bind(console),
|
||||
warn = console.warn.bind(console),
|
||||
error = console.error.bind(console);
|
||||
|
||||
const stdout = log;
|
||||
const stderr = error;
|
||||
|
||||
const test1 = function(db){
|
||||
db.exec("create table if not exists t(a);")
|
||||
.transaction(function(db){
|
||||
db.prepare("insert into t(a) values(?)")
|
||||
.bind(new Date().getTime())
|
||||
.stepFinalize();
|
||||
stdout("Number of values in table t:",
|
||||
db.selectValue("select count(*) from t"));
|
||||
});
|
||||
};
|
||||
|
||||
const runTests = function(sqlite3){
|
||||
const capi = sqlite3.capi,
|
||||
oo = sqlite3.oo1,
|
||||
wasm = sqlite3.wasm;
|
||||
stdout("Loaded module:",sqlite3);
|
||||
stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
|
||||
const persistentDir = capi.sqlite3_wasmfs_opfs_dir();
|
||||
if(persistentDir){
|
||||
stdout("Persistent storage dir:",persistentDir);
|
||||
}else{
|
||||
stderr("No persistent storage available.");
|
||||
}
|
||||
const startTime = performance.now();
|
||||
let db;
|
||||
try {
|
||||
db = new oo.DB(persistentDir+'/foo.db');
|
||||
stdout("DB filename:",db.filename);
|
||||
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
|
||||
banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
|
||||
[
|
||||
test1
|
||||
].forEach((f)=>{
|
||||
const n = performance.now();
|
||||
stdout(banner1,"Running",f.name+"()...");
|
||||
f(db, sqlite3);
|
||||
stdout(banner2,f.name+"() took ",(performance.now() - n),"ms");
|
||||
});
|
||||
}finally{
|
||||
if(db) db.close();
|
||||
}
|
||||
stdout("Total test time:",(performance.now() - startTime),"ms");
|
||||
};
|
||||
|
||||
sqlite3InitModule().then(runTests);
|
||||
+36
-131
@@ -10,141 +10,46 @@
|
||||
</head>
|
||||
<body>
|
||||
<header id='titlebar'><span>speedtest1-wasmfs.wasm</span></header>
|
||||
<div>See also: <a href='speedtest1-worker.html'>A Worker-thread variant of this page.</a></div>
|
||||
<!-- emscripten bits -->
|
||||
<figure id="module-spinner">
|
||||
<div class="spinner"></div>
|
||||
<div class='center'><strong>Initializing app...</strong></div>
|
||||
<div class='center'>
|
||||
On a slow internet connection this may take a moment. If this
|
||||
message displays for "a long time", intialization may have
|
||||
failed and the JavaScript console may contain clues as to why.
|
||||
</div>
|
||||
</figure>
|
||||
<div class="emscripten" id="module-status">Downloading...</div>
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
|
||||
</div><!-- /emscripten bits -->
|
||||
<div class='warning'>This page starts running the main exe when it loads, which will
|
||||
block the UI until it finishes! Adding UI controls to manually configure and start it
|
||||
are TODO.</div>
|
||||
</div>
|
||||
<div>See also: <a href='speedtest1-worker.html'>speedtest1-worker</a></div>
|
||||
<div class='warning'>Achtung: running it with the dev tools open may
|
||||
<em>drastically</em> slow it down. For faster results, keep the dev
|
||||
tools closed when running it!
|
||||
</div>
|
||||
<div>Output is delayed/buffered because we cannot update the UI while the
|
||||
speedtest is running. Output will appear below when ready...
|
||||
<div id='test-output'></div>
|
||||
<script src="common/SqliteTestUtil.js"></script>
|
||||
<script src="speedtest1-wasmfs.js"></script>
|
||||
<script>(function(){
|
||||
/**
|
||||
If this environment contains OPFS, this function initializes it and
|
||||
returns the name of the dir on which OPFS is mounted, else it returns
|
||||
an empty string.
|
||||
*/
|
||||
const wasmfsDir = function f(wasmUtil,dirName="/opfs"){
|
||||
if(undefined !== f._) return f._;
|
||||
if( !self.FileSystemHandle
|
||||
|| !self.FileSystemDirectoryHandle
|
||||
|| !self.FileSystemFileHandle){
|
||||
return f._ = "";
|
||||
}
|
||||
try{
|
||||
if(0===wasmUtil.xCallWrapped(
|
||||
'sqlite3_wasm_init_wasmfs', 'i32', ['string'], dirName
|
||||
)){
|
||||
return f._ = dirName;
|
||||
}else{
|
||||
return f._ = "";
|
||||
}
|
||||
}catch(e){
|
||||
// sqlite3_wasm_init_wasmfs() is not available
|
||||
return f._ = "";
|
||||
}
|
||||
};
|
||||
wasmfsDir._ = undefined;
|
||||
|
||||
const eOut = document.querySelector('#test-output');
|
||||
const log2 = function(cssClass,...args){
|
||||
const ln = document.createElement('div');
|
||||
if(cssClass) ln.classList.add(cssClass);
|
||||
ln.append(document.createTextNode(args.join(' ')));
|
||||
eOut.append(ln);
|
||||
//this.e.output.lastElementChild.scrollIntoViewIfNeeded();
|
||||
};
|
||||
const logList = [];
|
||||
const dumpLogList = function(){
|
||||
logList.forEach((v)=>log2('',v));
|
||||
logList.length = 0;
|
||||
};
|
||||
/* can't update DOM while speedtest is running unless we run
|
||||
speedtest in a worker thread. */;
|
||||
const log = (...args)=>{
|
||||
console.log(...args);
|
||||
logList.push(args.join(' '));
|
||||
};
|
||||
const logErr = function(...args){
|
||||
console.error(...args);
|
||||
logList.push('ERROR: '+args.join(' '));
|
||||
};
|
||||
|
||||
const runTests = function(sqlite3){
|
||||
console.log("Module inited.");
|
||||
const wasm = sqlite3.wasm;
|
||||
const __unlink = wasm.xWrap("sqlite3_wasm_vfs_unlink", "int", ["*","string"]);
|
||||
const unlink = (fn)=>__unlink(0,fn);
|
||||
const pDir = wasmfsDir(wasm);
|
||||
if(pDir) log2('',"Persistent storage:",pDir);
|
||||
else{
|
||||
log2('error',"Expecting persistent storage in this build.");
|
||||
return;
|
||||
}
|
||||
const scope = wasm.scopedAllocPush();
|
||||
const dbFile = pDir+"/speedtest1.db";
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const argv = ["speedtest1"];
|
||||
if(urlParams.has('flags')){
|
||||
argv.push(...(urlParams.get('flags').split(',')));
|
||||
let i = argv.indexOf('--vfs');
|
||||
if(i>=0) argv.splice(i,2);
|
||||
}else{
|
||||
argv.push(
|
||||
"--singlethread",
|
||||
"--nomutex",
|
||||
"--nosync",
|
||||
"--nomemstat"
|
||||
);
|
||||
//"--memdb", // note that memdb trumps the filename arg
|
||||
}
|
||||
|
||||
if(argv.indexOf('--memdb')>=0){
|
||||
log2('error',"WARNING: --memdb flag trumps db filename.");
|
||||
}
|
||||
argv.push("--big-transactions"/*important for tests 410 and 510!*/,
|
||||
dbFile);
|
||||
console.log("argv =",argv);
|
||||
// These log messages are not emitted to the UI until after main() returns. Fixing that
|
||||
// requires moving the main() call and related cleanup into a timeout handler.
|
||||
if(pDir) unlink(dbFile);
|
||||
log2('',"Starting native app:\n ",argv.join(' '));
|
||||
log2('',"This will take a while and the browser might warn about the runaway JS.",
|
||||
"Give it time...");
|
||||
logList.length = 0;
|
||||
setTimeout(function(){
|
||||
wasm.xCall('wasm_main', argv.length,
|
||||
wasm.scopedAllocMainArgv(argv));
|
||||
wasm.scopedAllocPop(scope);
|
||||
if(pDir) unlink(dbFile);
|
||||
logList.unshift("Done running native main(). Output:");
|
||||
dumpLogList();
|
||||
}, 25);
|
||||
}/*runTests()*/;
|
||||
|
||||
self.sqlite3TestModule.print = log;
|
||||
self.sqlite3TestModule.printErr = logErr;
|
||||
sqlite3InitModule(self.sqlite3TestModule).then(runTests);
|
||||
})();</script>
|
||||
<script>
|
||||
(function(){
|
||||
const eOut = document.querySelector('#test-output');
|
||||
const log2 = function(cssClass,...args){
|
||||
const ln = document.createElement('div');
|
||||
if(cssClass) ln.classList.add(cssClass);
|
||||
ln.append(document.createTextNode(args.join(' ')));
|
||||
eOut.append(ln);
|
||||
//this.e.output.lastElementChild.scrollIntoViewIfNeeded();
|
||||
};
|
||||
/* can't update DOM while speedtest is running unless we run
|
||||
speedtest in a worker thread. */;
|
||||
const log = (...args)=>{
|
||||
console.log(...args);
|
||||
log2('',...args);
|
||||
};
|
||||
const logErr = function(...args){
|
||||
console.error(...args);
|
||||
log2('error',...args);
|
||||
};
|
||||
const W = new Worker(
|
||||
'speedtest1-wasmfs.mjs'+globalThis.location.search,{
|
||||
type: 'module'
|
||||
});
|
||||
log("Starting up...");
|
||||
W.onmessage = function({data}){
|
||||
switch(data.type){
|
||||
case 'log': log(...data.args); break;
|
||||
case 'logErr': logErr(...data.args); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import sqlite3InitModule from './jswasm/speedtest1-wasmfs.mjs';
|
||||
const wMsg = (type,...args)=>{
|
||||
postMessage({type, args});
|
||||
};
|
||||
wMsg('log',"speedtest1-wasmfs starting...");
|
||||
/**
|
||||
If this environment contains OPFS, this function initializes it and
|
||||
returns the name of the dir on which OPFS is mounted, else it returns
|
||||
an empty string.
|
||||
*/
|
||||
const wasmfsDir = function f(wasmUtil,dirName="/opfs"){
|
||||
if(undefined !== f._) return f._;
|
||||
if( !self.FileSystemHandle
|
||||
|| !self.FileSystemDirectoryHandle
|
||||
|| !self.FileSystemFileHandle){
|
||||
return f._ = "";
|
||||
}
|
||||
try{
|
||||
if(0===wasmUtil.xCallWrapped(
|
||||
'sqlite3_wasm_init_wasmfs', 'i32', ['string'], dirName
|
||||
)){
|
||||
return f._ = dirName;
|
||||
}else{
|
||||
return f._ = "";
|
||||
}
|
||||
}catch(e){
|
||||
// sqlite3_wasm_init_wasmfs() is not available
|
||||
return f._ = "";
|
||||
}
|
||||
};
|
||||
wasmfsDir._ = undefined;
|
||||
|
||||
const log = (...args)=>wMsg('log',...args);
|
||||
const logErr = (...args)=>wMsg('logErr',...args);
|
||||
|
||||
const runTests = function(sqlite3){
|
||||
console.log("Module inited.",sqlite3);
|
||||
const wasm = sqlite3.wasm;
|
||||
const __unlink = wasm.xWrap("sqlite3_wasm_vfs_unlink", "int", ["*","string"]);
|
||||
const unlink = (fn)=>__unlink(0,fn);
|
||||
const pDir = wasmfsDir(wasm);
|
||||
if(pDir) log("Persistent storage:",pDir);
|
||||
else{
|
||||
logErr("Expecting persistent storage in this build.");
|
||||
return;
|
||||
}
|
||||
const scope = wasm.scopedAllocPush();
|
||||
const dbFile = pDir+"/speedtest1.db";
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const argv = ["speedtest1"];
|
||||
if(urlParams.has('flags')){
|
||||
argv.push(...(urlParams.get('flags').split(',')));
|
||||
let i = argv.indexOf('--vfs');
|
||||
if(i>=0) argv.splice(i,2);
|
||||
}else{
|
||||
argv.push(
|
||||
"--singlethread",
|
||||
"--nomutex",
|
||||
//"--nosync",
|
||||
"--nomemstat",
|
||||
"--size", "10"
|
||||
);
|
||||
}
|
||||
|
||||
if(argv.indexOf('--memdb')>=0){
|
||||
logErr("WARNING: --memdb flag trumps db filename.");
|
||||
}
|
||||
argv.push("--big-transactions"/*important for tests 410 and 510!*/,
|
||||
dbFile);
|
||||
//log("argv =",argv);
|
||||
// These log messages are not emitted to the UI until after main() returns. Fixing that
|
||||
// requires moving the main() call and related cleanup into a timeout handler.
|
||||
if(pDir) unlink(dbFile);
|
||||
log("Starting native app:\n ",argv.join(' '));
|
||||
log("This will take a while and the browser might warn about the runaway JS.",
|
||||
"Give it time...");
|
||||
setTimeout(function(){
|
||||
if(pDir) unlink(dbFile);
|
||||
wasm.xCall('wasm_main', argv.length,
|
||||
wasm.scopedAllocMainArgv(argv));
|
||||
wasm.scopedAllocPop(scope);
|
||||
if(pDir) unlink(dbFile);
|
||||
log("Done running native main()");
|
||||
}, 25);
|
||||
}/*runTests()*/;
|
||||
|
||||
sqlite3InitModule({
|
||||
print: log,
|
||||
printErr: logErr
|
||||
}).then(runTests);
|
||||
@@ -259,7 +259,7 @@
|
||||
flags["--utf16be"] = "Set text encoding to UTF-16BE";
|
||||
flags["--utf16le"] = "Set text encoding to UTF-16LE";
|
||||
flags["--verify"] = "Run additional verification steps.";
|
||||
flags["--without"] = "rowid Use WITHOUT ROWID where appropriate";
|
||||
flags["--without-rowid"] = "Use WITHOUT ROWID where appropriate";
|
||||
const preselectedFlags = [
|
||||
'--big-transactions',
|
||||
'--singlethread'
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
*/
|
||||
//#if target=es6-module
|
||||
import {default as sqlite3InitModule} from './jswasm/sqlite3.mjs';
|
||||
self.sqlite3InitModule = sqlite3InitModule;
|
||||
globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
//#else
|
||||
'use strict';
|
||||
//#endif
|
||||
@@ -57,7 +57,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
*/
|
||||
let logClass;
|
||||
/* Predicate for tests/groups. */
|
||||
const isUIThread = ()=>(self.window===self && self.document);
|
||||
const isUIThread = ()=>(globalThis.window===self && globalThis.document);
|
||||
/* Predicate for tests/groups. */
|
||||
const isWorker = ()=>!isUIThread();
|
||||
/* Predicate for tests/groups. */
|
||||
@@ -3050,14 +3050,15 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
log("Loading and initializing sqlite3 WASM module...");
|
||||
if(0){
|
||||
self.sqlite3ApiConfig = {
|
||||
globalThis.sqlite3ApiConfig = {
|
||||
debug: ()=>{},
|
||||
log: ()=>{},
|
||||
warn: ()=>{},
|
||||
error: ()=>{}
|
||||
}
|
||||
}
|
||||
if(!self.sqlite3InitModule && !isUIThread()){
|
||||
//#ifnot target=es6-module
|
||||
if(!globalThis.sqlite3InitModule && !isUIThread()){
|
||||
/* Vanilla worker, as opposed to an ES6 module worker */
|
||||
/*
|
||||
If sqlite3.js is in a directory other than this script, in order
|
||||
@@ -3070,27 +3071,28 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
that's not needed.
|
||||
|
||||
URL arguments passed as part of the filename via importScripts()
|
||||
are simply lost, and such scripts see the self.location of
|
||||
are simply lost, and such scripts see the globalThis.location of
|
||||
_this_ script.
|
||||
*/
|
||||
let sqlite3Js = 'sqlite3.js';
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const urlParams = new URL(globalThis.location.href).searchParams;
|
||||
if(urlParams.has('sqlite3.dir')){
|
||||
sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js;
|
||||
}
|
||||
importScripts(sqlite3Js);
|
||||
}
|
||||
self.sqlite3InitModule.__isUnderTest =
|
||||
//#endif
|
||||
globalThis.sqlite3InitModule.__isUnderTest =
|
||||
true /* disables certain API-internal cleanup so that we can
|
||||
test internal APIs from here */;
|
||||
self.sqlite3InitModule({
|
||||
globalThis.sqlite3InitModule({
|
||||
print: log,
|
||||
printErr: error
|
||||
}).then(function(sqlite3){
|
||||
//console.log('sqlite3 =',sqlite3);
|
||||
log("Done initializing WASM/JS bits. Running tests...");
|
||||
sqlite3.config.warn("Installing sqlite3 bits as global S for local dev/test purposes.");
|
||||
self.S = sqlite3;
|
||||
globalThis.S = sqlite3;
|
||||
capi = sqlite3.capi;
|
||||
wasm = sqlite3.wasm;
|
||||
log("sqlite3 version:",capi.sqlite3_libversion(),
|
||||
|
||||
+58
-75
@@ -6,14 +6,10 @@
|
||||
# GNUMakefile.
|
||||
########################################################################
|
||||
MAKEFILE.wasmfs := $(lastword $(MAKEFILE_LIST))
|
||||
$(warning The WASMFS build is currently incomplete.)
|
||||
|
||||
# Maintenance reminder: these particular files cannot be built into a
|
||||
# subdirectory because loading of the auxiliary
|
||||
# sqlite3-wasmfs.worker.js file it creates fails if sqlite3-wasmfs.js
|
||||
# is loaded from any directory other than the one in which the
|
||||
# containing HTML lives. Similarly, they cannot be loaded from a
|
||||
# Worker to an Emscripten quirk regarding loading nested Workers.
|
||||
dir.wasmfs := $(dir.wasm)
|
||||
#dir.wasmfs := $(dir.wasm)
|
||||
dir.wasmfs := $(dir.dout)
|
||||
sqlite3-wasmfs.js := $(dir.wasmfs)/sqlite3-wasmfs.js
|
||||
sqlite3-wasmfs.mjs := $(dir.wasmfs)/sqlite3-wasmfs.mjs
|
||||
sqlite3-wasmfs.wasm := $(dir.wasmfs)/sqlite3-wasmfs.wasm
|
||||
@@ -28,105 +24,92 @@ CLEAN_FILES += $(sqlite3-wasmfs.js) $(sqlite3-wasmfs.wasm) \
|
||||
cflags.sqlite3-wasmfs :=
|
||||
cflags.sqlite3-wasmfs += -std=c99 -fPIC
|
||||
cflags.sqlite3-wasmfs += -pthread
|
||||
cflags.sqlite3-wasmfs += $(cflags.speedtest1)
|
||||
cflags.sqlite3-wasmfs += $(SQLITE_OPT) -DSQLITE_ENABLE_WASMFS
|
||||
cflags.sqlite3-wasmfs += -DSQLITE_ENABLE_WASMFS
|
||||
|
||||
########################################################################
|
||||
# emcc flags specific to building the final .js/.wasm file...
|
||||
emcc.flags.sqlite3-wasmfs := -fPIC
|
||||
emcc.flags.sqlite3-wasmfs += --no-entry
|
||||
emcc.flags.sqlite3-wasmfs += --minify 0
|
||||
emcc.flags.sqlite3-wasmfs += -sMODULARIZE
|
||||
emcc.flags.sqlite3-wasmfs += -sEXPORT_NAME=$(sqlite3.js.init-func)
|
||||
emcc.flags.sqlite3-wasmfs += -sSTRICT_JS
|
||||
emcc.flags.sqlite3-wasmfs += -sDYNAMIC_EXECUTION=0
|
||||
emcc.flags.sqlite3-wasmfs += -sNO_POLYFILL
|
||||
emcc.flags.sqlite3-wasmfs += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
|
||||
emcc.flags.sqlite3-wasmfs += -sEXPORTED_FUNCTIONS=@$(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api)
|
||||
emcc.flags.sqlite3-wasmfs += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack
|
||||
emcc.flags.sqlite3-wasmfs :=
|
||||
emcc.flags.sqlite3-wasmfs += \
|
||||
-sEXPORTED_RUNTIME_METHODS=wasmMemory,allocateUTF8OnStack,stringToUTF8OnStack
|
||||
# wasmMemory ==> for -sIMPORTED_MEMORY
|
||||
# allocateUTF8OnStack ==> wasmfs internals
|
||||
# *OnStack ==> wasmfs internals (leaky abstraction)
|
||||
emcc.flags.sqlite3-wasmfs += -sUSE_CLOSURE_COMPILER=0
|
||||
emcc.flags.sqlite3-wasmfs += -Wno-limited-postlink-optimizations
|
||||
# ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag.
|
||||
emcc.flags.sqlite3-wasmfs += -sALLOW_TABLE_GROWTH
|
||||
emcc.flags.sqlite3-wasmfs += -sSTACK_SIZE=512KB
|
||||
emcc.flags.sqlite3-wasmfs += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr.
|
||||
emcc.flags.sqlite3-wasmfs += -sMEMORY64=0
|
||||
emcc.flags.sqlite3-wasmfs += -sIMPORTED_MEMORY
|
||||
emcc.flags.sqlite3-wasmfs += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.128)
|
||||
# ^^^^ 64MB is not enough for WASMFS/OPFS test runs using batch-runner.js
|
||||
sqlite3-wasmfs.fsflags := -pthread -sWASMFS \
|
||||
-sPTHREAD_POOL_SIZE=2 -sENVIRONMENT=web,worker \
|
||||
-sERROR_ON_UNDEFINED_SYMBOLS=0 -sLLD_REPORT_UNDEFINED
|
||||
-sPTHREAD_POOL_SIZE=1 \
|
||||
-sERROR_ON_UNDEFINED_SYMBOLS=0 -sLLD_REPORT_UNDEFINED
|
||||
# ^^^^^ why undefined symbols are necessary for the wasmfs build is anyone's guess.
|
||||
emcc.flags.sqlite3-wasmfs += $(sqlite3-wasmfs.fsflags)
|
||||
#emcc.flags.sqlite3-wasmfs += -sALLOW_MEMORY_GROWTH
|
||||
emcc.flags.sqlite3-wasmfs += -sALLOW_MEMORY_GROWTH=0
|
||||
#^^^ using ALLOW_MEMORY_GROWTH produces a warning from emcc:
|
||||
# USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly,
|
||||
# see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth]
|
||||
# And, indeed, it runs slowly if memory is permitted to grow.
|
||||
emcc.flags.sqlite3-wasmfs.vanilla :=
|
||||
emcc.flags.sqlite3-wasmfs.esm := -sEXPORT_ES6 -sUSE_ES6_IMPORT_META
|
||||
$(eval $(call call-make-pre-js,sqlite3-wasmfs,vanilla))
|
||||
$(eval $(call call-make-pre-js,sqlite3-wasmfs,esm))
|
||||
Xemcc.flags.sqlite3-wasmfs.vanilla += \
|
||||
$(pre-post-common.flags.vanilla) \
|
||||
$(pre-post-sqlite3-wasmfs.flags.vanilla)
|
||||
Xemcc.flags.sqlite3-wasmfs.esm += \
|
||||
$(pre-post-common.flags.esm) \
|
||||
$(pre-post-sqlite3-wasmfs.flags.esm)
|
||||
$(sqlite3-wasmfs.js) $(sqlite3-wasmfs.mjs): $(sqlite3-wasm.c) \
|
||||
$(EXPORTED_FUNCTIONS.api) $(MAKEFILE) $(MAKEFILE.wasmfs)
|
||||
$(sqlite3-wasmfs.js): $(pre-post-sqlite3-wasmfs.deps.vanilla)
|
||||
$(sqlite3-wasmfs.mjs): $(pre-post-sqlite3-wasmfs.deps.esm)
|
||||
# SQLITE3-WASMFS.xJS.RECIPE is the wasmfs-specific counterpart
|
||||
# of SQLITE3.xJS.RECIPE from the main makefile.
|
||||
define SQLITE3-WASMFS.xJS.RECIPE
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) -o $@ $(emcc_opt_full) $(emcc.flags) \
|
||||
$(cflags.sqlite3-wasmfs) \
|
||||
$(emcc.flags.sqlite3-wasmfs) $(emcc.flags.sqlite3-wasmfs.$(1)) \
|
||||
$(pre-post-sqlite3-wasmfs.flags.$(1)) \
|
||||
$(sqlite3-wasm.c)
|
||||
@$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,$(1))
|
||||
chmod -x $(sqlite3-wasmfs.wasm)
|
||||
$(maybe-wasm-strip) $(sqlite3-wasmfs.wasm)
|
||||
@ls -la $(sqlite3-wasmfs.wasm) sqlite3-wasmfs*js
|
||||
endef
|
||||
$(sqlite3-wasmfs.js):
|
||||
$(call SQLITE3-WASMFS.xJS.RECIPE,vanilla)
|
||||
$(sqlite3-wasmfs.mjs): $(sqlite3-wasmfs.js)
|
||||
$(call SQLITE3-WASMFS.xJS.RECIPE,esm)
|
||||
$(sqlite3-wasmfs.wasm): $(sqlite3-wasmfs.js)
|
||||
wasmfs: $(sqlite3-wasmfs.js) $(sqlite3-wasmfs.mjs)
|
||||
#emcc.flags.sqlite3-wasmfs.vanilla :=
|
||||
#emcc.flags.sqlite3-wasmfs.esm := -sEXPORT_ES6 -sUSE_ES6_IMPORT_META
|
||||
sqlite3-api.mjs.wasmfs := $(dir.tmp)/sqlite3-api-wasmfs.mjs
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3-wasmfs,esm,1,\
|
||||
$(sqlite3-api.mjs.wasmfs), $(sqlite3-wasmfs.mjs),\
|
||||
$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs,\
|
||||
-sEXPORT_ES6 -sUSE_ES6_IMPORT_META\
|
||||
))
|
||||
$(sqlite3-wasmfs.js) $(sqlite3-wasmfs.mjs): $(MAKEFILE.wasmfs)
|
||||
########################################################################
|
||||
# Build quirk: we cannot build BOTH .js and .mjs with our current
|
||||
# build infrastructure because the supplemental *.worker.js files get
|
||||
# generated with the name of the main module file
|
||||
# ($(sqlite3-wasmfs.{js,mjs})) hard-coded in them. Thus the last one
|
||||
# to get built gets the *.worker.js files mapped to it. In order to
|
||||
# build both modes they would need to have distinct base names or
|
||||
# output directories. "The problem" with giving them distinct base
|
||||
# names is that it means that the corresponding .wasm file is also
|
||||
# built/saved multiple times.
|
||||
#
|
||||
wasmfs.build.ext := mjs
|
||||
$(sqlite3-wasmfs.js) $(sqlite3-wasmfs.mjs): $(SOAP.js.bld)
|
||||
ifeq (js,$(wasmfs.build.ext))
|
||||
$(sqlite3-wasmfs.wasm): $(sqlite3-wasmfs.js)
|
||||
wasmfs: $(sqlite3-wasmfs.js)
|
||||
else
|
||||
$(sqlite3-wasmfs.wasm): $(sqlite3-wasmfs.mjs)
|
||||
wasmfs: $(sqlite3-wasmfs.mjs)
|
||||
endif
|
||||
#all: wasmfs
|
||||
|
||||
########################################################################
|
||||
# speedtest1 for wasmfs.
|
||||
speedtest1-wasmfs.js := $(dir.wasmfs)/speedtest1-wasmfs.js
|
||||
speedtest1-wasmfs.wasm := $(subst .js,.wasm,$(speedtest1-wasmfs.js))
|
||||
speedtest1-wasmfs.mjs := $(dir.wasmfs)/speedtest1-wasmfs.mjs
|
||||
speedtest1-wasmfs.wasm := $(subst .mjs,.wasm,$(speedtest1-wasmfs.mjs))
|
||||
emcc.flags.speedtest1-wasmfs := $(sqlite3-wasmfs.fsflags)
|
||||
emcc.flags.speedtest1-wasmfs += $(SQLITE_OPT) -DSQLITE_ENABLE_WASMFS
|
||||
emcc.flags.speedtest1-wasmfs += $(SQLITE_OPT)
|
||||
emcc.flags.speedtest1-wasmfs += -sALLOW_MEMORY_GROWTH=0
|
||||
emcc.flags.speedtest1-wasmfs += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.128)
|
||||
#$(eval $(call call-make-pre-js,speedtest1-wasmfs,vanilla))
|
||||
$(speedtest1-wasmfs.js): $(speedtest1.cses) $(sqlite3-wasmfs.js) \
|
||||
#$(eval $(call call-make-pre-js,speedtest1-wasmfs,ems))
|
||||
$(speedtest1-wasmfs.mjs): $(speedtest1.cfiles) $(sqlite3-wasmfs.js) \
|
||||
$(MAKEFILE) $(MAKEFILE.wasmfs) \
|
||||
$(pre-post-sqlite3-wasmfs.deps) \
|
||||
$(pre-post-sqlite3-wasmfs-esm.deps) \
|
||||
$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) \
|
||||
$(emcc.speedtest1.common) $(emcc.flags.speedtest1-wasmfs) \
|
||||
$(pre-post-sqlite3-wasmfs.flags.vanilla) \
|
||||
$(pre-post-sqlite3-wasmfs-esm.flags) \
|
||||
$(cflags.common) \
|
||||
$(cflags.sqlite3-wasmfs) \
|
||||
-o $@ $(speedtest1.cses) -lm
|
||||
$(emcc.speedtest1.common) \
|
||||
$(emcc.flags.speedtest1-vanilla) \
|
||||
$(emcc.flags.sqlite3-wasmfs) \
|
||||
$(emcc.flags.speedtest1-wasmfs) \
|
||||
-o $@ $(speedtest1.cfiles) -lm
|
||||
@$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,1)
|
||||
$(maybe-wasm-strip) $(speedtest1-wasmfs.wasm)
|
||||
chmod -x $(speedtest1-wasmfs.wasm)
|
||||
ls -la $@ $(speedtest1-wasmfs.wasm)
|
||||
|
||||
#speedtest1: $(speedtest1-wasmfs.js)
|
||||
wasmfs: $(speedtest1-wasmfs.js)
|
||||
CLEAN_FILES += $(speedtest1-wasmfs.js) $(speedtest1-wasmfs.wasm) \
|
||||
$(subst .js,.worker.js,$(speedtest1-wasmfs.js))
|
||||
wasmfs: $(speedtest1-wasmfs.mjs)
|
||||
CLEAN_FILES += $(speedtest1-wasmfs.mjs) $(speedtest1-wasmfs.wasm) \
|
||||
$(subst .js,.worker.js,$(speedtest1-wasmfs.mjs))
|
||||
# end speedtest1.js
|
||||
########################################################################
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
C This\sis\sa\sfailed\sattempt\sto\soptimize\sthe\susage\sof\sthe\spage\scache\sin\nsqlite3BtreeIndexMoveto()\sby\sdeferring\scalls\sto\ssqlite3PagerUnref()\sfor\nchild\spages\sof\sthe\sbtree\suntil\swe\sare\ssure\sthose\spages\swill\snot\sbe\sreused.\nThe\scode\sdoes\snot\swork\sin\stwo\ssenses:\s\s(1)\s\sMany\stests\sstill\sfail.\n(2)\sThe\sperformance\sgain\sis\sless\sthan\s2\smillion\scycles\sand\sdoes\snot\sseem\nworth\sthe\sextra\srisk\sand\scomplication.
|
||||
D 2023-06-21T18:12:07.803
|
||||
C Enhance\sthe\sJSON\sparser\scache\ssuch\sthat\sit\sis\sable\sto\sextract\slines\sfrom\nthe\scache\sand\suse\sthem\sfor\swriting,\sthough\sthey\sthen\shave\sto\sbe\sevicted\sfrom\nthe\scache.\s\sThis\swas\san\sexperiment\sin\strying\sto\sreduce\sthe\samount\sof\sparsing\nneeded\sfor\sa\sbig\sUPDATE,\sbut\sit\sdoes\snot\sseem\sto\shelp\sany.\s\sRetained\sfor\nreference\sonly.
|
||||
D 2023-07-19T12:52:09.770
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
F Makefile.in 0f4cb3955aaff8a40ec3857ba1784bd98b69802e51eff979f874b65713b627b2
|
||||
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
|
||||
F Makefile.msc 7248d860f71ab164b4cec3c415e6cc1bd9fee860c370d65bd8bb49e9572521e2
|
||||
F README.md 8ff80689b9cb9f6e9b842edf31a3358ff53bc538c351799e03dd3e5455e637e5
|
||||
F README.md c1c4218efcc4071a6e26db2b517fdbc1035696a29b370edd655faddbef02b224
|
||||
F VERSION c6366dc72582d3144ce87b013cc35fe48d62f6d07d5be0c9716ea33c862144aa
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
@@ -92,7 +92,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292
|
||||
F ext/fts5/fts5_config.c 051056a9052f5d3a4d1c695f996fd364f920e341f136c60ab2c04aa7e267113f
|
||||
F ext/fts5/fts5_expr.c 58fb8ceddfb1cefcd54510f9f2f33c220ef9d1b3fa77462111f5ae2a825ab7b1
|
||||
F ext/fts5/fts5_hash.c d4fb70940359f2120ccd1de7ffe64cc3efe65de9e8995b822cd536ff64c96982
|
||||
F ext/fts5/fts5_index.c fe98ebd8835760b9c787d20f6b50d648a761afd8e3b55780e718ee34c694743b
|
||||
F ext/fts5/fts5_index.c 430d3edfc5b51d02f0671e2b8268c12bf485f7dc80db2b61c996e74814bc46b0
|
||||
F ext/fts5/fts5_main.c b4dba04a36aaf9b8e8cef0100b6dbb422cc74753eacc11d6401cac7a87c0f38d
|
||||
F ext/fts5/fts5_storage.c 76c6085239eb44424004c022e9da17a5ecd5aaec859fba90ad47d3b08f4c8082
|
||||
F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae
|
||||
@@ -287,13 +287,13 @@ F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8b
|
||||
F ext/misc/compress.c 3354c77a7c8e86e07d849916000cdac451ed96500bfb5bd83b20eb61eee012c9
|
||||
F ext/misc/csv.c ca8d6dafc5469639de81937cb66ae2e6b358542aba94c4f791910d355a8e7f73
|
||||
F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f823e01
|
||||
F ext/misc/decimal.c 57d85fa20a5a74d3b0dfc78ab7934ae6c9f5aa8eed915faa2b5246bec87ddc6d
|
||||
F ext/misc/decimal.c a61343b36672760e1d6d5b20a42cb52264db55bcd11d0a44e2e06e8ce23227e3
|
||||
F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1
|
||||
F ext/misc/explain.c 0086fab288d4352ea638cf40ac382aad3b0dc5e845a1ea829a694c015fd970fe
|
||||
F ext/misc/fileio.c 4e7f7cd30de8df4820c552f14af3c9ca451c5ffe1f2e7bef34d598a12ebfb720
|
||||
F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5
|
||||
F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d
|
||||
F ext/misc/ieee754.c 984d51fe23e956484ec1049df6f5257002e3ab338cabceb39761c2e80ad10bf4
|
||||
F ext/misc/ieee754.c 62a90978204d2c956d5036eb89e548e736ca5fac0e965912867ddd7bb833256d
|
||||
F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d
|
||||
F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b
|
||||
F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b
|
||||
@@ -482,28 +482,27 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
||||
F ext/wasm/GNUmakefile 38700d5074af690f004e4e5f3533164ab49693b9d0832929c4ecf97a0bc09494
|
||||
F ext/wasm/GNUmakefile 74e351ff45b4061cfed8df237d301819a04182ae304a99118883b064baa25fc2
|
||||
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
|
||||
F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
|
||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||
F ext/wasm/api/README.md 77a2f1f2fc60a35def7455dffc8d3f2c56385d6ac5c6cecc60fa938252ea2c54
|
||||
F ext/wasm/api/extern-post-js.c-pp.js 393ab78b807da94096eae1a68bfddb999a2299936a185d910162fe87a57a9a3a
|
||||
F ext/wasm/api/extern-post-js.c-pp.js 116749b7e55b7519129de06d3d353e19df68cfb24b12204aa4dc30c9a83023fe
|
||||
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
|
||||
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
|
||||
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
|
||||
F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js cc21e3486da748463e02bbe51e2464c6ac136587cdfd5aa00cd0b5385f6ca808
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js 23ceec5ef74a0e649b19694ca985fd89e335771e21f24f50df352a626a8c81bf
|
||||
F ext/wasm/api/sqlite3-api-glue.js f1b2dcb944de5138bb5bd9a1559d2e76a4f3ec25260963d709e8237476688803
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 17f4ec398ba34c5c666fea8e8c4eb82064a35b302f2f2eb355283cd8d3f68ed5
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 7d1c1ef59b9dcc42ad3a9cec9da972c42e29316a270cd126e7f660509b09027b
|
||||
F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec
|
||||
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 961bbc3ccc1fa4e91d6519a96e8811ad7ae60173bd969fee7775dacb6eee1da2
|
||||
F ext/wasm/api/sqlite3-v-helper.js e5c202a9ecde9ef818536d3f5faf26c03a1a9f5192b1ddea8bdabf30d75ef487
|
||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 05f2563ddebfdc7a0f0ac0eb7cb381bb72043299aae1600ba9367c12f52b3fcc
|
||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 891f3a18d9ac9b0422b32fd975319dfcd0af5a8ca392f0cce850524e51b49c87
|
||||
F ext/wasm/api/sqlite3-wasm.c 12a096d8e58a0af0589142bae5a3c27a0c7e19846755a1a37d2c206352fbedda
|
||||
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
|
||||
F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75
|
||||
@@ -513,7 +512,7 @@ F ext/wasm/c-pp.c 6d80d8569d85713effe8b0818a3cf51dc779e3f0bf8dc88771b8998552ee25
|
||||
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
|
||||
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
|
||||
F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f
|
||||
F ext/wasm/common/whwasmutil.js 749a1f81f85835e9a384e9706f2a955a7158f2b8cc9da33f41105cac7775a305
|
||||
F ext/wasm/common/whwasmutil.js ae263dec9d7384f4c530f324b99d00516a4d6f26424372daee65031e00eb49b3
|
||||
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
|
||||
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
|
||||
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
|
||||
@@ -531,14 +530,15 @@ F ext/wasm/fiddle/fiddle-worker.js 163d6139a93fab4bcb72064923df050d4e7c0ff0d8aa0
|
||||
F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5653284071715
|
||||
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
|
||||
F ext/wasm/index-dist.html 22379774f0ad4edcaaa8cf9c674c82e794cc557719a8addabed74eb8069d412e
|
||||
F ext/wasm/index.html dd900891844caebd9cadbddd704f66bd841d7c12fd69ce5af490e2c10fb49f45
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.js 8287c0537fa0750414edbe75ce64668a81c8716df5ec4c3e6bb4f11bd1c36031
|
||||
F ext/wasm/index.html b768e8659b4fe311912e54d42906449d51c0f84b7f036cca47ec1f93bf3f91de
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.md 37911f00db12cbcca73aa1ed72594430365f30aafae2fa9c886961de74e5e0eb
|
||||
F ext/wasm/module-symbols.html 841de62fc198988b8330e238c260e70ec93028b096e1a1234db31b187a899d10
|
||||
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
||||
F ext/wasm/scratchpad-wasmfs-main.js 4c140457f4d6da9d646a49addd91edb6e9ad1643c6c48e3258b5bce24725dc18
|
||||
F ext/wasm/speedtest1-wasmfs.html 7a301f4f5b6ad4f5d37fd6e7ca03a2f5d5547fd289da60a39075a93d7646d354
|
||||
F ext/wasm/speedtest1-worker.html 82869822e641c1bef3ec0cd2d7d2b6a42d0b4f68a7b160fb2e1dd0b523940a9b
|
||||
F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96
|
||||
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
|
||||
F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
|
||||
F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe
|
||||
F ext/wasm/speedtest1-worker.html 97c2bf5f8534091ce718de05801090d5a80c3f13575996f095ba23638e1bdca0
|
||||
F ext/wasm/speedtest1-worker.js 13b57c4a41729678a1194014afec2bd5b94435dcfc8d1039dfa9a533ac819ee1
|
||||
F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
|
||||
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
||||
@@ -548,12 +548,12 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
||||
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
|
||||
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
|
||||
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
|
||||
F ext/wasm/tester1.c-pp.js 1a05497ae2b2fcca008d43b37072f9b841e1c970c06c01eb0faf675db567bfc8
|
||||
F ext/wasm/tester1.c-pp.js 4420eb97b6b4fc79e4e156b4b8010dd9f373365f4230dd76d823fb04ce28ffde
|
||||
F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1
|
||||
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
||||
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
||||
F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
|
||||
F ext/wasm/wasmfs.make cf9a68162d92ca2bcb0b9528b244cb36d5cc2d84ccc9c2d398461927d6e75aea
|
||||
F ext/wasm/wasmfs.make 8a4955882aaa0783b3f60a9484a1f0f3d8b6f775c0fcd17c082f31966f1bc16a
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
|
||||
@@ -576,7 +576,7 @@ F src/auth.c 19b7ccacae3dfba23fc6f1d0af68134fa216e9040e53b0681b4715445ea030b4
|
||||
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
|
||||
F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
|
||||
F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
|
||||
F src/btree.c 9a505e73aa1a7ce96c3e6b5480485a0bca83248c9e79f63f0481444b459e50a0
|
||||
F src/btree.c 2281facb0531d53fb42c03d1f32bc1b5903564d782ec5ff4ffc63171d960e2aa
|
||||
F src/btree.h aa354b9bad4120af71e214666b35132712b8f2ec11869cb2315c52c81fad45cc
|
||||
F src/btreeInt.h 3b4eff7155c0cea6971dc51f62e3529934a15a6640ec607dd42a767e379cb3a9
|
||||
F src/build.c a8ae3b32d9aa9bbd2c0e97d7c0dd80def9fbca408425de1608f57ee6f47f45f4
|
||||
@@ -587,20 +587,20 @@ F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94
|
||||
F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
|
||||
F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
|
||||
F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be
|
||||
F src/expr.c 36f6a47c8a2c20ec3c267a60fc598857876edd60af0cb40caf7b69b651fd73bf
|
||||
F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36
|
||||
F src/func.c 6303e1ccb80dbd0d9b52f902a01d3b105981486fdfd66f9e1ddfd74aaf3032fc
|
||||
F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30
|
||||
F src/func.c 25f2e0204c011be56fc3c9a180534b68ca4866c61ec19806880136450434112d
|
||||
F src/global.c 29f56a330ed9d1b5cd9b79ac0ca36f97ac3afc730ff8bfa987b0db9e559d684d
|
||||
F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
|
||||
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
|
||||
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276
|
||||
F src/json.c 14c474fb1249a46eb44e878e2361f36abfe686b134039b0d1883d93d61505b4a
|
||||
F src/json.c c011e8a5c2a6ef3b310412dd16de933370655d643c0dff33c81cf96a1088e223
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465
|
||||
F src/main.c 5fd4b65d61ae6155f36756ed508a39b38b49355b031188961e8d923f43f4bc49
|
||||
F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002
|
||||
F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
|
||||
@@ -633,21 +633,21 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
|
||||
F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a
|
||||
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
|
||||
F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1
|
||||
F src/printf.c b9320cdbeca0b336c3f139fd36dd121e4167dd62b35fbe9ccaa9bab44c0af38d
|
||||
F src/printf.c 84b7b4b647f336934a5ab2e7f0c52555833cc0778d2d60e016cca52ee8c6cd8f
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c 383b9dba12493c365ee2036bcadd73013b7c0f7d2afcda0c378317c335d60ac2
|
||||
F src/shell.c.in bdd1fdfc77a67651cdc5a158bc9107cf3c2cf3ddb62d7a4da06c6eaaa5e72037
|
||||
F src/sqlite.h.in 3076d78836b6dac53b3ab0875fc8fd15bca8077aad4d33c85336e05af6aef8c7
|
||||
F src/select.c 3ab1186290a311a8ceed1286c0e286209f7fe97b2d02c7593258004ce295dd88
|
||||
F src/shell.c.in d320d8a13636de06d777cc1eab981caca304e175464e98183cf4ea68d93db818
|
||||
F src/sqlite.h.in f999ef3642f381d69679b2516b430dbcb6c5a2a951b7f5e43dc4751b474a5774
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4
|
||||
F src/sqliteInt.h 8974b60740b108269f51e833e85191be6bf9f06f317ee34a53b7ec215762cf8c
|
||||
F src/sqliteInt.h dcb1a885e8b6cb78df618944b89d44361a99d0fe33e1bba2c150a855f7dc5599
|
||||
F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6
|
||||
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
F src/tclsqlite.c ecbc3c99c0d0c3ed122a913f143026c26d38d57f33e06bb71185dd5c1efe37cd
|
||||
F src/test1.c e6ab4a00671f052366a01bcb7fdf2e2f6bb4aa884cd01e738c5590dcf47a99ca
|
||||
F src/test1.c 9111b12427b3b94429aff68bbcf5b125ae70438f6ce2f3f033e2a69626ec26c9
|
||||
F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef
|
||||
F src/test3.c e5178558c41ff53236ae0271e9acb3d6885a94981d2eb939536ee6474598840e
|
||||
F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
|
||||
@@ -705,26 +705,26 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0
|
||||
F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731
|
||||
F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 6f9d2f278dcc8d41c618980cd3cfe88e1bafc0626209b917c6773d8202d29ef6
|
||||
F src/util.c fea6fffdee3cdae917a66b70deec59d4f238057cfd6de623d15cf990c196940d
|
||||
F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
|
||||
F src/vdbe.c 6c0de640ef3be08cf2992d588a7501aee0f1003027bc952a6916a35f6e33b4cf
|
||||
F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0
|
||||
F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0
|
||||
F src/vdbeInt.h 7bd49eef8f89c1a271fbf12d80a206bf56c876814c5fc6bee340f4e1907095ae
|
||||
F src/vdbeapi.c de9703f8705afc393cc2864669ce28cf9516983c8331d59aa2b978de01634365
|
||||
F src/vdbeaux.c 4d5e68a3850d0b193a692eca6442d7afe35252aaf29728a67adcb542ecabd9ce
|
||||
F src/vdbeaux.c b5e3f7e158518b4eca6f166ac43900640a3fe9735c710e12bfa119af21059339
|
||||
F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce
|
||||
F src/vdbemem.c 710119a8e35e47813681c48703d65a80ba22792192de90bc51dc0d6366f2a79e
|
||||
F src/vdbemem.c cf4a1556dd5b18c071cf7c243373c29ce752eb516022e3ad49ba72f08b785033
|
||||
F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015
|
||||
F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
|
||||
F src/vdbevtab.c aae4bd769410eb7e1d02c42613eec961d514459b1c3c1c63cfc84e92a137daac
|
||||
F src/vtab.c 1ecf8c3745d29275688d583e12822fa984d421e0286b5ef50c137bc3bf6d7a64
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c cbfeeb7415baa545efa244dd34bb5af4ae953a206fed720c6fa7f1ef763ec122
|
||||
F src/wal.c dd843f619ac60d5dadab7109cf402432ba74dde0c301505fd1c202add07659e3
|
||||
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
|
||||
F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
|
||||
F src/where.c 2dc708cf8b6a691fb79f16bbc46567497ee6f991043318d421e294b2da114d93
|
||||
F src/where.c 477fcc5e561ef169e6002499602af6b805156c2aae6b2f5c2c93ef8c1cd64768
|
||||
F src/whereInt.h c7d19902863beadec1d04e66aca39c0bcd60b74f05f0eaa7422c7005dfc5d51a
|
||||
F src/wherecode.c bff0bc56cb1a382de266c2db3a691135c18a4360b6ad5e069e5c415d57eb0c38
|
||||
F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1
|
||||
F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00
|
||||
F src/window.c b7ad9cff3ce8ae6f8cc25e18e1a258426cb6bd2999aace6f5248d781b2a74098
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
@@ -775,7 +775,7 @@ F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
|
||||
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
|
||||
F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a
|
||||
F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0
|
||||
F test/atof1.test 1f24a6f16e44892848298bd8e488159d4a60cf47be003d32747a136103c9bbac
|
||||
F test/atof1.test f2765d7fdc1348ae58b279d096d301a208e46da623213877b2ba580dc2768975
|
||||
F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061
|
||||
F test/atomic2.test b6863b4aa552543874f80b42fb3063f1c8c2e3d8e56b6562f00a3cc347b5c1da
|
||||
F test/atrc.c c388fac43dbba05c804432a7135ae688b32e8f25818e9994ffba4b64cf60c27c
|
||||
@@ -939,7 +939,7 @@ F test/dbpage.test fce29035c7566fd7835ec0f19422cb4b9c6944ce0e1b936ff8452443f92e8
|
||||
F test/dbpagefault.test d9111a62f3601d3efc6841ace3940181937342d245f92a1cca6cba8206d4f58a
|
||||
F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759
|
||||
F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef
|
||||
F test/decimal.test fcf403fd5585f47342234e153c4a4338cd737b8e0884ac66fc484df47dbcf1a7
|
||||
F test/decimal.test 18e7b4cb12e8d5c60d768b686ba52af3e1ca3ced4f870231f0476666fd9fab7e
|
||||
F test/default.test 9687cfb16717e4b8238c191697c98be88c0b16e568dd5368cd9284154097ef50
|
||||
F test/delete.test 2686e1c98d552ef37d79ad55b17b93fe96fad9737786917ce3839767f734c48f
|
||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||
@@ -1014,6 +1014,8 @@ F test/fkey8.test 51deda7f1a1448bca95875e4a6e1a3a75b4bd7215e924e845bd60de60e4d84
|
||||
F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
|
||||
F test/fordelete.test ba98f14446b310f9c9d935b97ec748753d0144a28b356ba30d1f4f6958fdde5c
|
||||
F test/format4.test eeae341953db8b6bda7f549044797c3278a6cc345d11ada81471671b654f8ef4
|
||||
F test/fp-speed-1.c ca95152d579530c967240ead1387dc1208052d310dfa22bc9ebded56d4e6c026
|
||||
F test/fpconv1.test d5d8aa0c427533006c112fb1957cdd1ea68c1d0709470dabb9ca02c2e4c06ad8
|
||||
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
|
||||
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
|
||||
F test/fts3_common.tcl dffad248f9ce090800e272017d2898005c28ee6314fc1dd5550643a02666907a
|
||||
@@ -1107,7 +1109,7 @@ F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f75
|
||||
F test/fts4unicode.test 82a9c16b68ba2f358a856226bb2ee02f81583797bc4744061c54401bf1a0f4c9
|
||||
F test/fts4upfrom.test f25835162c989dffd5e2ef91ec24c4848cc9973093e2d492d1c7b32afac1b49d
|
||||
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
|
||||
F test/func.test 4be8bed4be235e333f1e0ea31e32f5be3c9f456c30780363e7fcb15e3ff3e6bc
|
||||
F test/func.test 2acd982669b2c15ceccc046385b458e985f90c9ca58b14d0ef75eec79c32f45b
|
||||
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
||||
F test/func3.test 600a632c305a88f3946d38f9a51efe145c989b2e13bd2b2a488db47fe76bab6a
|
||||
F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31
|
||||
@@ -1391,7 +1393,7 @@ F test/recover.test fd5199f928757cb308661b5fdca1abc19398a798ff7f24b57c3071e9f8e0
|
||||
F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1
|
||||
F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed1368b5
|
||||
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
|
||||
F test/releasetest_data.tcl b550dd1b122a9c969df794d05ea272df535f10ff1a245062e7ba080822378016
|
||||
F test/releasetest_data.tcl c8cf85aeb313a771e18dae01396f5ca70e85d4574df681990629b1103f185afe
|
||||
F test/resetdb.test 54c06f18bc832ac6d6319e5ab23d5c8dd49fdbeec7c696d791682a8006bd5fc3
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/returning1.test db532cde29d6aebbc48c6ddc3149b30476f8e69ca7a2c4b53986c7635e6fd8ec
|
||||
@@ -1399,7 +1401,7 @@ F test/returningfault.test ae4c4b5e8745813287a359d9ccdb9d5c883c2e68afb18fb076793
|
||||
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
|
||||
F test/rollback2.test 3f3a4e20401825017df7e7671e9f31b6de5fae5620c2b9b49917f52f8c160a8f
|
||||
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
||||
F test/round1.test 1bb32cf3fc505eed9e86b5e523d07e15d4428189665524587512fbcc85d114bb
|
||||
F test/round1.test 29c3c9039936ed024d672f003c4d35ee11c14c0acb75c5f7d6188ff16190cfd4
|
||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test e29025be95baf6b32f0d5edef59a7633028325896a98f1caa8019559ca910350
|
||||
@@ -1425,7 +1427,7 @@ F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
|
||||
F test/savepoint7.test cde525ea3075283eb950cdcdefe23ead4f700daa
|
||||
F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2
|
||||
F test/scanstatus.test b249328caf4d317e71058006872b8012598a5fa045b30bf24a81eeff650ab49e
|
||||
F test/scanstatus2.test 9a00becb1d60d168944f8e2f3585d44d46123aa95c5c7c25563309e1f15f924d
|
||||
F test/scanstatus2.test d0434bc3b356fb9d948f3417846b2ed5bbc4bd4cc49bddb38ac86469f754bfb0
|
||||
F test/schema.test 5dd11c96ba64744de955315d2e4f8992e447533690153b93377dffb2a5ef5431
|
||||
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
|
||||
F test/schema3.test 8ed4ae66e082cdd8b1b1f22d8549e1e7a0db4527a8e6ee8b6193053ee1e5c9ce
|
||||
@@ -1469,7 +1471,7 @@ F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8e
|
||||
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test 300b77328aaafb9f3e7a53a26e4162fbf92181d92251d259ff105a2275ff998d
|
||||
F test/shell2.test 09a202f57e7cd99788537f763e0845796a173fcea06a0d199a08d69446fe1daf
|
||||
F test/shell2.test 35226c070a8c7f64fd016dfac2a0db2a40f709b3131f61daacd9dad61536c9cb
|
||||
F test/shell3.test 91febeac0412812bf6370abb8ed72700e32bf8f9878849414518f662dfd55e8a
|
||||
F test/shell4.test 9abd0c12a7e20a4c49e84d5be208d2124fa6c09e728f56f1f4bee0f02853935f
|
||||
F test/shell5.test c8b6c54f26ec537f8558273d7ed293ca3725ef42e6b12b8f151718628bd1473b
|
||||
@@ -1509,7 +1511,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
|
||||
F test/speedtest1.c 9703465daf778dd90bad7adadc1d2ee205ec90a79437cababab0a4111190f0c8
|
||||
F test/speedtest1.c f9505ad6f9c2c3c488a370a2d193e9603a030e51126ef3ecfeb056d21f0e7ad5
|
||||
F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
|
||||
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
|
||||
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
|
||||
@@ -1996,7 +1998,7 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
|
||||
F tool/showwal.c 65ecabae3a2dcff4116301d5a8dbb8c4964814da1b2aff6d85c806a88b71fa4e
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/spaceanal.tcl 1b5be34c6223cb1af06da2a10fb77863eb869b1962d055820b0a11cf2336ab45
|
||||
F tool/speed-check.sh c24c30cddd0ecb6d1d0775411d22f7619f55fa696a305487645e27b1b8fc05a2
|
||||
F tool/speed-check.sh 72dc85b2c0484af971ee3e7d10775f72b4e771e27e162c2099b3bf25517c25fb
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@@ -2041,11 +2043,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 61dfa92b44ad38a7aac76a09e167819ce5d0acace3e06ba9ed17b3264cc043c1
|
||||
R 92ef9d4b2ad4bcd79b4a344e5a5e7673
|
||||
T *branch * pcache-opt
|
||||
T *sym-pcache-opt *
|
||||
P a0d3e7571aded8d1e03908059d2d5aa5d62ec49bff099cb38f6f35df5e4b18b5
|
||||
R 1ad9eb2d7a5390b633470ac87bec02c6
|
||||
T *branch * json-write-cache
|
||||
T *sym-json-write-cache *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z 279ebc4b1e0c74835cf97ad5712d1d20
|
||||
Z 11a0a92f5fb8088da9a1185f8dea3559
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
84731a1fd25bd74c8f326a8a8ce3921831eb3d73872564b973bbd81aa406333d
|
||||
2e6fbebc41f1a24f8233b136131e6c392d7474f24946ef164fe3600afbd02357
|
||||
+55
-106
@@ -1534,7 +1534,7 @@ static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){
|
||||
pPage->xParseCell(pPage, pCell, &info);
|
||||
if( info.nLocal<info.nPayload ){
|
||||
Pgno ovfl;
|
||||
if( SQLITE_WITHIN(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){
|
||||
if( SQLITE_OVERFLOW(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){
|
||||
testcase( pSrc!=pPage );
|
||||
*pRC = SQLITE_CORRUPT_BKPT;
|
||||
return;
|
||||
@@ -2318,68 +2318,41 @@ Pgno sqlite3BtreeLastPage(Btree *p){
|
||||
|
||||
/*
|
||||
** Get a page from the pager and initialize it.
|
||||
**
|
||||
** If pCur!=0 then the page is being fetched as part of a moveToChild()
|
||||
** call. Do additional sanity checking on the page in this case.
|
||||
** And if the fetch fails, this routine must decrement pCur->iPage.
|
||||
**
|
||||
** The page is fetched as read-write unless pCur is not NULL and is
|
||||
** a read-only cursor.
|
||||
**
|
||||
** If an error occurs, then *ppPage is undefined. It
|
||||
** may remain unchanged, or it may be set to an invalid value.
|
||||
*/
|
||||
static int getAndInitPage(
|
||||
BtShared *pBt, /* The database file */
|
||||
Pgno pgno, /* Number of the page to get */
|
||||
MemPage **ppPage, /* Write the page pointer here */
|
||||
BtCursor *pCur, /* Cursor to receive the page, or NULL */
|
||||
int bReadOnly /* True for a read-only page */
|
||||
){
|
||||
int rc;
|
||||
DbPage *pDbPage;
|
||||
MemPage *pPage;
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
assert( pCur==0 || ppPage==&pCur->pPage );
|
||||
assert( pCur==0 || bReadOnly==pCur->curPagerFlags );
|
||||
assert( pCur==0 || pCur->iPage>0 );
|
||||
|
||||
if( pgno>btreePagecount(pBt) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto getAndInitPage_error1;
|
||||
*ppPage = 0;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly);
|
||||
if( rc ){
|
||||
goto getAndInitPage_error1;
|
||||
*ppPage = 0;
|
||||
return rc;
|
||||
}
|
||||
*ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
|
||||
if( (*ppPage)->isInit==0 ){
|
||||
pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
|
||||
if( pPage->isInit==0 ){
|
||||
btreePageFromDbPage(pDbPage, pgno, pBt);
|
||||
rc = btreeInitPage(*ppPage);
|
||||
rc = btreeInitPage(pPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto getAndInitPage_error2;
|
||||
releasePage(pPage);
|
||||
*ppPage = 0;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
assert( (*ppPage)->pgno==pgno || CORRUPT_DB );
|
||||
assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) );
|
||||
|
||||
/* If obtaining a child page for a cursor, we must verify that the page is
|
||||
** compatible with the root page. */
|
||||
if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){
|
||||
rc = SQLITE_CORRUPT_PGNO(pgno);
|
||||
goto getAndInitPage_error2;
|
||||
}
|
||||
assert( pPage->pgno==pgno || CORRUPT_DB );
|
||||
assert( pPage->aData==sqlite3PagerGetData(pDbPage) );
|
||||
*ppPage = pPage;
|
||||
return SQLITE_OK;
|
||||
|
||||
getAndInitPage_error2:
|
||||
releasePage(*ppPage);
|
||||
getAndInitPage_error1:
|
||||
if( pCur ){
|
||||
pCur->iPage--;
|
||||
pCur->pPage = pCur->apPage[pCur->iPage];
|
||||
}
|
||||
testcase( pgno==0 );
|
||||
assert( pgno!=0 || rc!=SQLITE_OK );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5353,6 +5326,7 @@ const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){
|
||||
** vice-versa).
|
||||
*/
|
||||
static int moveToChild(BtCursor *pCur, u32 newPgno){
|
||||
int rc;
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
|
||||
@@ -5366,8 +5340,18 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
|
||||
pCur->apPage[pCur->iPage] = pCur->pPage;
|
||||
pCur->ix = 0;
|
||||
pCur->iPage++;
|
||||
return getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur,
|
||||
pCur->curPagerFlags);
|
||||
rc = getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur->curPagerFlags);
|
||||
assert( pCur->pPage!=0 || rc!=SQLITE_OK );
|
||||
if( rc==SQLITE_OK
|
||||
&& (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey)
|
||||
){
|
||||
releasePage(pCur->pPage);
|
||||
rc = SQLITE_CORRUPT_PGNO(newPgno);
|
||||
}
|
||||
if( rc ){
|
||||
pCur->pPage = pCur->apPage[--pCur->iPage];
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
@@ -5474,7 +5458,7 @@ static int moveToRoot(BtCursor *pCur){
|
||||
sqlite3BtreeClearCursor(pCur);
|
||||
}
|
||||
rc = getAndInitPage(pCur->pBt, pCur->pgnoRoot, &pCur->pPage,
|
||||
0, pCur->curPagerFlags);
|
||||
pCur->curPagerFlags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
return rc;
|
||||
@@ -5586,7 +5570,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
|
||||
*pRes = 0;
|
||||
rc = moveToLeftmost(pCur);
|
||||
}else if( rc==SQLITE_EMPTY ){
|
||||
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
||||
assert( pCur->pgnoRoot==0 || (pCur->pPage!=0 && pCur->pPage->nCell==0) );
|
||||
*pRes = 1;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
@@ -5900,7 +5884,6 @@ int sqlite3BtreeIndexMoveto(
|
||||
){
|
||||
int rc;
|
||||
RecordCompare xRecordCompare;
|
||||
int bNeedUnref; /* Deferred Unref() calls for pCur->apPage[] */
|
||||
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
@@ -5950,43 +5933,19 @@ int sqlite3BtreeIndexMoveto(
|
||||
if( !pCur->pPage->isInit ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
bNeedUnref = 0;
|
||||
goto bypass_moveto_root; /* Start search on the current page */
|
||||
}
|
||||
pIdxKey->errCode = SQLITE_OK;
|
||||
}
|
||||
|
||||
if( pCur->eState==CURSOR_VALID
|
||||
&& pCur->iPage>0
|
||||
&& pCur->apPage[0]->nCell>0
|
||||
){
|
||||
/* This block is similar to moveToRoot() except that it does not
|
||||
** call releasePageNotNull() on the child pages found in the cursor.
|
||||
** The pCur->apPage[x] pointers for x>=1 up to x==bNeedUnref
|
||||
** remain valid and available for reuse. This means that the state of
|
||||
** the BtCursor object is technical invalid when bNeedUnref>0. This
|
||||
** routine must invoke releasePageNotNull() on apPage[] entries that
|
||||
** are not reused prior to existing this function, in order to bring
|
||||
** the cursor back into a valid state.
|
||||
*/
|
||||
bNeedUnref = pCur->iPage;
|
||||
pCur->apPage[bNeedUnref] = pCur->pPage;
|
||||
pCur->iPage = 0;
|
||||
pCur->pPage = pCur->apPage[0];
|
||||
pCur->ix = 0;
|
||||
pCur->info.nSize = 0;
|
||||
pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
|
||||
}else{
|
||||
bNeedUnref = 0;
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc ){
|
||||
if( rc==SQLITE_EMPTY ){
|
||||
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
||||
*pRes = -1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc ){
|
||||
if( rc==SQLITE_EMPTY ){
|
||||
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
||||
*pRes = -1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bypass_moveto_root:
|
||||
@@ -6113,47 +6072,37 @@ bypass_moveto_root:
|
||||
chldPg = get4byte(findCell(pPage, lwr));
|
||||
}
|
||||
|
||||
/* The following is similar to an in-lined version of
|
||||
/* This block is similar to an in-lined version of:
|
||||
**
|
||||
** pCur->ix = (u16)lwr;
|
||||
** rc = moveToChild(pCur, chldPg);
|
||||
**
|
||||
** But with the additional feature that pCur->apPage[] entries
|
||||
** that have had their Unref() calls deferred might be reused.
|
||||
** Reusing an apPage[] entry bypasses a lot of look-up and
|
||||
** initialization logic and hence saves many CPU cycles.
|
||||
** if( rc ) break;
|
||||
*/
|
||||
pCur->info.nSize = 0;
|
||||
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
|
||||
if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
|
||||
assert( bNeedUnref==0 );
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)lwr;
|
||||
pCur->apPage[pCur->iPage] = pCur->pPage;
|
||||
pCur->ix = 0;
|
||||
pCur->iPage++;
|
||||
if( bNeedUnref && pCur->apPage[pCur->iPage]->pgno==chldPg ){
|
||||
pCur->pPage = pCur->apPage[pCur->iPage];
|
||||
}else{
|
||||
if( bNeedUnref ){
|
||||
while( bNeedUnref>=pCur->iPage ){
|
||||
releasePageNotNull(pCur->apPage[bNeedUnref--]);
|
||||
}
|
||||
bNeedUnref = 0;
|
||||
}
|
||||
rc = getAndInitPage(pCur->pBt, chldPg, &pCur->pPage, pCur,
|
||||
pCur->curPagerFlags);
|
||||
if( rc ) break;
|
||||
rc = getAndInitPage(pCur->pBt, chldPg, &pCur->pPage, pCur->curPagerFlags);
|
||||
if( rc==SQLITE_OK
|
||||
&& (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey)
|
||||
){
|
||||
releasePage(pCur->pPage);
|
||||
rc = SQLITE_CORRUPT_PGNO(chldPg);
|
||||
}
|
||||
}
|
||||
if( rc ){
|
||||
pCur->pPage = pCur->apPage[--pCur->iPage];
|
||||
break;
|
||||
}
|
||||
/*
|
||||
***** End of in-lined moveToChild() call */
|
||||
}
|
||||
moveto_index_finish:
|
||||
pCur->info.nSize = 0;
|
||||
if( bNeedUnref ){
|
||||
while( bNeedUnref>pCur->iPage ){
|
||||
releasePageNotNull(pCur->apPage[bNeedUnref--]);
|
||||
}
|
||||
}
|
||||
assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
|
||||
return rc;
|
||||
}
|
||||
@@ -8205,7 +8154,7 @@ static int balance_nonroot(
|
||||
pgno = get4byte(pRight);
|
||||
while( 1 ){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
|
||||
rc = getAndInitPage(pBt, pgno, &apOld[i], 0);
|
||||
}
|
||||
if( rc ){
|
||||
memset(apOld, 0, (i+1)*sizeof(MemPage*));
|
||||
@@ -8746,7 +8695,7 @@ static int balance_nonroot(
|
||||
assert( iOvflSpace <= (int)pBt->pageSize );
|
||||
for(k=0; ALWAYS(k<NB*2) && b.ixNx[k]<=j; k++){}
|
||||
pSrcEnd = b.apEnd[k];
|
||||
if( SQLITE_WITHIN(pSrcEnd, pCell, pCell+sz) ){
|
||||
if( SQLITE_OVERFLOW(pSrcEnd, pCell, pCell+sz) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
@@ -10090,7 +10039,7 @@ static int clearDatabasePage(
|
||||
if( pgno>btreePagecount(pBt) ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
rc = getAndInitPage(pBt, pgno, &pPage, 0, 0);
|
||||
rc = getAndInitPage(pBt, pgno, &pPage, 0);
|
||||
if( rc ) return rc;
|
||||
if( (pBt->openFlags & BTREE_SINGLE)==0
|
||||
&& sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1))
|
||||
|
||||
+9
-6
@@ -4668,10 +4668,10 @@ expr_code_doover:
|
||||
r1 = sqlite3GetTempRange(pParse, nFarg);
|
||||
}
|
||||
|
||||
/* For length() and typeof() functions with a column argument,
|
||||
/* For length() and typeof() and octet_length() functions,
|
||||
** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG
|
||||
** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data
|
||||
** loading.
|
||||
** or OPFLAG_TYPEOFARG or OPFLAG_BYTELENARG respectively, to avoid
|
||||
** unnecessary data loading.
|
||||
*/
|
||||
if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
|
||||
u8 exprOp;
|
||||
@@ -4681,9 +4681,12 @@ expr_code_doover:
|
||||
if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){
|
||||
assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG );
|
||||
assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG );
|
||||
testcase( pDef->funcFlags & OPFLAG_LENGTHARG );
|
||||
pFarg->a[0].pExpr->op2 =
|
||||
pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG);
|
||||
assert( SQLITE_FUNC_BYTELEN==OPFLAG_BYTELENARG );
|
||||
assert( (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG)==OPFLAG_BYTELENARG );
|
||||
testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_LENGTHARG );
|
||||
testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_TYPEOFARG );
|
||||
testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_BYTELENARG);
|
||||
pFarg->a[0].pExpr->op2 = pDef->funcFlags & OPFLAG_BYTELENARG;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+185
-25
@@ -149,6 +149,42 @@ static void lengthFunc(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the octet_length() function
|
||||
*/
|
||||
static void bytelengthFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_BLOB: {
|
||||
sqlite3_result_int(context, sqlite3_value_bytes(argv[0]));
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT: {
|
||||
i64 m = sqlite3_context_db_handle(context)->enc<=SQLITE_UTF8 ? 1 : 2;
|
||||
sqlite3_result_int64(context, sqlite3_value_bytes(argv[0])*m);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
if( sqlite3_value_encoding(argv[0])<=SQLITE_UTF8 ){
|
||||
sqlite3_result_int(context, sqlite3_value_bytes(argv[0]));
|
||||
}else{
|
||||
sqlite3_result_int(context, sqlite3_value_bytes16(argv[0]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sqlite3_result_null(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the abs() function.
|
||||
**
|
||||
@@ -425,7 +461,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
}else if( n==0 ){
|
||||
r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5)));
|
||||
}else{
|
||||
zBuf = sqlite3_mprintf("%.*f",n,r);
|
||||
zBuf = sqlite3_mprintf("%!.*f",n,r);
|
||||
if( zBuf==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
@@ -1634,13 +1670,68 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
*/
|
||||
typedef struct SumCtx SumCtx;
|
||||
struct SumCtx {
|
||||
double rSum; /* Floating point sum */
|
||||
i64 iSum; /* Integer sum */
|
||||
double rSum; /* Running sum as as a double */
|
||||
double rErr; /* Error term for Kahan-Babushka-Neumaier summation */
|
||||
i64 iSum; /* Running sum as a signed integer */
|
||||
i64 cnt; /* Number of elements summed */
|
||||
u8 overflow; /* True if integer overflow seen */
|
||||
u8 approx; /* True if non-integer value was input to the sum */
|
||||
u8 approx; /* True if any non-integer value was input to the sum */
|
||||
u8 ovrfl; /* Integer overflow seen */
|
||||
};
|
||||
|
||||
/*
|
||||
** Do one step of the Kahan-Babushka-Neumaier summation.
|
||||
**
|
||||
** https://en.wikipedia.org/wiki/Kahan_summation_algorithm
|
||||
**
|
||||
** Variables are marked "volatile" to defeat c89 x86 floating point
|
||||
** optimizations can mess up this algorithm.
|
||||
*/
|
||||
static void kahanBabuskaNeumaierStep(
|
||||
volatile SumCtx *pSum,
|
||||
volatile double r
|
||||
){
|
||||
volatile double s = pSum->rSum;
|
||||
volatile double t = s + r;
|
||||
if( fabs(s) > fabs(r) ){
|
||||
pSum->rErr += (s - t) + r;
|
||||
}else{
|
||||
pSum->rErr += (r - t) + s;
|
||||
}
|
||||
pSum->rSum = t;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a (possibly large) integer to the running sum.
|
||||
*/
|
||||
static void kahanBabuskaNeumaierStepInt64(volatile SumCtx *pSum, i64 iVal){
|
||||
if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){
|
||||
i64 iBig, iSm;
|
||||
iSm = iVal % 16384;
|
||||
iBig = iVal - iSm;
|
||||
kahanBabuskaNeumaierStep(pSum, iBig);
|
||||
kahanBabuskaNeumaierStep(pSum, iSm);
|
||||
}else{
|
||||
kahanBabuskaNeumaierStep(pSum, (double)iVal);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize the Kahan-Babaska-Neumaier sum from a 64-bit integer
|
||||
*/
|
||||
static void kahanBabuskaNeumaierInit(
|
||||
volatile SumCtx *p,
|
||||
i64 iVal
|
||||
){
|
||||
if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){
|
||||
i64 iSm = iVal % 16384;
|
||||
p->rSum = (double)(iVal - iSm);
|
||||
p->rErr = (double)iSm;
|
||||
}else{
|
||||
p->rSum = (double)iVal;
|
||||
p->rErr = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Routines used to compute the sum, average, and total.
|
||||
**
|
||||
@@ -1660,15 +1751,29 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
type = sqlite3_value_numeric_type(argv[0]);
|
||||
if( p && type!=SQLITE_NULL ){
|
||||
p->cnt++;
|
||||
if( type==SQLITE_INTEGER ){
|
||||
i64 v = sqlite3_value_int64(argv[0]);
|
||||
p->rSum += v;
|
||||
if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){
|
||||
p->approx = p->overflow = 1;
|
||||
if( p->approx==0 ){
|
||||
if( type!=SQLITE_INTEGER ){
|
||||
kahanBabuskaNeumaierInit(p, p->iSum);
|
||||
p->approx = 1;
|
||||
kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0]));
|
||||
}else{
|
||||
i64 x = p->iSum;
|
||||
if( sqlite3AddInt64(&x, sqlite3_value_int64(argv[0]))==0 ){
|
||||
p->iSum = x;
|
||||
}else{
|
||||
p->ovrfl = 1;
|
||||
kahanBabuskaNeumaierInit(p, p->iSum);
|
||||
p->approx = 1;
|
||||
kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0]));
|
||||
}
|
||||
}
|
||||
}else{
|
||||
p->rSum += sqlite3_value_double(argv[0]);
|
||||
p->approx = 1;
|
||||
if( type==SQLITE_INTEGER ){
|
||||
kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0]));
|
||||
}else{
|
||||
p->ovrfl = 0;
|
||||
kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1685,13 +1790,18 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
if( ALWAYS(p) && type!=SQLITE_NULL ){
|
||||
assert( p->cnt>0 );
|
||||
p->cnt--;
|
||||
assert( type==SQLITE_INTEGER || p->approx );
|
||||
if( type==SQLITE_INTEGER && p->approx==0 ){
|
||||
i64 v = sqlite3_value_int64(argv[0]);
|
||||
p->rSum -= v;
|
||||
p->iSum -= v;
|
||||
if( !p->approx ){
|
||||
p->iSum -= sqlite3_value_int64(argv[0]);
|
||||
}else if( type==SQLITE_INTEGER ){
|
||||
i64 iVal = sqlite3_value_int64(argv[0]);
|
||||
if( iVal!=SMALLEST_INT64 ){
|
||||
kahanBabuskaNeumaierStepInt64(p, -iVal);
|
||||
}else{
|
||||
kahanBabuskaNeumaierStepInt64(p, LARGEST_INT64);
|
||||
kahanBabuskaNeumaierStepInt64(p, 1);
|
||||
}
|
||||
}else{
|
||||
p->rSum -= sqlite3_value_double(argv[0]);
|
||||
kahanBabuskaNeumaierStep(p, -sqlite3_value_double(argv[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1702,10 +1812,12 @@ static void sumFinalize(sqlite3_context *context){
|
||||
SumCtx *p;
|
||||
p = sqlite3_aggregate_context(context, 0);
|
||||
if( p && p->cnt>0 ){
|
||||
if( p->overflow ){
|
||||
sqlite3_result_error(context,"integer overflow",-1);
|
||||
}else if( p->approx ){
|
||||
sqlite3_result_double(context, p->rSum);
|
||||
if( p->approx ){
|
||||
if( p->ovrfl ){
|
||||
sqlite3_result_error(context,"integer overflow",-1);
|
||||
}else{
|
||||
sqlite3_result_double(context, p->rSum+p->rErr);
|
||||
}
|
||||
}else{
|
||||
sqlite3_result_int64(context, p->iSum);
|
||||
}
|
||||
@@ -1715,14 +1827,27 @@ static void avgFinalize(sqlite3_context *context){
|
||||
SumCtx *p;
|
||||
p = sqlite3_aggregate_context(context, 0);
|
||||
if( p && p->cnt>0 ){
|
||||
sqlite3_result_double(context, p->rSum/(double)p->cnt);
|
||||
double r;
|
||||
if( p->approx ){
|
||||
r = p->rSum+p->rErr;
|
||||
}else{
|
||||
r = (double)(p->iSum);
|
||||
}
|
||||
sqlite3_result_double(context, r/(double)p->cnt);
|
||||
}
|
||||
}
|
||||
static void totalFinalize(sqlite3_context *context){
|
||||
SumCtx *p;
|
||||
double r = 0.0;
|
||||
p = sqlite3_aggregate_context(context, 0);
|
||||
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
sqlite3_result_double(context, p ? p->rSum : (double)0);
|
||||
if( p ){
|
||||
if( p->approx ){
|
||||
r = p->rSum+p->rErr;
|
||||
}else{
|
||||
r = (double)(p->iSum);
|
||||
}
|
||||
}
|
||||
sqlite3_result_double(context, r);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2312,6 +2437,37 @@ static void signFunc(
|
||||
sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Implementation of fpdecode(x,y,z) function.
|
||||
**
|
||||
** x is a real number that is to be decoded. y is the precision.
|
||||
** z is the maximum real precision.
|
||||
*/
|
||||
static void fpdecodeFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
FpDecode s;
|
||||
double x;
|
||||
int y, z;
|
||||
char zBuf[100];
|
||||
UNUSED_PARAMETER(argc);
|
||||
assert( argc==3 );
|
||||
x = sqlite3_value_double(argv[0]);
|
||||
y = sqlite3_value_int(argv[1]);
|
||||
z = sqlite3_value_int(argv[2]);
|
||||
sqlite3FpDecode(&s, x, y, z);
|
||||
if( s.isSpecial==2 ){
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN");
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP);
|
||||
}
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** All of the FuncDef structures in the aBuiltinFunc[] array above
|
||||
** to the global function hash table. This occurs at start-time (as
|
||||
@@ -2376,12 +2532,16 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
|
||||
FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF),
|
||||
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
|
||||
FUNCTION2(octet_length, 1, 0, 0, bytelengthFunc,SQLITE_FUNC_BYTELEN),
|
||||
FUNCTION(instr, 2, 0, 0, instrFunc ),
|
||||
FUNCTION(printf, -1, 0, 0, printfFunc ),
|
||||
FUNCTION(format, -1, 0, 0, printfFunc ),
|
||||
FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
|
||||
FUNCTION(char, -1, 0, 0, charFunc ),
|
||||
FUNCTION(abs, 1, 0, 0, absFunc ),
|
||||
#ifdef SQLITE_DEBUG
|
||||
FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
FUNCTION(round, 1, 0, 0, roundFunc ),
|
||||
FUNCTION(round, 2, 0, 0, roundFunc ),
|
||||
|
||||
@@ -243,6 +243,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
|
||||
0, /* bSmallMalloc */
|
||||
1, /* bExtraSchemaChecks */
|
||||
sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
0, /* neverCorrupt */
|
||||
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
|
||||
|
||||
+93
-54
@@ -135,9 +135,10 @@ struct JsonParse {
|
||||
u8 nErr; /* Number of errors seen */
|
||||
u8 oom; /* Set to true if out of memory */
|
||||
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
|
||||
u8 nRef; /* Number of references to this object */
|
||||
int nJson; /* Length of the zJson string in bytes */
|
||||
u32 iErr; /* Error location in zJson[] */
|
||||
u32 iHold; /* Replace cache line with the lowest iHold value */
|
||||
u32 iHold; /* Creation sequence number, for LRU replacement */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -505,14 +506,19 @@ static void jsonParseReset(JsonParse *pParse){
|
||||
pParse->nAlloc = 0;
|
||||
sqlite3_free(pParse->aUp);
|
||||
pParse->aUp = 0;
|
||||
pParse->nRef = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a JsonParse object that was obtained from sqlite3_malloc().
|
||||
*/
|
||||
static void jsonParseFree(JsonParse *pParse){
|
||||
jsonParseReset(pParse);
|
||||
sqlite3_free(pParse);
|
||||
if( pParse->nRef>1 ){
|
||||
pParse->nRef--;
|
||||
}else{
|
||||
jsonParseReset(pParse);
|
||||
sqlite3_free(pParse);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1516,6 +1522,7 @@ static int jsonParse(
|
||||
memset(pParse, 0, sizeof(*pParse));
|
||||
if( zJson==0 ) return 1;
|
||||
pParse->zJson = zJson;
|
||||
/* printf("JSON parse: %s\n", zJson); fflush(stdout); */
|
||||
i = jsonParseValue(pParse, 0);
|
||||
if( pParse->oom ) i = -1;
|
||||
if( i>0 ){
|
||||
@@ -1596,11 +1603,21 @@ static int jsonParseFindParents(JsonParse *pParse){
|
||||
** Obtain a complete parse of the JSON found in the first argument
|
||||
** of the argv array. Use the sqlite3_get_auxdata() cache for this
|
||||
** parse if it is available. If the cache is not available or if it
|
||||
** is no longer valid, parse the JSON again and return the new parse,
|
||||
** and also register the new parse so that it will be available for
|
||||
** future sqlite3_get_auxdata() calls.
|
||||
** is no longer valid, parse the JSON again and return the new parse.
|
||||
**
|
||||
** If an error occurs and pErrCtx!=0 then report the error on pErrCtx
|
||||
** If a new parse is created, it is stored in the cache unless bWrite
|
||||
** is true. If the parse is cached and pErrCtx!=0 then the caller does
|
||||
** not need to free the returned parse since the parse belongs to the
|
||||
** cache and will be freed when the cache is cleared. On the other hand,
|
||||
** the returned parse should be treated as read-only.
|
||||
**
|
||||
** If the bWrite field is true, then it is assumed that the caller will
|
||||
** make changes to the parse. In this case, the parse is not cached (or
|
||||
** it is removed from the cache). Ownership of the parse passes to the
|
||||
** caller and the caller is expected to call jsonParseFree() when it has
|
||||
** finished using the parse.
|
||||
**
|
||||
** If an error occurs and pErrCtx!=0 then an error is reported on pErrCtx
|
||||
** and return NULL.
|
||||
**
|
||||
** If an error occurs and pErrCtx==0 then return the Parse object with
|
||||
@@ -1612,7 +1629,8 @@ static int jsonParseFindParents(JsonParse *pParse){
|
||||
static JsonParse *jsonParseCached(
|
||||
sqlite3_context *pCtx,
|
||||
sqlite3_value **argv,
|
||||
sqlite3_context *pErrCtx
|
||||
sqlite3_context *pErrCtx,
|
||||
int bWrite
|
||||
){
|
||||
const char *zJson = (const char*)sqlite3_value_text(argv[0]);
|
||||
int nJson = sqlite3_value_bytes(argv[0]);
|
||||
@@ -1629,12 +1647,19 @@ static JsonParse *jsonParseCached(
|
||||
iMinKey = iKey;
|
||||
break;
|
||||
}
|
||||
assert( p->nRef==1 );
|
||||
if( pMatch==0
|
||||
&& p->nJson==nJson
|
||||
&& memcmp(p->zJson,zJson,nJson)==0
|
||||
){
|
||||
p->nErr = 0;
|
||||
pMatch = p;
|
||||
if( bWrite ){
|
||||
p->nRef = 2;
|
||||
sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iKey, 0, 0);
|
||||
/* printf("JSON cache hit and purge (%d)\n", iKey); fflush(stdout); */
|
||||
}
|
||||
/* else{ printf("JSON cache hit (%d)\n", iKey); fflush(stdout); } */
|
||||
}else if( p->iHold<iMinHold ){
|
||||
iMinHold = p->iHold;
|
||||
iMinKey = iKey;
|
||||
@@ -1646,6 +1671,7 @@ static JsonParse *jsonParseCached(
|
||||
if( pMatch ){
|
||||
pMatch->nErr = 0;
|
||||
pMatch->iHold = iMaxHold+1;
|
||||
assert( pMatch->nRef==1 );
|
||||
return pMatch;
|
||||
}
|
||||
p = sqlite3_malloc64( sizeof(*p) + nJson + 1 );
|
||||
@@ -1656,9 +1682,11 @@ static JsonParse *jsonParseCached(
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->zJson = (char*)&p[1];
|
||||
memcpy((char*)p->zJson, zJson, nJson+1);
|
||||
/* printf("JSON cache miss\n"); fflush(stdout); */
|
||||
if( jsonParse(p, pErrCtx, p->zJson) ){
|
||||
if( pErrCtx==0 ){
|
||||
p->nErr = 1;
|
||||
p->nRef = 1;
|
||||
return p;
|
||||
}
|
||||
sqlite3_free(p);
|
||||
@@ -1666,9 +1694,15 @@ static JsonParse *jsonParseCached(
|
||||
}
|
||||
p->nJson = nJson;
|
||||
p->iHold = iMaxHold+1;
|
||||
sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
|
||||
(void(*)(void*))jsonParseFree);
|
||||
return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
|
||||
p->nRef = 1;
|
||||
if( !bWrite ){
|
||||
/* printf("JSON cache insert (%d)\n", iMinKey); fflush(stdout); */
|
||||
sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
|
||||
(void(*)(void*))jsonParseFree);
|
||||
p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
|
||||
}
|
||||
assert( p==0 || p->nRef==1 );
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2095,7 +2129,7 @@ static void jsonArrayLengthFunc(
|
||||
u32 i;
|
||||
JsonNode *pNode;
|
||||
|
||||
p = jsonParseCached(ctx, argv, ctx);
|
||||
p = jsonParseCached(ctx, argv, ctx, 0);
|
||||
if( p==0 ) return;
|
||||
assert( p->nNode );
|
||||
if( argc==2 ){
|
||||
@@ -2157,7 +2191,7 @@ static void jsonExtractFunc(
|
||||
JsonString jx;
|
||||
|
||||
if( argc<2 ) return;
|
||||
p = jsonParseCached(ctx, argv, ctx);
|
||||
p = jsonParseCached(ctx, argv, ctx, 0);
|
||||
if( p==0 ) return;
|
||||
if( argc==2 ){
|
||||
/* With a single PATH argument */
|
||||
@@ -2313,25 +2347,27 @@ static void jsonPatchFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
JsonParse x; /* The JSON that is being patched */
|
||||
JsonParse y; /* The patch */
|
||||
JsonParse *pX; /* The JSON that is being patched */
|
||||
JsonParse *pY; /* The patch */
|
||||
JsonNode *pResult; /* The result of the merge */
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){
|
||||
jsonParseReset(&x);
|
||||
pX = jsonParseCached(ctx, argv, ctx, 1);
|
||||
if( pX==0 ) return;
|
||||
pY = jsonParseCached(ctx, argv+1, ctx, 1);
|
||||
if( pY==0 ){
|
||||
jsonParseFree(pX);
|
||||
return;
|
||||
}
|
||||
pResult = jsonMergePatch(&x, 0, y.aNode);
|
||||
assert( pResult!=0 || x.oom );
|
||||
pResult = jsonMergePatch(pX, 0, pY->aNode);
|
||||
assert( pResult!=0 || pX->oom );
|
||||
if( pResult ){
|
||||
jsonReturnJson(pResult, ctx, 0);
|
||||
}else{
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
}
|
||||
jsonParseReset(&x);
|
||||
jsonParseReset(&y);
|
||||
jsonParseFree(pX);
|
||||
jsonParseFree(pY);
|
||||
}
|
||||
|
||||
|
||||
@@ -2387,26 +2423,27 @@ static void jsonRemoveFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
JsonParse x; /* The parse */
|
||||
JsonParse *p; /* The parse */
|
||||
JsonNode *pNode;
|
||||
const char *zPath;
|
||||
u32 i;
|
||||
|
||||
if( argc<1 ) return;
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
assert( x.nNode );
|
||||
p = jsonParseCached(ctx, argv, ctx, 1);
|
||||
if( p==0 ) return;
|
||||
assert( p->nNode );
|
||||
for(i=1; i<(u32)argc; i++){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
if( zPath==0 ) goto remove_done;
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
if( x.nErr ) goto remove_done;
|
||||
pNode = jsonLookup(p, zPath, 0, ctx);
|
||||
if( p->nErr ) goto remove_done;
|
||||
if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
|
||||
}
|
||||
if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
|
||||
jsonReturnJson(x.aNode, ctx, 0);
|
||||
if( (p->aNode[0].jnFlags & JNODE_REMOVE)==0 ){
|
||||
jsonReturnJson(p->aNode, ctx, 0);
|
||||
}
|
||||
remove_done:
|
||||
jsonParseReset(&x);
|
||||
jsonParseFree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2420,7 +2457,7 @@ static void jsonReplaceFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
JsonParse x; /* The parse */
|
||||
JsonParse *p; /* The parse */
|
||||
JsonNode *pNode;
|
||||
const char *zPath;
|
||||
u32 i;
|
||||
@@ -2430,12 +2467,13 @@ static void jsonReplaceFunc(
|
||||
jsonWrongNumArgs(ctx, "replace");
|
||||
return;
|
||||
}
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
assert( x.nNode );
|
||||
p = jsonParseCached(ctx, argv, ctx, 1);
|
||||
if( p==0 ) return;
|
||||
assert( p->nNode );
|
||||
for(i=1; i<(u32)argc; i+=2){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
if( x.nErr ) goto replace_err;
|
||||
pNode = jsonLookup(p, zPath, 0, ctx);
|
||||
if( p->nErr ) goto replace_err;
|
||||
if( pNode ){
|
||||
assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 );
|
||||
testcase( pNode->eU!=0 && pNode->eU!=1 );
|
||||
@@ -2444,14 +2482,14 @@ static void jsonReplaceFunc(
|
||||
pNode->u.iReplace = i + 1;
|
||||
}
|
||||
}
|
||||
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
assert( x.aNode[0].eU==4 );
|
||||
sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
||||
if( p->aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
assert( p->aNode[0].eU==4 );
|
||||
sqlite3_result_value(ctx, argv[p->aNode[0].u.iReplace]);
|
||||
}else{
|
||||
jsonReturnJson(x.aNode, ctx, argv);
|
||||
jsonReturnJson(p->aNode, ctx, argv);
|
||||
}
|
||||
replace_err:
|
||||
jsonParseReset(&x);
|
||||
jsonParseFree(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -2472,7 +2510,7 @@ static void jsonSetFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
JsonParse x; /* The parse */
|
||||
JsonParse *p; /* The parse */
|
||||
JsonNode *pNode;
|
||||
const char *zPath;
|
||||
u32 i;
|
||||
@@ -2484,16 +2522,17 @@ static void jsonSetFunc(
|
||||
jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
|
||||
return;
|
||||
}
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
assert( x.nNode );
|
||||
p = jsonParseCached(ctx, argv, ctx, 1);
|
||||
if( p==0 ) return;
|
||||
assert( p->nNode );
|
||||
for(i=1; i<(u32)argc; i+=2){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
bApnd = 0;
|
||||
pNode = jsonLookup(&x, zPath, &bApnd, ctx);
|
||||
if( x.oom ){
|
||||
pNode = jsonLookup(p, zPath, &bApnd, ctx);
|
||||
if( p->oom ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
goto jsonSetDone;
|
||||
}else if( x.nErr ){
|
||||
}else if( p->nErr ){
|
||||
goto jsonSetDone;
|
||||
}else if( pNode && (bApnd || bIsSet) ){
|
||||
testcase( pNode->eU!=0 && pNode->eU!=1 );
|
||||
@@ -2503,14 +2542,14 @@ static void jsonSetFunc(
|
||||
pNode->u.iReplace = i + 1;
|
||||
}
|
||||
}
|
||||
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
assert( x.aNode[0].eU==4 );
|
||||
sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
||||
if( p->aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
assert( p->aNode[0].eU==4 );
|
||||
sqlite3_result_value(ctx, argv[p->aNode[0].u.iReplace]);
|
||||
}else{
|
||||
jsonReturnJson(x.aNode, ctx, argv);
|
||||
jsonReturnJson(p->aNode, ctx, argv);
|
||||
}
|
||||
jsonSetDone:
|
||||
jsonParseReset(&x);
|
||||
jsonParseFree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2529,7 +2568,7 @@ static void jsonTypeFunc(
|
||||
const char *zPath;
|
||||
JsonNode *pNode;
|
||||
|
||||
p = jsonParseCached(ctx, argv, ctx);
|
||||
p = jsonParseCached(ctx, argv, ctx, 0);
|
||||
if( p==0 ) return;
|
||||
if( argc==2 ){
|
||||
zPath = (const char*)sqlite3_value_text(argv[1]);
|
||||
@@ -2556,7 +2595,7 @@ static void jsonValidFunc(
|
||||
JsonParse *p; /* The parse */
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
p = jsonParseCached(ctx, argv, 0);
|
||||
p = jsonParseCached(ctx, argv, 0, 0);
|
||||
if( p==0 || p->oom ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
sqlite3_free(p);
|
||||
@@ -2602,7 +2641,7 @@ static void jsonErrorFunc(
|
||||
JsonParse *p; /* The parse */
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
p = jsonParseCached(ctx, argv, 0);
|
||||
p = jsonParseCached(ctx, argv, 0, 0);
|
||||
if( p==0 || p->oom ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
sqlite3_free(p);
|
||||
|
||||
+15
@@ -4470,6 +4470,21 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, int X);
|
||||
**
|
||||
** X<0 Make no changes to the bUseLongDouble. Just report value.
|
||||
** X==0 Disable bUseLongDouble
|
||||
** X==1 Enable bUseLongDouble
|
||||
** X==2 Set bUseLongDouble to its default value for this platform
|
||||
*/
|
||||
case SQLITE_TESTCTRL_USELONGDOUBLE: {
|
||||
int b = va_arg(ap, int);
|
||||
if( b==2 ) b = sizeof(LONGDOUBLE_TYPE)>8;
|
||||
if( b>=0 ) sqlite3Config.bUseLongDouble = b>0;
|
||||
rc = sqlite3Config.bUseLongDouble!=0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue)
|
||||
|
||||
+50
-150
@@ -105,57 +105,6 @@ static const et_info fmtinfo[] = {
|
||||
** %!S Like %S but prefer the zName over the zAlias
|
||||
*/
|
||||
|
||||
/* Floating point constants used for rounding */
|
||||
static const double arRound[] = {
|
||||
5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05,
|
||||
5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10,
|
||||
};
|
||||
|
||||
/*
|
||||
** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point
|
||||
** conversions will work.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
/*
|
||||
** "*val" is a double such that 0.1 <= *val < 10.0
|
||||
** Return the ascii code for the leading digit of *val, then
|
||||
** multiply "*val" by 10.0 to renormalize.
|
||||
**
|
||||
** Example:
|
||||
** input: *val = 3.14159
|
||||
** output: *val = 1.4159 function return = '3'
|
||||
**
|
||||
** The counter *cnt is incremented each time. After counter exceeds
|
||||
** 16 (the number of significant digits in a 64-bit float) '0' is
|
||||
** always returned.
|
||||
*/
|
||||
static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
int digit;
|
||||
LONGDOUBLE_TYPE d;
|
||||
if( (*cnt)<=0 ) return '0';
|
||||
(*cnt)--;
|
||||
digit = (int)*val;
|
||||
d = digit;
|
||||
digit += '0';
|
||||
*val = (*val - d)*10.0;
|
||||
return (char)digit;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_FLOATING_POINT */
|
||||
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
/*
|
||||
** "*val" is a u64. *msd is a divisor used to extract the
|
||||
** most significant digit of *val. Extract that most significant
|
||||
** digit and return it.
|
||||
*/
|
||||
static char et_getdigit_int(u64 *val, u64 *msd){
|
||||
u64 x = (*val)/(*msd);
|
||||
*val -= x*(*msd);
|
||||
if( *msd>=10 ) *msd /= 10;
|
||||
return '0' + (char)(x & 15);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_FLOATING_POINT */
|
||||
|
||||
/*
|
||||
** Set the StrAccum object to an error mode.
|
||||
*/
|
||||
@@ -247,20 +196,15 @@ void sqlite3_str_vappendf(
|
||||
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
sqlite_uint64 longvalue; /* Value for integer types */
|
||||
LONGDOUBLE_TYPE realvalue; /* Value for real types */
|
||||
sqlite_uint64 msd; /* Divisor to get most-significant-digit
|
||||
** of longvalue */
|
||||
double realvalue; /* Value for real types */
|
||||
const et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char *zOut; /* Rendering buffer */
|
||||
int nOut; /* Size of the rendering buffer */
|
||||
char *zExtra = 0; /* Malloced memory used by some conversion */
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
int exp, e2; /* exponent of real numbers */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
double rounder; /* Used for rounding floating point values */
|
||||
int exp, e2; /* exponent of real numbers */
|
||||
etByte flag_dp; /* True if decimal point should be shown */
|
||||
etByte flag_rtz; /* True if trailing zeros should be removed */
|
||||
#endif
|
||||
|
||||
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
|
||||
@@ -535,94 +479,61 @@ void sqlite3_str_vappendf(
|
||||
break;
|
||||
case etFLOAT:
|
||||
case etEXP:
|
||||
case etGENERIC:
|
||||
case etGENERIC: {
|
||||
FpDecode s;
|
||||
int iRound;
|
||||
int j;
|
||||
|
||||
if( bArgList ){
|
||||
realvalue = getDoubleArg(pArgList);
|
||||
}else{
|
||||
realvalue = va_arg(ap,double);
|
||||
}
|
||||
#ifdef SQLITE_OMIT_FLOATING_POINT
|
||||
length = 0;
|
||||
#else
|
||||
if( precision<0 ) precision = 6; /* Set default precision */
|
||||
#ifdef SQLITE_FP_PRECISION_LIMIT
|
||||
if( precision>SQLITE_FP_PRECISION_LIMIT ){
|
||||
precision = SQLITE_FP_PRECISION_LIMIT;
|
||||
}
|
||||
#endif
|
||||
if( realvalue<0.0 ){
|
||||
realvalue = -realvalue;
|
||||
if( xtype==etFLOAT ){
|
||||
iRound = -precision;
|
||||
}else if( xtype==etGENERIC ){
|
||||
iRound = precision;
|
||||
}else{
|
||||
iRound = precision+1;
|
||||
}
|
||||
sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16);
|
||||
if( s.isSpecial ){
|
||||
if( s.isSpecial==2 ){
|
||||
bufpt = flag_zeropad ? "null" : "NaN";
|
||||
length = sqlite3Strlen30(bufpt);
|
||||
break;
|
||||
}else if( flag_zeropad ){
|
||||
s.z[0] = '9';
|
||||
s.iDP = 1000;
|
||||
s.n = 1;
|
||||
}else{
|
||||
memcpy(buf, "-Inf", 5);
|
||||
bufpt = buf;
|
||||
if( s.sign=='-' ){
|
||||
/* no-op */
|
||||
}else if( flag_prefix ){
|
||||
buf[0] = flag_prefix;
|
||||
}else{
|
||||
bufpt++;
|
||||
}
|
||||
length = sqlite3Strlen30(bufpt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( s.sign=='-' ){
|
||||
prefix = '-';
|
||||
}else{
|
||||
prefix = flag_prefix;
|
||||
}
|
||||
exp = 0;
|
||||
if( xtype==etGENERIC && precision>0 ) precision--;
|
||||
testcase( precision>0xfff );
|
||||
if( realvalue<1.0e+16
|
||||
&& realvalue==(LONGDOUBLE_TYPE)(longvalue = (u64)realvalue)
|
||||
){
|
||||
/* Number is a pure integer that can be represented as u64 */
|
||||
for(msd=1; msd*10<=longvalue; msd *= 10, exp++){}
|
||||
if( exp>precision && xtype!=etFLOAT ){
|
||||
u64 rnd = msd/2;
|
||||
int kk = precision;
|
||||
while( kk-- > 0 ){ rnd /= 10; }
|
||||
longvalue += rnd;
|
||||
}
|
||||
}else{
|
||||
msd = 0;
|
||||
longvalue = 0; /* To prevent a compiler warning */
|
||||
idx = precision & 0xfff;
|
||||
rounder = arRound[idx%10];
|
||||
while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; }
|
||||
if( xtype==etFLOAT ){
|
||||
double rx = (double)realvalue;
|
||||
sqlite3_uint64 u;
|
||||
int ex;
|
||||
memcpy(&u, &rx, sizeof(u));
|
||||
ex = -1023 + (int)((u>>52)&0x7ff);
|
||||
if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16;
|
||||
realvalue += rounder;
|
||||
}
|
||||
if( sqlite3IsNaN((double)realvalue) ){
|
||||
if( flag_zeropad ){
|
||||
bufpt = "null";
|
||||
length = 4;
|
||||
}else{
|
||||
bufpt = "NaN";
|
||||
length = 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
||||
if( ALWAYS(realvalue>0.0) ){
|
||||
LONGDOUBLE_TYPE scale = 1.0;
|
||||
while( realvalue>=1e100*scale && exp<=350){ scale*=1e100;exp+=100;}
|
||||
while( realvalue>=1e10*scale && exp<=350 ){ scale*=1e10; exp+=10; }
|
||||
while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
|
||||
realvalue /= scale;
|
||||
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
|
||||
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
|
||||
if( exp>350 ){
|
||||
if( flag_zeropad ){
|
||||
realvalue = 9.0;
|
||||
exp = 999;
|
||||
}else{
|
||||
bufpt = buf;
|
||||
buf[0] = prefix;
|
||||
memcpy(buf+(prefix!=0),"Inf",4);
|
||||
length = 3+(prefix!=0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( xtype!=etFLOAT ){
|
||||
realvalue += rounder;
|
||||
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
|
||||
}
|
||||
}
|
||||
}
|
||||
exp = s.iDP-1;
|
||||
if( xtype==etGENERIC && precision>0 ) precision--;
|
||||
|
||||
/*
|
||||
** If the field type is etGENERIC, then convert to either etEXP
|
||||
@@ -642,9 +553,8 @@ void sqlite3_str_vappendf(
|
||||
if( xtype==etEXP ){
|
||||
e2 = 0;
|
||||
}else{
|
||||
e2 = exp;
|
||||
e2 = s.iDP - 1;
|
||||
}
|
||||
nsd = 16 + flag_altform2*10;
|
||||
bufpt = buf;
|
||||
{
|
||||
i64 szBufNeeded; /* Size of a temporary buffer needed */
|
||||
@@ -662,16 +572,12 @@ void sqlite3_str_vappendf(
|
||||
*(bufpt++) = prefix;
|
||||
}
|
||||
/* Digits prior to the decimal point */
|
||||
j = 0;
|
||||
if( e2<0 ){
|
||||
*(bufpt++) = '0';
|
||||
}else if( msd>0 ){
|
||||
for(; e2>=0; e2--){
|
||||
*(bufpt++) = et_getdigit_int(&longvalue,&msd);
|
||||
if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
|
||||
}
|
||||
}else{
|
||||
for(; e2>=0; e2--){
|
||||
*(bufpt++) = et_getdigit(&realvalue,&nsd);
|
||||
*(bufpt++) = j<s.n ? s.z[j++] : '0';
|
||||
if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
|
||||
}
|
||||
}
|
||||
@@ -681,19 +587,12 @@ void sqlite3_str_vappendf(
|
||||
}
|
||||
/* "0" digits after the decimal point but before the first
|
||||
** significant digit of the number */
|
||||
for(e2++; e2<0; precision--, e2++){
|
||||
assert( precision>0 );
|
||||
for(e2++; e2<0 && precision>0; precision--, e2++){
|
||||
*(bufpt++) = '0';
|
||||
}
|
||||
/* Significant digits after the decimal point */
|
||||
if( msd>0 ){
|
||||
while( (precision--)>0 ){
|
||||
*(bufpt++) = et_getdigit_int(&longvalue,&msd);
|
||||
}
|
||||
}else{
|
||||
while( (precision--)>0 ){
|
||||
*(bufpt++) = et_getdigit(&realvalue,&nsd);
|
||||
}
|
||||
while( (precision--)>0 ){
|
||||
*(bufpt++) = j<s.n ? s.z[j++] : '0';
|
||||
}
|
||||
/* Remove trailing zeros and the "." if no digits follow the "." */
|
||||
if( flag_rtz && flag_dp ){
|
||||
@@ -709,6 +608,7 @@ void sqlite3_str_vappendf(
|
||||
}
|
||||
/* Add the "eNNN" suffix */
|
||||
if( xtype==etEXP ){
|
||||
exp = s.iDP - 1;
|
||||
*(bufpt++) = aDigits[infop->charset];
|
||||
if( exp<0 ){
|
||||
*(bufpt++) = '-'; exp = -exp;
|
||||
@@ -742,8 +642,8 @@ void sqlite3_str_vappendf(
|
||||
while( nPad-- ) bufpt[i++] = '0';
|
||||
length = width;
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
|
||||
break;
|
||||
}
|
||||
case etSIZE:
|
||||
if( !bArgList ){
|
||||
*(va_arg(ap,int*)) = pAccum->nChar;
|
||||
|
||||
+11
-2
@@ -8009,9 +8009,13 @@ int sqlite3Select(
|
||||
int nCol;
|
||||
int nGroupBy;
|
||||
|
||||
explainTempTable(pParse,
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
int addrExp; /* Address of OP_Explain instruction */
|
||||
#endif
|
||||
ExplainQueryPlan2(addrExp, (pParse, 0, "USE TEMP B-TREE FOR %s",
|
||||
(sDistinct.isTnct && (p->selFlags&SF_Distinct)==0) ?
|
||||
"DISTINCT" : "GROUP BY");
|
||||
"DISTINCT" : "GROUP BY"
|
||||
));
|
||||
|
||||
groupBySort = 1;
|
||||
nGroupBy = pGroupBy->nExpr;
|
||||
@@ -8036,18 +8040,23 @@ int sqlite3Select(
|
||||
}
|
||||
pAggInfo->directMode = 0;
|
||||
regRecord = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeScanStatusCounters(v, addrExp, 0, sqlite3VdbeCurrentAddr(v));
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
|
||||
sqlite3VdbeScanStatusRange(v, addrExp, sqlite3VdbeCurrentAddr(v)-2, -1);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
|
||||
sortOut = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeScanStatusCounters(v, addrExp, sqlite3VdbeCurrentAddr(v), 0);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd);
|
||||
VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v);
|
||||
pAggInfo->useSortingIdx = 1;
|
||||
sqlite3VdbeScanStatusRange(v, addrExp, -1, sortPTab);
|
||||
sqlite3VdbeScanStatusRange(v, addrExp, -1, pAggInfo->sortingIdx);
|
||||
}
|
||||
|
||||
/* If there are entries in pAgggInfo->aFunc[] that contain subexpressions
|
||||
|
||||
+67
-13
@@ -1207,6 +1207,46 @@ static char *shellFakeSchema(
|
||||
return s.z;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: strtod(X)
|
||||
**
|
||||
** Use the C-library strtod() function to convert string X into a double.
|
||||
** Used for comparing the accuracy of SQLite's internal text-to-float conversion
|
||||
** routines against the C-library.
|
||||
*/
|
||||
static void shellStrtod(
|
||||
sqlite3_context *pCtx,
|
||||
int nVal,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
char *z = (char*)sqlite3_value_text(apVal[0]);
|
||||
UNUSED_PARAMETER(nVal);
|
||||
if( z==0 ) return;
|
||||
sqlite3_result_double(pCtx, strtod(z,0));
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: dtostr(X)
|
||||
**
|
||||
** Use the C-library printf() function to convert real value X into a string.
|
||||
** Used for comparing the accuracy of SQLite's internal float-to-text conversion
|
||||
** routines against the C-library.
|
||||
*/
|
||||
static void shellDtostr(
|
||||
sqlite3_context *pCtx,
|
||||
int nVal,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
double r = sqlite3_value_double(apVal[0]);
|
||||
int n = nVal>=2 ? sqlite3_value_int(apVal[1]) : 26;
|
||||
char z[400];
|
||||
if( n<1 ) n = 1;
|
||||
if( n>350 ) n = 350;
|
||||
sprintf(z, "%#+.*e", n, r);
|
||||
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** SQL function: shell_module_schema(X)
|
||||
**
|
||||
@@ -5463,6 +5503,12 @@ static void open_db(ShellState *p, int openFlags){
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3_create_function(p->db, "strtod", 1, SQLITE_UTF8, 0,
|
||||
shellStrtod, 0, 0);
|
||||
sqlite3_create_function(p->db, "dtostr", 1, SQLITE_UTF8, 0,
|
||||
shellDtostr, 0, 0);
|
||||
sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0,
|
||||
shellDtostr, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
|
||||
shellAddSchemaName, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
|
||||
@@ -10532,7 +10578,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
"with tabcols as materialized(\n"
|
||||
"select tname, cname\n"
|
||||
"from ("
|
||||
" select ss.tname as tname, ti.name as cname\n"
|
||||
" select printf('\"%%w\"',ss.tname) as tname,"
|
||||
" printf('\"%%w\"',ti.name) as cname\n"
|
||||
" from (%z) ss\n inner join pragma_table_info(tname) ti))\n"
|
||||
"select 'SELECT total(bad_text_count) AS bad_text_count\n"
|
||||
"FROM ('||group_concat(query, ' UNION ALL ')||')' as btc_query\n"
|
||||
@@ -10804,7 +10851,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
static const struct {
|
||||
const char *zCtrlName; /* Name of a test-control option */
|
||||
int ctrlCode; /* Integer code for that option */
|
||||
int unSafe; /* Not valid for --safe mode */
|
||||
int unSafe; /* Not valid unless --unsafe-testing */
|
||||
const char *zUsage; /* Usage notes */
|
||||
} aCtrl[] = {
|
||||
{"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
|
||||
@@ -10829,6 +10876,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
{"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" },
|
||||
{"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" },
|
||||
{"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" },
|
||||
{"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"},
|
||||
};
|
||||
int testctrl = -1;
|
||||
int iCtrl = -1;
|
||||
@@ -10837,12 +10885,6 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
int i, n2;
|
||||
const char *zCmd = 0;
|
||||
|
||||
if( !ShellHasFlag(p,SHFLG_TestingMode) ){
|
||||
utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n",
|
||||
"testctrl");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
open_db(p, 0);
|
||||
zCmd = nArg>=2 ? azArg[1] : "help";
|
||||
|
||||
@@ -10856,6 +10898,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( cli_strcmp(zCmd,"help")==0 ){
|
||||
utf8_printf(p->out, "Available test-controls:\n");
|
||||
for(i=0; i<ArraySize(aCtrl); i++){
|
||||
if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue;
|
||||
utf8_printf(p->out, " .testctrl %s %s\n",
|
||||
aCtrl[i].zCtrlName, aCtrl[i].zUsage);
|
||||
}
|
||||
@@ -10867,6 +10910,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
** of the option name, or a numerical value. */
|
||||
n2 = strlen30(zCmd);
|
||||
for(i=0; i<ArraySize(aCtrl); i++){
|
||||
if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue;
|
||||
if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
|
||||
if( testctrl<0 ){
|
||||
testctrl = aCtrl[i].ctrlCode;
|
||||
@@ -10882,11 +10926,6 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( testctrl<0 ){
|
||||
utf8_printf(stderr,"Error: unknown test-control: %s\n"
|
||||
"Use \".testctrl --help\" for help\n", zCmd);
|
||||
}else if( aCtrl[iCtrl].unSafe && p->bSafeMode ){
|
||||
utf8_printf(stderr,
|
||||
"line %d: \".testctrl %s\" may not be used in safe mode\n",
|
||||
p->lineno, aCtrl[iCtrl].zCtrlName);
|
||||
exit(1);
|
||||
}else{
|
||||
switch(testctrl){
|
||||
|
||||
@@ -10959,6 +10998,21 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
break;
|
||||
|
||||
/* sqlite3_test_control(int, int) */
|
||||
case SQLITE_TESTCTRL_USELONGDOUBLE: {
|
||||
int opt = -1;
|
||||
if( nArg==3 ){
|
||||
if( cli_strcmp(azArg[2],"default")==0 ){
|
||||
opt = 2;
|
||||
}else{
|
||||
opt = booleanValue(azArg[2]);
|
||||
}
|
||||
}
|
||||
rc2 = sqlite3_test_control(testctrl, opt);
|
||||
isOk = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(sqlite3*) */
|
||||
case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS:
|
||||
rc2 = sqlite3_test_control(testctrl, p->db);
|
||||
|
||||
+2
-1
@@ -8176,7 +8176,8 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_TRACEFLAGS 31
|
||||
#define SQLITE_TESTCTRL_TUNE 32
|
||||
#define SQLITE_TESTCTRL_LOGEST 33
|
||||
#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */
|
||||
#define SQLITE_TESTCTRL_USELONGDOUBLE 34
|
||||
#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Keyword Checking
|
||||
|
||||
+45
-1
@@ -889,8 +889,31 @@ typedef INT16_TYPE LogEst;
|
||||
** the end of buffer S. This macro returns true if P points to something
|
||||
** contained within the buffer S.
|
||||
*/
|
||||
#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E)))
|
||||
#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E)))
|
||||
|
||||
/*
|
||||
** P is one byte past the end of a large buffer. Return true if a span of bytes
|
||||
** between S..E crosses the end of that buffer. In other words, return true
|
||||
** if the sub-buffer S..E-1 overflows the buffer show last byte is P-1.
|
||||
**
|
||||
** S is the start of the span. E is one byte past the end of end of span.
|
||||
**
|
||||
** P
|
||||
** |-----------------| FALSE
|
||||
** |-------|
|
||||
** S E
|
||||
**
|
||||
** P
|
||||
** |-----------------|
|
||||
** |-------| TRUE
|
||||
** S E
|
||||
**
|
||||
** P
|
||||
** |-----------------|
|
||||
** |-------| FALSE
|
||||
** S E
|
||||
*/
|
||||
#define SQLITE_OVERFLOW(P,S,E) (((uptr)(S)<(uptr)(P))&&((uptr)(E)>(uptr)(P)))
|
||||
|
||||
/*
|
||||
** Macros to determine whether the machine is big or little endian,
|
||||
@@ -1234,10 +1257,12 @@ typedef struct Cte Cte;
|
||||
typedef struct CteUse CteUse;
|
||||
typedef struct Db Db;
|
||||
typedef struct DbFixer DbFixer;
|
||||
typedef struct DblDbl DblDbl;
|
||||
typedef struct Schema Schema;
|
||||
typedef struct Expr Expr;
|
||||
typedef struct ExprList ExprList;
|
||||
typedef struct FKey FKey;
|
||||
typedef struct FpDecode FpDecode;
|
||||
typedef struct FuncDestructor FuncDestructor;
|
||||
typedef struct FuncDef FuncDef;
|
||||
typedef struct FuncDefHash FuncDefHash;
|
||||
@@ -1930,6 +1955,7 @@ struct FuncDestructor {
|
||||
** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd
|
||||
** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
|
||||
** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
|
||||
** SQLITE_FUNC_BYTELEN == OPFLAG_BYTELENARG
|
||||
** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
|
||||
** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API
|
||||
** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!!
|
||||
@@ -1948,6 +1974,7 @@ struct FuncDestructor {
|
||||
#define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/
|
||||
#define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */
|
||||
#define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */
|
||||
#define SQLITE_FUNC_BYTELEN 0x00c0 /* Built-in octet_length() function */
|
||||
#define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */
|
||||
/* 0x0200 -- available for reuse */
|
||||
#define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */
|
||||
@@ -3891,6 +3918,7 @@ struct AuthContext {
|
||||
#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
|
||||
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
|
||||
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
|
||||
#define OPFLAG_BYTELENARG 0xc0 /* OP_Column only for octet_length() */
|
||||
#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
|
||||
#define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */
|
||||
#define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */
|
||||
@@ -4085,6 +4113,7 @@ struct Sqlite3Config {
|
||||
u8 bUseCis; /* Use covering indices for full-scans */
|
||||
u8 bSmallMalloc; /* Avoid large memory allocations if true */
|
||||
u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */
|
||||
u8 bUseLongDouble; /* Make use of long double */
|
||||
int mxStrlen; /* Maximum string length */
|
||||
int neverCorrupt; /* Database is always well-formed */
|
||||
int szLookaside; /* Default lookaside buffer size */
|
||||
@@ -4593,6 +4622,20 @@ struct PrintfArguments {
|
||||
sqlite3_value **apArg; /* The argument values */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this object receives the decoding of a floating point
|
||||
** value into an approximate decimal representation.
|
||||
*/
|
||||
struct FpDecode {
|
||||
char sign; /* '+' or '-' */
|
||||
char isSpecial; /* 1: Infinity 2: NaN */
|
||||
int n; /* Significant digits in the decode */
|
||||
int iDP; /* Location of the decimal point */
|
||||
char *z; /* Start of significant digits */
|
||||
char zBuf[24]; /* Storage for significant digits */
|
||||
};
|
||||
|
||||
void sqlite3FpDecode(FpDecode*,double,int,int);
|
||||
char *sqlite3MPrintf(sqlite3*,const char*, ...);
|
||||
char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
||||
@@ -5032,6 +5075,7 @@ int sqlite3FixSrcList(DbFixer*, SrcList*);
|
||||
int sqlite3FixSelect(DbFixer*, Select*);
|
||||
int sqlite3FixExpr(DbFixer*, Expr*);
|
||||
int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
|
||||
|
||||
int sqlite3RealSameAsInt(double,sqlite3_int64);
|
||||
i64 sqlite3RealToI64(double);
|
||||
int sqlite3Int64ToText(i64,char*);
|
||||
|
||||
+137
-1
@@ -991,6 +991,46 @@ static void intrealFunction(
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, context);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: strtod(X)
|
||||
**
|
||||
** Use the C-library strtod() function to convert string X into a double.
|
||||
** Used for comparing the accuracy of SQLite's internal text-to-float conversion
|
||||
** routines against the C-library.
|
||||
*/
|
||||
static void shellStrtod(
|
||||
sqlite3_context *pCtx,
|
||||
int nVal,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
char *z = (char*)sqlite3_value_text(apVal[0]);
|
||||
UNUSED_PARAMETER(nVal);
|
||||
if( z==0 ) return;
|
||||
sqlite3_result_double(pCtx, strtod(z,0));
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: dtostr(X)
|
||||
**
|
||||
** Use the C-library printf() function to convert real value X into a string.
|
||||
** Used for comparing the accuracy of SQLite's internal float-to-text conversion
|
||||
** routines against the C-library.
|
||||
*/
|
||||
static void shellDtostr(
|
||||
sqlite3_context *pCtx,
|
||||
int nVal,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
double r = sqlite3_value_double(apVal[0]);
|
||||
int n = nVal>=2 ? sqlite3_value_int(apVal[1]) : 26;
|
||||
char z[400];
|
||||
if( n<1 ) n = 1;
|
||||
if( n>350 ) n = 350;
|
||||
sprintf(z, "%#+.*e", n, r);
|
||||
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_create_function DB
|
||||
**
|
||||
@@ -1063,6 +1103,27 @@ static int SQLITE_TCLAPI test_create_function(
|
||||
0, intrealFunction, 0, 0);
|
||||
}
|
||||
|
||||
/* Functions strtod() and dtostr() work as in the shell. These routines
|
||||
** use the standard C library to convert between floating point and
|
||||
** text. This is used to compare SQLite's internal conversion routines
|
||||
** against the standard library conversion routines.
|
||||
**
|
||||
** Both routines copy/pasted from the shell.c.in implementation
|
||||
** on 2023-07-03.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "strtod", 1, SQLITE_UTF8, 0,
|
||||
shellStrtod, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "dtostr", 1, SQLITE_UTF8, 0,
|
||||
shellDtostr, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "dtostr", 2, SQLITE_UTF8, 0,
|
||||
shellDtostr, 0, 0);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/* Use the sqlite3_create_function16() API here. Mainly for fun, but also
|
||||
** because it is not tested anywhere else. */
|
||||
@@ -2216,6 +2277,7 @@ static int SQLITE_TCLAPI test_stmt_scanstatus(
|
||||
int flags = 0;
|
||||
int iSelectId = 0;
|
||||
int iParentId = 0;
|
||||
int bDebug = 0;
|
||||
|
||||
if( objc==5 ){
|
||||
struct Flag {
|
||||
@@ -2223,6 +2285,7 @@ static int SQLITE_TCLAPI test_stmt_scanstatus(
|
||||
int flag;
|
||||
} aTbl[] = {
|
||||
{"complex", SQLITE_SCANSTAT_COMPLEX},
|
||||
{"debug", -1},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
@@ -2239,7 +2302,11 @@ static int SQLITE_TCLAPI test_stmt_scanstatus(
|
||||
interp, aFlag[ii], aTbl, sizeof(aTbl[0]), "flag", 0, &iVal
|
||||
);
|
||||
if( res ) return TCL_ERROR;
|
||||
flags |= aTbl[iVal].flag;
|
||||
if( aTbl[iVal].flag==-1 ){
|
||||
bDebug = 1;
|
||||
}else{
|
||||
flags |= aTbl[iVal].flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2253,6 +2320,13 @@ static int SQLITE_TCLAPI test_stmt_scanstatus(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( bDebug && 0==(flags & SQLITE_SCANSTAT_COMPLEX) ){
|
||||
Tcl_SetObjResult(interp,
|
||||
Tcl_NewStringObj("may not specify debug without complex", -1)
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( idx<0 ){
|
||||
Tcl_Obj *pRet = Tcl_NewObj();
|
||||
res = sqlite3_stmt_scanstatus_v2(
|
||||
@@ -2299,6 +2373,36 @@ static int SQLITE_TCLAPI test_stmt_scanstatus(
|
||||
pStmt, idx, SQLITE_SCANSTAT_NCYCLE, flags, (void*)&nCycle);
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nCycle", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nCycle));
|
||||
|
||||
if( bDebug ){
|
||||
int ii;
|
||||
ScanStatus *pScan = &((Vdbe*)pStmt)->aScan[idx];
|
||||
Tcl_Obj *pRange = Tcl_NewObj();
|
||||
Tcl_Obj *pCsr = Tcl_NewObj();
|
||||
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("debug_loop", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(pScan->addrLoop));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("debug_visit", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(pScan->addrVisit));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("debug_explain",-1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(pScan->addrExplain));
|
||||
for(ii=0; ii<ArraySize(pScan->aAddrRange)/2; ii++){
|
||||
int iStart = pScan->aAddrRange[ii*2];
|
||||
int iEnd = pScan->aAddrRange[ii*2+1];
|
||||
if( iStart>0 ){
|
||||
Tcl_ListObjAppendElement(0, pRange, Tcl_NewIntObj(iStart));
|
||||
Tcl_ListObjAppendElement(0, pRange, Tcl_NewIntObj(iEnd));
|
||||
}else if( iStart<0 ){
|
||||
Tcl_ListObjAppendElement(0, pCsr, Tcl_NewIntObj(iEnd));
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("debug_range", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, pRange);
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("debug_csr", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, pCsr);
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
@@ -6994,6 +7098,37 @@ static int SQLITE_TCLAPI extra_schema_checks(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: use_long_double BOOLEAN|"default"
|
||||
**
|
||||
** If no argument, report the current value of the use-long-double flag.
|
||||
**
|
||||
** If argument is "default", set the use-long-double flag to the default
|
||||
** value for this build, based on the size of LONGDOUBLE_TYPE.
|
||||
**
|
||||
** If argument is a boolean, set the use-long-double flag accordingly.
|
||||
**
|
||||
** Return the new setting.
|
||||
*/
|
||||
static int SQLITE_TCLAPI use_long_double(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
int i = -1;
|
||||
if( objc==2 ){
|
||||
if( strcmp(Tcl_GetString(objv[1]),"default")==0 ){
|
||||
i = 2;
|
||||
}else{
|
||||
if( Tcl_GetBooleanFromObj(interp,objv[1],&i) ) return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
i = sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, i);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(i));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: database_may_be_corrupt
|
||||
**
|
||||
@@ -8882,6 +9017,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "reset_prng_state", reset_prng_state, 0 },
|
||||
{ "prng_seed", prng_seed, 0 },
|
||||
{ "extra_schema_checks", extra_schema_checks, 0},
|
||||
{ "use_long_double", use_long_double, 0},
|
||||
{ "database_never_corrupt", database_never_corrupt, 0},
|
||||
{ "database_may_be_corrupt", database_may_be_corrupt, 0},
|
||||
{ "optimization_control", optimization_control,0},
|
||||
|
||||
+246
-102
@@ -386,43 +386,40 @@ u8 sqlite3StrIHash(const char *z){
|
||||
return h;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute 10 to the E-th power. Examples: E==1 results in 10.
|
||||
** E==2 results in 100. E==50 results in 1.0e50.
|
||||
/* Double-Double multiplication. (x[0],x[1]) *= (y,yy)
|
||||
**
|
||||
** This routine only works for values of E between 1 and 341.
|
||||
** Reference:
|
||||
** T. J. Dekker, "A Floating-Point Technique for Extending the
|
||||
** Available Precision". 1971-07-26.
|
||||
*/
|
||||
static LONGDOUBLE_TYPE sqlite3Pow10(int E){
|
||||
#if defined(_MSC_VER)
|
||||
static const LONGDOUBLE_TYPE x[] = {
|
||||
1.0e+001L,
|
||||
1.0e+002L,
|
||||
1.0e+004L,
|
||||
1.0e+008L,
|
||||
1.0e+016L,
|
||||
1.0e+032L,
|
||||
1.0e+064L,
|
||||
1.0e+128L,
|
||||
1.0e+256L
|
||||
};
|
||||
LONGDOUBLE_TYPE r = 1.0;
|
||||
int i;
|
||||
assert( E>=0 && E<=307 );
|
||||
for(i=0; E!=0; i++, E >>=1){
|
||||
if( E & 1 ) r *= x[i];
|
||||
}
|
||||
return r;
|
||||
#else
|
||||
LONGDOUBLE_TYPE x = 10.0;
|
||||
LONGDOUBLE_TYPE r = 1.0;
|
||||
while(1){
|
||||
if( E & 1 ) r *= x;
|
||||
E >>= 1;
|
||||
if( E==0 ) break;
|
||||
x *= x;
|
||||
}
|
||||
return r;
|
||||
#endif
|
||||
static void dekkerMul2(volatile double *x, double y, double yy){
|
||||
/*
|
||||
** The "volatile" keywords on parameter x[] and on local variables
|
||||
** below are needed force intermediate results to be truncated to
|
||||
** binary64 rather than be carried around in an extended-precision
|
||||
** format. The truncation is necessary for the Dekker algorithm to
|
||||
** work. Intel x86 floating point might omit the truncation without
|
||||
** the use of volatile.
|
||||
*/
|
||||
volatile double tx, ty, p, q, c, cc;
|
||||
double hx, hy;
|
||||
u64 m;
|
||||
memcpy(&m, (void*)&x[0], 8);
|
||||
m &= 0xfffffffffc000000LL;
|
||||
memcpy(&hx, &m, 8);
|
||||
tx = x[0] - hx;
|
||||
memcpy(&m, &y, 8);
|
||||
m &= 0xfffffffffc000000LL;
|
||||
memcpy(&hy, &m, 8);
|
||||
ty = y - hy;
|
||||
p = hx*hy;
|
||||
q = hx*ty + tx*hy;
|
||||
c = p+q;
|
||||
cc = p - c + q + tx*ty;
|
||||
cc = x[0]*yy + x[1]*y + cc;
|
||||
x[0] = c + cc;
|
||||
x[1] = c - x[0];
|
||||
x[1] += cc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -463,12 +460,11 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
const char *zEnd;
|
||||
/* sign * significand * (10 ^ (esign * exponent)) */
|
||||
int sign = 1; /* sign of significand */
|
||||
i64 s = 0; /* significand */
|
||||
u64 s = 0; /* significand */
|
||||
int d = 0; /* adjust exponent for shifting decimal point */
|
||||
int esign = 1; /* sign of exponent */
|
||||
int e = 0; /* exponent */
|
||||
int eValid = 1; /* True exponent is either not used or is well-formed */
|
||||
double result;
|
||||
int nDigit = 0; /* Number of digits processed */
|
||||
int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
|
||||
|
||||
@@ -508,7 +504,7 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z+=incr; nDigit++;
|
||||
if( s>=((LARGEST_INT64-9)/10) ){
|
||||
if( s>=((LARGEST_UINT64-9)/10) ){
|
||||
/* skip non-significant significand digits
|
||||
** (increase exponent by d to shift decimal left) */
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ){ z+=incr; d++; }
|
||||
@@ -523,7 +519,7 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
/* copy digits from after decimal to significand
|
||||
** (decrease exponent by d to shift decimal right) */
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ){
|
||||
if( s<((LARGEST_INT64-9)/10) ){
|
||||
if( s<((LARGEST_UINT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
d--;
|
||||
nDigit++;
|
||||
@@ -563,78 +559,79 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
|
||||
|
||||
do_atof_calc:
|
||||
/* Zero is a special case */
|
||||
if( s==0 ){
|
||||
*pResult = sign<0 ? -0.0 : +0.0;
|
||||
goto atof_return;
|
||||
}
|
||||
|
||||
/* adjust exponent by d, and update sign */
|
||||
e = (e*esign) + d;
|
||||
if( e<0 ) {
|
||||
esign = -1;
|
||||
e *= -1;
|
||||
} else {
|
||||
esign = 1;
|
||||
|
||||
/* Try to adjust the exponent to make it smaller */
|
||||
while( e>0 && s<(LARGEST_UINT64/10) ){
|
||||
s *= 10;
|
||||
e--;
|
||||
}
|
||||
while( e<0 && (s%10)==0 ){
|
||||
s /= 10;
|
||||
e++;
|
||||
}
|
||||
|
||||
if( s==0 ) {
|
||||
/* In the IEEE 754 standard, zero is signed. */
|
||||
result = sign<0 ? -(double)0 : (double)0;
|
||||
} else {
|
||||
/* Attempt to reduce exponent.
|
||||
**
|
||||
** Branches that are not required for the correct answer but which only
|
||||
** help to obtain the correct answer faster are marked with special
|
||||
** comments, as a hint to the mutation tester.
|
||||
*/
|
||||
while( e>0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
if( esign>0 ){
|
||||
if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/
|
||||
s *= 10;
|
||||
}else{
|
||||
if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/
|
||||
s /= 10;
|
||||
}
|
||||
e--;
|
||||
}
|
||||
|
||||
/* adjust the sign of significand */
|
||||
s = sign<0 ? -s : s;
|
||||
|
||||
if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
result = (double)s;
|
||||
if( e==0 ){
|
||||
*pResult = s;
|
||||
}else if( sqlite3Config.bUseLongDouble ){
|
||||
LONGDOUBLE_TYPE r = (LONGDOUBLE_TYPE)s;
|
||||
if( e>0 ){
|
||||
while( e>=100 ){ e-=100; r *= 1.0e+100L; }
|
||||
while( e>=10 ){ e-=10; r *= 1.0e+10L; }
|
||||
while( e>=1 ){ e-=1; r *= 1.0e+01L; }
|
||||
}else{
|
||||
/* attempt to handle extremely small/large numbers better */
|
||||
if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308);
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
result /= 1.0e+308;
|
||||
}else{
|
||||
result = s * scale;
|
||||
result *= 1.0e+308;
|
||||
}
|
||||
}else{ assert( e>=342 );
|
||||
if( esign<0 ){
|
||||
result = 0.0*s;
|
||||
}else{
|
||||
#ifdef INFINITY
|
||||
result = INFINITY*s;
|
||||
#else
|
||||
result = 1e308*1e308*s; /* Infinity */
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}else{
|
||||
LONGDOUBLE_TYPE scale = sqlite3Pow10(e);
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
}else{
|
||||
result = s * scale;
|
||||
}
|
||||
while( e<=-100 ){ e+=100; r *= 1.0e-100L; }
|
||||
while( e<=-10 ){ e+=10; r *= 1.0e-10L; }
|
||||
while( e<=-1 ){ e+=1; r *= 1.0e-01L; }
|
||||
}
|
||||
*pResult = r;
|
||||
}else{
|
||||
double rr[2];
|
||||
u64 s2;
|
||||
rr[0] = (double)s;
|
||||
s2 = (u64)rr[0];
|
||||
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
|
||||
if( e>0 ){
|
||||
while( e>=100 ){
|
||||
e -= 100;
|
||||
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
|
||||
}
|
||||
while( e>=10 ){
|
||||
e -= 10;
|
||||
dekkerMul2(rr, 1.0e+10, 0.0);
|
||||
}
|
||||
while( e>=1 ){
|
||||
e -= 1;
|
||||
dekkerMul2(rr, 1.0e+01, 0.0);
|
||||
}
|
||||
}else{
|
||||
while( e<=-100 ){
|
||||
e += 100;
|
||||
dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
|
||||
}
|
||||
while( e<=-10 ){
|
||||
e += 10;
|
||||
dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
|
||||
}
|
||||
while( e<=-1 ){
|
||||
e += 1;
|
||||
dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
|
||||
}
|
||||
}
|
||||
*pResult = rr[0]+rr[1];
|
||||
if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300;
|
||||
}
|
||||
if( sign<0 ) *pResult = -*pResult;
|
||||
assert( !sqlite3IsNaN(*pResult) );
|
||||
|
||||
/* store the result */
|
||||
*pResult = result;
|
||||
|
||||
atof_return:
|
||||
/* return true if number and no extra non-whitespace characters after */
|
||||
if( z==zEnd && nDigit>0 && eValid && eType>0 ){
|
||||
return eType;
|
||||
@@ -928,6 +925,153 @@ int sqlite3Atoi(const char *z){
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode a floating-point value into an approximate decimal
|
||||
** representation.
|
||||
**
|
||||
** Round the decimal representation to n significant digits if
|
||||
** n is positive. Or round to -n signficant digits after the
|
||||
** decimal point if n is negative. No rounding is performed if
|
||||
** n is zero.
|
||||
**
|
||||
** The significant digits of the decimal representation are
|
||||
** stored in p->z[] which is a often (but not always) a pointer
|
||||
** into the middle of p->zBuf[]. There are p->n significant digits.
|
||||
** The p->z[] array is *not* zero-terminated.
|
||||
*/
|
||||
void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){
|
||||
int i;
|
||||
u64 v;
|
||||
int e, exp = 0;
|
||||
p->isSpecial = 0;
|
||||
p->z = p->zBuf;
|
||||
|
||||
/* Convert negative numbers to positive. Deal with Infinity, 0.0, and
|
||||
** NaN. */
|
||||
if( r<0.0 ){
|
||||
p->sign = '-';
|
||||
r = -r;
|
||||
}else if( r==0.0 ){
|
||||
p->sign = '+';
|
||||
p->n = 1;
|
||||
p->iDP = 1;
|
||||
p->z = "0";
|
||||
return;
|
||||
}else{
|
||||
p->sign = '+';
|
||||
}
|
||||
memcpy(&v,&r,8);
|
||||
e = v>>52;
|
||||
if( (e&0x7ff)==0x7ff ){
|
||||
p->isSpecial = 1 + (v!=0x7ff0000000000000LL);
|
||||
p->n = 0;
|
||||
p->iDP = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Multiply r by powers of ten until it lands somewhere in between
|
||||
** 1.0e+19 and 1.0e+17.
|
||||
*/
|
||||
if( sqlite3Config.bUseLongDouble ){
|
||||
LONGDOUBLE_TYPE rr = r;
|
||||
if( rr>=1.0e+19 ){
|
||||
while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; }
|
||||
while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; }
|
||||
while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; }
|
||||
}else{
|
||||
while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; }
|
||||
while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; }
|
||||
while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; }
|
||||
}
|
||||
v = (u64)rr;
|
||||
}else{
|
||||
/* If high-precision floating point is not available using "long double",
|
||||
** then use Dekker-style double-double computation to increase the
|
||||
** precision.
|
||||
**
|
||||
** The error terms on constants like 1.0e+100 computed using the
|
||||
** decimal extension, for example as follows:
|
||||
**
|
||||
** SELECT decimal_sci(decimal_sub('1.0e+100',decimal(1.0e+100)));
|
||||
*/
|
||||
double rr[2];
|
||||
rr[0] = r;
|
||||
rr[1] = 0.0;
|
||||
if( rr[0]>1.84e+19 ){
|
||||
while( rr[0]>1.84e+119 ){
|
||||
exp += 100;
|
||||
dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
|
||||
}
|
||||
while( rr[0]>1.84e+29 ){
|
||||
exp += 10;
|
||||
dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
|
||||
}
|
||||
while( rr[0]>1.84e+19 ){
|
||||
exp += 1;
|
||||
dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
|
||||
}
|
||||
}else{
|
||||
while( rr[0]<1.84e-82 ){
|
||||
exp -= 100;
|
||||
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
|
||||
}
|
||||
while( rr[0]<1.84e+08 ){
|
||||
exp -= 10;
|
||||
dekkerMul2(rr, 1.0e+10, 0.0);
|
||||
}
|
||||
while( rr[0]<1.84e+18 ){
|
||||
exp -= 1;
|
||||
dekkerMul2(rr, 1.0e+01, 0.0);
|
||||
}
|
||||
}
|
||||
v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1];
|
||||
}
|
||||
|
||||
|
||||
/* Extract significant digits. */
|
||||
i = sizeof(p->zBuf)-1;
|
||||
assert( v>0 );
|
||||
while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; }
|
||||
assert( i>=0 && i<sizeof(p->zBuf)-1 );
|
||||
p->n = sizeof(p->zBuf) - 1 - i;
|
||||
assert( p->n>0 );
|
||||
assert( p->n<sizeof(p->zBuf) );
|
||||
p->iDP = p->n + exp;
|
||||
if( iRound<0 ){
|
||||
iRound = p->iDP - iRound;
|
||||
if( iRound==0 && p->zBuf[i+1]>='5' ){
|
||||
iRound = 1;
|
||||
p->zBuf[i--] = '0';
|
||||
p->n++;
|
||||
p->iDP++;
|
||||
}
|
||||
}
|
||||
if( iRound>0 && (iRound<p->n || p->n>mxRound) ){
|
||||
char *z = &p->zBuf[i+1];
|
||||
if( iRound>mxRound ) iRound = mxRound;
|
||||
p->n = iRound;
|
||||
if( z[iRound]>='5' ){
|
||||
int j = iRound-1;
|
||||
while( 1 /*exit-by-break*/ ){
|
||||
z[j]++;
|
||||
if( z[j]<='9' ) break;
|
||||
z[j] = '0';
|
||||
if( j==0 ){
|
||||
p->z[i--] = '1';
|
||||
p->n++;
|
||||
p->iDP++;
|
||||
break;
|
||||
}else{
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p->z = &p->zBuf[i+1];
|
||||
assert( i+p->n < sizeof(p->zBuf) );
|
||||
while( ALWAYS(p->n>0) && p->z[p->n-1]=='0' ){ p->n--; }
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to convert z into an unsigned 32-bit integer. Return true on
|
||||
** success and false if there is an error.
|
||||
|
||||
+9
-5
@@ -3056,10 +3056,14 @@ op_column_restart:
|
||||
pDest->flags = aFlag[t&1];
|
||||
}
|
||||
}else{
|
||||
u8 p5;
|
||||
pDest->enc = encoding;
|
||||
/* This branch happens only when content is on overflow pages */
|
||||
if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
|
||||
&& ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0))
|
||||
if( ((p5 = (pOp->p5 & OPFLAG_BYTELENARG))!=0
|
||||
&& (p5==OPFLAG_TYPEOFARG
|
||||
|| (t>=12 && ((t&1)==0 || p5==OPFLAG_BYTELENARG))
|
||||
)
|
||||
)
|
||||
|| (len = sqlite3VdbeSerialTypeLen(t))==0
|
||||
){
|
||||
/* Content is irrelevant for
|
||||
@@ -5847,7 +5851,7 @@ case OP_SorterCompare: {
|
||||
** parameter P3. Clearing the P3 column cache as part of this opcode saves
|
||||
** us from having to issue a separate NullRow instruction to clear that cache.
|
||||
*/
|
||||
case OP_SorterData: {
|
||||
case OP_SorterData: { /* ncycle */
|
||||
VdbeCursor *pC;
|
||||
|
||||
pOut = &aMem[pOp->p2];
|
||||
@@ -6122,8 +6126,8 @@ case OP_IfSmaller: { /* jump */
|
||||
** regression tests can determine whether or not the optimizer is
|
||||
** correctly optimizing out sorts.
|
||||
*/
|
||||
case OP_SorterSort: /* jump */
|
||||
case OP_Sort: { /* jump */
|
||||
case OP_SorterSort: /* jump ncycle */
|
||||
case OP_Sort: { /* jump ncycle */
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_sort_count++;
|
||||
sqlite3_search_count--;
|
||||
|
||||
+3
-3
@@ -538,7 +538,7 @@ int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
|
||||
if( bPush){
|
||||
pParse->addrExplain = iThis;
|
||||
}
|
||||
sqlite3VdbeScanStatus(v, iThis, 0, 0, 0, 0);
|
||||
sqlite3VdbeScanStatus(v, iThis, -1, -1, 0, 0);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
@@ -1250,8 +1250,8 @@ void sqlite3VdbeScanStatusCounters(
|
||||
pScan = 0;
|
||||
}
|
||||
if( pScan ){
|
||||
pScan->addrLoop = addrLoop;
|
||||
pScan->addrVisit = addrVisit;
|
||||
if( addrLoop>0 ) pScan->addrLoop = addrLoop;
|
||||
if( addrVisit>0 ) pScan->addrVisit = addrVisit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-34
@@ -575,36 +575,6 @@ void sqlite3VdbeMemReleaseMalloc(Mem *p){
|
||||
if( p->szMalloc ) vdbeMemClear(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a 64-bit IEEE double into a 64-bit signed integer.
|
||||
** If the double is out of range of a 64-bit signed integer then
|
||||
** return the closest available 64-bit signed integer.
|
||||
*/
|
||||
static SQLITE_NOINLINE i64 doubleToInt64(double r){
|
||||
#ifdef SQLITE_OMIT_FLOATING_POINT
|
||||
/* When floating-point is omitted, double and int64 are the same thing */
|
||||
return r;
|
||||
#else
|
||||
/*
|
||||
** Many compilers we encounter do not define constants for the
|
||||
** minimum and maximum 64-bit integers, or they define them
|
||||
** inconsistently. And many do not understand the "LL" notation.
|
||||
** So we define our own static constants here using nothing
|
||||
** larger than a 32-bit integer constant.
|
||||
*/
|
||||
static const i64 maxInt = LARGEST_INT64;
|
||||
static const i64 minInt = SMALLEST_INT64;
|
||||
|
||||
if( r<=(double)minInt ){
|
||||
return minInt;
|
||||
}else if( r>=(double)maxInt ){
|
||||
return maxInt;
|
||||
}else{
|
||||
return (i64)r;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Return some kind of integer value which is the best we can do
|
||||
** at representing the value that *pMem describes as an integer.
|
||||
@@ -631,7 +601,7 @@ i64 sqlite3VdbeIntValue(const Mem *pMem){
|
||||
testcase( flags & MEM_IntReal );
|
||||
return pMem->u.i;
|
||||
}else if( flags & MEM_Real ){
|
||||
return doubleToInt64(pMem->u.r);
|
||||
return sqlite3RealToI64(pMem->u.r);
|
||||
}else if( (flags & (MEM_Str|MEM_Blob))!=0 && pMem->z!=0 ){
|
||||
return memIntValue(pMem);
|
||||
}else{
|
||||
@@ -693,7 +663,7 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){
|
||||
if( pMem->flags & MEM_IntReal ){
|
||||
MemSetTypeFlag(pMem, MEM_Int);
|
||||
}else{
|
||||
i64 ix = doubleToInt64(pMem->u.r);
|
||||
i64 ix = sqlite3RealToI64(pMem->u.r);
|
||||
|
||||
/* Only mark the value as an integer if
|
||||
**
|
||||
@@ -761,8 +731,8 @@ int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){
|
||||
** from UBSAN.
|
||||
*/
|
||||
i64 sqlite3RealToI64(double r){
|
||||
if( r<=(double)SMALLEST_INT64 ) return SMALLEST_INT64;
|
||||
if( r>=(double)LARGEST_INT64) return LARGEST_INT64;
|
||||
if( r<-9223372036854774784.0 ) return SMALLEST_INT64;
|
||||
if( r>+9223372036854774784.0 ) return LARGEST_INT64;
|
||||
return (i64)r;
|
||||
}
|
||||
|
||||
|
||||
@@ -1818,23 +1818,15 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
|
||||
nByte = sizeof(WalIterator)
|
||||
+ (nSegment-1)*sizeof(struct WalSegment)
|
||||
+ iLast*sizeof(ht_slot);
|
||||
p = (WalIterator *)sqlite3_malloc64(nByte);
|
||||
p = (WalIterator *)sqlite3_malloc64(nByte
|
||||
+ sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
|
||||
);
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memset(p, 0, nByte);
|
||||
p->nSegment = nSegment;
|
||||
|
||||
/* Allocate temporary space used by the merge-sort routine. This block
|
||||
** of memory will be freed before this function returns.
|
||||
*/
|
||||
aTmp = (ht_slot *)sqlite3_malloc64(
|
||||
sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
|
||||
);
|
||||
if( !aTmp ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
aTmp = (ht_slot*)&(((u8*)p)[nByte]);
|
||||
for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && i<nSegment; i++){
|
||||
WalHashLoc sLoc;
|
||||
|
||||
@@ -1862,8 +1854,6 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
|
||||
p->aSegment[i].aPgno = (u32 *)sLoc.aPgno;
|
||||
}
|
||||
}
|
||||
sqlite3_free(aTmp);
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
walIteratorFree(p);
|
||||
p = 0;
|
||||
|
||||
+2
-1
@@ -5122,9 +5122,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
|
||||
/* TUNING: A full-scan of a VIEW or subquery in the outer loop
|
||||
** is not so bad. */
|
||||
if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 ){
|
||||
if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 && nLoop>1 ){
|
||||
rCost += -10;
|
||||
nOut += -30;
|
||||
WHERETRACE(0x80,("VIEWSCAN cost reduction for %c\n",pWLoop->cId));
|
||||
}
|
||||
|
||||
/* Check to see if pWLoop should be added to the set of
|
||||
|
||||
@@ -315,6 +315,12 @@ void sqlite3WhereAddScanStatus(
|
||||
if( wsFlags & WHERE_INDEXED ){
|
||||
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
|
||||
}
|
||||
}else{
|
||||
int addr = pSrclist->a[pLvl->iFrom].addrFillSub;
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1);
|
||||
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine );
|
||||
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr );
|
||||
sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-13
@@ -15,18 +15,10 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
if {$::longdouble_size<=8} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
if {$::tcl_platform(machine)!="x86_64"} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set mxpow [expr {[use_long_double]?100:35}]
|
||||
expr srand(1)
|
||||
for {set i 1} {$i<20000} {incr i} {
|
||||
set pow [expr {int((rand()-0.5)*100)}]
|
||||
set pow [expr {int((rand()-0.5)*$mxpow)}]
|
||||
set x [expr {pow((rand()-0.5)*2*rand(),$pow)}]
|
||||
set xf [format %.32e $x]
|
||||
|
||||
@@ -51,10 +43,16 @@ for {set i 1} {$i<20000} {incr i} {
|
||||
set y [db eval {SELECT $x=CAST(quote($x) AS real)}]
|
||||
if {!$y} {
|
||||
db eval {SELECT real2hex($x) a, real2hex(CAST(quote($x) AS real)) b} {}
|
||||
puts "\nIN: $a $xf"
|
||||
puts [format {QUOTE: %16s %s} {} [db eval {SELECT quote($x)}]]
|
||||
puts ""
|
||||
if {$x<0} {
|
||||
puts "[format {!SCALE: %17s 1 23456789 123456789 123456789} {}]"
|
||||
} else {
|
||||
puts "[format {!SCALE: %16s 1 23456789 123456789 123456789} {}]"
|
||||
}
|
||||
puts "!IN: $a $xf"
|
||||
puts [format {!QUOTE: %16s %s} {} [db eval {SELECT quote($x)}]]
|
||||
db eval {SELECT CAST(quote($x) AS real) c} {}
|
||||
puts "OUT: $b [format %.32e $c]"
|
||||
puts "!OUT: $b [format %.32e $c]"
|
||||
}
|
||||
set y
|
||||
} {1}
|
||||
|
||||
+16
-9
@@ -23,31 +23,34 @@ do_execsql_test 1000 {
|
||||
SELECT decimal(1);
|
||||
} {1}
|
||||
do_execsql_test 1010 {
|
||||
SELECT decimal(1.0);
|
||||
SELECT decimal('1.0');
|
||||
} {1.0}
|
||||
do_execsql_test 1020 {
|
||||
SELECT decimal(0001.0);
|
||||
SELECT decimal('0001.0');
|
||||
} {1.0}
|
||||
do_execsql_test 1030 {
|
||||
SELECT decimal(+0001.0);
|
||||
SELECT decimal('+0001.0');
|
||||
} {1.0}
|
||||
do_execsql_test 1040 {
|
||||
SELECT decimal(-0001.0);
|
||||
SELECT decimal('-0001.0');
|
||||
} {-1.0}
|
||||
do_execsql_test 1050 {
|
||||
SELECT decimal(1.0e72);
|
||||
SELECT decimal('1.0e72');
|
||||
} {1000000000000000000000000000000000000000000000000000000000000000000000000}
|
||||
# 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123
|
||||
do_execsql_test 1060 {
|
||||
SELECT decimal(1.0e-72);
|
||||
SELECT decimal('1.0e-72');
|
||||
} {0.0000000000000000000000000000000000000000000000000000000000000000000000010}
|
||||
# 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123
|
||||
do_execsql_test 1070 {
|
||||
SELECT decimal(-123e-4);
|
||||
SELECT decimal('-123e-4');
|
||||
} {-0.0123}
|
||||
do_execsql_test 1080 {
|
||||
SELECT decimal(+123e+4);
|
||||
} {1230000.0}
|
||||
SELECT decimal('+123e+4');
|
||||
} {1230000}
|
||||
do_execsql_test 1081 {
|
||||
SELECT decimal_sci('+123e+4');
|
||||
} {+1.23e+06}
|
||||
|
||||
|
||||
do_execsql_test 2000 {
|
||||
@@ -161,6 +164,10 @@ do_execsql_test 6010 {
|
||||
SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v)
|
||||
FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n);
|
||||
} {0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625}
|
||||
do_execsql_test 6011 {
|
||||
WITH c(n) AS (SELECT ieee754_from_blob(x'0000000000000001'))
|
||||
SELECT decimal(c.n) FROM c;
|
||||
} {0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625}
|
||||
do_execsql_test 6020 {
|
||||
WITH c(n) AS (SELECT ieee754_from_blob(x'7fefffffffffffff'))
|
||||
SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v)
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
** Performance testing of floating-point binary-to-decimal conversion for
|
||||
** SQLite versus the standard library.
|
||||
**
|
||||
** To compile:
|
||||
**
|
||||
** make sqlite3.c
|
||||
** gcc -Os -c sqlite3.c
|
||||
** gcc -I. -Os test/fp-speed-1.c sqlite3.o -ldl -lm -lpthread
|
||||
**
|
||||
** To run the test:
|
||||
**
|
||||
** time ./a.out 0 10000000 <-- standard library
|
||||
** time ./a.out 1 10000000 <-- SQLite
|
||||
** time ./a.out 2 10000000 <-- SQLite with long-double disabled
|
||||
*/
|
||||
static double aVal[] = {
|
||||
-1.0163830486285643089e+063,
|
||||
+0.0049243807391586981e-019,
|
||||
+7.3818732407343994867e-095,
|
||||
+7.0678595219225717143e+000,
|
||||
+9.2807266319850025655e+120,
|
||||
+5.8871050861933394663e+135,
|
||||
-2.2998023621259662805e-168,
|
||||
-1.5903581924910847482e+181,
|
||||
+2.4313441816844978216e-001,
|
||||
-3.8290987328945399091e-147,
|
||||
+1.8787914062744001349e-009,
|
||||
+0.7270653560487146653e-014,
|
||||
+0.0639577697913183665e+107,
|
||||
+5.2292338879315861930e-103,
|
||||
+6.3747682672872231319e-021,
|
||||
+8.6972339538329106984e-129,
|
||||
-9.5074486051597691937e-026,
|
||||
-8.6480257845368753018e-005,
|
||||
-3.5657595775797470332e+017,
|
||||
-7.8323106179731761351e+161,
|
||||
+7.7813875274120800372e+077,
|
||||
-1.8739718928360196596e-050,
|
||||
+8.6898145915593357218e-051,
|
||||
+6.0566766359877837871e+002,
|
||||
+4.1703379217148160678e+199,
|
||||
+2.1283871288746651560e+150,
|
||||
-6.8395236083891810637e+128,
|
||||
-6.2114299763939529155e-147,
|
||||
-2.0753525742614637350e-149,
|
||||
+5.8727459803944290257e-007,
|
||||
+8.5888991062002101817e+010,
|
||||
+6.8624461031355917632e+026,
|
||||
-3.3053986756670905167e-075,
|
||||
-4.3596843152763444945e-108,
|
||||
+0.0834139520104099673e+098,
|
||||
-8.8581986548904222440e-192,
|
||||
-3.6622095428727698202e+038,
|
||||
-6.6965852297025063260e+005,
|
||||
+1.8204169347406488441e-054,
|
||||
+6.5234508038649000384e-065,
|
||||
+1.5923006018219011453e+083,
|
||||
+1.7362555291656389480e+018,
|
||||
+7.2875431976854785882e+160,
|
||||
+1.2835880105958926748e-146,
|
||||
+8.0516253320320819420e-113,
|
||||
+6.6324633399381145440e-030,
|
||||
-1.7126500070280062058e-149,
|
||||
+1.6995738341583712335e+042,
|
||||
+7.6048766923738663725e-112,
|
||||
+0.6159117235449455043e-004,
|
||||
+5.7544894355415943289e-135,
|
||||
+8.2970228592690581890e-023,
|
||||
-6.5531925360163067447e+020,
|
||||
+5.8321334606187030050e+120,
|
||||
+5.6557166509571891727e+095,
|
||||
+0.3322789708438408704e-114,
|
||||
-7.1210648776698686623e-050,
|
||||
-9.6721262526706343301e+179,
|
||||
-3.4583916571377395084e-106,
|
||||
+4.7896094323214750793e-172,
|
||||
-9.6926028040004137875e-056,
|
||||
+7.0683848275381385483e-198,
|
||||
-5.2970114182162961964e-007,
|
||||
-4.4287021200905393271e-170,
|
||||
+0.0728891155732808799e-189,
|
||||
-9.1855462025879447465e+175,
|
||||
+3.7294126234131007796e+015,
|
||||
+2.6857421882792719241e+003,
|
||||
-4.7070607333624685339e+039,
|
||||
+7.2175820768279334274e+136,
|
||||
-8.3678412534261163481e-115,
|
||||
+2.2174844304241822163e+019,
|
||||
+0.1949824588606861016e+112,
|
||||
-9.7334052955672071912e+151,
|
||||
-9.7793887766936999879e-142,
|
||||
-5.1561164587416931561e+139,
|
||||
-7.5048993577765174789e-022,
|
||||
+7.3556076568687784568e+107,
|
||||
-5.0681628575533599865e-144,
|
||||
+1.5209705642027747811e+165,
|
||||
-7.5989782535048296040e-101,
|
||||
+1.3654137203389775871e-016,
|
||||
-1.6441720554651372066e+087,
|
||||
-4.9042433196141125923e+000,
|
||||
-7.7063611961649130777e+118,
|
||||
+0.1699427460930766201e+174,
|
||||
+8.3374317849572216870e-145,
|
||||
-5.2355330480469580072e+081,
|
||||
-3.8510045942194147919e+141,
|
||||
-6.3513622544326339887e-147,
|
||||
+2.3869303484454428988e+049,
|
||||
+3.8352715871620360268e-165,
|
||||
-3.1263120493136887902e+035,
|
||||
-5.5794797002556490823e+051,
|
||||
-8.8109874479595604379e+142,
|
||||
-4.3727360120203216922e+070,
|
||||
-3.1109951189668539974e+170,
|
||||
-9.4841878031704268232e+011,
|
||||
-3.7398451668304407277e+067,
|
||||
+4.8984042008915959963e-091,
|
||||
};
|
||||
#define NN (sizeof(aVal)/sizeof(aVal[0]))
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv){
|
||||
int i;
|
||||
int cnt;
|
||||
int fg;
|
||||
char zBuf[1000];
|
||||
|
||||
if( argc!=3 ){
|
||||
fprintf(stderr, "Usage: %s FLAG COUNT\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
cnt = atoi(argv[2]);
|
||||
fg = atoi(argv[1]);
|
||||
|
||||
switch( fg % 3 ){
|
||||
case 0: {
|
||||
printf("Doing %d calls to C-lib sprintf()\n", cnt);
|
||||
for(i=0; i<cnt; i++){
|
||||
sprintf(zBuf, "%.26g", aVal[i%NN]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
printf("Doing %d calls to sqlite3_snprintf()\n", cnt);
|
||||
for(i=0; i<cnt; i++){
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.26g", aVal[i%NN]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
printf("Doing %d calls to sqlite3_snprintf() without long double\n", cnt);
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, 0);
|
||||
for(i=0; i<cnt; i++){
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.26g", aVal[i%NN]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# 2023-07-03
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains a test that attempts to verify the claim that the
|
||||
# floatpoint-to-text conversion routines built into SQLite maintain at
|
||||
# least 15 significant digits of accuracy.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
if {[catch {load_static_extension db decimal} error]} {
|
||||
puts "Skipping decimal tests, hit load error: $error"
|
||||
finish_test; return
|
||||
}
|
||||
|
||||
sqlite3_create_function db
|
||||
do_execsql_test fpconv1-1.0 {
|
||||
WITH RECURSIVE
|
||||
/* Number of random floating-point values to try.
|
||||
** On a circa 2016 x64 linux box, this test runs at
|
||||
** about 80000 cases per second -------------------vvvvvv */
|
||||
c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100000),
|
||||
fp(y) AS MATERIALIZED (
|
||||
SELECT CAST( format('%+d.%019d0e%+03d',
|
||||
random()%10,abs(random()),random()%200) AS real)
|
||||
FROM c
|
||||
)
|
||||
SELECT y FROM fp
|
||||
WHERE -log10(abs(decimal_sub(dtostr(y,24),format('%!.24e',y))/y))<15.0;
|
||||
/* Number of digits of accuracy required -------^^^^ */
|
||||
} {}
|
||||
# ^---- Expect a empty set as the result. The output is all tested numbers
|
||||
# that fail to preserve at least 15 significant digits of accuracy.
|
||||
|
||||
finish_test
|
||||
+47
-24
@@ -42,6 +42,10 @@ do_test func-0.1 {
|
||||
do_test func-1.0 {
|
||||
execsql {SELECT length(t1) FROM tbl1 ORDER BY t1}
|
||||
} {4 2 7 8 4}
|
||||
set isutf16 [regexp 16 [db one {PRAGMA encoding}]]
|
||||
do_execsql_test func-1.0b {
|
||||
SELECT octet_length(t1) FROM tbl1 ORDER BY t1;
|
||||
} [expr {$isutf16?"8 4 14 16 8":"4 2 7 8 4"}]
|
||||
do_test func-1.1 {
|
||||
set r [catch {execsql {SELECT length(*) FROM tbl1 ORDER BY t1}} msg]
|
||||
lappend r $msg
|
||||
@@ -57,9 +61,29 @@ do_test func-1.3 {
|
||||
do_test func-1.4 {
|
||||
execsql {SELECT coalesce(length(a),-1) FROM t2}
|
||||
} {1 -1 3 -1 5}
|
||||
do_execsql_test func-1.5 {
|
||||
SELECT octet_length(12345);
|
||||
} [expr {(1+($isutf16!=0))*5}]
|
||||
db null NULL
|
||||
do_execsql_test func-1.6 {
|
||||
SELECT octet_length(NULL);
|
||||
} {NULL}
|
||||
do_execsql_test func-1.7 {
|
||||
SELECT octet_length(7.5);
|
||||
} [expr {(1+($isutf16!=0))*3}]
|
||||
do_execsql_test func-1.8 {
|
||||
SELECT octet_length(x'30313233');
|
||||
} {4}
|
||||
do_execsql_test func-1.9 {
|
||||
WITH c(x) AS (VALUES(char(350,351,352,353,354)))
|
||||
SELECT length(x), octet_length(x) FROM c;
|
||||
} {5 10}
|
||||
|
||||
|
||||
|
||||
# Check out the substr() function
|
||||
#
|
||||
db null {}
|
||||
do_test func-2.0 {
|
||||
execsql {SELECT substr(t1,1,2) FROM tbl1 ORDER BY t1}
|
||||
} {fr is pr so th}
|
||||
@@ -839,30 +863,13 @@ do_test func-18.11 {
|
||||
}
|
||||
} integer
|
||||
ifcapable floatingpoint {
|
||||
do_test func-18.12 {
|
||||
catchsql {
|
||||
INSERT INTO t6 VALUES(1<<62);
|
||||
SELECT sum(x) - ((1<<62)*2.0+1) from t6;
|
||||
}
|
||||
do_catchsql_test func-18.12 {
|
||||
INSERT INTO t6 VALUES(1<<62);
|
||||
SELECT sum(x) - ((1<<62)*2.0+1) from t6;
|
||||
} {1 {integer overflow}}
|
||||
do_test func-18.13 {
|
||||
execsql {
|
||||
SELECT total(x) - ((1<<62)*2.0+1) FROM t6
|
||||
}
|
||||
} 0.0
|
||||
}
|
||||
ifcapable !floatingpoint {
|
||||
do_test func-18.12 {
|
||||
catchsql {
|
||||
INSERT INTO t6 VALUES(1<<62);
|
||||
SELECT sum(x) - ((1<<62)*2+1) from t6;
|
||||
}
|
||||
} {1 {integer overflow}}
|
||||
do_test func-18.13 {
|
||||
execsql {
|
||||
SELECT total(x) - ((1<<62)*2+1) FROM t6
|
||||
}
|
||||
} 0.0
|
||||
do_catchsql_test func-18.13 {
|
||||
SELECT total(x) - ((1<<62)*2.0+1) FROM t6
|
||||
} {0 0.0}
|
||||
}
|
||||
if {[working_64bit_int]} {
|
||||
do_test func-18.14 {
|
||||
@@ -1520,6 +1527,22 @@ do_execsql_test func-36.110 {
|
||||
SELECT 123 ->> 456
|
||||
} {123->>456}
|
||||
|
||||
|
||||
# 2023-06-26
|
||||
# Enhanced precision of SUM().
|
||||
#
|
||||
reset_db
|
||||
do_catchsql_test func-37.100 {
|
||||
WITH c(x) AS (VALUES(9223372036854775807),(9223372036854775807),
|
||||
(123),(-9223372036854775807),(-9223372036854775807))
|
||||
SELECT sum(x) FROM c;
|
||||
} {1 {integer overflow}}
|
||||
do_catchsql_test func-37.110 {
|
||||
WITH c(x) AS (VALUES(9223372036854775807),(1))
|
||||
SELECT sum(x) FROM c;
|
||||
} {1 {integer overflow}}
|
||||
do_catchsql_test func-37.120 {
|
||||
WITH c(x) AS (VALUES(9223372036854775807),(10000),(-10010))
|
||||
SELECT sum(x) FROM c;
|
||||
} {1 {integer overflow}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -257,6 +257,7 @@ array set ::Configs [strip_comments {
|
||||
-DSQLITE_ENABLE_FTS4
|
||||
-DSQLITE_ENABLE_RTREE
|
||||
-DSQLITE_ENABLE_HIDDEN_COLUMNS
|
||||
-DLONGDOUBLE_TYPE=double
|
||||
-DCONFIG_SLOWDOWN_FACTOR=8.0
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -34,8 +34,8 @@ for {set iTest 1} {$iTest<=50000} {incr iTest} {
|
||||
set r2 $x1.$x4
|
||||
set ans [string trimright $r2 0]
|
||||
if {[string match *. $ans]} {set ans ${ans}0}
|
||||
do_test $iTest/$n/${r}5=>$ans {
|
||||
set x [db one "SELECT round(${r}5,$n)"]
|
||||
do_test $iTest/$n/${r}6=>$ans {
|
||||
set x [db one "SELECT round(${r}6,$n)"]
|
||||
} $ans
|
||||
}
|
||||
|
||||
|
||||
+65
-4
@@ -55,11 +55,12 @@ proc get_cycles {stmt} {
|
||||
dict get $r nCycle
|
||||
}
|
||||
|
||||
proc foreach_scan {varname stmt body} {
|
||||
proc foreach_scan {varname stmt body {debug 0}} {
|
||||
upvar $varname var
|
||||
|
||||
for {set ii 0} {1} {incr ii} {
|
||||
set r [sqlite3_stmt_scanstatus -flags complex $stmt $ii]
|
||||
set f "complex"
|
||||
if {$debug} { set f "complex debug" }
|
||||
set r [sqlite3_stmt_scanstatus -flags $f $stmt $ii]
|
||||
if {[llength $r]==0} break
|
||||
array set var $r
|
||||
uplevel $body
|
||||
@@ -102,6 +103,15 @@ proc puts_graph {sql} {
|
||||
puts [string trim [get_graph $stmt]]
|
||||
}
|
||||
|
||||
proc puts_debug_info {sql} {
|
||||
db eval $sql
|
||||
set stmt [db version -last-stmt-ptr]
|
||||
foreach_scan X $stmt {
|
||||
puts -nonewline "$X(debug_explain) $X(zExplain): "
|
||||
puts -nonewline "loop=$X(debug_loop) visit=$X(debug_visit) "
|
||||
puts "csr=$X(debug_csr) range=$X(debug_range)"
|
||||
} 1
|
||||
}
|
||||
|
||||
do_zexplain_test 0 1.1 {
|
||||
SELECT (SELECT a FROM t1 WHERE b=x) FROM t2 WHERE y=2
|
||||
@@ -235,7 +245,7 @@ do_graph_test 4.5 {
|
||||
QUERY (nCycle=nnn)
|
||||
--CO-ROUTINE v1
|
||||
----SCAN rt2 (nCycle=nnn)
|
||||
----USE TEMP B-TREE FOR GROUP BY
|
||||
----USE TEMP B-TREE FOR GROUP BY (nCycle=nnn)
|
||||
--SCAN rt1 (nCycle=nnn)
|
||||
--CREATE AUTOMATIC INDEX ON v1(x1, cnt) (nCycle=nnn)
|
||||
--BLOOM FILTER ON v1 (x1=?)
|
||||
@@ -270,6 +280,57 @@ ifcapable trace {
|
||||
} {{SCAN t1} {SCAN t1} {SCAN t1}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
sqlite3_db_config db STMT_SCANSTATUS 1
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
INSERT INTO t1 VALUES(6, 'six');
|
||||
INSERT INTO t1 VALUES(7, 'seven');
|
||||
INSERT INTO t1 VALUES(8, 'eight');
|
||||
}
|
||||
|
||||
do_graph_test 6.1 {
|
||||
SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1
|
||||
} {
|
||||
QUERY (nCycle=nnn)
|
||||
--SCAN t1 (nCycle=nnn)
|
||||
--USE TEMP B-TREE FOR GROUP BY (nCycle=nnn)
|
||||
}
|
||||
|
||||
set sql {
|
||||
WITH xy(x, y) AS ( SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1)
|
||||
SELECT * FROM xy WHERE x=1
|
||||
}
|
||||
do_graph_test 6.2 $sql {
|
||||
QUERY (nCycle=nnn)
|
||||
--CO-ROUTINE xy
|
||||
----SCAN t1 (nCycle=nnn)
|
||||
----USE TEMP B-TREE FOR GROUP BY (nCycle=nnn)
|
||||
--SCAN xy (nCycle=nnn)
|
||||
}
|
||||
|
||||
do_graph_test 6.3 {
|
||||
WITH xy(x, y) AS ( SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1)
|
||||
SELECT * FROM xy, xy AS xy2
|
||||
} {
|
||||
QUERY (nCycle=nnn)
|
||||
--MATERIALIZE xy (nCycle=nnn)
|
||||
----SCAN t1 (nCycle=nnn)
|
||||
----USE TEMP B-TREE FOR GROUP BY (nCycle=nnn)
|
||||
--SCAN xy (nCycle=nnn)
|
||||
--SCAN xy2 (nCycle=nnn)
|
||||
}
|
||||
|
||||
#explain_i { SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1 }
|
||||
#puts_debug_info { SELECT (a % 2), group_concat(b) FROM t1 GROUP BY 1 }
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -262,5 +262,14 @@ do_test shell2-1.4.11 {
|
||||
SELECT count(*) FROM t;}]]
|
||||
} {0 1}
|
||||
|
||||
# Bug from forum post 7cbe081746dd3803
|
||||
# Keywords as column names were producing an error message.
|
||||
do_test shell2-1.4.12 {
|
||||
set res [catchcmd :memory: [string trim {
|
||||
CREATE TABLE "group"("order" text);
|
||||
INSERT INTO "group" VALUES ('ABC');
|
||||
.sha3sum}]]
|
||||
} {0 ca08bc02b7e95c7df431a3a4b1cc0f8d8743914793473f55b5558e03}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -21,6 +21,7 @@ static const char zHelp[] =
|
||||
" --memdb Use an in-memory database\n"
|
||||
" --mmap SZ MMAP the first SZ bytes of the database file\n"
|
||||
" --multithread Set multithreaded mode\n"
|
||||
" --nolongdouble Disable the use of long double\n"
|
||||
" --nomemstat Disable memory statistics\n"
|
||||
" --nomutex Open db with SQLITE_OPEN_NOMUTEX\n"
|
||||
" --nosync Set PRAGMA synchronous=OFF\n"
|
||||
@@ -2307,6 +2308,10 @@ int main(int argc, char **argv){
|
||||
ARGC_VALUE_CHECK(1);
|
||||
mmapSize = integerValue(argv[++i]);
|
||||
#endif
|
||||
}else if( strcmp(z,"nolongdouble")==0 ){
|
||||
#ifdef SQLITE_TESTCTRL_USELONGDOUBLE
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, 0);
|
||||
#endif
|
||||
}else if( strcmp(z,"nomutex")==0 ){
|
||||
openFlags |= SQLITE_OPEN_NOMUTEX;
|
||||
}else if( strcmp(z,"nosync")==0 ){
|
||||
|
||||
@@ -99,6 +99,9 @@ while test "$1" != ""; do
|
||||
--stmtcache)
|
||||
shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --stmtcache $1"
|
||||
;;
|
||||
--nolongdouble)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --nolongdouble"
|
||||
;;
|
||||
--checkpoint)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --checkpoint"
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user