Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch exists-to-join Excluding Merge-Ins
This is equivalent to a diff from ea9d88f9ca to 9084a4c872
2024-08-01
| ||
14:16 | Add the "errors" command to testrunner.tcl. (check-in: 03b7f99229 user: drh tags: trunk) | |
01:06 | Merge the latest trunk enhancements into the reuse-schema branch. (check-in: ff9fdd07d2 user: drh tags: reuse-schema) | |
00:38 | Merge the latest trunk enhancements into the wal2 branch. (check-in: eac582056f user: drh tags: wal2) | |
00:02 | Merge all the latest trunk enhancements into the begin-concurrent branch. (check-in: 3b131bd9aa user: drh tags: begin-concurrent) | |
2024-07-31
| ||
23:49 | Merge the latest trunk enhancements into the lateral-join branch. (check-in: ac6ae84626 user: drh tags: lateral-join) | |
23:46 | Merge all the latest trunk enhancements into the exists-to-join branch. (Leaf check-in: 9084a4c872 user: drh tags: exists-to-join) | |
22:36 | Fix the tclsqlite.c TCL interface module so that it works with both Tcl8 and Tcl9. Make changes across the TCL-based test harness to enable SQLite to be tested with either Tcl8 or Tcl9. Get the --with-tcl= argument on the configure script working. Testing changes only - no changes to the SQLite core. I believe the previous merge attempt didn't work because of errors in the merge itself, not because of faults in the code. Trying again... (check-in: ea9d88f9ca user: drh tags: trunk) | |
22:26 | testrunner.db should be in WAL mode for improved concurrency while running "watch sqlite3 test/testrunner.tcl status". (Leaf check-in: f23ad09798 user: drh tags: tcl9) | |
2024-07-27
| ||
20:28 | Fix documentation typo. Forum post 993cb82402 (check-in: 86de4e755e user: drh tags: trunk) | |
2024-06-27
| ||
14:54 | Merge the latest trunk enhancements into the exists-to-join branch. (check-in: fc643f8a12 user: drh tags: exists-to-join) | |
Changes to src/build.c.
︙ | |||
5045 5046 5047 5048 5049 5050 5051 | 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 | - + + - + - + + + | /* ** Append the contents of SrcList p2 to SrcList p1 and return the resulting ** SrcList. Or, if an error occurs, return NULL. In all cases, p1 and p2 ** are deleted by this function. */ SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){ |
︙ |
Changes to src/resolve.c.
︙ | |||
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 | 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 | + | }else{ sqlite3WalkSelect(pWalker, pExpr->x.pSelect); } assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); pExpr->x.pSelect->selFlags |= SF_Correlated; if( pExpr->op==TK_EXISTS ) pParse->bHasExists = 1; } pNC->ncFlags |= NC_Subquery; } break; } case TK_VARIABLE: { testcase( pNC->ncFlags & NC_IsCheck ); |
︙ |
Changes to src/select.c.
︙ | |||
7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 | 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | if( i==0 ) break; i--; pItem--; if( pItem->pSelect!=0 ) return 0; /* (1c-i) */ } return 1; } /* ** sqlite3WalkExpr() callback used by exprReferencesTable(). */ static int exprReferencesTableExprCb(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_COLUMN && pExpr->iTable==pWalker->u.iCur ){ pWalker->eCode = 1; } return WRC_Continue; } /* ** Return true if the expression passed as the first argument refers ** to cursor number iCur. Otherwise return false. */ static int exprReferencesTable(Expr *pExpr, int iCur){ Walker w; memset(&w, 0, sizeof(w)); w.u.iCur = iCur; w.xExprCallback = exprReferencesTableExprCb; w.xSelectCallback = sqlite3SelectWalkNoop; sqlite3WalkExpr(&w, pExpr); return w.eCode; } /* ** Index pIdx is a UNIQUE index on the table accessed by cursor number ** iCsr. This function returns a mask of the index columns that are ** constrained to match a single, non-NULL value by the WHERE clause ** passed as the 4th argument. For example, if the index is: ** ** CREATE UNIQUE INDEX idx ON tbl(a, b, c); ** ** and pWhere: ** ** WHERE a=? AND c=? ** ** then this function returns 5. */ static u64 findConstIdxTerms( Parse *pParse, int iCsr, Index *pIdx, Expr *pWhere ){ u64 m = 0; if( pWhere->op==TK_AND ){ m = findConstIdxTerms(pParse, iCsr, pIdx, pWhere->pLeft); m |= findConstIdxTerms(pParse, iCsr, pIdx, pWhere->pRight); }else if( pWhere->op==TK_EQ ){ Expr *pLeft = sqlite3ExprSkipCollateAndLikely(pWhere->pLeft); Expr *pRight = sqlite3ExprSkipCollateAndLikely(pWhere->pRight); if( pRight->op==TK_COLUMN && pRight->iTable==iCsr ){ SWAP(Expr*, pLeft, pRight); } if( pLeft->op==TK_COLUMN && pLeft->iTable==iCsr && exprReferencesTable(pRight, iCsr)==0 ){ if( pIdx ){ int ii; for(ii=0; ii<pIdx->nKeyCol; ii++){ assert( pIdx->azColl[ii] ); if( pLeft->iColumn==pIdx->aiColumn[ii] ){ CollSeq *pColl = sqlite3ExprCompareCollSeq(pParse, pWhere); if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[ii])==0 ){ m |= ((u64)1 << ii); break; } } } }else{ if( pLeft->iColumn<0 ) m = 1; } } } return m; } /* ** Argument pWhere is the WHERE clause belonging to SELECT statement p. This ** function attempts to transform expressions of the form: ** ** EXISTS (SELECT ...) ** ** into joins. For example, given ** ** CREATE TABLE sailors(sid INTEGER PRIMARY KEY, name TEXT); ** CREATE TABLE reserves(sid INT, day DATE, PRIMARY KEY(sid, day)); ** ** SELECT name FROM sailors AS S WHERE EXISTS ( ** SELECT * FROM reserves AS R WHERE S.sid = R.sid AND R.day = '2022-10-25' ** ); ** ** the SELECT statement may be transformed as follows: ** ** SELECT name FROM sailors AS S, reserves AS R ** WHERE S.sid = R.sid AND R.day = '2022-10-25'; */ static void existsToJoin(Parse *pParse, Select *p, Expr *pWhere){ if( pWhere && !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) && p->pSrc->nSrc>0 && p->pSrc->nSrc<BMS && pParse->db->mallocFailed==0 ){ if( pWhere->op==TK_AND ){ Expr *pRight = pWhere->pRight; existsToJoin(pParse, p, pWhere->pLeft); existsToJoin(pParse, p, pRight); } else if( pWhere->op==TK_EXISTS ){ Select *pSub = pWhere->x.pSelect; if( pSub->pSrc->nSrc==1 && (pSub->selFlags & (SF_Aggregate|SF_Correlated))==SF_Correlated && pSub->pWhere ){ int bTransform = 0; /* True if EXISTS can be made into join */ Table *pTab = pSub->pSrc->a[0].pTab; int iCsr = pSub->pSrc->a[0].iCursor; Index *pIdx; if( HasRowid(pTab) && findConstIdxTerms(pParse, iCsr, 0,pSub->pWhere) ){ bTransform = 1; } for(pIdx=pTab->pIndex; pIdx && bTransform==0; pIdx=pIdx->pNext){ if( pIdx->onError && pIdx->nKeyCol<=63 ){ u64 c = findConstIdxTerms(pParse, iCsr, pIdx, pSub->pWhere); if( c==((u64)1 << pIdx->nKeyCol)-1 ){ bTransform = 1; } } } if( bTransform ){ memset(pWhere, 0, sizeof(*pWhere)); pWhere->op = TK_INTEGER; pWhere->u.iValue = 1; ExprSetProperty(pWhere, EP_IntValue); assert( p->pWhere!=0 ); p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSub->pWhere); pSub->pWhere = 0; pSub->pSrc = 0; sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSub); #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x100000 ){ TREETRACE(0x100000,pParse,p, ("After EXISTS-to-JOIN optimization:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif } } } } } /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are returned according to the SelectDest structure. ** See comments in sqliteInt.h for further information. ** |
︙ | |||
7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 | 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 | + + + + + + + | sqlite3TreeViewSelect(0, p, 0); } #endif if( p->pNext==0 ) ExplainQueryPlanPop(pParse); return rc; } #endif /* If there may be an "EXISTS (SELECT ...)" in the WHERE clause, attempt ** to change it into a join. */ if( pParse->bHasExists && OptimizationEnabled(db,SQLITE_ExistsToJoin) ){ existsToJoin(pParse, p, p->pWhere); pTabList = p->pSrc; } /* Do the WHERE-clause constant propagation optimization if this is ** a join. No need to speed time on this operation for non-join queries ** as the equivalent optimization will be handled by query planner in ** sqlite3WhereBegin(). */ if( p->pWhere!=0 |
︙ |
Changes to src/sqliteInt.h.
︙ | |||
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 | 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 | + | ** 0x00002000 Constant propagation ** 0x00004000 Push-down optimization ** 0x00008000 After all FROM-clause analysis ** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing ** 0x00020000 Transform DISTINCT into GROUP BY ** 0x00040000 SELECT tree dump after all code has been generated ** 0x00080000 NOT NULL strength reduction ** 0x00100000 EXISTS-to-JOIN optimization */ /* ** Macros for "wheretrace" */ extern u32 sqlite3WhereTrace; #if defined(SQLITE_DEBUG) \ |
︙ | |||
1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 | 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 | + | #define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */ #define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ #define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ #define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ #define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ #define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ #define SQLITE_ExistsToJoin 0x10000000 /* The EXISTS-to-JOIN optimization */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. */ #define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) #define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) |
︙ | |||
3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 | 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 | + | u8 mayAbort; /* True if statement may throw an ABORT exception */ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ u8 bHasWith; /* True if statement contains WITH */ u8 bHasExists; /* Has a correlated "EXISTS (SELECT ....)" expression */ u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif #ifdef SQLITE_DEBUG u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ #endif |
︙ |
Added test/existsexpr.test.