Attempt to optimize "x IS NULL" and "x IS NOT NULL" expressions when x is a column with a NOT NULL constraint.

FossilOrigin-Name: 5ecd842555009ce27ee6390325ac5c2504143474b12b730933f0833b3dad788a
This commit is contained in:
dan 2021-02-26 20:14:32 +00:00
parent 4b17455ab2
commit 8ddf686267
6 changed files with 150 additions and 9 deletions

View File

@ -1,5 +1,5 @@
C Minor\ssimplification\sin\sresolve.c.
D 2021-02-26T15:20:17.903
C Attempt\sto\soptimize\s"x\sIS\sNULL"\sand\s"x\sIS\sNOT\sNULL"\sexpressions\swhen\sx\sis\sa\scolumn\swith\sa\sNOT\sNULL\sconstraint.
D 2021-02-26T20:14:32.131
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -540,9 +540,9 @@ F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
F src/prepare.c f634a9e799a6b1c136d8ee12479cffa22862bfb807d307b1db406aa0cdb042a5
F src/printf.c 10e61ec79dd9d41fdc77afee4e0df04fbb427f309c043118fe0b26a7d7db488a
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c f3380c5570207e60cc23f2d34a8ac493a8e12f7cfeba0c89090497801dd22017
F src/resolve.c 2272b62c9f20cd6628c1d19963c4f96e41297b50cdfeffdcc47fcfded6a607d7
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 5d66f394afb481eb812927283a3036f7ffbda48442e3a0517d1fa1c3248aca8c
F src/select.c 7cf048e52f678726dadc448c41fab40262f6da37eeb1d8ff0f14ef1b17feed9e
F src/shell.c.in 844417f84df1f6c4fce1c815629a888cfdcf219e86513e9c332bbcc38832f477
F src/sqlite.h.in 8855a19f37ade8dad189a9e48233a2ebe1b46faf469c7eb0906a654e252dcc57
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
@ -632,7 +632,7 @@ F src/walker.c d42d6c80ea363ef689a462e65eefcfe87deab924c50de5baa37ecb6af7d7ddaa
F src/where.c a02138440d7230493b5e508664d629f3e1e7615737a5d83aac3a2955d3a654ff
F src/whereInt.h 446e5e8018f83358ef917cf32d8e6a86dc8430113d0b17e720f1839d3faa44c4
F src/wherecode.c e57a8690311a75d06e723e8d379f9831de04aba300e07174d236e32a7f9c7a13
F src/whereexpr.c 2dc51263e1fb8d8723e97a077a9a137ab0534e59e4cb88b3195f65edbe43cc32
F src/whereexpr.c 5bbce43bc16b8e53cd670bbbb0a8583296b1d1517303abe94a770d9d792413a8
F src/window.c fdf01316f6cecf060378aa1713a29e527ab683823ba7d15b8978ec70165e8bdb
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
@ -1224,6 +1224,7 @@ F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
F test/notnull2.test a5fb3d643322e5471117c7c1d70e6f116134bca84378a4ea92d021cc6a989d0d
F test/null.test b7ff206a1c60fe01aa2abd33ef9ea83c93727d993ca8a613de86e925c9f2bc6f
F test/nulls1.test 82c5bc33148405f21205865abf13c786084438d573a4ac4e87e11b6091cde526
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
@ -1907,7 +1908,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 062b338ff2ea71633b4fb3c75c6a47b5fc4fe9c2a72daacd987d1eca0bda5217
R 07651de30eadccb5d0a1b765b39186be
P 310dac342e7b1f9b5a5df6a9d598e85d5fef59bba9307d9230baf77c8f2351a2
R 5da9c9b015e5cf6e85f36d061ac9c73d
T *branch * ifnull-opt
T *sym-ifnull-opt *
T -sym-trunk *
U dan
Z a8b3d49e868cbc0b6002cfdac6e7fd25
Z 9cbe82fbe7c5328bb13207c38d4a331e

View File

@ -1 +1 @@
310dac342e7b1f9b5a5df6a9d598e85d5fef59bba9307d9230baf77c8f2351a2
5ecd842555009ce27ee6390325ac5c2504143474b12b730933f0833b3dad788a

View File

@ -785,6 +785,44 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
break;
}
/* An "<expr> IS NOT NULL" or "<expr> IS NULL". After resolving the
** LHS, check if there is a NOT NULL constraint in the schema that
** means the value of the expression can be determined immediately.
** If that is the case, replace the current expression node with
** a TK_TRUEFALSE node.
**
** If the node is replaced with a TK_TRUEFALSE node, then also restore
** the NameContext ref-counts to the state they where in before the
** LHS expression was resolved. This prevents the current select
** from being erroneously marked as correlated in some cases.
*/
case TK_NOTNULL:
case TK_ISNULL: {
int anRef[8];
NameContext *p;
int i;
for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
anRef[i] = p->nRef;
}
sqlite3WalkExpr(pWalker, pExpr->pLeft);
if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) ){
if( pExpr->op==TK_NOTNULL ){
pExpr->u.zToken = "true";
ExprSetProperty(pExpr, EP_IsTrue);
}else{
pExpr->u.zToken = "false";
ExprSetProperty(pExpr, EP_IsFalse);
}
pExpr->op = TK_TRUEFALSE;
for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
p->nRef = anRef[i];
}
sqlite3ExprDelete(pParse->db, pExpr->pLeft);
pExpr->pLeft = 0;
}
return WRC_Prune;
}
/* A column name: ID
** Or table name and column name: ID.ID
** Or a database, table and column: ID.ID.ID

View File

@ -407,6 +407,9 @@ static void unsetJoinExpr(Expr *p, int iTable){
&& (iTable<0 || p->iRightJoinTable==iTable) ){
ExprClearProperty(p, EP_FromJoin);
}
if( p->op==TK_COLUMN && p->iTable==iTable ){
ExprClearProperty(p, EP_CanBeNull);
}
if( p->op==TK_FUNCTION && p->x.pList ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){

View File

@ -1407,6 +1407,11 @@ static void exprAnalyze(
pNew->prereqRight = prereqLeft | extraRight;
pNew->prereqAll = prereqAll;
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
}else if( op==TK_ISNULL && 0==sqlite3ExprCanBeNull(pLeft) ){
pExpr->op = TK_TRUEFALSE;
ExprSetProperty(pExpr, EP_IsFalse);
pTerm->prereqAll = 0;
pTerm->eOperator = 0;
}
}

91
test/notnull2.test Normal file
View File

@ -0,0 +1,91 @@
# 2021 February 15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing optimizations associated with "IS NULL"
# and "IS NOT NULL" operators on columns with NOT NULL constraints.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix notnull2
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d NOT NULL);
WITH x(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<1000
)
INSERT INTO t1 SELECT i, i FROM x;
INSERT INTO t2 SELECT * FROM t1;
}
proc do_vmstep_test {tn sql nstep {res {}}} {
uplevel [list do_execsql_test $tn.0 $sql $res]
set vmstep [db status vmstep]
if {[string range $nstep 0 0]=="+"} {
set body "if {$vmstep<$nstep} {
error \"got $vmstep, expected more than [string range $nstep 1 end]\"
}"
} else {
set body "if {$vmstep>$nstep} {
error \"got $vmstep, expected less than $nstep\"
}"
}
# set name "$tn.vmstep=$vmstep,expect=$nstep"
set name "$tn.1"
uplevel [list do_test $name $body {}]
}
do_vmstep_test 1.1.1 {
SELECT * FROM t1 LEFT JOIN t2 WHERE a=c AND d IS NULL;
} 100 {}
do_vmstep_test 1.1.2 {
SELECT * FROM t1 LEFT JOIN t2 WHERE a=c AND c IS NULL;
} +1000 {}
do_vmstep_test 1.2.1 {
SELECT * FROM ( SELECT * FROM t2 ) WHERE d IS NULL
} 100 {}
do_vmstep_test 1.2.2 {
SELECT * FROM ( SELECT * FROM t2 ) WHERE c IS NULL
} +1000 {}
do_vmstep_test 1.3.1 {
SELECT * FROM t2 WHERE d IS NULL
} 100 {}
do_vmstep_test 1.3.2 {
SELECT * FROM t2 WHERE c IS NULL
} +1000 {}
do_vmstep_test 1.4.1 {
SELECT (d IS NOT NULL) FROM t2 WHERE 0==( d IS NOT NULL )
} 100 {}
do_vmstep_test 1.4.2 {
SELECT * FROM t2 WHERE 0==( c IS NOT NULL )
} +1000 {}
do_vmstep_test 1.5.1 {
SELECT count(*) FROM t2 WHERE EXISTS(
SELECT t2.d IS NULL FROM t1 WHERE t1.a=450
)
} 10000 {1000}
do_vmstep_test 1.5.2 {
SELECT count(*) FROM t2 WHERE EXISTS(
SELECT t2.c IS NULL FROM t1 WHERE t1.a=450
)
} +100000 {1000}
finish_test