Fix a problem with vector range constraints involving the rowid column. And other issues.
FossilOrigin-Name: 3ef75d45ebcd8ede91596d69e55fe7d685008a60
This commit is contained in:
parent
870a0705fe
commit
553168c706
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sa\sproblem\swith\sIN(...)\sconstraints\swhere\sthe\sLHS\sis\sa\ssub-select\sthat\sis\san\saggregate\squery.
|
||||
D 2016-08-01T16:37:43.292
|
||||
C Fix\sa\sproblem\swith\svector\srange\sconstraints\sinvolving\sthe\srowid\scolumn.\sAnd\sother\sissues.
|
||||
D 2016-08-01T20:14:31.976
|
||||
F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233
|
||||
@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7
|
||||
F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39
|
||||
F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0
|
||||
F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f
|
||||
F src/expr.c cdde4d3ed7f7cf1911b961141d61a4f06e6f7ebd
|
||||
F src/expr.c 4db65a0c33003a00314fb56dca32d9cdbb6448a6
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413
|
||||
F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045
|
||||
@ -383,7 +383,7 @@ F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c bb2a1583fddbf8c2bbba2abbe411882b89e14a12
|
||||
F src/select.c 952aa1b4148ed4c0024586e9049b8742a765c6f9
|
||||
F src/shell.c 9351fc6de11e1d908648c0a92d85627138e3dee5
|
||||
F src/sqlite.h.in cd10e4206b91c4bf03f121ab9209a14af0b48f14
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
@ -463,9 +463,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
|
||||
F src/wal.h 6dd221ed384afdc204bc61e25c23ef7fd5a511f2
|
||||
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
|
||||
F src/where.c 61db3a409b6b89383abd0e746965da424676f8ea
|
||||
F src/where.c a1c2fde60e806f1e045fdf745bfa449e3be82f55
|
||||
F src/whereInt.h 14dd243e13b81cbb0a66063d38b70f93a7d6e613
|
||||
F src/wherecode.c 6131be0cb19702665c3decbf243dae58ecc15937
|
||||
F src/wherecode.c c01c8af9311b5d6d65de311101f72d94a11ae506
|
||||
F src/whereexpr.c 3f5d76b585ab193bb9ae15aadb8deb94346c93e7
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
@ -1018,7 +1018,7 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
|
||||
F test/rowvalue.test 5f00f33fb1aa9259b35b44261b651361580d5a34
|
||||
F test/rowvalue.test 56b34d31d91340a6e922e753b798880170cc1aa7
|
||||
F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff
|
||||
F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770
|
||||
F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be
|
||||
@ -1512,7 +1512,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 059d0d05354e6efab7892c97b339ffa0b5303587
|
||||
R b99b47b8f7c934c8cffa76149cc68eeb
|
||||
P 1f4dba87da4a44ad26223ad965731164c0d9bad9
|
||||
R 4b01df8319f11080e804e7c115ca8550
|
||||
U dan
|
||||
Z 9e6109253ab0bc1e8a84d8639e4d1848
|
||||
Z c86f923fed6f969ee44eaf1f0098c9a2
|
||||
|
@ -1 +1 @@
|
||||
1f4dba87da4a44ad26223ad965731164c0d9bad9
|
||||
3ef75d45ebcd8ede91596d69e55fe7d685008a60
|
81
src/expr.c
81
src/expr.c
@ -1888,7 +1888,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
|
||||
** An existing b-tree might be used if the RHS expression pX is a simple
|
||||
** subquery such as:
|
||||
**
|
||||
** SELECT <column> FROM <table>
|
||||
** SELECT <column1>, <column2>... FROM <table>
|
||||
**
|
||||
** If the RHS of the IN operator is a list or a more complex subquery, then
|
||||
** an ephemeral table might need to be generated from the RHS and then
|
||||
@ -1904,14 +1904,14 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
|
||||
**
|
||||
** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate
|
||||
** through the set members) then the b-tree must not contain duplicates.
|
||||
** An epheremal table must be used unless the selected <column> is guaranteed
|
||||
** to be unique - either because it is an INTEGER PRIMARY KEY or it
|
||||
** has a UNIQUE constraint or UNIQUE index.
|
||||
** An epheremal table must be used unless the selected columns are guaranteed
|
||||
** to be unique - either because it is an INTEGER PRIMARY KEY or due to
|
||||
** a UNIQUE constraint or index.
|
||||
**
|
||||
** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used
|
||||
** for fast set membership tests) then an epheremal table must
|
||||
** be used unless <column> is an INTEGER PRIMARY KEY or an index can
|
||||
** be found with <column> as its left-most column.
|
||||
** be used unless <columns> is a single INTEGER PRIMARY KEY column or an
|
||||
** index can be found with the specified <columns> as its left-most.
|
||||
**
|
||||
** If the IN_INDEX_NOOP_OK and IN_INDEX_MEMBERSHIP are both set and
|
||||
** if the RHS of the IN operator is a list (not a subquery) then this
|
||||
@ -1932,6 +1932,17 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
|
||||
** the value in that register will be NULL if the b-tree contains one or more
|
||||
** NULL values, and it will be some non-NULL value if the b-tree contains no
|
||||
** NULL values.
|
||||
**
|
||||
** If the aiMap parameter is not NULL, it must point to an array containing
|
||||
** one element for each column returned by the SELECT statement on the RHS
|
||||
** of the IN(...) operator. The i'th entry of the array is populated with the
|
||||
** offset of the index column that matches the i'th column returned by the
|
||||
** SELECT. For example, if the expression and selected index are:
|
||||
**
|
||||
** (?,?,?) IN (SELECT a, b, c FROM t1)
|
||||
** CREATE INDEX i1 ON t1(b, c, a);
|
||||
**
|
||||
** then aiMap[] is populated with {2, 0, 1}.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3FindInIndex(
|
||||
@ -2119,24 +2130,32 @@ int sqlite3FindInIndex(
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Argument pExpr is an (?, ?...) IN(...) expression. This
|
||||
** function allocates and returns a nul-terminated string containing
|
||||
** the affinities to be used for each column of the comparison.
|
||||
**
|
||||
** It is the responsibility of the caller to ensure that the returned
|
||||
** string is eventually freed using sqlite3DbFree().
|
||||
*/
|
||||
static char *exprINAffinity(Parse *pParse, Expr *pExpr){
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
int nVal = sqlite3ExprVectorSize(pLeft);
|
||||
Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0;
|
||||
char *zRet;
|
||||
|
||||
assert( pExpr->op==TK_IN );
|
||||
zRet = sqlite3DbMallocZero(pParse->db, nVal+1);
|
||||
if( zRet ){
|
||||
int i;
|
||||
for(i=0; i<nVal; i++){
|
||||
Expr *pA;
|
||||
char a;
|
||||
if( nVal==1 && 0 ){
|
||||
pA = pLeft;
|
||||
Expr *pA = exprVectorField(pLeft, i);
|
||||
char a = sqlite3ExprAffinity(pA);
|
||||
if( pSelect ){
|
||||
zRet[i] = sqlite3CompareAffinity(pSelect->pEList->a[i].pExpr, a);
|
||||
}else{
|
||||
pA = exprVectorField(pLeft, i);
|
||||
zRet[i] = a;
|
||||
}
|
||||
a = sqlite3ExprAffinity(pA);
|
||||
zRet[i] = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[i].pExpr, a);
|
||||
}
|
||||
zRet[nVal] = '\0';
|
||||
}
|
||||
@ -2229,11 +2248,12 @@ int sqlite3CodeSubselect(
|
||||
int nVal; /* Size of vector pLeft */
|
||||
|
||||
nVal = sqlite3ExprVectorSize(pLeft);
|
||||
assert( !isRowid || nVal==1 );
|
||||
|
||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||
** expression it is handled the same way. An ephemeral table is
|
||||
** filled with single-field index keys representing the results
|
||||
** from the SELECT or the <exprlist>.
|
||||
** filled with index keys representing the results from the
|
||||
** SELECT or the <exprlist>.
|
||||
**
|
||||
** If the 'x' expression is a column value, or the SELECT...
|
||||
** statement returns a column value, then the affinity of that
|
||||
@ -2470,18 +2490,19 @@ static void sqlite3ExprCodeIN(
|
||||
int *aiMap = 0; /* Map from vector field to index column */
|
||||
char *zAff = 0; /* Affinity string for comparisons */
|
||||
int nVector; /* Size of vectors for this IN(...) op */
|
||||
int regSelect = 0;
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
int i;
|
||||
|
||||
if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
|
||||
zAff = exprINAffinity(pParse, pExpr);
|
||||
nVector = sqlite3ExprVectorSize(pExpr->pLeft);
|
||||
aiMap = (int*)sqlite3DbMallocZero(
|
||||
pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1
|
||||
);
|
||||
if( !aiMap ) return;
|
||||
zAff = (char*)&aiMap[nVector];
|
||||
|
||||
if( !zAff || !aiMap ){
|
||||
sqlite3DbFree(pParse->db, aiMap);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attempt to compute the RHS. After this step, if anything other than
|
||||
** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable
|
||||
@ -2505,23 +2526,14 @@ static void sqlite3ExprCodeIN(
|
||||
r1 = sqlite3GetTempRange(pParse, nVector);
|
||||
sqlite3ExprCachePush(pParse);
|
||||
if( nVector>1 && (pLeft->flags & EP_xIsSelect) ){
|
||||
regSelect = sqlite3CodeSubselect(pParse, pLeft, 0, 0);
|
||||
}
|
||||
int regSelect = sqlite3CodeSubselect(pParse, pLeft, 0, 0);
|
||||
for(i=0; i<nVector; i++){
|
||||
int iCol = aiMap[i];
|
||||
Expr *pLhs = exprVectorField(pLeft, i);
|
||||
|
||||
if( regSelect ){
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regSelect+i, r1+iCol, 0);
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pLhs, r1+iCol);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regSelect+i, r1+aiMap[i], 0);
|
||||
}
|
||||
|
||||
zAff[iCol] = sqlite3ExprAffinity(pLhs);
|
||||
if( pExpr->flags & EP_xIsSelect ){
|
||||
zAff[iCol] = sqlite3CompareAffinity(
|
||||
pExpr->x.pSelect->pEList->a[iCol].pExpr, zAff[iCol]
|
||||
);
|
||||
}else{
|
||||
for(i=0; i<nVector; i++){
|
||||
Expr *pLhs = exprVectorField(pLeft, i);
|
||||
sqlite3ExprCode(pParse, pLhs, r1+aiMap[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2678,6 +2690,7 @@ static void sqlite3ExprCodeIN(
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ExprCachePop(pParse);
|
||||
sqlite3DbFree(pParse->db, aiMap);
|
||||
sqlite3DbFree(pParse->db, zAff);
|
||||
VdbeComment((v, "end IN expr"));
|
||||
}
|
||||
#endif /* SQLITE_OMIT_SUBQUERY */
|
||||
|
@ -878,7 +878,7 @@ static void selectInnerLoop(
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
|
||||
r1, pDest->zAffSdst, 1);
|
||||
r1, pDest->zAffSdst, nResultCol);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
@ -1261,13 +1261,12 @@ static void generateSortTail(
|
||||
case SRT_Set: {
|
||||
assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
|
||||
pDest->zAffSdst, 1);
|
||||
pDest->zAffSdst, nColumn);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
|
||||
break;
|
||||
}
|
||||
case SRT_Mem: {
|
||||
/* sqlite3ExprCodeMove(pParse, regRow, iParm, nColumn); */
|
||||
/* The LIMIT clause will terminate the loop for us */
|
||||
break;
|
||||
}
|
||||
|
15
src/where.c
15
src/where.c
@ -2189,7 +2189,20 @@ static void whereLoopOutputAdjust(
|
||||
|
||||
/*
|
||||
** Term pTerm is a vector range comparison operation. The first comparison
|
||||
** in the vector can be optimized using column nEq of the index.
|
||||
** in the vector can be optimized using column nEq of the index. This
|
||||
** function returns the total number of vector elements that can be used
|
||||
** as part of the range comparison.
|
||||
**
|
||||
** For example, if the query is:
|
||||
**
|
||||
** WHERE a = ? AND (b, c, d) > (?, ?, ?)
|
||||
**
|
||||
** and the index:
|
||||
**
|
||||
** CREATE INDEX ... ON (a, b, c, d, e)
|
||||
**
|
||||
** then this function would be invoked with nEq=1. The value returned in
|
||||
** this case is 3.
|
||||
*/
|
||||
int whereRangeVectorLen(
|
||||
Parse *pParse, int iCur, Index *pIdx, int nEq, WhereTerm *pTerm
|
||||
|
@ -950,6 +950,15 @@ static void codeDeferredSeek(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If the expression passed as the second argument is a vector, generate
|
||||
** code to write the first nReg elements of the vector into an array
|
||||
** of registers starting with iReg.
|
||||
**
|
||||
** If the expression is not a vector, then nReg must be passed 1. In
|
||||
** this case, generate code to evaluate the expression and leave the
|
||||
** result in register iReg.
|
||||
*/
|
||||
static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
|
||||
assert( nReg>0 );
|
||||
if( sqlite3ExprIsVector(p) ){
|
||||
@ -1238,8 +1247,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}else{
|
||||
testOp = bRev ? OP_Lt : OP_Gt;
|
||||
}
|
||||
if( 0==sqlite3ExprIsVector(pX->pRight) ){
|
||||
disableTerm(pLevel, pEnd);
|
||||
}
|
||||
}
|
||||
start = sqlite3VdbeCurrentAddr(v);
|
||||
pLevel->op = bRev ? OP_Prev : OP_Next;
|
||||
pLevel->p1 = iCur;
|
||||
|
@ -201,6 +201,24 @@ do_execsql_test 8.1 {
|
||||
SELECT * FROM j1 WHERE (select min(a) FROM j1) IN (?, ?, ?)
|
||||
}
|
||||
|
||||
do_execsql_test 9.0 {
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t2 VALUES(1, 1, 1);
|
||||
INSERT INTO t2 VALUES(2, 2, 2);
|
||||
INSERT INTO t2 VALUES(3, 3, 3);
|
||||
INSERT INTO t2 VALUES(4, 4, 4);
|
||||
INSERT INTO t2 VALUES(5, 5, 5);
|
||||
}
|
||||
|
||||
foreach {tn q res} {
|
||||
1 "(a, b) > (2, 1)" {2 3 4 5}
|
||||
2 "(a, b) > (2, 2)" {3 4 5}
|
||||
3 "(a, b) < (4, 5)" {1 2 3 4}
|
||||
4 "(a, b) < (4, 3)" {1 2 3}
|
||||
} {
|
||||
do_execsql_test 9.$tn "SELECT c FROM t2 WHERE $q" $res
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user