mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
Merge all the latest trunk enhancements into the coroutines-exp2 enhancement
branch. FossilOrigin-Name: ea278438b72e85d217e72c836cbefd68bd8b336baf33507b2d8d12ef436424cd
This commit is contained in:
@@ -243,6 +243,60 @@ eval [string map [list %WR% $trailing] {
|
||||
sqlite3changeset_apply db $cinv xConflict
|
||||
execsql { SELECT * FROM t7 }
|
||||
} {1 1 ccc 2 2 ccc 3 3 ccc}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
reset_test
|
||||
do_execsql_test $tn.7.0 {
|
||||
CREATE TABLE t8(a PRIMARY KEY, b, c);
|
||||
}
|
||||
do_execsql_test -db db2 $tn.7.1 {
|
||||
CREATE TABLE t8(a PRIMARY KEY, b, c, d DEFAULT 'D', e DEFAULT 'E');
|
||||
}
|
||||
|
||||
do_then_apply_sql {
|
||||
INSERT INTO t8 VALUES(1, 2, 3);
|
||||
INSERT INTO t8 VALUES(4, 5, 6);
|
||||
}
|
||||
do_execsql_test $tn.7.2.1 {
|
||||
SELECT * FROM t8
|
||||
} {1 2 3 4 5 6}
|
||||
do_execsql_test -db db2 $tn.7.2.2 {
|
||||
SELECT * FROM t8
|
||||
} {1 2 3 D E 4 5 6 D E}
|
||||
|
||||
do_then_apply_sql {
|
||||
UPDATE t8 SET c=45 WHERE a=4;
|
||||
}
|
||||
do_execsql_test $tn.7.3.1 {
|
||||
SELECT * FROM t8
|
||||
} {1 2 3 4 5 45}
|
||||
do_execsql_test -db db2 $tn.7.3.2 {
|
||||
SELECT * FROM t8
|
||||
} {1 2 3 D E 4 5 45 D E}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
reset_test
|
||||
do_execsql_test $tn.8.0 {
|
||||
CREATE TABLE t9(a PRIMARY KEY, b, c, d, e, f, g, h);
|
||||
}
|
||||
do_execsql_test -db db2 $tn.8.1 {
|
||||
CREATE TABLE t9(a PRIMARY KEY, b, c, d, e, f, g, h, i, j, k, l);
|
||||
}
|
||||
do_then_apply_sql {
|
||||
INSERT INTO t9 VALUES(1, 2, 3, 4, 5, 6, 7, 8);
|
||||
}
|
||||
do_then_apply_sql {
|
||||
UPDATE t9 SET h=450 WHERE a=1
|
||||
}
|
||||
do_execsql_test -db db2 $tn.8.2 {
|
||||
SELECT * FROM t9
|
||||
} {1 2 3 4 5 6 7 450 {} {} {} {}}
|
||||
do_then_apply_sql {
|
||||
UPDATE t9 SET h=NULL
|
||||
}
|
||||
do_execsql_test -db db2 $tn.8.2 {
|
||||
SELECT * FROM t9
|
||||
} {1 2 3 4 5 6 7 {} {} {} {} {}}
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -3327,6 +3327,22 @@ static int sessionChangesetNextOne(
|
||||
if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE;
|
||||
else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT;
|
||||
}
|
||||
|
||||
/* If this is an UPDATE that is part of a changeset, then check that
|
||||
** there are no fields in the old.* record that are not (a) PK fields,
|
||||
** or (b) also present in the new.* record.
|
||||
**
|
||||
** Such records are technically corrupt, but the rebaser was at one
|
||||
** point generating them. Under most circumstances this is benign, but
|
||||
** can cause spurious SQLITE_RANGE errors when applying the changeset. */
|
||||
if( p->bPatchset==0 && p->op==SQLITE_UPDATE){
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( p->abPK[i]==0 && p->apValue[i+p->nCol]==0 ){
|
||||
sqlite3ValueFree(p->apValue[i]);
|
||||
p->apValue[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_ROW;
|
||||
@@ -5523,7 +5539,7 @@ static void sessionAppendPartialUpdate(
|
||||
if( !pIter->abPK[i] && a1[0] ) bData = 1;
|
||||
memcpy(pOut, a1, n1);
|
||||
pOut += n1;
|
||||
}else if( a2[0]!=0xFF ){
|
||||
}else if( a2[0]!=0xFF && a1[0] ){
|
||||
bData = 1;
|
||||
memcpy(pOut, a2, n2);
|
||||
pOut += n2;
|
||||
|
||||
@@ -98,6 +98,19 @@ int sql_exec_changeset(
|
||||
}
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
static int sqlite3_test_changeset(int, void *, char **);
|
||||
static void assert_changeset_is_ok(int n, void *p){
|
||||
int rc = 0;
|
||||
char *z = 0;
|
||||
rc = sqlite3_test_changeset(n, p, &z);
|
||||
assert( z==0 );
|
||||
}
|
||||
#else
|
||||
# define assert_changeset_is_ok(n,p)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Tclcmd: sql_exec_changeset DB SQL
|
||||
*/
|
||||
@@ -127,6 +140,7 @@ static int SQLITE_TCLAPI test_sql_exec_changeset(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
assert_changeset_is_ok(nChangeset, pChangeset);
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChangeset, nChangeset));
|
||||
sqlite3_free(pChangeset);
|
||||
return TCL_OK;
|
||||
@@ -295,6 +309,7 @@ static int SQLITE_TCLAPI test_session_cmd(
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
assert_changeset_is_ok(o.n, o.p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(o.p, o.n));
|
||||
}
|
||||
sqlite3_free(o.p);
|
||||
@@ -953,6 +968,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_invert(
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
assert_changeset_is_ok(sOut.n, sOut.p);
|
||||
Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
|
||||
}
|
||||
sqlite3_free(sOut.p);
|
||||
@@ -1001,6 +1017,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
assert_changeset_is_ok(sOut.n, sOut.p);
|
||||
Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
|
||||
}
|
||||
sqlite3_free(sOut.p);
|
||||
@@ -1236,6 +1253,7 @@ static int SQLITE_TCLAPI test_rebaser_cmd(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
assert_changeset_is_ok(sOut.n, sOut.p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(sOut.p, sOut.n));
|
||||
}
|
||||
sqlite3_free(sOut.p);
|
||||
@@ -1282,6 +1300,107 @@ static int SQLITE_TCLAPI test_sqlite3rebaser_create(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run some sanity checks on the changeset in nChangeset byte buffer
|
||||
** pChangeset. If any fail, return a non-zero value and, optionally,
|
||||
** set output variable (*pzErr) to point to a buffer containing an
|
||||
** English language error message describing the problem. In this
|
||||
** case it is the responsibility of the caller to free the buffer
|
||||
** using sqlite3_free().
|
||||
**
|
||||
** Or, if the changeset appears to be well-formed, this function
|
||||
** returns SQLITE_OK and sets (*pzErr) to NULL.
|
||||
*/
|
||||
static int sqlite3_test_changeset(
|
||||
int nChangeset,
|
||||
void *pChangeset,
|
||||
char **pzErr
|
||||
){
|
||||
sqlite3_changeset_iter *pIter = 0;
|
||||
char *zErr = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int bPatch = (nChangeset>0 && ((char*)pChangeset)[0]=='P');
|
||||
|
||||
rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
|
||||
unsigned char *aPk = 0;
|
||||
int nCol = 0;
|
||||
int op = 0;
|
||||
const char *zTab = 0;
|
||||
|
||||
sqlite3changeset_pk(pIter, &aPk, &nCol);
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
|
||||
|
||||
if( op==SQLITE_UPDATE ){
|
||||
int iCol;
|
||||
for(iCol=0; iCol<nCol; iCol++){
|
||||
sqlite3_value *pNew = 0;
|
||||
sqlite3_value *pOld = 0;
|
||||
sqlite3changeset_new(pIter, iCol, &pNew);
|
||||
sqlite3changeset_old(pIter, iCol, &pOld);
|
||||
|
||||
if( aPk[iCol] ){
|
||||
if( pOld==0 ) rc = SQLITE_ERROR;
|
||||
}else if( bPatch ){
|
||||
if( pOld ) rc = SQLITE_ERROR;
|
||||
}else{
|
||||
if( (pOld==0)!=(pNew==0) ) rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
zErr = sqlite3_mprintf(
|
||||
"unexpected SQLITE_UPDATE (bPatch=%d pk=%d pOld=%d pNew=%d)",
|
||||
bPatch, (int)aPk[iCol], pOld!=0, pNew!=0
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rc2 = sqlite3changeset_finalize(pIter);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
|
||||
*pzErr = zErr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** test_changeset CHANGESET
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_changeset(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void *pChangeset = 0; /* Buffer containing changeset */
|
||||
int nChangeset = 0; /* Size of buffer aChangeset in bytes */
|
||||
int rc = SQLITE_OK;
|
||||
char *z = 0;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[1], &nChangeset);
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
rc = sqlite3_test_changeset(nChangeset, pChangeset, &z);
|
||||
if( rc!=SQLITE_OK ){
|
||||
char *zErr = sqlite3_mprintf("(%d) - \"%s\"", rc, z);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
sqlite3_free(z);
|
||||
|
||||
return rc ? TCL_ERROR : TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3rebaser_configure OP VALUE
|
||||
*/
|
||||
@@ -1337,6 +1456,7 @@ int TestSession_Init(Tcl_Interp *interp){
|
||||
{ "sql_exec_changeset", test_sql_exec_changeset },
|
||||
{ "sqlite3rebaser_create", test_sqlite3rebaser_create },
|
||||
{ "sqlite3session_config", test_sqlite3session_config },
|
||||
{ "test_changeset", test_changeset },
|
||||
};
|
||||
int i;
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
the range of supported argument types. */
|
||||
[
|
||||
"sqlite3_progress_handler", undefined, [
|
||||
"sqlite3*", "int", new wasm.xWrap.FuncPtrAdapter({
|
||||
"sqlite3*", "int", wasm.xWrap.FuncPtrAdapter({
|
||||
name: 'xProgressHandler',
|
||||
signature: 'i(p)',
|
||||
bindScope: 'context',
|
||||
@@ -180,7 +180,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
"**", "**", "*", "*", "*"],
|
||||
["sqlite3_total_changes", "int", "sqlite3*"],
|
||||
["sqlite3_trace_v2", "int", "sqlite3*", "int",
|
||||
new wasm.xWrap.FuncPtrAdapter({
|
||||
wasm.xWrap.FuncPtrAdapter({
|
||||
name: 'sqlite3_trace_v2::callback',
|
||||
signature: 'i(ippp)',
|
||||
contextKey: (argIndex, argv)=>'sqlite3@'+argv[0]
|
||||
@@ -462,22 +462,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
const __collationContextKey = (argIndex,argv)=>{
|
||||
return 'argv['+argIndex+']:sqlite3@'+argv[0]+
|
||||
':'+((/*THIS IS WRONG. We can't sensibly use a converted-to-C-string
|
||||
address here and don't have access to the JS string (IF ANY)
|
||||
which the user passed in.*/
|
||||
''+argv[1]
|
||||
).toLowerCase());
|
||||
':'+wasm.cstrToJs(argv[1]).toLowerCase()
|
||||
};
|
||||
const __ccv2 = wasm.xWrap(
|
||||
'sqlite3_create_collation_v2', 'int',
|
||||
'sqlite3*','string','int','*','*','*'
|
||||
/* int(*xCompare)(void*,int,const void*,int,const void*) */
|
||||
/* void(*xDestroy(void*) */
|
||||
);
|
||||
if(0){
|
||||
// Problem: we cannot, due to xWrap() arg-passing limitations,
|
||||
// currently easily/efficiently get a per-collation distinct
|
||||
// key for purposes of creating distinct FuncPtrAdapter contexts.
|
||||
'sqlite3*','string','int','*',
|
||||
new wasm.xWrap.FuncPtrAdapter({
|
||||
/* int(*xCompare)(void*,int,const void*,int,const void*) */
|
||||
name: 'xCompare',
|
||||
@@ -492,7 +481,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
bindScope: 'context',
|
||||
contextKey: __collationContextKey
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
Works exactly like C's sqlite3_create_collation_v2() except that:
|
||||
@@ -518,18 +507,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
);
|
||||
}
|
||||
let rc, pfCompare, pfDestroy;
|
||||
try{
|
||||
if(xCompare instanceof Function){
|
||||
pfCompare = wasm.installFunction(xCompare, 'i(pipip)');
|
||||
}
|
||||
if(xDestroy instanceof Function){
|
||||
pfDestroy = wasm.installFunction(xDestroy, 'v(p)');
|
||||
}
|
||||
rc = __ccv2(pDb, zName, eTextRep, pArg,
|
||||
pfCompare || xCompare, pfDestroy || xDestroy);
|
||||
try{
|
||||
rc = __ccv2(pDb, zName, eTextRep, pArg, xCompare, xDestroy);
|
||||
}catch(e){
|
||||
if(pfCompare) wasm.uninstallFunction(pfCompare);
|
||||
if(pfDestroy) wasm.uninstallFunction(pfDestroy);
|
||||
rc = util.sqlite3_wasm_db_error(pDb, e);
|
||||
}
|
||||
return rc;
|
||||
@@ -539,7 +519,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return (5===arguments.length)
|
||||
? capi.sqlite3_create_collation_v2(pDb,zName,eTextRep,pArg,xCompare,0)
|
||||
: __dbArgcMismatch(pDb, 'sqlite3_create_collation', 5);
|
||||
}
|
||||
};
|
||||
|
||||
}/*sqlite3_create_collation() and friends*/
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || 0);
|
||||
pDb = wasm.peekPtr(pPtr);
|
||||
checkSqlite3Rc(pDb, rc);
|
||||
capi.sqlite3_extended_result_codes(pDb, 1);
|
||||
if(flagsStr.indexOf('t')>=0){
|
||||
capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT,
|
||||
__dbTraceToConsole, 0);
|
||||
@@ -439,9 +440,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
out.cbArg = (stmt)=>stmt.get(opt.rowMode);
|
||||
break;
|
||||
}else if('string'===typeof opt.rowMode && opt.rowMode.length>1){
|
||||
/* "$X", ":X", and "@X" fetch column named "X" (case-sensitive!) */
|
||||
const prefix = opt.rowMode[0];
|
||||
if(':'===prefix || '@'===prefix || '$'===prefix){
|
||||
/* "$X": fetch column named "X" (case-sensitive!). Prior
|
||||
to 2022-12-14 ":X" and "@X" were also permitted, but
|
||||
having so many options is unnecessary and likely to
|
||||
cause confusion. */
|
||||
if('$'===opt.rowMode[0]){
|
||||
out.cbArg = function(stmt){
|
||||
const rc = stmt.get(this.obj)[this.colName];
|
||||
return (undefined===rc) ? toss3("exec(): unknown result column:",this.colName) : rc;
|
||||
@@ -739,17 +742,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
row. Only that one single value will be passed on.
|
||||
|
||||
C) A string with a minimum length of 2 and leading character of
|
||||
':', '$', or '@' will fetch the row as an object, extract that
|
||||
one field, and pass that field's value to the callback. Note
|
||||
that these keys are case-sensitive so must match the case used
|
||||
in the SQL. e.g. `"select a A from t"` with a `rowMode` of
|
||||
`'$A'` would work but `'$a'` would not. A reference to a column
|
||||
not in the result set will trigger an exception on the first
|
||||
row (as the check is not performed until rows are fetched).
|
||||
Note also that `$` is a legal identifier character in JS so
|
||||
need not be quoted. (Design note: those 3 characters were
|
||||
chosen because they are the characters support for naming bound
|
||||
parameters.)
|
||||
'$' will fetch the row as an object, extract that one field,
|
||||
and pass that field's value to the callback. Note that these
|
||||
keys are case-sensitive so must match the case used in the
|
||||
SQL. e.g. `"select a A from t"` with a `rowMode` of `'$A'`
|
||||
would work but `'$a'` would not. A reference to a column not in
|
||||
the result set will trigger an exception on the first row (as
|
||||
the check is not performed until rows are fetched). Note also
|
||||
that `$` is a legal identifier character in JS so need not be
|
||||
quoted.
|
||||
|
||||
Any other `rowMode` value triggers an exception.
|
||||
|
||||
@@ -800,6 +801,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
let bind = opt.bind;
|
||||
let evalFirstResult = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */;
|
||||
const stack = wasm.scopedAllocPush();
|
||||
const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : undefined;
|
||||
try{
|
||||
const isTA = util.isSQLableTypedArray(arg.sql)
|
||||
/* Optimization: if the SQL is a TypedArray we can save some string
|
||||
@@ -833,9 +835,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
pSql = wasm.peekPtr(pzTail);
|
||||
sqlByteLen = pSqlEnd - pSql;
|
||||
if(!pStmt) continue;
|
||||
if(Array.isArray(opt.saveSql)){
|
||||
opt.saveSql.push(capi.sqlite3_sql(pStmt).trim());
|
||||
}
|
||||
if(saveSql) saveSql.push(capi.sqlite3_sql(pStmt).trim());
|
||||
stmt = new Stmt(this, pStmt, BindTypes);
|
||||
if(bind && stmt.parameterCount){
|
||||
stmt.bind(bind);
|
||||
|
||||
@@ -1673,6 +1673,15 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
build-generated object.
|
||||
*/
|
||||
version: Object.create(null),
|
||||
|
||||
/**
|
||||
The library reserves the 'client' property for client-side use
|
||||
and promises to never define a property with this name nor to
|
||||
ever rely on specific contents of it. It makes no such guarantees
|
||||
for other properties.
|
||||
*/
|
||||
client: undefined,
|
||||
|
||||
/**
|
||||
Performs any optional asynchronous library-level initialization
|
||||
which might be required. This function returns a Promise which
|
||||
|
||||
@@ -759,13 +759,51 @@ self.WhWasmUtilInstaller = function(target){
|
||||
target.peekPtr = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), ptrIR );
|
||||
|
||||
/**
|
||||
A variant of poke() intended for setting
|
||||
pointer-to-pointer values. Its differences from poke() are
|
||||
that (1) it defaults to a value of 0, (2) it always writes
|
||||
to the pointer-sized heap view, and (3) it returns `this`.
|
||||
A variant of poke() intended for setting pointer-to-pointer
|
||||
values. Its differences from poke() are that (1) it defaults to a
|
||||
value of 0 and (2) it always writes to the pointer-sized heap
|
||||
view.
|
||||
*/
|
||||
target.pokePtr = (ptr, value=0)=>target.poke(ptr, value, ptrIR);
|
||||
|
||||
/**
|
||||
Convenience form of peek() intended for fetching i8 values. If
|
||||
passed a single non-array argument it returns the value of that
|
||||
one pointer address. If passed multiple arguments, or a single
|
||||
array of arguments, it returns an array of their values.
|
||||
*/
|
||||
target.peek8 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i8' );
|
||||
/**
|
||||
Convience form of poke() intended for setting individual bytes.
|
||||
Its difference from poke() is that it always writes to the
|
||||
i8-sized heap view.
|
||||
*/
|
||||
target.poke8 = (ptr, value)=>target.poke(ptr, value, 'i8');
|
||||
/** i16 variant of peek8(). */
|
||||
target.peek16 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i16' );
|
||||
/** i16 variant of poke8(). */
|
||||
target.poke16 = (ptr, value)=>target.poke(ptr, value, 'i16');
|
||||
/** i32 variant of peek8(). */
|
||||
target.peek32 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i32' );
|
||||
/** i32 variant of poke8(). */
|
||||
target.poke32 = (ptr, value)=>target.poke(ptr, value, 'i32');
|
||||
/** i64 variant of peek8(). Will throw if this build is not
|
||||
configured for BigInt support. */
|
||||
target.peek64 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i64' );
|
||||
/** i64 variant of poke8(). Will throw if this build is not
|
||||
configured for BigInt support. Note that this returns
|
||||
a BigInt-type value, not a Number-type value. */
|
||||
target.poke64 = (ptr, value)=>target.poke(ptr, value, 'i64');
|
||||
/** f32 variant of peek8(). */
|
||||
target.peekF32 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'f32' );
|
||||
/** f32 variant of poke8(). */
|
||||
target.pokeF32 = (ptr, value)=>target.poke(ptr, value, 'f32');
|
||||
/** f64 variant of peek8(). */
|
||||
target.peekF64 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'f64' );
|
||||
/** f64 variant of poke8(). */
|
||||
target.pokeF64 = (ptr, value)=>target.poke(ptr, value, 'f64');
|
||||
|
||||
|
||||
/** Deprecated alias for getMemValue() */
|
||||
target.getMemValue = target.peek;
|
||||
/** Deprecated alias for peekPtr() */
|
||||
@@ -1447,21 +1485,21 @@ self.WhWasmUtilInstaller = function(target){
|
||||
Requires an options object with these properties:
|
||||
|
||||
- name (optional): string describing the function binding. This
|
||||
is solely for debugging and error-reporting purposes. If not
|
||||
provided, an empty string is assumed.
|
||||
is solely for debugging and error-reporting purposes. If not
|
||||
provided, an empty string is assumed.
|
||||
|
||||
- signature: an function signature compatible with
|
||||
jsFuncToWasm().
|
||||
- signature: a function signature string compatible with
|
||||
jsFuncToWasm().
|
||||
|
||||
- bindScope (string): one of ('transient', 'context',
|
||||
'singleton'). Bind scopes are:
|
||||
|
||||
- transient: it will convert JS functions to WASM only for the
|
||||
duration of the xWrap()'d function call, using
|
||||
- 'transient': it will convert JS functions to WASM only for
|
||||
the duration of the xWrap()'d function call, using
|
||||
scopedInstallFunction(). Before that call returns, the
|
||||
WASM-side binding will be uninstalled.
|
||||
|
||||
- singleton: holds one function-pointer binding for this
|
||||
- 'singleton': holds one function-pointer binding for this
|
||||
instance. If it's called with a different function pointer,
|
||||
it uninstalls the previous one after converting the new
|
||||
value. This is only useful for use with "global" functions
|
||||
@@ -1470,23 +1508,19 @@ self.WhWasmUtilInstaller = function(target){
|
||||
to be mapped to some sort of state object (e.g. an sqlite3*)
|
||||
then "context" (see below) is the proper mode.
|
||||
|
||||
- context: similar to singleton mode but for a given "context",
|
||||
where the context is a key provided by the user and possibly
|
||||
dependent on a small amount of call-time context. This mode
|
||||
is the default if bindScope is _not_ set but a property named
|
||||
contextKey (described below) is.
|
||||
- 'context': similar to singleton mode but for a given
|
||||
"context", where the context is a key provided by the user
|
||||
and possibly dependent on a small amount of call-time
|
||||
context. This mode is the default if bindScope is _not_ set
|
||||
but a property named contextKey (described below) is.
|
||||
|
||||
FIXME: the contextKey definition is only useful for very basic
|
||||
contexts and breaks down with dynamic ones like
|
||||
sqlite3_create_collation().
|
||||
|
||||
- contextKey (function): only used if bindScope is not set or is
|
||||
'context'. This function gets passed (argIndex,argv), where
|
||||
argIndex is the index of this function pointer in its
|
||||
- contextKey (function): is only used if bindScope is not set or
|
||||
is 'context'. This function gets passed (argIndex,argv), where
|
||||
argIndex is the index of _this_ function pointer in its
|
||||
_wrapping_ function's arguments and argv is the _current_
|
||||
being-xWrap()-processed args array. All arguments to the left
|
||||
of argIndex will have been processed by xWrap() by the time
|
||||
this is called. argv[argIndex] will be the value the user
|
||||
still-being-xWrap()-processed args array. All arguments to the
|
||||
left of argIndex will have been processed by xWrap() by the
|
||||
time this is called. argv[argIndex] will be the value the user
|
||||
passed in to the xWrap()'d function for the argument this
|
||||
FuncPtrAdapter is mapped to. Arguments to the right of
|
||||
argv[argIndex] will not yet have been converted before this is
|
||||
@@ -1500,23 +1534,26 @@ self.WhWasmUtilInstaller = function(target){
|
||||
might return 'T@'+argv[1], or even just argv[1]. Note,
|
||||
however, that the (X*) argument will not yet have been
|
||||
processed by the time this is called and should not be used as
|
||||
part of that key. Similarly, C-string-type keys should not be
|
||||
used as part of keys because they are normally transient in
|
||||
this environment.
|
||||
part of that key because its pre-conversion data type might be
|
||||
unpredictable. Similarly, care must be taken with C-string-type
|
||||
arguments: those to the left in argv will, when this is called,
|
||||
be WASM pointers, whereas those to the right might (and likely
|
||||
do) have another data type. When using C-strings in keys, never
|
||||
use their pointers in the key because most C-strings in this
|
||||
constellation are transient.
|
||||
|
||||
Yes, that ^^^ is a bit awkward, but it's what we have.
|
||||
|
||||
The constructor only saves the above state for later, and does
|
||||
not actually bind any functions. Its convertArg() methor is
|
||||
not actually bind any functions. Its convertArg() method is
|
||||
called via xWrap() to perform any bindings.
|
||||
|
||||
Caveats:
|
||||
|
||||
- singleton is globally singleton. This type does not currently
|
||||
have enough context to apply, e.g., a different singleton for
|
||||
each (sqlite3*) db handle.
|
||||
If this is called like a function, instead of a constructor,
|
||||
it behaves as if it were called like a constructor.
|
||||
*/
|
||||
xArg.FuncPtrAdapter = function ctor(opt) {
|
||||
if(!(this instanceof xArg.FuncPtrAdapter)){
|
||||
toss("FuncPtrAdapter can only be used as a constructor. Use 'new'.");
|
||||
return new xArg.FuncPtrAdapter(opt);
|
||||
}
|
||||
this.signature = opt.signature;
|
||||
if(!opt.bindScope && (opt.contextKey instanceof Function)){
|
||||
@@ -1530,20 +1567,21 @@ self.WhWasmUtilInstaller = function(target){
|
||||
if(opt.contextKey) this.contextKey = opt.contextKey /*else inherit one*/;
|
||||
this.isTransient = 'transient'===this.bindScope;
|
||||
this.isContext = 'context'===this.bindScope;
|
||||
if( ('singleton'===this.bindScope) ){
|
||||
this.singleton = [];
|
||||
}else{
|
||||
this.singleton = undefined;
|
||||
}
|
||||
if( ('singleton'===this.bindScope) ) this.singleton = [];
|
||||
else this.singleton = undefined;
|
||||
//console.warn("FuncPtrAdapter()",opt,this);
|
||||
};
|
||||
xArg.FuncPtrAdapter.bindScopes = [
|
||||
'transient', 'context', 'singleton'
|
||||
];
|
||||
xArg.FuncPtrAdapter.prototype = {
|
||||
/* Dummy impl. Overwritten per-instance as needed. */
|
||||
contextKey: function(argIndex,argv){
|
||||
return this;
|
||||
},
|
||||
/* Returns this objects mapping for the given context key, in the
|
||||
form of an an array, creating the mapping if needed. The key
|
||||
may be anything suitable for use in a Map. */
|
||||
contextMap: function(key){
|
||||
const cm = (this.__cmap || (this.__cmap = new Map));
|
||||
let rc = cm.get(key);
|
||||
@@ -1553,11 +1591,28 @@ self.WhWasmUtilInstaller = function(target){
|
||||
/**
|
||||
Gets called via xWrap() to "convert" v to a WASM-bound function
|
||||
pointer. If v is one of (a pointer, null, undefined) then
|
||||
(v||0) is returned, otherwise v must be a Function, for which
|
||||
it creates (if needed) a WASM function binding and returns the
|
||||
WASM pointer to that binding. It will remember the binding for
|
||||
at least the next call, to avoid recreating the function
|
||||
unnecessarily.
|
||||
(v||0) is returned and any earlier function installed by this
|
||||
mapping _might_, depending on how it's bound, be
|
||||
uninstalled. If v is not one of those types, it must be a
|
||||
Function, for which it creates (if needed) a WASM function
|
||||
binding and returns the WASM pointer to that binding. If this
|
||||
instance is not in 'transient' mode, it will remember the
|
||||
binding for at least the next call, to avoid recreating the
|
||||
function binding unnecessarily.
|
||||
|
||||
If it's passed a pointer(ish) value for v, it does _not_
|
||||
perform any function binding, so this object's bindMode is
|
||||
irrelevant for such cases.
|
||||
|
||||
argIndex is the argv index of _this_ argument in the
|
||||
being-xWrap()'d call. argv is the current argument list
|
||||
undergoing xWrap() argument conversion. argv entries to the
|
||||
left of argIndex will have already undergone transformation and
|
||||
those to the right will not have (they will have the values the
|
||||
client-level code passed in, awaiting conversion). The RHS
|
||||
indexes must never be relied upon for anything because their
|
||||
types are indeterminate, whereas the LHS values will be
|
||||
WASM-compatible values by the time this is called.
|
||||
*/
|
||||
convertArg: function(v,argIndex,argv){
|
||||
//console.warn("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
|
||||
@@ -1569,6 +1624,7 @@ self.WhWasmUtilInstaller = function(target){
|
||||
if(v instanceof Function){
|
||||
const fp = __installFunction(v, this.signature, this.isTransient);
|
||||
if(pair){
|
||||
/* Replace existing stashed mapping */
|
||||
if(pair[1]){
|
||||
try{target.uninstallFunction(pair[1])}
|
||||
catch(e){/*ignored*/}
|
||||
@@ -1578,7 +1634,9 @@ self.WhWasmUtilInstaller = function(target){
|
||||
}
|
||||
return fp;
|
||||
}else if(target.isPtr(v) || null===v || undefined===v){
|
||||
if(pair && pair[1]){
|
||||
if(pair && pair[1] && pair[1]!==v){
|
||||
/* uninstall stashed mapping and replace stashed mapping with v. */
|
||||
//console.warn("FuncPtrAdapter is uninstalling function", this.contextKey(argIndex,argv),v);
|
||||
try{target.uninstallFunction(pair[1])}
|
||||
catch(e){/*ignored*/}
|
||||
pair[0] = pair[1] = (v || 0);
|
||||
@@ -1586,11 +1644,12 @@ self.WhWasmUtilInstaller = function(target){
|
||||
return v || 0;
|
||||
}else{
|
||||
throw new TypeError("Invalid FuncPtrAdapter argument type. "+
|
||||
"Expecting "+(this.name ? this.name+' ' : '')+
|
||||
"Expecting a function pointer or a "+
|
||||
(this.name ? this.name+' ' : '')+
|
||||
"function matching signature "+
|
||||
this.signature+".");
|
||||
}
|
||||
}
|
||||
}/*convertArg()*/
|
||||
}/*FuncPtrAdapter.prototype*/;
|
||||
|
||||
const __xArgAdapterCheck =
|
||||
@@ -1778,6 +1837,21 @@ self.WhWasmUtilInstaller = function(target){
|
||||
if(args.length!==xf.length) __argcMismatch(fname, xf.length);
|
||||
const scope = target.scopedAllocPush();
|
||||
try{
|
||||
/*
|
||||
Maintenance reminder re. arguments passed to convertArgs():
|
||||
The public interface of argument adapters is that they take
|
||||
ONE argument and return a (possibly) converted result for
|
||||
it. The passing-on of arguments after the first is an
|
||||
internal impl. detail for the sake of FuncPtrAdapter, and
|
||||
not to be relied on or documented for other cases. The fact
|
||||
that this is how FuncPtrAdapter.convertArgs() gets its 2nd+
|
||||
arguments, and how FuncPtrAdapter.contextKey() gets its
|
||||
args, is also an implementation detail and subject to
|
||||
change. i.e. the public interface of 1 argument is stable.
|
||||
The fact that any arguments may be passed in after that one,
|
||||
and what those arguments are, is _not_ part of the public
|
||||
interface and is _not_ stable.
|
||||
*/
|
||||
for(const i in args) args[i] = cxw.convertArg(argTypes[i], args[i], i, args);
|
||||
return cxw.convertResult(resultType, xf.apply(null,args));
|
||||
}finally{
|
||||
|
||||
@@ -472,14 +472,15 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
m = w.allocFromTypedArray(u);
|
||||
for(let i = 0; i < u.length; ++i){
|
||||
T.assert(u[i] === byteList[i])
|
||||
.assert(u[i] === w.peek(m + i, 'i8'));
|
||||
.assert(u[i] === w.peek8(m + i));
|
||||
}
|
||||
w.dealloc(m);
|
||||
m = w.allocFromTypedArray(u.buffer);
|
||||
for(let i = 0; i < u.length; ++i){
|
||||
T.assert(u[i] === byteList[i])
|
||||
.assert(u[i] === w.peek(m + i, 'i8'));
|
||||
.assert(u[i] === w.peek8(m + i));
|
||||
}
|
||||
|
||||
w.dealloc(m);
|
||||
T.mustThrowMatching(
|
||||
()=>w.allocFromTypedArray(1),
|
||||
@@ -487,6 +488,25 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
);
|
||||
}
|
||||
|
||||
{ // Test peekXYZ()/pokeXYZ()...
|
||||
const m = w.alloc(8);
|
||||
T.assert( 17 === w.poke8(m,17).peek8(m) )
|
||||
.assert( 31987 === w.poke16(m,31987).peek16(m) )
|
||||
.assert( 345678 === w.poke32(m,345678).peek32(m) )
|
||||
.assert(
|
||||
T.eqApprox( 345678.9, w.pokeF32(m,345678.9).peekF32(m) )
|
||||
).assert(
|
||||
T.eqApprox( 4567890123.4, w.pokeF64(m, 4567890123.4).peekF64(m) )
|
||||
);
|
||||
if(w.bigIntEnabled){
|
||||
T.assert(
|
||||
BigInt(Number.MAX_SAFE_INTEGER) ===
|
||||
w.poke64(m, Number.MAX_SAFE_INTEGER).peek64(m)
|
||||
);
|
||||
}
|
||||
w.dealloc(m);
|
||||
}
|
||||
|
||||
// isPtr32()
|
||||
{
|
||||
const ip = w.isPtr32;
|
||||
@@ -557,14 +577,14 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
let rc = w.cstrncpy(cpy, cStr, n+10);
|
||||
T.assert(n+1 === rc).
|
||||
assert("hello" === w.cstrToJs(cpy)).
|
||||
assert(chr('o') === w.peek(cpy+n-1)).
|
||||
assert(0 === w.peek(cpy+n));
|
||||
assert(chr('o') === w.peek8(cpy+n-1)).
|
||||
assert(0 === w.peek8(cpy+n));
|
||||
let cStr2 = w.scopedAllocCString("HI!!!");
|
||||
rc = w.cstrncpy(cpy, cStr2, 3);
|
||||
T.assert(3===rc).
|
||||
assert("HI!lo" === w.cstrToJs(cpy)).
|
||||
assert(chr('!') === w.peek(cpy+2)).
|
||||
assert(chr('l') === w.peek(cpy+3));
|
||||
assert(chr('!') === w.peek8(cpy+2)).
|
||||
assert(chr('l') === w.peek8(cpy+3));
|
||||
}finally{
|
||||
w.scopedAllocPop(scope);
|
||||
}
|
||||
@@ -587,8 +607,8 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
const jstr = "hällo, world!";
|
||||
const [cstr, n] = w.allocCString(jstr, true);
|
||||
T.assert(14 === n)
|
||||
.assert(0===w.peek(cstr+n))
|
||||
.assert(chr('!')===w.peek(cstr+n-1));
|
||||
.assert(0===w.peek8(cstr+n))
|
||||
.assert(chr('!')===w.peek8(cstr+n-1));
|
||||
w.dealloc(cstr);
|
||||
}
|
||||
|
||||
@@ -624,8 +644,8 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
|
||||
const [z1, z2, z3] = w.scopedAllocPtr(3);
|
||||
T.assert('number'===typeof z1).assert(z2>z1).assert(z3>z2)
|
||||
.assert(0===w.peek(z1,'i32'), 'allocPtr() must zero the targets')
|
||||
.assert(0===w.peek(z3,'i32'));
|
||||
.assert(0===w.peek32(z1), 'allocPtr() must zero the targets')
|
||||
.assert(0===w.peek32(z3));
|
||||
}finally{
|
||||
// Pop them in "incorrect" order to make sure they behave:
|
||||
w.scopedAllocPop(asc);
|
||||
@@ -645,8 +665,8 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert(1===w.scopedAlloc.level);
|
||||
const [cstr, n] = w.scopedAllocCString("hello, world", true);
|
||||
T.assert(12 === n)
|
||||
.assert(0===w.peek(cstr+n))
|
||||
.assert(chr('d')===w.peek(cstr+n-1));
|
||||
.assert(0===w.peek8(cstr+n))
|
||||
.assert(chr('d')===w.peek8(cstr+n-1));
|
||||
});
|
||||
}/*scopedAlloc()*/
|
||||
|
||||
@@ -710,7 +730,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
const pI1 = w.scopedAlloc(8), pI2 = pI1+4;
|
||||
w.pokePtr([pI1, pI2], 0);
|
||||
const f = w.xWrap('sqlite3_wasm_test_int64_minmax',undefined,['i64*','i64*']);
|
||||
const [r1, r2] = w.peek([pI1, pI2], 'i64');
|
||||
const [r1, r2] = w.peek64([pI1, pI2]);
|
||||
T.assert(!Number.isSafeInteger(r1)).assert(!Number.isSafeInteger(r2));
|
||||
});
|
||||
}
|
||||
@@ -954,8 +974,8 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
try{
|
||||
const n = 520;
|
||||
const p = wasm.pstack.alloc(n);
|
||||
T.assert(0===wasm.peek(p))
|
||||
.assert(0===wasm.peek(p+n-1));
|
||||
T.assert(0===wasm.peek8(p))
|
||||
.assert(0===wasm.peek8(p+n-1));
|
||||
T.assert(undefined === capi.sqlite3_randomness(n - 10, p));
|
||||
let j, check = 0;
|
||||
const heap = wasm.heap8u();
|
||||
@@ -1076,7 +1096,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
rc = capi.sqlite3_db_status(this.db, capi.SQLITE_DBSTATUS_LOOKASIDE_USED,
|
||||
pCur, pHi, 0);
|
||||
T.assert(0===rc);
|
||||
if(wasm.peek(pCur, 'i32')){
|
||||
if(wasm.peek32(pCur)){
|
||||
warn("Cannot test db_config(SQLITE_DBCONFIG_LOOKASIDE)",
|
||||
"while lookaside memory is in use.");
|
||||
}else{
|
||||
@@ -1084,21 +1104,21 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
0, 4096, 12);
|
||||
T.assert(0 === rc);
|
||||
}
|
||||
wasm.poke([pCur, pHi], 0, 'i32');
|
||||
let [vCur, vHi] = wasm.peek([pCur, pHi], 'i32');
|
||||
wasm.poke32([pCur, pHi], 0);
|
||||
let [vCur, vHi] = wasm.peek32(pCur, pHi);
|
||||
T.assert(0===vCur).assert(0===vHi);
|
||||
rc = capi.sqlite3_status(capi.SQLITE_STATUS_MEMORY_USED,
|
||||
pCur, pHi, 0);
|
||||
[vCur, vHi] = wasm.peek([pCur, pHi], 'i32');
|
||||
[vCur, vHi] = wasm.peek32(pCur, pHi);
|
||||
//console.warn("i32 vCur,vHi",vCur,vHi);
|
||||
T.assert(0 === rc).assert(vCur > 0).assert(vHi >= vCur);
|
||||
if(wasm.bigIntEnabled){
|
||||
// Again in 64-bit. Recall that pCur and pHi are allocated
|
||||
// large enough to account for this re-use.
|
||||
wasm.poke([pCur, pHi], 0, 'i64');
|
||||
wasm.poke64([pCur, pHi], 0);
|
||||
rc = capi.sqlite3_status64(capi.SQLITE_STATUS_MEMORY_USED,
|
||||
pCur, pHi, 0);
|
||||
[vCur, vHi] = wasm.peek([pCur, pHi], 'i64');
|
||||
[vCur, vHi] = wasm.peek64([pCur, pHi]);
|
||||
//console.warn("i64 vCur,vHi",vCur,vHi);
|
||||
T.assert(0 === rc).assert(vCur > 0).assert(vHi >= vCur);
|
||||
}
|
||||
@@ -1246,9 +1266,19 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
rowMode: 'object',
|
||||
resultRows: list,
|
||||
columnNames: colNames,
|
||||
_myState: 3 /* Accessible from the callback */,
|
||||
callback: function(row,stmt){
|
||||
++counter;
|
||||
T.assert((row.a%2 && row.a<6) || 'blob'===row.a);
|
||||
T.assert(
|
||||
3 === this._myState
|
||||
/* Recall that "this" is the options object. */
|
||||
).assert(
|
||||
this.columnNames[0]==='a' && this.columnNames[1]==='b'
|
||||
/* options.columnNames is filled out before the first
|
||||
Stmt.step(). */
|
||||
).assert(
|
||||
(row.a%2 && row.a<6) || 'blob'===row.a
|
||||
);
|
||||
}
|
||||
});
|
||||
T.assert(2 === colNames.length)
|
||||
@@ -1316,9 +1346,9 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert(0===rc)
|
||||
.assert("INTEGER"===wasm.cstrToJs(wasm.peekPtr(pzDT)))
|
||||
.assert("BINARY"===wasm.cstrToJs(wasm.peekPtr(pzColl)))
|
||||
.assert(0===wasm.peek(pNotNull,'i32'))
|
||||
.assert(1===wasm.peek(pPK,'i32'))
|
||||
.assert(0===wasm.peek(pAuto,'i32'))
|
||||
.assert(0===wasm.peek32(pNotNull))
|
||||
.assert(1===wasm.peek32(pPK))
|
||||
.assert(0===wasm.peek32(pAuto))
|
||||
}finally{
|
||||
wasm.pstack.restore(stack);
|
||||
}
|
||||
@@ -1465,11 +1495,11 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
name: 'summer',
|
||||
xStep: (pCtx, n)=>{
|
||||
const ac = sjac(pCtx, 4);
|
||||
wasm.poke(ac, wasm.peek(ac,'i32') + Number(n), 'i32');
|
||||
wasm.poke32(ac, wasm.peek32(ac) + Number(n));
|
||||
},
|
||||
xFinal: (pCtx)=>{
|
||||
const ac = sjac(pCtx, 0);
|
||||
return ac ? wasm.peek(ac,'i32') : 0;
|
||||
return ac ? wasm.peek32(ac) : 0;
|
||||
}
|
||||
});
|
||||
let v = db.selectValue([
|
||||
@@ -1488,13 +1518,13 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
arity: -1,
|
||||
xStep: (pCtx, ...args)=>{
|
||||
const ac = sjac(pCtx, 4);
|
||||
let sum = wasm.peek(ac, 'i32');
|
||||
let sum = wasm.peek32(ac);
|
||||
for(const v of args) sum += Number(v);
|
||||
wasm.poke(ac, sum, 'i32');
|
||||
wasm.poke32(ac, sum);
|
||||
},
|
||||
xFinal: (pCtx)=>{
|
||||
const ac = sjac(pCtx, 0);
|
||||
capi.sqlite3_result_int( pCtx, ac ? wasm.peek(ac,'i32') : 0 );
|
||||
capi.sqlite3_result_int( pCtx, ac ? wasm.peek32(ac) : 0 );
|
||||
// xFinal() may either return its value directly or call
|
||||
// sqlite3_result_xyz() and return undefined. Both are
|
||||
// functionally equivalent.
|
||||
@@ -1536,11 +1566,11 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
name: 'summer64',
|
||||
xStep: (pCtx, n)=>{
|
||||
const ac = sjac(pCtx, 8);
|
||||
wasm.poke(ac, wasm.peek(ac,'i64') + BigInt(n), 'i64');
|
||||
wasm.poke64(ac, wasm.peek64(ac) + BigInt(n));
|
||||
},
|
||||
xFinal: (pCtx)=>{
|
||||
const ac = sjac(pCtx, 0);
|
||||
return ac ? wasm.peek(ac,'i64') : 0n;
|
||||
return ac ? wasm.peek64(ac) : 0n;
|
||||
}
|
||||
});
|
||||
let v = db.selectValue([
|
||||
@@ -1563,11 +1593,11 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
const sjac = (cx,n=4)=>capi.sqlite3_js_aggregate_context(cx,n);
|
||||
const xValueFinal = (pCtx)=>{
|
||||
const ac = sjac(pCtx, 0);
|
||||
return ac ? wasm.peek(ac,'i32') : 0;
|
||||
return ac ? wasm.peek32(ac) : 0;
|
||||
};
|
||||
const xStepInverse = (pCtx, n)=>{
|
||||
const ac = sjac(pCtx);
|
||||
wasm.poke(ac, wasm.peek(ac,'i32') + Number(n), 'i32');
|
||||
wasm.poke32(ac, wasm.peek32(ac) + Number(n));
|
||||
};
|
||||
db.createFunction({
|
||||
name: 'winsumint',
|
||||
@@ -1686,35 +1716,26 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
const stack = w.scopedAllocPush();
|
||||
let ptrInt;
|
||||
const origValue = 512;
|
||||
const ptrValType = 'i32';
|
||||
try{
|
||||
ptrInt = w.scopedAlloc(4);
|
||||
w.poke(ptrInt,origValue, ptrValType);
|
||||
w.poke32(ptrInt,origValue);
|
||||
const cf = w.xGet('sqlite3_wasm_test_intptr');
|
||||
const oldPtrInt = ptrInt;
|
||||
//log('ptrInt',ptrInt);
|
||||
//log('peek(ptrInt)',w.peek(ptrInt));
|
||||
T.assert(origValue === w.peek(ptrInt, ptrValType));
|
||||
T.assert(origValue === w.peek32(ptrInt));
|
||||
const rc = cf(ptrInt);
|
||||
//log('cf(ptrInt)',rc);
|
||||
//log('ptrInt',ptrInt);
|
||||
//log('peek(ptrInt)',w.peek(ptrInt,ptrValType));
|
||||
T.assert(2*origValue === rc).
|
||||
assert(rc === w.peek(ptrInt,ptrValType)).
|
||||
assert(rc === w.peek32(ptrInt)).
|
||||
assert(oldPtrInt === ptrInt);
|
||||
const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/;
|
||||
const o64 = 0x010203040506/*>32-bit integer*/;
|
||||
const ptrType64 = 'i64';
|
||||
if(w.bigIntEnabled){
|
||||
w.poke(pi64, o64, ptrType64);
|
||||
w.poke64(pi64, o64);
|
||||
//log("pi64 =",pi64, "o64 = 0x",o64.toString(16), o64);
|
||||
const v64 = ()=>w.peek(pi64,ptrType64)
|
||||
//log("peek(pi64)",v64());
|
||||
const v64 = ()=>w.peek64(pi64)
|
||||
T.assert(v64() == o64);
|
||||
//T.assert(o64 === w.peek(pi64, ptrType64));
|
||||
//T.assert(o64 === w.peek64(pi64));
|
||||
const cf64w = w.xGet('sqlite3_wasm_test_int64ptr');
|
||||
cf64w(pi64);
|
||||
//log("peek(pi64)",v64());
|
||||
T.assert(v64() == BigInt(2 * o64));
|
||||
cf64w(pi64);
|
||||
T.assert(v64() == BigInt(4 * o64));
|
||||
@@ -1726,8 +1747,8 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
|
||||
const pMin = w.scopedAlloc(16);
|
||||
const pMax = pMin + 8;
|
||||
const g64 = (p)=>w.peek(p,ptrType64);
|
||||
w.poke([pMin, pMax], 0, ptrType64);
|
||||
const g64 = (p)=>w.peek64(p);
|
||||
w.poke64([pMin, pMax], 0);
|
||||
const minMaxI64 = [
|
||||
w.xCall('sqlite3_wasm_test_int64_min'),
|
||||
w.xCall('sqlite3_wasm_test_int64_max')
|
||||
@@ -1739,7 +1760,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch").
|
||||
assert(g64(pMax) === minMaxI64[1], "int64 mismatch");
|
||||
//log("pMin",g64(pMin), "pMax",g64(pMax));
|
||||
w.poke(pMin, minMaxI64[0], ptrType64);
|
||||
w.poke64(pMin, minMaxI64[0]);
|
||||
T.assert(g64(pMin) === minMaxI64[0]).
|
||||
assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))).
|
||||
assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax)));
|
||||
@@ -2125,8 +2146,10 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
})/*custom vtab #2*/
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t('Custom collation', function(sqlite3){
|
||||
let collationCounter = 0;
|
||||
let myCmp = function(pArg,n1,p1,n2,p2){
|
||||
//int (*)(void*,int,const void*,int,const void*)
|
||||
++collationCounter;
|
||||
const rc = wasm.exports.sqlite3_strnicmp(p1,p2,(n1<n2?n1:n2));
|
||||
return rc ? rc : (n1 - n2);
|
||||
};
|
||||
@@ -2134,18 +2157,51 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
0, myCmp, 0);
|
||||
this.db.checkRc(rc);
|
||||
rc = this.db.selectValue("select 'hi' = 'HI' collate mycollation");
|
||||
T.assert(1===rc);
|
||||
T.assert(1===rc).assert(1===collationCounter);
|
||||
rc = this.db.selectValue("select 'hii' = 'HI' collate mycollation");
|
||||
T.assert(0===rc);
|
||||
T.assert(0===rc).assert(2===collationCounter);
|
||||
rc = this.db.selectValue("select 'hi' = 'HIi' collate mycollation");
|
||||
T.assert(0===rc);
|
||||
T.assert(0===rc).assert(3===collationCounter);
|
||||
rc = capi.sqlite3_create_collation(this.db,"hi",capi.SQLITE_UTF8/*not enough args*/);
|
||||
T.assert(capi.SQLITE_MISUSE === rc);
|
||||
rc = capi.sqlite3_create_collation_v2(this.db,"hi",0/*wrong encoding*/,0,0,0);
|
||||
T.assert(capi.SQLITE_FORMAT === rc)
|
||||
.mustThrowMatching(()=>this.db.checkRc(rc),
|
||||
/SQLITE_UTF8 is the only supported encoding./);
|
||||
})
|
||||
/*
|
||||
We need to ensure that replacing that collation function does
|
||||
the right thing. We don't have a handle to the underlying WASM
|
||||
pointer from here, so cannot verify (without digging through
|
||||
internal state) that the old one gets uninstalled, but we can
|
||||
verify that a new one properly replaces it. (That said,
|
||||
console.warn() output has shown that the uninstallation does
|
||||
happen.)
|
||||
*/
|
||||
collationCounter = 0;
|
||||
myCmp = function(pArg,n1,p1,n2,p2){
|
||||
--collationCounter;
|
||||
return 0;
|
||||
};
|
||||
rc = capi.sqlite3_create_collation_v2(this.db, "MYCOLLATION", capi.SQLITE_UTF8,
|
||||
0, myCmp, 0);
|
||||
this.db.checkRc(rc);
|
||||
rc = this.db.selectValue("select 'hi' = 'HI' collate mycollation");
|
||||
T.assert(rc>0).assert(-1===collationCounter);
|
||||
rc = this.db.selectValue("select 'a' = 'b' collate mycollation");
|
||||
T.assert(rc>0).assert(-2===collationCounter);
|
||||
rc = capi.sqlite3_create_collation_v2(this.db, "MYCOLLATION", capi.SQLITE_UTF8,
|
||||
0, null, 0);
|
||||
this.db.checkRc(rc);
|
||||
rc = 0;
|
||||
try {
|
||||
this.db.selectValue("select 'a' = 'b' collate mycollation");
|
||||
}catch(e){
|
||||
/* Why is e.resultCode not automatically an extended result
|
||||
code? The DB() class enables those automatically. */
|
||||
rc = sqlite3.capi.sqlite3_extended_errcode(this.db);
|
||||
}
|
||||
T.assert(capi.SQLITE_ERROR_MISSING_COLLSEQ === rc);
|
||||
})/*custom collation*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t('Close db', function(){
|
||||
|
||||
48
manifest
48
manifest
@@ -1,5 +1,5 @@
|
||||
C Merge\srecent\strunk\schanges\sinto\sthe\scoroutine-exp2\sbranch.
|
||||
D 2022-12-13T00:51:34.741
|
||||
C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sthe\scoroutines-exp2\senhancement\nbranch.
|
||||
D 2022-12-14T17:52:15.943
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -472,7 +472,7 @@ F ext/session/sessionG.test 3828b944cd1285f4379340fd36f8b64c464fc84df6ff3ccbc955
|
||||
F ext/session/sessionH.test b17afdbd3b8f17e9bab91e235acf167cf35485db2ab2df0ea8893fbb914741a4
|
||||
F ext/session/session_common.tcl f613174665456b2d916ae8df3e5735092a1c1712f36f46840172e9a01e8cc53e
|
||||
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
|
||||
F ext/session/sessionat.test 52993535f1230a42f70886643574ba7ae60ef854f8add9c8e3fcc3eb5c564bd2
|
||||
F ext/session/sessionat.test 46fd847f6ed194ebb7ebef9fe68b2e2ec88d9c2383a6846cddc5604b35f1d4ae
|
||||
F ext/session/sessionbig.test 890ade19e3f80f3d3a3e83821ff79c5e2af906a67ecb5450879f0015cadf101e
|
||||
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
|
||||
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
||||
@@ -484,9 +484,9 @@ F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2
|
||||
F ext/session/sessionsize.test 6f644aff31c7f1e4871e9ff3542766e18da68fc7e587b83a347ea9820a002dd8
|
||||
F ext/session/sessionstat1.test 218d351cf9fcd6648f125a26b607b140310160184723c2666091b54450a68fb5
|
||||
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
|
||||
F ext/session/sqlite3session.c 1d019c5caf51936ef24c761db63552b06e0e0d951c8740bba9639b17fa0cb107
|
||||
F ext/session/sqlite3session.c b4254dd6e785cdd206c9ca7118796cf82273627fe2d4fd647597f08c2f821f96
|
||||
F ext/session/sqlite3session.h 0907de79bc13a2e3af30a6dc29acc60792a3eaf7d33d44cf52500d0f3c2b2171
|
||||
F ext/session/test_session.c f433f68a8a8c64b0f5bc74dc725078f12483301ad4ae8375205eef790274a787
|
||||
F ext/session/test_session.c 94364b91cf4571d320ef5b1e04075d2c58c79b63afdf20c9e470555a691ca5b1
|
||||
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
||||
@@ -503,13 +503,13 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08
|
||||
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
|
||||
F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
|
||||
F ext/wasm/api/sqlite3-api-glue.js 1ff6deb11bd192c13cbd247e333a4739e2303aad3d1a9dc68defced2d7393375
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 6d10849609231ccd46fa11b1d3fbbe0f45d9fe84c66a0b054601036540844300
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 39fbca8f25219c218d631433828ede53be8d518aa9f0da480758a3ea8abc1be8
|
||||
F ext/wasm/api/sqlite3-api-glue.js a8010bf3fa184886d1e9e76b0d0d1e290b5fa25130510644a59e91a18f779ef4
|
||||
F ext/wasm/api/sqlite3-api-oo1.js c0c4ccc269cccee657ffd03f094da7e270e1367b7928926b3730d543555a12a6
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 86eb4488f2be85e68c23ebcfad0834c24b6075e1645c67890cc4163c462efac1
|
||||
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
|
||||
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 7795b84b66a7a8dedc791340709b310bb497c3c72a80bef364fa2a58e2ddae3f
|
||||
F ext/wasm/api/sqlite3-v-helper.js 6f6c3e390a72e08b0a5b16a0d567d7af3c04d172831853a29d72a6f1dd40ff24 w ext/wasm/api/sqlite3-vfs-helper.js
|
||||
F ext/wasm/api/sqlite3-v-helper.js 6f6c3e390a72e08b0a5b16a0d567d7af3c04d172831853a29d72a6f1dd40ff24
|
||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 66daf6fb6843bea615fe193109e1542efbeca24f560ee9da63375a910bb48115
|
||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||
F ext/wasm/api/sqlite3-wasm.c 0d3d021c7f32d4422872cb1af8a163cd4cc63c0be314eb5cf6e56931260213c8
|
||||
@@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07
|
||||
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
|
||||
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
|
||||
F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f
|
||||
F ext/wasm/common/whwasmutil.js 85bdcefc3ae4b550231da1f94b196e15458828d7652b1f61f86d20873ff915ed
|
||||
F ext/wasm/common/whwasmutil.js d8167cf3b02ad2376b86d81e728b7460b4ee7044c60fb547b24df650158cfc3b
|
||||
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
|
||||
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
|
||||
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
|
||||
@@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
||||
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
|
||||
F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9
|
||||
F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406
|
||||
F ext/wasm/tester1.c-pp.js ee609a41cc1aabc971a6514b5d1b155f5f15d092ee015f5d03a204880532e62d
|
||||
F ext/wasm/tester1.c-pp.js 6d692f46f1d8bb0615b547680c71892bc8fa82ef054fa2b55134a6ba6c511180
|
||||
F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
|
||||
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
||||
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
||||
@@ -587,7 +587,7 @@ F src/btmutex.c 6ffb0a22c19e2f9110be0964d0731d2ef1c67b5f7fabfbaeb7b9dabc4b7740ca
|
||||
F src/btree.c 2f794c217e52fdf4322bf37ee7778331b4d93aed2c00b5d67f914c0239a9edcc
|
||||
F src/btree.h 49da925329574798be3cbb745a49d069a9e67c99900d8a0d04b1e934d60394ea
|
||||
F src/btreeInt.h 88ad499c92b489afedbfefc3f067c4d15023ec021afe622db240dc9d2277cfa5
|
||||
F src/build.c d3e43e950e4e377c1d451a4862556792acdef1faba14a03f899d30d09731c48b
|
||||
F src/build.c 51b46b657d914877dc64d0f66e2d82021b6ff622f43287cebc105efee0165600
|
||||
F src/callback.c 4cd7225b26a97f7de5fee5ae10464bed5a78f2adefe19534cc2095b3a8ca484a
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 20507cc0b0a6c19cd882fcd0eaeda32ae6a4229fb4b024cfdf3183043d9b703d
|
||||
@@ -595,7 +595,7 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5
|
||||
F src/dbpage.c f1a87f4ebcf22284e0aaf0697862f4ccfc120dcd6db3d8dfa3b049b2580c01d8
|
||||
F src/dbstat.c a56a7ad1163a9888d46cd5820be2e65354fb1aa04ed6909f7c3e5831e0ee2c29
|
||||
F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e
|
||||
F src/expr.c d0ce060008452653e9c7bcdce8b1e3ff078fbbd5f9f80b530b3ab99bb70a8078
|
||||
F src/expr.c c788575fc6a101b6360bc44eb6f973b8aa25f9af984a43a43e376fbceb19cb6c
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002
|
||||
F src/func.c 7e86074afc4dc702691a29b7801f6dcc191db092b52e8bbe69dcd2f7be52194d
|
||||
@@ -645,12 +645,12 @@ F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 4ad0aa2b21a94b118f6f6f1c9baf8e932d14227e8a9a5022b86e661049681c3a
|
||||
F src/select.c 505b265ec68da42e40498b35d57c9937481c94065e91b151a27501cd3ab4d65e
|
||||
F src/shell.c.in 8d9dc02dd03f8fc93f3e3cdb17d8d16e8ddb985dddad213985c08186900a3ebb
|
||||
F src/sqlite.h.in e752f82b9d71f1d42b259b1900e4b1caf0965e844d756cd5cc91cc2cf45ed925
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
|
||||
F src/sqliteInt.h ef0c1af2d7b46ce89fe698ce5f1a26d19110780ac19ec1ef5a227f7e51e97de3
|
||||
F src/sqliteInt.h 58dac6ab1352193715dc7de71cf900cafce6c3064a7331dc8b2029a6165a2b92
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -717,7 +717,7 @@ F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 313f3154e2b85a447326f5dd15de8d31a4df6ab0c3579bd58f426ff634ec9050
|
||||
F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd
|
||||
F src/vdbe.c df9ff3fcaa3c60fa9b0bca340c3f3cfc63a51361901ca06fe18bdeadeb2a1a24
|
||||
F src/vdbe.c 705df5b079978ea98ae18db8ffcefbdf529f7465e9c8fa19b022530556fc68e1
|
||||
F src/vdbe.h 73b904a6b3bb27f308c6cc287a5751ebc7f1f89456be0ed068a12b92844c6e8c
|
||||
F src/vdbeInt.h 8651e4c4e04d1860d0bdcf330cb8294e3778a9d4222be30ce4c490d9220af783
|
||||
F src/vdbeapi.c df3f73a4d0a487f2068e3c84776cd6e3fba5ae80ff612659dcfda4307686420b
|
||||
@@ -739,7 +739,7 @@ F src/whereexpr.c 05295b44b54eea76d1ba766f0908928d0e20e990c249344c9521454d3d09c7
|
||||
F src/window.c 14836767adb26573b50f528eb37f8b1336f2c430ab38de7cead1e5c546bb4d8c
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
|
||||
F test/affinity3.test b5c19d504dec222c0dc66642673d23dce915d35737b68e74d9f237b80493eb53
|
||||
F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 7269d07ac879fce161cb26c8fabe65cba5715742fac8a1fccac570dcdaf28f00
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
@@ -1328,7 +1328,7 @@ F test/minmax.test fe638b55d77d2375531a8f549b338eafcd9adfbd2f72df37ed77d9b26ca0a
|
||||
F test/minmax2.test cf9311babb6f0518d04e42fd6a42c619531c4309a9dd790a2c4e9b3bc595e0de
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/minmax4.test 272ca395257f05937dc96441c9dde4bc9fbf116a8d4fa02baeb0d13d50e36c87
|
||||
F test/misc1.test 294c97185354030c4ce40e7141b72f7a589585f2a44b666825381eb3df98f07c
|
||||
F test/misc1.test 9955e70cab5e284d77e358cfa1f1dd43f5e4bc00a421018581b0fbd62206a6a1
|
||||
F test/misc2.test 71e746af479119386ac2ed7ab7d81d99970e75b49ffd3e8efffee100b4b5f350
|
||||
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
|
||||
F test/misc4.test 10cd6addb2fa9093df4751a1b92b50440175dd5468a6ec84d0386e78f087db0e
|
||||
@@ -1409,7 +1409,7 @@ F test/printf.test 390d0d7fcffc3c4ea3c1bb4cbb267444e32b33b048ae21895f23a291844fe
|
||||
F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224cce60
|
||||
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/pushdown.test c69f0970ea17e0afc674b89741f60c172cb6f761d81665fc71015f674f0f66ba
|
||||
F test/pushdown.test f270b8071c02efc218430e0d388c155e1962eaa1d3a3ab186dd38ad6d7e178a4
|
||||
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
|
||||
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
|
||||
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
|
||||
@@ -1782,7 +1782,7 @@ F test/tt3_vacuum.c 71b254cde1fc49d6c8c44efd54f4668f3e57d7b3a8f4601ade069f75a999
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unionall.test bfeeea6c18c09a46f7e8bed69173e31c71336152e6253fb37c7257c76509f0e4
|
||||
F test/unionall.test eb9afa030897af75fd2f0dd28354ef63c8a5897b6c76aa1f15acae61a12eabcf
|
||||
F test/unionall2.test 71e8fa08d5699d50dc9f9dc0c9799c2e7a6bb7931a330d369307a4df7f157fa1
|
||||
F test/unionallfault.test 652bfbb630e6c43135965dc1e8f0a9a791da83aec885d626a632fe1909c56f73
|
||||
F test/unionvtab.test e1704ab1b4c1bb3ffc9da4681f8e85a0b909fd80b937984fc94b27415ac8e5a4
|
||||
@@ -1819,7 +1819,7 @@ F test/vacuum6.test b137b04bf3392d3f5c3b8fda0ce85a6775a70ca112f6559f74ff52dc9ce0
|
||||
F test/vacuummem.test 4b30f5b95a9ff86e9d5c20741e50a898b2dc10b0962a3211571eb165357003fb
|
||||
F test/varint.test bbce22cda8fc4d135bcc2b589574be8410614e62
|
||||
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
||||
F test/view.test a5662e9c7425d77b1e1eb4b07fb891fa15f20317013eddcb6f6bee009f2fccc9
|
||||
F test/view.test a7c463254fd7ce86e861b60ddf489d5c14f22e8052f5ab2f5ff57d3eb17ccdea
|
||||
F test/view2.test db32c8138b5b556f610b35dfddd38c5a58a292f07fda5281eedb0851b2672679
|
||||
F test/view3.test ad8a8290ee2b55ff6ce66c9ef1ce3f1e47926273a3814e1c425293e128a95456
|
||||
F test/vt02.c 33ecddc0832d4cd24e9e9fa83d868981b1e049462f4ec9080710353f6479a534
|
||||
@@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 78723a9a7e72b42d28fc5645661da17f20cedcf864819b861800ad9340007be1 f0325359d5795237b79f90f21b42d7d942c7e918137cb0231d404365d3041e81
|
||||
R fab8476faef04e68db98cfe4c1810ebc
|
||||
P c43f433bcab29db0f1f8afd3948f5a4149e1f277c853c66f99c31f226d82bc0f 27655c9353620aa58105e87d1e171d1f0a637deedde41c081824078385cd49ac
|
||||
R a3c06794ccb982a7d2a817a1735839a4
|
||||
U drh
|
||||
Z 1279761382706bd160c45650b0901daf
|
||||
Z 37129f29a116af5663e258b7d55fecf7
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
@@ -1 +1 @@
|
||||
c43f433bcab29db0f1f8afd3948f5a4149e1f277c853c66f99c31f226d82bc0f
|
||||
ea278438b72e85d217e72c836cbefd68bd8b336baf33507b2d8d12ef436424cd
|
||||
@@ -3140,8 +3140,7 @@ static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
&& pTable->nCol==pSel->pEList->nExpr
|
||||
){
|
||||
assert( db->mallocFailed==0 );
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel,
|
||||
SQLITE_AFF_NONE);
|
||||
sqlite3SubqueryColumnTypes(pParse, pTable, pSel, SQLITE_AFF_NONE);
|
||||
}
|
||||
}else{
|
||||
/* CREATE VIEW name AS... without an argument list. Construct
|
||||
|
||||
139
src/expr.c
139
src/expr.c
@@ -44,48 +44,117 @@ char sqlite3TableColumnAffinity(const Table *pTab, int iCol){
|
||||
*/
|
||||
char sqlite3ExprAffinity(const Expr *pExpr){
|
||||
int op;
|
||||
while( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
|
||||
assert( pExpr->op==TK_COLLATE
|
||||
|| pExpr->op==TK_IF_NULL_ROW
|
||||
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );
|
||||
pExpr = pExpr->pLeft;
|
||||
assert( pExpr!=0 );
|
||||
}
|
||||
op = pExpr->op;
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
assert( pExpr->y.pTab!=0 );
|
||||
return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
|
||||
}
|
||||
if( op==TK_SELECT ){
|
||||
assert( ExprUseXSelect(pExpr) );
|
||||
assert( pExpr->x.pSelect!=0 );
|
||||
assert( pExpr->x.pSelect->pEList!=0 );
|
||||
assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 );
|
||||
return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
|
||||
}
|
||||
while( 1 /* exit-by-break */ ){
|
||||
if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
assert( pExpr->y.pTab!=0 );
|
||||
return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
|
||||
}
|
||||
if( op==TK_SELECT ){
|
||||
assert( ExprUseXSelect(pExpr) );
|
||||
assert( pExpr->x.pSelect!=0 );
|
||||
assert( pExpr->x.pSelect->pEList!=0 );
|
||||
assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 );
|
||||
return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_CAST
|
||||
if( op==TK_CAST ){
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
return sqlite3AffinityType(pExpr->u.zToken, 0);
|
||||
}
|
||||
if( op==TK_CAST ){
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
return sqlite3AffinityType(pExpr->u.zToken, 0);
|
||||
}
|
||||
#endif
|
||||
if( op==TK_SELECT_COLUMN ){
|
||||
assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) );
|
||||
assert( pExpr->iColumn < pExpr->iTable );
|
||||
assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr );
|
||||
return sqlite3ExprAffinity(
|
||||
pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
|
||||
);
|
||||
}
|
||||
if( op==TK_VECTOR ){
|
||||
assert( ExprUseXList(pExpr) );
|
||||
return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
|
||||
if( op==TK_SELECT_COLUMN ){
|
||||
assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) );
|
||||
assert( pExpr->iColumn < pExpr->iTable );
|
||||
assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr );
|
||||
return sqlite3ExprAffinity(
|
||||
pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
|
||||
);
|
||||
}
|
||||
if( op==TK_VECTOR ){
|
||||
assert( ExprUseXList(pExpr) );
|
||||
return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
|
||||
assert( pExpr->op==TK_COLLATE
|
||||
|| pExpr->op==TK_IF_NULL_ROW
|
||||
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );
|
||||
pExpr = pExpr->pLeft;
|
||||
op = pExpr->op;
|
||||
continue;
|
||||
}
|
||||
if( op!=TK_REGISTER || (op = pExpr->op2)==TK_REGISTER ) break;
|
||||
}
|
||||
return pExpr->affExpr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make a guess at all the possible datatypes of the result that could
|
||||
** be returned by an expression. Return a bitmask indicating the answer:
|
||||
**
|
||||
** 0x01 Numeric
|
||||
** 0x02 Text
|
||||
** 0x04 Blob
|
||||
**
|
||||
** If the expression must return NULL, then 0x00 is returned.
|
||||
*/
|
||||
int sqlite3ExprDataType(const Expr *pExpr){
|
||||
while( pExpr ){
|
||||
switch( pExpr->op ){
|
||||
case TK_COLLATE:
|
||||
case TK_IF_NULL_ROW:
|
||||
case TK_UPLUS: {
|
||||
pExpr = pExpr->pLeft;
|
||||
break;
|
||||
}
|
||||
case TK_NULL: {
|
||||
return 0x00;
|
||||
}
|
||||
case TK_STRING: {
|
||||
return 0x02;
|
||||
}
|
||||
case TK_BLOB: {
|
||||
return 0x04;
|
||||
}
|
||||
case TK_CONCAT: {
|
||||
return 0x06;
|
||||
}
|
||||
case TK_VARIABLE:
|
||||
case TK_AGG_FUNCTION:
|
||||
case TK_FUNCTION: {
|
||||
return 0x07;
|
||||
}
|
||||
case TK_COLUMN:
|
||||
case TK_AGG_COLUMN:
|
||||
case TK_SELECT:
|
||||
case TK_CAST:
|
||||
case TK_SELECT_COLUMN:
|
||||
case TK_VECTOR: {
|
||||
int aff = sqlite3ExprAffinity(pExpr);
|
||||
if( aff>=SQLITE_AFF_NUMERIC ) return 0x05;
|
||||
if( aff==SQLITE_AFF_TEXT ) return 0x06;
|
||||
return 0x07;
|
||||
}
|
||||
case TK_CASE: {
|
||||
int res = 0;
|
||||
int ii;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
assert( ExprUseXList(pExpr) && pList!=0 );
|
||||
assert( pList->nExpr > 0);
|
||||
for(ii=1; ii<pList->nExpr; ii+=2){
|
||||
res |= sqlite3ExprDataType(pList->a[ii].pExpr);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
default: {
|
||||
return 0x01;
|
||||
}
|
||||
} /* End of switch(op) */
|
||||
} /* End of while(pExpr) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the collating sequence for expression pExpr to be the collating
|
||||
** sequence named by pToken. Return a pointer to a new Expr node that
|
||||
|
||||
96
src/select.c
96
src/select.c
@@ -1298,9 +1298,6 @@ static void selectInnerLoop(
|
||||
testcase( eDest==SRT_Fifo );
|
||||
testcase( eDest==SRT_DistFifo );
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
|
||||
if( pDest->zAffSdst ){
|
||||
sqlite3VdbeChangeP4(v, -1, pDest->zAffSdst, nResultCol);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
if( eDest==SRT_DistFifo ){
|
||||
/* If the destination is DistFifo, then cursor (iParm+1) is open
|
||||
@@ -2291,39 +2288,38 @@ int sqlite3ColumnsFromExprList(
|
||||
|
||||
/*
|
||||
** This bit, when added to the "aff" parameter of
|
||||
** sqlite3SelectAddColumnTypeAndCollation() means that result set
|
||||
** sqlite3ColumnTypeOfSubquery() means that result set
|
||||
** expressions of the form "CAST(expr AS NUMERIC)" should result in
|
||||
** NONE affinity rather than NUMERIC affinity.
|
||||
*/
|
||||
#define SQLITE_AFF_FLAG1 0x10
|
||||
|
||||
/*
|
||||
** Add type and collation information to a column list based on
|
||||
** a SELECT statement.
|
||||
**
|
||||
** The column list presumably came from selectColumnNamesFromExprList().
|
||||
** The column list has only names, not types or collations. This
|
||||
** routine goes through and adds the types and collations.
|
||||
** pTab is a transient Table object that represents a subquery of some
|
||||
** kind (maybe a parenthesized subquery in the FROM clause of a larger
|
||||
** query, or a VIEW, or a CTE). This routine computes type information
|
||||
** for that Table object based on the Select object that implements the
|
||||
** subquery. For the purposes of this routine, "type infomation" means:
|
||||
**
|
||||
** This routine requires that all identifiers in the SELECT
|
||||
** statement be resolved.
|
||||
** * The datatype name, as it might appear in a CREATE TABLE statement
|
||||
** * Which collating sequence to use for the column
|
||||
** * The affinity of the column
|
||||
**
|
||||
** The SQLITE_AFF_FLAG1 bit added to parameter aff means that a
|
||||
** result set column of the form "CAST(expr AS NUMERIC)" should use
|
||||
** NONE affinity rather than NUMERIC affinity. See the
|
||||
** 2022-12-10 "reopen" of ticket https://sqlite.org/src/tktview/57c47526c3.
|
||||
*/
|
||||
void sqlite3SelectAddColumnTypeAndCollation(
|
||||
Parse *pParse, /* Parsing contexts */
|
||||
Table *pTab, /* Add column type information to this table */
|
||||
Select *pSelect, /* SELECT used to determine types and collations */
|
||||
char aff /* Default affinity. Maybe with SQLITE_AFF_FLAG1 too */
|
||||
void sqlite3SubqueryColumnTypes(
|
||||
Parse *pParse, /* Parsing contexts */
|
||||
Table *pTab, /* Add column type information to this table */
|
||||
Select *pSelect, /* SELECT used to determine types and collations */
|
||||
char aff /* Default affinity. Maybe with SQLITE_AFF_FLAG1 too */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
NameContext sNC;
|
||||
Column *pCol;
|
||||
CollSeq *pColl;
|
||||
int i;
|
||||
int i,j;
|
||||
Expr *p;
|
||||
struct ExprList_item *a;
|
||||
|
||||
@@ -2332,22 +2328,42 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
||||
assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
|
||||
if( db->mallocFailed ) return;
|
||||
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pSrcList = pSelect->pSrc;
|
||||
a = pSelect->pEList->a;
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
const char *zType;
|
||||
i64 n, m;
|
||||
pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
|
||||
p = a[i].pExpr;
|
||||
zType = columnType(&sNC, p, 0, 0, 0);
|
||||
/* pCol->szEst = ... // Column size est for SELECT tables never used */
|
||||
pCol->affinity = sqlite3ExprAffinity(p);
|
||||
if( pCol->affinity==SQLITE_AFF_NUMERIC
|
||||
&& p->op==TK_CAST
|
||||
&& (aff & SQLITE_AFF_FLAG1)!=0
|
||||
){
|
||||
pCol->affinity = SQLITE_AFF_NONE;
|
||||
if( pCol->affinity<=SQLITE_AFF_NONE ){
|
||||
assert( (SQLITE_AFF_FLAG1 & SQLITE_AFF_MASK)==0 );
|
||||
pCol->affinity = aff & SQLITE_AFF_MASK;
|
||||
}
|
||||
if( aff & SQLITE_AFF_FLAG1 ){
|
||||
if( pCol->affinity==SQLITE_AFF_NUMERIC && p->op==TK_CAST ){
|
||||
pCol->affinity = SQLITE_AFF_NONE;
|
||||
}
|
||||
}
|
||||
if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){
|
||||
int m = 0;
|
||||
Select *pS2;
|
||||
for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){
|
||||
m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
|
||||
}
|
||||
if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){
|
||||
pCol->affinity = SQLITE_AFF_BLOB;
|
||||
}else
|
||||
if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){
|
||||
pCol->affinity = SQLITE_AFF_BLOB;
|
||||
}
|
||||
}
|
||||
zType = 0;
|
||||
for(j=0; j<SQLITE_N_STDTYPE; j++){
|
||||
if( sqlite3StdTypeAffinity[j]==pCol->affinity ){
|
||||
zType = sqlite3StdType[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( zType ){
|
||||
m = sqlite3Strlen30(zType);
|
||||
@@ -2361,10 +2377,6 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
||||
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
|
||||
}
|
||||
}
|
||||
if( pCol->affinity<=SQLITE_AFF_NONE ){
|
||||
assert( (SQLITE_AFF_FLAG1 & SQLITE_AFF_MASK)==0 );
|
||||
pCol->affinity = aff & SQLITE_AFF_MASK;
|
||||
}
|
||||
pColl = sqlite3ExprCollSeq(pParse, p);
|
||||
if( pColl ){
|
||||
assert( pTab->pIndex==0 );
|
||||
@@ -2398,7 +2410,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, char aff){
|
||||
pTab->zName = 0;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff);
|
||||
sqlite3SubqueryColumnTypes(pParse, pTab, pSelect, aff);
|
||||
pTab->iPKey = -1;
|
||||
if( db->mallocFailed ){
|
||||
sqlite3DeleteTable(db, pTab);
|
||||
@@ -6209,14 +6221,14 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
|
||||
** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo()
|
||||
** interface.
|
||||
**
|
||||
** For each FROM-clause subquery, add Column.zType and Column.zColl
|
||||
** information to the Table structure that represents the result set
|
||||
** of that subquery.
|
||||
** For each FROM-clause subquery, add Column.zType, Column.zColl, and
|
||||
** Column.affinity information to the Table structure that represents
|
||||
** the result set of that subquery.
|
||||
**
|
||||
** The Table structure that represents the result set was constructed
|
||||
** by selectExpander() but the type and collation information was omitted
|
||||
** at that point because identifiers had not yet been resolved. This
|
||||
** routine is called after identifier resolution.
|
||||
** by selectExpander() but the type and collation and affinity information
|
||||
** was omitted at that point because identifiers had not yet been resolved.
|
||||
** This routine is called after identifier resolution.
|
||||
*/
|
||||
static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
|
||||
Parse *pParse;
|
||||
@@ -6236,9 +6248,8 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
|
||||
/* A sub-query in the FROM clause of a SELECT */
|
||||
Select *pSel = pFrom->pSelect;
|
||||
if( pSel ){
|
||||
while( pSel->pPrior ) pSel = pSel->pPrior;
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel,
|
||||
SQLITE_AFF_NONE|SQLITE_AFF_FLAG1);
|
||||
sqlite3SubqueryColumnTypes(pParse, pTab, pSel,
|
||||
SQLITE_AFF_NONE|SQLITE_AFF_FLAG1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7391,10 +7402,7 @@ int sqlite3Select(
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
|
||||
ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem));
|
||||
dest.zAffSdst = sqlite3TableAffinityStr(db, pItem->pTab);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
sqlite3DbFree(db, dest.zAffSdst);
|
||||
dest.zAffSdst = 0;
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1);
|
||||
|
||||
@@ -3575,7 +3575,7 @@ struct SelectDest {
|
||||
int iSDParm2; /* A second parameter for the eDest disposal method */
|
||||
int iSdst; /* Base register where results are written */
|
||||
int nSdst; /* Number of registers allocated */
|
||||
char *zAffSdst; /* Affinity used for SRT_Set, SRT_Table, and similar */
|
||||
char *zAffSdst; /* Affinity used for SRT_Set */
|
||||
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
|
||||
};
|
||||
|
||||
@@ -4680,7 +4680,7 @@ const char *sqlite3ColumnColl(Column*);
|
||||
void sqlite3DeleteColumnNames(sqlite3*,Table*);
|
||||
void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect);
|
||||
int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
|
||||
void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char);
|
||||
void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char);
|
||||
Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
|
||||
void sqlite3OpenSchemaTable(Parse *, int);
|
||||
Index *sqlite3PrimaryKeyIndex(Table*);
|
||||
@@ -5051,6 +5051,7 @@ char sqlite3CompareAffinity(const Expr *pExpr, char aff2);
|
||||
int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity);
|
||||
char sqlite3TableColumnAffinity(const Table*,int);
|
||||
char sqlite3ExprAffinity(const Expr *pExpr);
|
||||
int sqlite3ExprDataType(const Expr *pExpr);
|
||||
int sqlite3Atoi64(const char*, i64*, int, u8);
|
||||
int sqlite3DecOrHexToI64(const char*, i64*);
|
||||
void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
|
||||
|
||||
@@ -2115,7 +2115,6 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
flags1 = pIn1->flags;
|
||||
flags3 = pIn3->flags;
|
||||
if( (flags1 & flags3 & MEM_Int)!=0 ){
|
||||
assert( (pOp->p5 & SQLITE_AFF_MASK)!=SQLITE_AFF_TEXT || CORRUPT_DB );
|
||||
/* Common case of comparison of two integers */
|
||||
if( pIn3->u.i > pIn1->u.i ){
|
||||
if( sqlite3aGTb[pOp->opcode] ){
|
||||
@@ -2183,7 +2182,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
applyNumericAffinity(pIn3,0);
|
||||
}
|
||||
}
|
||||
}else if( affinity==SQLITE_AFF_TEXT ){
|
||||
}else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
|
||||
if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
|
||||
testcase( pIn1->flags & MEM_Int );
|
||||
testcase( pIn1->flags & MEM_Real );
|
||||
|
||||
@@ -104,20 +104,20 @@ do_execsql_test affinity3-200 {
|
||||
CREATE TABLE mzed AS SELECT * FROM idmap;
|
||||
}
|
||||
|
||||
#do_execsql_test affinity3-210 {
|
||||
#PRAGMA automatic_index=ON;
|
||||
#SELECT * FROM data JOIN idmap USING(id);
|
||||
#} {1 abc a 4 xyz e}
|
||||
do_execsql_test affinity3-210 {
|
||||
PRAGMA automatic_index=ON;
|
||||
SELECT * FROM data JOIN idmap USING(id);
|
||||
} {4 xyz e}
|
||||
do_execsql_test affinity3-220 {
|
||||
SELECT * FROM data JOIN mzed USING(id);
|
||||
} {1 abc a 4 xyz e}
|
||||
} {4 xyz e}
|
||||
|
||||
do_execsql_test affinity3-250 {
|
||||
PRAGMA automatic_index=OFF;
|
||||
SELECT * FROM data JOIN idmap USING(id);
|
||||
} {1 abc a 4 xyz e}
|
||||
} {4 xyz e}
|
||||
do_execsql_test affinity3-260 {
|
||||
SELECT * FROM data JOIN mzed USING(id);
|
||||
} {1 abc a 4 xyz e}
|
||||
} {4 xyz e}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -605,14 +605,17 @@ do_execsql_test misc1-19.2 {
|
||||
SELECT * FROM t19b;
|
||||
} {4 5 6}
|
||||
|
||||
# 2015-05-20: CREATE TABLE AS should not store INT value is a TEXT
|
||||
# 2015-05-20: CREATE TABLE AS should not store INT value in a TEXT
|
||||
# column.
|
||||
#
|
||||
# 2022-12-14: Change: The column is not TEXT if the AS SELECT is
|
||||
# a compound with different types on each arm.
|
||||
#
|
||||
do_execsql_test misc1-19.3 {
|
||||
CREATE TABLE t19c(x TEXT);
|
||||
CREATE TABLE t19d AS SELECT * FROM t19c UNION ALL SELECT 1234;
|
||||
SELECT x, typeof(x) FROM t19d;
|
||||
} {1234 text}
|
||||
} {1234 integer}
|
||||
|
||||
# 2014-05-16: Tests for the SQLITE_TESTCTRL_FAULT_INSTALL feature.
|
||||
#
|
||||
@@ -622,11 +625,11 @@ proc fault_callback {n} {
|
||||
lappend ::fault_callbacks $n
|
||||
return 0
|
||||
}
|
||||
do_test misc1-19.1 {
|
||||
do_test misc1-19.11 {
|
||||
sqlite3_test_control_fault_install fault_callback
|
||||
set fault_callbacks
|
||||
} {0}
|
||||
do_test misc1-19.2 {
|
||||
do_test misc1-19.12 {
|
||||
sqlite3_test_control_fault_install
|
||||
set fault_callbacks
|
||||
} {0}
|
||||
|
||||
@@ -95,22 +95,32 @@ do_execsql_test 3.1 {
|
||||
CREATE TABLE t0(c0 INT);
|
||||
INSERT INTO t0 VALUES(0);
|
||||
CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
|
||||
INSERT INTO t1_a VALUES(1,'one'); --,(4,'four');
|
||||
INSERT INTO t1_a VALUES(1,'one');
|
||||
CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
|
||||
INSERT INTO t1_b VALUES(2,'two'); --,(5,'five');
|
||||
INSERT INTO t1_b VALUES(2,'two');
|
||||
CREATE VIEW v0 AS SELECT CAST(t0.c0 AS INTEGER) AS c0 FROM t0;
|
||||
CREATE VIEW t1 AS SELECT a, b FROM t1_a UNION ALL SELECT c, 0 FROM t1_b;
|
||||
SELECT t1.a, quote(t1.b), t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1;
|
||||
CREATE VIEW v1(a,b) AS SELECT a, b FROM t1_a UNION ALL SELECT c, 0 FROM t1_b;
|
||||
SELECT v1.a, quote(v1.b), t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,v1;
|
||||
} {
|
||||
1 'one' 0
|
||||
2 '0' 0
|
||||
2 0 0
|
||||
}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT a, quote(b), cd FROM (
|
||||
SELECT t1.a, t1.b, t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1
|
||||
SELECT v1.a, v1.b, t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0, v1
|
||||
) WHERE a=2 AND b='0' AND cd=0;
|
||||
} {}
|
||||
do_execsql_test 3.3 {
|
||||
SELECT a, quote(b), cd FROM (
|
||||
SELECT v1.a, v1.b, t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0, v1
|
||||
) WHERE a=1 AND b='one' AND cd=0;
|
||||
} {1 'one' 0}
|
||||
do_execsql_test 3.4 {
|
||||
SELECT a, quote(b), cd FROM (
|
||||
SELECT v1.a, v1.b, t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0, v1
|
||||
) WHERE a=2 AND b=0 AND cd=0;
|
||||
} {
|
||||
2 '0' 0
|
||||
2 0 0
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -408,37 +408,39 @@ do_execsql_test 8.1 {
|
||||
SELECT a, b FROM t1_a UNION ALL
|
||||
SELECT c, c FROM t1_b UNION ALL
|
||||
SELECT e, f FROM t1_c;
|
||||
}
|
||||
SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1;
|
||||
} {1 one 0 {} 4 four 0 {} 2 2 0 {} 5 5 0 {} 3 three 0 {} 6 six 0 {}}
|
||||
|
||||
optimization_control db all 1
|
||||
do_execsql_test 8.2 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b=2;
|
||||
} {2 2 0 {}}
|
||||
do_execsql_test 8.3 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b=2.0;
|
||||
} {}
|
||||
} {2 2 0 {}}
|
||||
do_execsql_test 8.4 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b='2';
|
||||
} {2 2 0 {}}
|
||||
} {}
|
||||
optimization_control db query-flattener,push-down 0
|
||||
do_execsql_test 8.5 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b=2;
|
||||
} {2 2 0 {}}
|
||||
do_execsql_test 8.6 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b=2.0;
|
||||
} {}
|
||||
} {2 2 0 {}}
|
||||
do_execsql_test 8.7 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b='2';
|
||||
} {2 2 0 {}}
|
||||
} {}
|
||||
optimization_control db all 0
|
||||
do_execsql_test 8.8 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b=2;
|
||||
} {2 2 0 {}}
|
||||
do_execsql_test 8.9 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b=2.0;
|
||||
} {}
|
||||
} {2 2 0 {}}
|
||||
do_execsql_test 8.10 {
|
||||
SELECT * FROM (SELECT t1.a, t1.b, t0.c0 AS c, v0.c0 AS d FROM t0 LEFT JOIN v0 ON v0.c0>'0',t1) WHERE b='2';
|
||||
} {2 2 0 {}}
|
||||
} {}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -123,16 +123,16 @@ do_execsql_test view-1.10 {
|
||||
} {}
|
||||
do_execsql_test view-1.11 {
|
||||
PRAGMA table_info(v9a);
|
||||
} {0 x INTEGER 0 {} 0}
|
||||
} {0 x INT 0 {} 0}
|
||||
do_execsql_test view-1.12 {
|
||||
PRAGMA table_info(v9b);
|
||||
} {0 x INTEGER 0 {} 0}
|
||||
} {0 x INT 0 {} 0}
|
||||
do_execsql_test view-1.13 {
|
||||
PRAGMA table_info(v9c);
|
||||
} {0 x INTEGER 0 {} 0}
|
||||
} {0 x INT 0 {} 0}
|
||||
do_execsql_test view-1.14 {
|
||||
PRAGMA table_info(v9d);
|
||||
} {0 x INTEGER 0 {} 0}
|
||||
} {0 x INT 0 {} 0}
|
||||
|
||||
do_test view-2.1 {
|
||||
execsql {
|
||||
@@ -778,6 +778,10 @@ do_catchsql_test view-29.1 {
|
||||
#-------------------------------------------------------------------------
|
||||
# 2022-12-11. https://sqlite.org/src/info/679ed6a2
|
||||
#
|
||||
# 2022-12-14 change: If the AS SELECT of a VIEW is a compound where
|
||||
# the datatypes on each arm of the compound are different, then the
|
||||
# datatype of the overall column is BLOB (ANY).
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test view-30.0 {
|
||||
CREATE TABLE t0(a INT, b TEXT);
|
||||
@@ -791,11 +795,10 @@ do_execsql_test view-30.0 {
|
||||
ifcapable schema_pragmas {
|
||||
do_execsql_test view-30.1 {
|
||||
PRAGMA table_info = t1;
|
||||
} { 0 a INT 0 {} 0 1 b TEXT 0 {} 0 }
|
||||
} { 0 a INT 0 {} 0 1 b BLOB 0 {} 0 }
|
||||
do_execsql_test view-30.2 {
|
||||
PRAGMA table_info = t2;
|
||||
} { 0 a INT 0 {} 0 1 b TEXT 0 {} 0 }
|
||||
} { 0 a INT 0 {} 0 1 b BLOB 0 {} 0 }
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
Reference in New Issue
Block a user