| | @@ -452,11 +452,11 @@ |
| 452 | 452 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 453 | 453 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 454 | 454 | */ |
| 455 | 455 | #define SQLITE_VERSION "3.39.0" |
| 456 | 456 | #define SQLITE_VERSION_NUMBER 3039000 |
| 457 | | -#define SQLITE_SOURCE_ID "2022-06-03 14:08:40 d18818afc6021a32989499c26ab38fa442e5a55e662e39bb06e5c0daa9c65e25" |
| 457 | +#define SQLITE_SOURCE_ID "2022-06-15 16:26:37 56c60a35ea457f06db58ec3f694a1ae16fd03e6625da1d7879d63d72bbcb1c62" |
| 458 | 458 | |
| 459 | 459 | /* |
| 460 | 460 | ** CAPI3REF: Run-Time Library Version Numbers |
| 461 | 461 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 462 | 462 | ** |
| | @@ -15724,17 +15724,18 @@ |
| 15724 | 15724 | #define OP_VInitIn 174 /* synopsis: r[P2]=ValueList(P1,P3) */ |
| 15725 | 15725 | #define OP_VColumn 175 /* synopsis: r[P3]=vcolumn(P2) */ |
| 15726 | 15726 | #define OP_VRename 176 |
| 15727 | 15727 | #define OP_Pagecount 177 |
| 15728 | 15728 | #define OP_MaxPgcnt 178 |
| 15729 | | -#define OP_FilterAdd 179 /* synopsis: filter(P1) += key(P3@P4) */ |
| 15730 | | -#define OP_Trace 180 |
| 15731 | | -#define OP_CursorHint 181 |
| 15732 | | -#define OP_ReleaseReg 182 /* synopsis: release r[P1@P2] mask P3 */ |
| 15733 | | -#define OP_Noop 183 |
| 15734 | | -#define OP_Explain 184 |
| 15735 | | -#define OP_Abortable 185 |
| 15729 | +#define OP_ClrSubtype 179 /* synopsis: r[P1].subtype = 0 */ |
| 15730 | +#define OP_FilterAdd 180 /* synopsis: filter(P1) += key(P3@P4) */ |
| 15731 | +#define OP_Trace 181 |
| 15732 | +#define OP_CursorHint 182 |
| 15733 | +#define OP_ReleaseReg 183 /* synopsis: release r[P1@P2] mask P3 */ |
| 15734 | +#define OP_Noop 184 |
| 15735 | +#define OP_Explain 185 |
| 15736 | +#define OP_Abortable 186 |
| 15736 | 15737 | |
| 15737 | 15738 | /* Properties such as "out2" or "jump" that are specified in |
| 15738 | 15739 | ** comments following the "case" for each opcode in the vdbe.c |
| 15739 | 15740 | ** are encoded into bitvectors as follows: |
| 15740 | 15741 | */ |
| | @@ -15765,12 +15766,12 @@ |
| 15765 | 15766 | /* 136 */ 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10, 0x00,\ |
| 15766 | 15767 | /* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ |
| 15767 | 15768 | /* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ |
| 15768 | 15769 | /* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ |
| 15769 | 15770 | /* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,\ |
| 15770 | | -/* 176 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ |
| 15771 | | -/* 184 */ 0x00, 0x00,} |
| 15771 | +/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\ |
| 15772 | +/* 184 */ 0x00, 0x00, 0x00,} |
| 15772 | 15773 | |
| 15773 | 15774 | /* The resolve3P2Values() routine is able to run faster if it knows |
| 15774 | 15775 | ** the value of the largest JUMP opcode. The smaller the maximum |
| 15775 | 15776 | ** JUMP opcode the better, so the mkopcodeh.tcl script that |
| 15776 | 15777 | ** generated this include file strives to group all JUMP opcodes |
| | @@ -18403,10 +18404,11 @@ |
| 18403 | 18404 | unsigned isRecursive :1; /* True for recursive reference in WITH */ |
| 18404 | 18405 | unsigned fromDDL :1; /* Comes from sqlite_schema */ |
| 18405 | 18406 | unsigned isCte :1; /* This is a CTE */ |
| 18406 | 18407 | unsigned notCte :1; /* This item may not match a CTE */ |
| 18407 | 18408 | unsigned isUsing :1; /* u3.pUsing is valid */ |
| 18409 | + unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ |
| 18408 | 18410 | unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */ |
| 18409 | 18411 | unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ |
| 18410 | 18412 | } fg; |
| 18411 | 18413 | int iCursor; /* The VDBE cursor number used to access this table */ |
| 18412 | 18414 | union { |
| | @@ -22254,11 +22256,11 @@ |
| 22254 | 22256 | u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ |
| 22255 | 22257 | #endif |
| 22256 | 22258 | Bool isEphemeral:1; /* True for an ephemeral table */ |
| 22257 | 22259 | Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ |
| 22258 | 22260 | Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ |
| 22259 | | - Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */ |
| 22261 | + Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */ |
| 22260 | 22262 | u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ |
| 22261 | 22263 | union { /* pBtx for isEphermeral. pAltMap otherwise */ |
| 22262 | 22264 | Btree *pBtx; /* Separate file holding temporary table */ |
| 22263 | 22265 | u32 *aAltMap; /* Mapping from table to index column numbers */ |
| 22264 | 22266 | } ub; |
| | @@ -31228,10 +31230,13 @@ |
| 31228 | 31230 | sqlite3_str_appendf(&x, " DDL"); |
| 31229 | 31231 | } |
| 31230 | 31232 | if( pItem->fg.isCte ){ |
| 31231 | 31233 | sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); |
| 31232 | 31234 | } |
| 31235 | + if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ |
| 31236 | + sqlite3_str_appendf(&x, " ON"); |
| 31237 | + } |
| 31233 | 31238 | sqlite3StrAccumFinish(&x); |
| 31234 | 31239 | sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1); |
| 31235 | 31240 | n = 0; |
| 31236 | 31241 | if( pItem->pSelect ) n++; |
| 31237 | 31242 | if( pItem->fg.isTabFunc ) n++; |
| | @@ -35433,17 +35438,18 @@ |
| 35433 | 35438 | /* 174 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), |
| 35434 | 35439 | /* 175 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), |
| 35435 | 35440 | /* 176 */ "VRename" OpHelp(""), |
| 35436 | 35441 | /* 177 */ "Pagecount" OpHelp(""), |
| 35437 | 35442 | /* 178 */ "MaxPgcnt" OpHelp(""), |
| 35438 | | - /* 179 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), |
| 35439 | | - /* 180 */ "Trace" OpHelp(""), |
| 35440 | | - /* 181 */ "CursorHint" OpHelp(""), |
| 35441 | | - /* 182 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), |
| 35442 | | - /* 183 */ "Noop" OpHelp(""), |
| 35443 | | - /* 184 */ "Explain" OpHelp(""), |
| 35444 | | - /* 185 */ "Abortable" OpHelp(""), |
| 35443 | + /* 179 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), |
| 35444 | + /* 180 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), |
| 35445 | + /* 181 */ "Trace" OpHelp(""), |
| 35446 | + /* 182 */ "CursorHint" OpHelp(""), |
| 35447 | + /* 183 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), |
| 35448 | + /* 184 */ "Noop" OpHelp(""), |
| 35449 | + /* 185 */ "Explain" OpHelp(""), |
| 35450 | + /* 186 */ "Abortable" OpHelp(""), |
| 35445 | 35451 | }; |
| 35446 | 35452 | return azName[i]; |
| 35447 | 35453 | } |
| 35448 | 35454 | #endif |
| 35449 | 35455 | |
| | @@ -56462,10 +56468,11 @@ |
| 56462 | 56468 | }else if( (currentSize+szPage)<=newSize ){ |
| 56463 | 56469 | char *pTmp = pPager->pTmpSpace; |
| 56464 | 56470 | memset(pTmp, 0, szPage); |
| 56465 | 56471 | testcase( (newSize-szPage) == currentSize ); |
| 56466 | 56472 | testcase( (newSize-szPage) > currentSize ); |
| 56473 | + sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &newSize); |
| 56467 | 56474 | rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); |
| 56468 | 56475 | } |
| 56469 | 56476 | if( rc==SQLITE_OK ){ |
| 56470 | 56477 | pPager->dbFileSize = nPage; |
| 56471 | 56478 | } |
| | @@ -70714,16 +70721,21 @@ |
| 70714 | 70721 | eMode = BTALLOC_LE; |
| 70715 | 70722 | iNear = nFin; |
| 70716 | 70723 | } |
| 70717 | 70724 | do { |
| 70718 | 70725 | MemPage *pFreePg; |
| 70726 | + Pgno dbSize = btreePagecount(pBt); |
| 70719 | 70727 | rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); |
| 70720 | 70728 | if( rc!=SQLITE_OK ){ |
| 70721 | 70729 | releasePage(pLastPg); |
| 70722 | 70730 | return rc; |
| 70723 | 70731 | } |
| 70724 | 70732 | releasePage(pFreePg); |
| 70733 | + if( iFreePg>dbSize ){ |
| 70734 | + releasePage(pLastPg); |
| 70735 | + return SQLITE_CORRUPT_BKPT; |
| 70736 | + } |
| 70725 | 70737 | }while( bCommit && iFreePg>nFin ); |
| 70726 | 70738 | assert( iFreePg<iLastPg ); |
| 70727 | 70739 | |
| 70728 | 70740 | rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, bCommit); |
| 70729 | 70741 | releasePage(pLastPg); |
| | @@ -80228,12 +80240,12 @@ |
| 80228 | 80240 | assert( !ExprHasProperty(pExpr, EP_IntValue) ); |
| 80229 | 80241 | aff = sqlite3AffinityType(pExpr->u.zToken,0); |
| 80230 | 80242 | rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); |
| 80231 | 80243 | testcase( rc!=SQLITE_OK ); |
| 80232 | 80244 | if( *ppVal ){ |
| 80233 | | - sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8); |
| 80234 | | - sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8); |
| 80245 | + sqlite3VdbeMemCast(*ppVal, aff, enc); |
| 80246 | + sqlite3ValueApplyAffinity(*ppVal, affinity, enc); |
| 80235 | 80247 | } |
| 80236 | 80248 | return rc; |
| 80237 | 80249 | } |
| 80238 | 80250 | |
| 80239 | 80251 | /* Handle negative integers in a single step. This is needed in the |
| | @@ -89757,14 +89769,19 @@ |
| 89757 | 89769 | pOut++; |
| 89758 | 89770 | }while( --n ); |
| 89759 | 89771 | break; |
| 89760 | 89772 | } |
| 89761 | 89773 | |
| 89762 | | -/* Opcode: Copy P1 P2 P3 * * |
| 89774 | +/* Opcode: Copy P1 P2 P3 * P5 |
| 89763 | 89775 | ** Synopsis: r[P2@P3+1]=r[P1@P3+1] |
| 89764 | 89776 | ** |
| 89765 | 89777 | ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. |
| 89778 | +** |
| 89779 | +** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the |
| 89780 | +** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot |
| 89781 | +** be merged. The 0x0001 bit is used by the query planner and does not |
| 89782 | +** come into play during query execution. |
| 89766 | 89783 | ** |
| 89767 | 89784 | ** This instruction makes a deep copy of the value. A duplicate |
| 89768 | 89785 | ** is made of any string or blob constant. See also OP_SCopy. |
| 89769 | 89786 | */ |
| 89770 | 89787 | case OP_Copy: { |
| | @@ -89776,10 +89793,13 @@ |
| 89776 | 89793 | assert( pOut!=pIn1 ); |
| 89777 | 89794 | while( 1 ){ |
| 89778 | 89795 | memAboutToChange(p, pOut); |
| 89779 | 89796 | sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); |
| 89780 | 89797 | Deephemeralize(pOut); |
| 89798 | + if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){ |
| 89799 | + pOut->flags &= ~MEM_Subtype; |
| 89800 | + } |
| 89781 | 89801 | #ifdef SQLITE_DEBUG |
| 89782 | 89802 | pOut->pScopyFrom = 0; |
| 89783 | 89803 | #endif |
| 89784 | 89804 | REGISTER_TRACE(pOp->p2+pOp->p3-n, pOut); |
| 89785 | 89805 | if( (n--)==0 ) break; |
| | @@ -92158,10 +92178,15 @@ |
| 92158 | 92178 | if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ |
| 92159 | 92179 | sqlite3ResetOneSchema(db, pOp->p1); |
| 92160 | 92180 | } |
| 92161 | 92181 | p->expired = 1; |
| 92162 | 92182 | rc = SQLITE_SCHEMA; |
| 92183 | + |
| 92184 | + /* Set changeCntOn to 0 to prevent the value returned by sqlite3_changes() |
| 92185 | + ** from being modified in sqlite3VdbeHalt(). If this statement is |
| 92186 | + ** reprepared, changeCntOn will be set again. */ |
| 92187 | + p->changeCntOn = 0; |
| 92163 | 92188 | } |
| 92164 | 92189 | if( rc ) goto abort_due_to_error; |
| 92165 | 92190 | break; |
| 92166 | 92191 | } |
| 92167 | 92192 | |
| | @@ -92457,12 +92482,12 @@ |
| 92457 | 92482 | pCx->pKeyInfo = pOrig->pKeyInfo; |
| 92458 | 92483 | pCx->isTable = pOrig->isTable; |
| 92459 | 92484 | pCx->pgnoRoot = pOrig->pgnoRoot; |
| 92460 | 92485 | pCx->isOrdered = pOrig->isOrdered; |
| 92461 | 92486 | pCx->ub.pBtx = pOrig->ub.pBtx; |
| 92462 | | - pCx->hasBeenDuped = 1; |
| 92463 | | - pOrig->hasBeenDuped = 1; |
| 92487 | + pCx->noReuse = 1; |
| 92488 | + pOrig->noReuse = 1; |
| 92464 | 92489 | rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, |
| 92465 | 92490 | pCx->pKeyInfo, pCx->uc.pCursor); |
| 92466 | 92491 | /* The sqlite3BtreeCursor() routine can only fail for the first cursor |
| 92467 | 92492 | ** opened for a database. Since there is already an open cursor when this |
| 92468 | 92493 | ** opcode is run, the sqlite3BtreeCursor() cannot fail */ |
| | @@ -92525,11 +92550,11 @@ |
| 92525 | 92550 | assert( aMem[pOp->p3].flags & MEM_Null ); |
| 92526 | 92551 | aMem[pOp->p3].n = 0; |
| 92527 | 92552 | aMem[pOp->p3].z = ""; |
| 92528 | 92553 | } |
| 92529 | 92554 | pCx = p->apCsr[pOp->p1]; |
| 92530 | | - if( pCx && !pCx->hasBeenDuped && ALWAYS(pOp->p2<=pCx->nField) ){ |
| 92555 | + if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){ |
| 92531 | 92556 | /* If the ephermeral table is already open and has no duplicates from |
| 92532 | 92557 | ** OP_OpenDup, then erase all existing content so that the table is |
| 92533 | 92558 | ** empty again, rather than creating a new table. */ |
| 92534 | 92559 | assert( pCx->isEphemeral ); |
| 92535 | 92560 | pCx->seqCount = 0; |
| | @@ -94112,10 +94137,11 @@ |
| 94112 | 94137 | ** pseudo-cursor that always gives null rows. */ |
| 94113 | 94138 | pC = allocateCursor(p, pOp->p1, 1, CURTYPE_PSEUDO); |
| 94114 | 94139 | if( pC==0 ) goto no_mem; |
| 94115 | 94140 | pC->seekResult = 0; |
| 94116 | 94141 | pC->isTable = 1; |
| 94142 | + pC->noReuse = 1; |
| 94117 | 94143 | pC->uc.pCursor = sqlite3BtreeFakeValidCursor(); |
| 94118 | 94144 | } |
| 94119 | 94145 | pC->nullRow = 1; |
| 94120 | 94146 | pC->cacheStatus = CACHE_STALE; |
| 94121 | 94147 | if( pC->eCurType==CURTYPE_BTREE ){ |
| | @@ -96244,18 +96270,18 @@ |
| 96244 | 96270 | Mem *pDest; |
| 96245 | 96271 | sqlite3_context sContext; |
| 96246 | 96272 | |
| 96247 | 96273 | VdbeCursor *pCur = p->apCsr[pOp->p1]; |
| 96248 | 96274 | assert( pCur!=0 ); |
| 96249 | | - assert( pCur->eCurType==CURTYPE_VTAB ); |
| 96250 | 96275 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 96251 | 96276 | pDest = &aMem[pOp->p3]; |
| 96252 | 96277 | memAboutToChange(p, pDest); |
| 96253 | 96278 | if( pCur->nullRow ){ |
| 96254 | 96279 | sqlite3VdbeMemSetNull(pDest); |
| 96255 | 96280 | break; |
| 96256 | 96281 | } |
| 96282 | + assert( pCur->eCurType==CURTYPE_VTAB ); |
| 96257 | 96283 | pVtab = pCur->uc.pVCur->pVtab; |
| 96258 | 96284 | pModule = pVtab->pModule; |
| 96259 | 96285 | assert( pModule->xColumn ); |
| 96260 | 96286 | memset(&sContext, 0, sizeof(sContext)); |
| 96261 | 96287 | sContext.pOut = pDest; |
| | @@ -96578,10 +96604,21 @@ |
| 96578 | 96604 | |
| 96579 | 96605 | REGISTER_TRACE(pOp->p3, pOut); |
| 96580 | 96606 | UPDATE_MAX_BLOBSIZE(pOut); |
| 96581 | 96607 | break; |
| 96582 | 96608 | } |
| 96609 | + |
| 96610 | +/* Opcode: ClrSubtype P1 * * * * |
| 96611 | +** Synopsis: r[P1].subtype = 0 |
| 96612 | +** |
| 96613 | +** Clear the subtype from register P1. |
| 96614 | +*/ |
| 96615 | +case OP_ClrSubtype: { /* in1 */ |
| 96616 | + pIn1 = &aMem[pOp->p1]; |
| 96617 | + pIn1->flags &= ~MEM_Subtype; |
| 96618 | + break; |
| 96619 | +} |
| 96583 | 96620 | |
| 96584 | 96621 | /* Opcode: FilterAdd P1 * P3 P4 * |
| 96585 | 96622 | ** Synopsis: filter(P1) += key(P3@P4) |
| 96586 | 96623 | ** |
| 96587 | 96624 | ** Compute a hash on the P4 registers starting with r[P3] and |
| | @@ -108130,13 +108167,29 @@ |
| 108130 | 108167 | ** Z is stored in pExpr->pList->a[1].pExpr. |
| 108131 | 108168 | */ |
| 108132 | 108169 | case TK_BETWEEN: { |
| 108133 | 108170 | exprCodeBetween(pParse, pExpr, target, 0, 0); |
| 108134 | 108171 | return target; |
| 108172 | + } |
| 108173 | + case TK_COLLATE: { |
| 108174 | + if( !ExprHasProperty(pExpr, EP_Collate) |
| 108175 | + && ALWAYS(pExpr->pLeft) |
| 108176 | + && pExpr->pLeft->op==TK_FUNCTION |
| 108177 | + ){ |
| 108178 | + inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); |
| 108179 | + if( inReg!=target ){ |
| 108180 | + sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); |
| 108181 | + inReg = target; |
| 108182 | + } |
| 108183 | + sqlite3VdbeAddOp1(v, OP_ClrSubtype, inReg); |
| 108184 | + return inReg; |
| 108185 | + }else{ |
| 108186 | + pExpr = pExpr->pLeft; |
| 108187 | + goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */ |
| 108188 | + } |
| 108135 | 108189 | } |
| 108136 | 108190 | case TK_SPAN: |
| 108137 | | - case TK_COLLATE: |
| 108138 | 108191 | case TK_UPLUS: { |
| 108139 | 108192 | pExpr = pExpr->pLeft; |
| 108140 | 108193 | goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */ |
| 108141 | 108194 | } |
| 108142 | 108195 | |
| | @@ -135899,31 +135952,37 @@ |
| 135899 | 135952 | /* Undo the work of sqlite3SetJoinExpr(). In the expression p, convert every |
| 135900 | 135953 | ** term that is marked with EP_OuterON and w.iJoin==iTable into |
| 135901 | 135954 | ** an ordinary term that omits the EP_OuterON mark. |
| 135902 | 135955 | ** |
| 135903 | 135956 | ** This happens when a LEFT JOIN is simplified into an ordinary JOIN. |
| 135957 | +** |
| 135958 | +** If nullable is true, that means that Expr p might evaluate to NULL even |
| 135959 | +** if it is a reference to a NOT NULL column. This can happen, for example, |
| 135960 | +** if the table that p references is on the left side of a RIGHT JOIN. |
| 135961 | +** If nullable is true, then take care to not remove the EP_CanBeNull bit. |
| 135962 | +** See forum thread https://sqlite.org/forum/forumpost/b40696f50145d21c |
| 135904 | 135963 | */ |
| 135905 | | -static void unsetJoinExpr(Expr *p, int iTable){ |
| 135964 | +static void unsetJoinExpr(Expr *p, int iTable, int nullable){ |
| 135906 | 135965 | while( p ){ |
| 135907 | 135966 | if( ExprHasProperty(p, EP_OuterON) |
| 135908 | 135967 | && (iTable<0 || p->w.iJoin==iTable) ){ |
| 135909 | 135968 | ExprClearProperty(p, EP_OuterON); |
| 135910 | 135969 | ExprSetProperty(p, EP_InnerON); |
| 135911 | 135970 | } |
| 135912 | | - if( p->op==TK_COLUMN && p->iTable==iTable ){ |
| 135971 | + if( p->op==TK_COLUMN && p->iTable==iTable && !nullable ){ |
| 135913 | 135972 | ExprClearProperty(p, EP_CanBeNull); |
| 135914 | 135973 | } |
| 135915 | 135974 | if( p->op==TK_FUNCTION ){ |
| 135916 | 135975 | assert( ExprUseXList(p) ); |
| 135917 | 135976 | if( p->x.pList ){ |
| 135918 | 135977 | int i; |
| 135919 | 135978 | for(i=0; i<p->x.pList->nExpr; i++){ |
| 135920 | | - unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); |
| 135979 | + unsetJoinExpr(p->x.pList->a[i].pExpr, iTable, nullable); |
| 135921 | 135980 | } |
| 135922 | 135981 | } |
| 135923 | 135982 | } |
| 135924 | | - unsetJoinExpr(p->pLeft, iTable); |
| 135983 | + unsetJoinExpr(p->pLeft, iTable, nullable); |
| 135925 | 135984 | p = p->pRight; |
| 135926 | 135985 | } |
| 135927 | 135986 | } |
| 135928 | 135987 | |
| 135929 | 135988 | /* |
| | @@ -136080,10 +136139,11 @@ |
| 136080 | 136139 | */ |
| 136081 | 136140 | else if( pRight->u3.pOn ){ |
| 136082 | 136141 | sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType); |
| 136083 | 136142 | p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn); |
| 136084 | 136143 | pRight->u3.pOn = 0; |
| 136144 | + pRight->fg.isOn = 1; |
| 136085 | 136145 | } |
| 136086 | 136146 | } |
| 136087 | 136147 | return 0; |
| 136088 | 136148 | } |
| 136089 | 136149 | |
| | @@ -139233,13 +139293,14 @@ |
| 139233 | 139293 | static Expr *substExpr( |
| 139234 | 139294 | SubstContext *pSubst, /* Description of the substitution */ |
| 139235 | 139295 | Expr *pExpr /* Expr in which substitution occurs */ |
| 139236 | 139296 | ){ |
| 139237 | 139297 | if( pExpr==0 ) return 0; |
| 139238 | | - if( ExprHasProperty(pExpr, EP_OuterON) |
| 139298 | + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) |
| 139239 | 139299 | && pExpr->w.iJoin==pSubst->iTable |
| 139240 | 139300 | ){ |
| 139301 | + testcase( ExprHasProperty(pExpr, EP_InnerON) ); |
| 139241 | 139302 | pExpr->w.iJoin = pSubst->iNewTable; |
| 139242 | 139303 | } |
| 139243 | 139304 | if( pExpr->op==TK_COLUMN |
| 139244 | 139305 | && pExpr->iTable==pSubst->iTable |
| 139245 | 139306 | && !ExprHasProperty(pExpr, EP_FixedCol) |
| | @@ -139638,10 +139699,15 @@ |
| 139638 | 139699 | ** (27) The subquery may not contain a FULL or RIGHT JOIN unless it |
| 139639 | 139700 | ** is the first element of the parent query. |
| 139640 | 139701 | ** |
| 139641 | 139702 | ** (28) The subquery is not a MATERIALIZED CTE. |
| 139642 | 139703 | ** |
| 139704 | +** (29) Either the subquery is not the right-hand operand of a join with an |
| 139705 | +** ON or USING clause nor the right-hand operand of a NATURAL JOIN, or |
| 139706 | +** the right-most table within the FROM clause of the subquery |
| 139707 | +** is not part of an outer join. |
| 139708 | +** |
| 139643 | 139709 | ** |
| 139644 | 139710 | ** In this routine, the "p" parameter is a pointer to the outer query. |
| 139645 | 139711 | ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query |
| 139646 | 139712 | ** uses aggregates. |
| 139647 | 139713 | ** |
| | @@ -139764,10 +139830,39 @@ |
| 139764 | 139830 | return 0; /* Restriction (27) */ |
| 139765 | 139831 | } |
| 139766 | 139832 | if( pSubitem->fg.isCte && pSubitem->u2.pCteUse->eM10d==M10d_Yes ){ |
| 139767 | 139833 | return 0; /* (28) */ |
| 139768 | 139834 | } |
| 139835 | + |
| 139836 | + /* Restriction (29): |
| 139837 | + ** |
| 139838 | + ** We do not want two constraints on the same term of the flattened |
| 139839 | + ** query where one constraint has EP_InnerON and the other is EP_OuterON. |
| 139840 | + ** To prevent this, one or the other of the following conditions must be |
| 139841 | + ** false: |
| 139842 | + ** |
| 139843 | + ** (29a) The right-most entry in the FROM clause of the subquery |
| 139844 | + ** must not be part of an outer join. |
| 139845 | + ** |
| 139846 | + ** (29b) The subquery itself must not be the right operand of a |
| 139847 | + ** NATURAL join or a join that as an ON or USING clause. |
| 139848 | + ** |
| 139849 | + ** These conditions are sufficient to keep an EP_OuterON from being |
| 139850 | + ** flattened into an EP_InnerON. Restrictions (3a) and (27) prevent |
| 139851 | + ** an EP_InnerON from being flattened into an EP_OuterON. |
| 139852 | + */ |
| 139853 | + if( pSubSrc->nSrc>=2 |
| 139854 | + && (pSubSrc->a[pSubSrc->nSrc-1].fg.jointype & JT_OUTER)!=0 |
| 139855 | + ){ |
| 139856 | + if( (pSubitem->fg.jointype & JT_NATURAL)!=0 |
| 139857 | + || pSubitem->fg.isUsing |
| 139858 | + || NEVER(pSubitem->u3.pOn!=0) /* ON clause already shifted into WHERE */ |
| 139859 | + || pSubitem->fg.isOn |
| 139860 | + ){ |
| 139861 | + return 0; |
| 139862 | + } |
| 139863 | + } |
| 139769 | 139864 | |
| 139770 | 139865 | /* Restriction (17): If the sub-query is a compound SELECT, then it must |
| 139771 | 139866 | ** use only the UNION ALL operator. And none of the simple select queries |
| 139772 | 139867 | ** that make up the compound SELECT are allowed to be aggregate or distinct |
| 139773 | 139868 | ** queries. |
| | @@ -140156,11 +140251,15 @@ |
| 140156 | 140251 | ** found, add it to the pConst structure. |
| 140157 | 140252 | */ |
| 140158 | 140253 | static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ |
| 140159 | 140254 | Expr *pRight, *pLeft; |
| 140160 | 140255 | if( NEVER(pExpr==0) ) return; |
| 140161 | | - if( ExprHasProperty(pExpr, EP_OuterON) ) return; |
| 140256 | + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){ |
| 140257 | + testcase( ExprHasProperty(pExpr, EP_OuterON) ); |
| 140258 | + testcase( ExprHasProperty(pExpr, EP_InnerON) ); |
| 140259 | + return; |
| 140260 | + } |
| 140162 | 140261 | if( pExpr->op==TK_AND ){ |
| 140163 | 140262 | findConstInWhere(pConst, pExpr->pRight); |
| 140164 | 140263 | findConstInWhere(pConst, pExpr->pLeft); |
| 140165 | 140264 | return; |
| 140166 | 140265 | } |
| | @@ -140497,11 +140596,11 @@ |
| 140497 | 140596 | nChng++; |
| 140498 | 140597 | pSubq->selFlags |= SF_PushDown; |
| 140499 | 140598 | while( pSubq ){ |
| 140500 | 140599 | SubstContext x; |
| 140501 | 140600 | pNew = sqlite3ExprDup(pParse->db, pWhere, 0); |
| 140502 | | - unsetJoinExpr(pNew, -1); |
| 140601 | + unsetJoinExpr(pNew, -1, 1); |
| 140503 | 140602 | x.pParse = pParse; |
| 140504 | 140603 | x.iTable = pSrc->iCursor; |
| 140505 | 140604 | x.iNewTable = pSrc->iCursor; |
| 140506 | 140605 | x.isOuterJoin = 0; |
| 140507 | 140606 | x.pEList = pSubq->pEList; |
| | @@ -142181,11 +142280,12 @@ |
| 142181 | 142280 | && OptimizationEnabled(db, SQLITE_SimplifyJoin) |
| 142182 | 142281 | ){ |
| 142183 | 142282 | SELECTTRACE(0x100,pParse,p, |
| 142184 | 142283 | ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); |
| 142185 | 142284 | pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); |
| 142186 | | - unsetJoinExpr(p->pWhere, pItem->iCursor); |
| 142285 | + unsetJoinExpr(p->pWhere, pItem->iCursor, |
| 142286 | + pTabList->a[0].fg.jointype & JT_LTORJ); |
| 142187 | 142287 | } |
| 142188 | 142288 | |
| 142189 | 142289 | /* No futher action if this term of the FROM clause is no a subquery */ |
| 142190 | 142290 | if( pSub==0 ) continue; |
| 142191 | 142291 | |
| | @@ -145977,11 +146077,11 @@ |
| 145977 | 146077 | if( pPk ){ |
| 145978 | 146078 | sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey); |
| 145979 | 146079 | }else{ |
| 145980 | 146080 | sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid); |
| 145981 | 146081 | } |
| 145982 | | - VdbeCoverageNeverTaken(v); |
| 146082 | + VdbeCoverage(v); |
| 145983 | 146083 | } |
| 145984 | 146084 | |
| 145985 | 146085 | /* Do FK constraint checks. */ |
| 145986 | 146086 | if( hasFK ){ |
| 145987 | 146087 | sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey); |
| | @@ -154477,10 +154577,11 @@ |
| 154477 | 154577 | if( pOp->opcode==OP_Column ){ |
| 154478 | 154578 | pOp->opcode = OP_Copy; |
| 154479 | 154579 | pOp->p1 = pOp->p2 + iRegister; |
| 154480 | 154580 | pOp->p2 = pOp->p3; |
| 154481 | 154581 | pOp->p3 = 0; |
| 154582 | + pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ |
| 154482 | 154583 | }else if( pOp->opcode==OP_Rowid ){ |
| 154483 | 154584 | pOp->opcode = OP_Sequence; |
| 154484 | 154585 | pOp->p1 = iAutoidxCur; |
| 154485 | 154586 | #ifdef SQLITE_ALLOW_ROWID_IN_VIEW |
| 154486 | 154587 | if( iAutoidxCur==0 ){ |
| | @@ -154551,18 +154652,21 @@ |
| 154551 | 154652 | const Bitmask notReady /* Tables in outer loops of the join */ |
| 154552 | 154653 | ){ |
| 154553 | 154654 | char aff; |
| 154554 | 154655 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 154555 | 154656 | if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; |
| 154556 | | - if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 |
| 154557 | | - && !ExprHasProperty(pTerm->pExpr, EP_OuterON) |
| 154558 | | - && (pTerm->eOperator & WO_IS) |
| 154559 | | - ){ |
| 154560 | | - /* Cannot use an IS term from the WHERE clause as an index driver for |
| 154561 | | - ** the RHS of a LEFT JOIN or for the LHS of a RIGHT JOIN. Such a term |
| 154562 | | - ** can only be used if it is from the ON clause. */ |
| 154563 | | - return 0; |
| 154657 | + assert( (pSrc->fg.jointype & JT_RIGHT)==0 ); |
| 154658 | + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ |
| 154659 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); |
| 154660 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); |
| 154661 | + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) |
| 154662 | + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); |
| 154663 | + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) |
| 154664 | + || pTerm->pExpr->w.iJoin != pSrc->iCursor |
| 154665 | + ){ |
| 154666 | + return 0; /* See tag-20191211-001 */ |
| 154667 | + } |
| 154564 | 154668 | } |
| 154565 | 154669 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 154566 | 154670 | assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); |
| 154567 | 154671 | if( pTerm->u.x.leftColumn<0 ) return 0; |
| 154568 | 154672 | aff = pSrc->pTab->aCol[pTerm->u.x.leftColumn].affinity; |
| | @@ -154972,17 +155076,24 @@ |
| 154972 | 155076 | assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); |
| 154973 | 155077 | assert( pTerm->u.x.leftColumn>=XN_ROWID ); |
| 154974 | 155078 | assert( pTerm->u.x.leftColumn<pTab->nCol ); |
| 154975 | 155079 | |
| 154976 | 155080 | /* tag-20191211-002: WHERE-clause constraints are not useful to the |
| 154977 | | - ** right-hand table of a LEFT JOIN nor to the left-hand table of a |
| 155081 | + ** right-hand table of a LEFT JOIN nor to the either table of a |
| 154978 | 155082 | ** RIGHT JOIN. See tag-20191211-001 for the |
| 154979 | 155083 | ** equivalent restriction for ordinary tables. */ |
| 154980 | | - if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 |
| 154981 | | - && !ExprHasProperty(pTerm->pExpr, EP_OuterON) |
| 154982 | | - ){ |
| 154983 | | - continue; |
| 155084 | + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ |
| 155085 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); |
| 155086 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT ); |
| 155087 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); |
| 155088 | + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ); |
| 155089 | + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); |
| 155090 | + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) |
| 155091 | + || pTerm->pExpr->w.iJoin != pSrc->iCursor |
| 155092 | + ){ |
| 155093 | + continue; |
| 155094 | + } |
| 154984 | 155095 | } |
| 154985 | 155096 | nTerm++; |
| 154986 | 155097 | pTerm->wtFlags |= TERM_OK; |
| 154987 | 155098 | } |
| 154988 | 155099 | |
| | @@ -156628,16 +156739,32 @@ |
| 156628 | 156739 | ** to mix with a lower range bound from some other source */ |
| 156629 | 156740 | if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; |
| 156630 | 156741 | |
| 156631 | 156742 | /* tag-20191211-001: Do not allow constraints from the WHERE clause to |
| 156632 | 156743 | ** be used by the right table of a LEFT JOIN nor by the left table of a |
| 156633 | | - ** RIGHT JOIN. Only constraints in the |
| 156634 | | - ** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */ |
| 156635 | | - if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 |
| 156636 | | - && !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) |
| 156637 | | - ){ |
| 156638 | | - continue; |
| 156744 | + ** RIGHT JOIN. Only constraints in the ON clause are allowed. |
| 156745 | + ** See tag-20191211-002 for the vtab equivalent. |
| 156746 | + ** |
| 156747 | + ** 2022-06-06: See https://sqlite.org/forum/forumpost/206d99a16dd9212f |
| 156748 | + ** for an example of a WHERE clause constraints that may not be used on |
| 156749 | + ** the right table of a RIGHT JOIN because the constraint implies a |
| 156750 | + ** not-NULL condition on the left table of the RIGHT JOIN. |
| 156751 | + ** |
| 156752 | + ** 2022-06-10: The same condition applies to termCanDriveIndex() above. |
| 156753 | + ** https://sqlite.org/forum/forumpost/51e6959f61 |
| 156754 | + */ |
| 156755 | + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ |
| 156756 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); |
| 156757 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT ); |
| 156758 | + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); |
| 156759 | + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) |
| 156760 | + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); |
| 156761 | + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) |
| 156762 | + || pTerm->pExpr->w.iJoin != pSrc->iCursor |
| 156763 | + ){ |
| 156764 | + continue; |
| 156765 | + } |
| 156639 | 156766 | } |
| 156640 | 156767 | |
| 156641 | 156768 | if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ |
| 156642 | 156769 | pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE; |
| 156643 | 156770 | }else{ |
| | @@ -156985,27 +157112,30 @@ |
| 156985 | 157112 | /* Check to see if a partial index with pPartIndexWhere can be used |
| 156986 | 157113 | ** in the current query. Return true if it can be and false if not. |
| 156987 | 157114 | */ |
| 156988 | 157115 | static int whereUsablePartialIndex( |
| 156989 | 157116 | int iTab, /* The table for which we want an index */ |
| 156990 | | - int isLeft, /* True if iTab is the right table of a LEFT JOIN */ |
| 157117 | + u8 jointype, /* The JT_* flags on the join */ |
| 156991 | 157118 | WhereClause *pWC, /* The WHERE clause of the query */ |
| 156992 | 157119 | Expr *pWhere /* The WHERE clause from the partial index */ |
| 156993 | 157120 | ){ |
| 156994 | 157121 | int i; |
| 156995 | 157122 | WhereTerm *pTerm; |
| 156996 | | - Parse *pParse = pWC->pWInfo->pParse; |
| 157123 | + Parse *pParse; |
| 157124 | + |
| 157125 | + if( jointype & JT_LTORJ ) return 0; |
| 157126 | + pParse = pWC->pWInfo->pParse; |
| 156997 | 157127 | while( pWhere->op==TK_AND ){ |
| 156998 | | - if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0; |
| 157128 | + if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; |
| 156999 | 157129 | pWhere = pWhere->pRight; |
| 157000 | 157130 | } |
| 157001 | 157131 | if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; |
| 157002 | 157132 | for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 157003 | 157133 | Expr *pExpr; |
| 157004 | 157134 | pExpr = pTerm->pExpr; |
| 157005 | 157135 | if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) |
| 157006 | | - && (isLeft==0 || ExprHasProperty(pExpr, EP_OuterON)) |
| 157136 | + && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) |
| 157007 | 157137 | && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) |
| 157008 | 157138 | && (pTerm->wtFlags & TERM_VNULL)==0 |
| 157009 | 157139 | ){ |
| 157010 | 157140 | return 1; |
| 157011 | 157141 | } |
| | @@ -157167,13 +157297,12 @@ |
| 157167 | 157297 | /* Loop over all indices. If there was an INDEXED BY clause, then only |
| 157168 | 157298 | ** consider index pProbe. */ |
| 157169 | 157299 | for(; rc==SQLITE_OK && pProbe; |
| 157170 | 157300 | pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++ |
| 157171 | 157301 | ){ |
| 157172 | | - int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0; |
| 157173 | 157302 | if( pProbe->pPartIdxWhere!=0 |
| 157174 | | - && !whereUsablePartialIndex(pSrc->iCursor, isLeft, pWC, |
| 157303 | + && !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC, |
| 157175 | 157304 | pProbe->pPartIdxWhere) |
| 157176 | 157305 | ){ |
| 157177 | 157306 | testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */ |
| 157178 | 157307 | continue; /* Partial index inappropriate for this query */ |
| 157179 | 157308 | } |
| | @@ -236446,11 +236575,11 @@ |
| 236446 | 236575 | int nArg, /* Number of args */ |
| 236447 | 236576 | sqlite3_value **apUnused /* Function arguments */ |
| 236448 | 236577 | ){ |
| 236449 | 236578 | assert( nArg==0 ); |
| 236450 | 236579 | UNUSED_PARAM2(nArg, apUnused); |
| 236451 | | - sqlite3_result_text(pCtx, "fts5: 2022-06-03 14:08:40 d18818afc6021a32989499c26ab38fa442e5a55e662e39bb06e5c0daa9c65e25", -1, SQLITE_TRANSIENT); |
| 236580 | + sqlite3_result_text(pCtx, "fts5: 2022-06-15 16:26:37 56c60a35ea457f06db58ec3f694a1ae16fd03e6625da1d7879d63d72bbcb1c62", -1, SQLITE_TRANSIENT); |
| 236452 | 236581 | } |
| 236453 | 236582 | |
| 236454 | 236583 | /* |
| 236455 | 236584 | ** Return true if zName is the extension on one of the shadow tables used |
| 236456 | 236585 | ** by this module. |
| 236457 | 236586 | |