1
0
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:
drh
2022-12-14 17:52:15 +00:00
20 changed files with 696 additions and 293 deletions

View File

@@ -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 {} {} {} {} {}}
}]
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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*/

View File

@@ -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);

View File

@@ -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

View File

@@ -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{

View File

@@ -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(){

View File

@@ -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.

View File

@@ -1 +1 @@
c43f433bcab29db0f1f8afd3948f5a4149e1f277c853c66f99c31f226d82bc0f
ea278438b72e85d217e72c836cbefd68bd8b336baf33507b2d8d12ef436424cd

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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*,...);

View File

@@ -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 );

View File

@@ -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

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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