Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Further performance tweaks to OP_Column. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | OP_Column-refactor |
Files: | files | file ages | folders |
SHA1: |
0e3f5df695216a27602a53eed5d25231 |
User & Date: | drh 2013-11-20 19:28:03.982 |
Context
2013-11-20
| ||
20:58 | Improved comments on the OP_Column changes. Optimize out loading of overflow pages for content with zero length. Add test cases for the latter. (check-in: 0e05679db7 user: drh tags: OP_Column-refactor) | |
19:28 | Further performance tweaks to OP_Column. (check-in: 0e3f5df695 user: drh tags: OP_Column-refactor) | |
17:25 | Refactoring the OP_Column opcode for improved performance and maintainability. (check-in: 7c914e3997 user: drh tags: OP_Column-refactor) | |
Changes
Changes to src/vdbe.c.
︙ | ︙ | |||
2251 2252 2253 2254 2255 2256 2257 | ** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when ** the result is guaranteed to only be used as the argument of a length() ** or typeof() function, respectively. The loading of large blobs can be ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { i64 payloadSize64; /* Number of bytes in the record */ | < < < < < > | < | > > | | | | | | | | | | | | | | | > > | 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 | ** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when ** the result is guaranteed to only be used as the argument of a length() ** or typeof() function, respectively. The loading of large blobs can be ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { i64 payloadSize64; /* Number of bytes in the record */ int p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ BtCursor *pCrsr; /* The BTree cursor */ u32 *aType; /* aType[i] holds the numeric type of the i-th column */ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ int len; /* The length of the serialized data for the column */ int i; /* Loop counter */ Mem *pDest; /* Where to write the extracted value */ Mem sMem; /* For storing the record being decoded */ const u8 *zData; /* Part of the record being decoded */ const u8 *zHdr; /* Next unparsed byte of the header */ const u8 *zEndHdr; /* Pointer to first byte after the header */ u32 offset; /* Offset into the data */ u32 szField; /* Number of bytes in the content of a field */ int avail; /* Number of bytes of available data */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ p2 = pOp->p2; assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( p2<pC->nField ); aType = pC->aType; aOffset = pC->aOffset; #ifndef SQLITE_OMIT_VIRTUALTABLE assert( pC->pVtabCursor==0 ); #endif pCrsr = pC->pCursor; assert( pCrsr!=0 || pC->pseudoTableReg>0 ); assert( pC->pseudoTableReg==0 || pC->nullRow ); /* If the cursor cache is stale, bring it up-to-date */ rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){ if( pC->nullRow ){ if( pCrsr==0 ){ assert( pC->pseudoTableReg>0 ); pReg = &aMem[pC->pseudoTableReg]; if( pC->multiPseudo ){ sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); Deephemeralize(pDest); goto op_column_out; } assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); pC->payloadSize = pC->szRow = avail = pReg->n; pC->aRow = (u8*)pReg->z; }else{ MemSetTypeFlag(pDest, MEM_Null); goto op_column_out; } }else{ assert( pCrsr ); if( pC->isIndex ){ assert( sqlite3BtreeCursorIsValid(pCrsr) ); VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64); assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the ** payload size, so it is impossible for payloadSize64 to be ** larger than 32 bits. */ |
︙ | ︙ | |||
2357 2358 2359 2360 2361 2362 2363 | ** types use so much data space that there can only be 4096 and 32 of ** them, respectively. So the maximum header length results from a ** 3-byte type for each of the maximum of 32768 columns plus three ** extra bytes for the header length itself. 32768*3 + 3 = 98307. */ if( offset > 98307 || offset > pC->payloadSize ){ rc = SQLITE_CORRUPT_BKPT; | | | > > > > | | | | > | | | | | | | | | | | | | | > | | | | | | | | | | | | | > | < > | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | < < < < < < < < < < | | | | < | | | | | | | | | | | > > | 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 | ** types use so much data space that there can only be 4096 and 32 of ** them, respectively. So the maximum header length results from a ** 3-byte type for each of the maximum of 32768 columns plus three ** extra bytes for the header length itself. 32768*3 + 3 = 98307. */ if( offset > 98307 || offset > pC->payloadSize ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_error; } } /* Make sure at least the first p2+1 entries of the header have been ** parsed and valid information is in aOffset[] and aType[]. */ if( pC->nHdrParsed<=p2 ){ /* If there is more header available for parsing, try to extract ** additional fields up through the p2-th field */ if( pC->iHdrOffset<aOffset[0] ){ /* Make sure zData points to enough of the record to cover the header. */ if( pC->aRow==0 ){ memset(&sMem, 0, sizeof(sMem)); rc = sqlite3VdbeMemFromBtree(pCrsr, 0, pC->aOffset[0], pC->isIndex, &sMem); if( rc!=SQLITE_OK ){ goto op_column_error; } zData = (u8*)sMem.z; }else{ zData = pC->aRow; } /* Fill in aType[i] and aOffset[i] values through the p2-th field. */ i = pC->nHdrParsed; offset = aOffset[i]; zHdr = zData + pC->iHdrOffset; zEndHdr = zData + aOffset[0]; assert( i<=p2 && zHdr<zEndHdr ); do{ if( zHdr[0]<0x80 ){ t = zHdr[0]; zHdr++; }else{ zHdr += sqlite3GetVarint32(zHdr, &t); } aType[i] = t; szField = sqlite3VdbeSerialTypeLen(t); offset += szField; if( offset<szField ){ /* True if offset overflows */ zHdr = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */ break; } i++; aOffset[i] = offset; }while( i<=p2 && zHdr<zEndHdr ); pC->nHdrParsed = i; pC->iHdrOffset = (u32)(zHdr - zData); if( pC->aRow==0 ){ sqlite3VdbeMemRelease(&sMem); sMem.flags = MEM_Null; } /* If we have read more header data than was contained in the header, ** or if the end of the last field appears to be past the end of the ** record, or if the end of the last field appears to be before the end ** of the record (when all fields present), then we must be dealing ** with a corrupt database. */ if( (zHdr > zEndHdr) || (offset > pC->payloadSize) || (zHdr==zEndHdr && offset!=pC->payloadSize) ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_error; } } /* If after nHdrParsed is still not up to p2, that means that the record ** has fewer than p2 columns. So the result will be either the default ** value or a NULL. */ if( pC->nHdrParsed<=p2 ){ if( pOp->p4type==P4_MEM ){ sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); }else{ MemSetTypeFlag(pDest, MEM_Null); } goto op_column_out; } } /* Get the column information. If aOffset[p2] is non-zero, then ** deserialize the value from the record. If aOffset[p2] is zero, ** then there are not enough fields in the record to satisfy the ** request. In this case, set the value NULL or to P4 if P4 is ** a pointer to a Mem object. */ assert( p2<pC->nHdrParsed ); assert( rc==SQLITE_OK ); if( pC->szRow>=aOffset[p2+1] ){ /* This is the common case where the whole row fits on a single page */ VdbeMemRelease(pDest); sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); }else{ /* This branch happens only when the row overflows onto multiple pages */ t = aType[p2]; if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0) ){ /* Content is irrelevant for the typeof() function and for ** the length(X) function if X is a blob. So we might as well use ** bogus content rather than reading content from disk. NULL works ** for text and blob and whatever is in the payloadSize64 variable ** will work for everything else. */ zData = t<12 ? (u8*)&payloadSize64 : 0; sMem.zMalloc = 0; }else{ len = sqlite3VdbeSerialTypeLen(t); memset(&sMem, 0, sizeof(sMem)); sqlite3VdbeMemMove(&sMem, pDest); rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, &sMem); if( rc!=SQLITE_OK ){ goto op_column_error; } zData = (u8*)sMem.z; } sqlite3VdbeSerialGet(zData, t, pDest); /* If we dynamically allocated space to hold the data (in the ** sqlite3VdbeMemFromBtree() call above) then transfer control of that ** dynamically allocated space over to the pDest structure. ** This prevents a memory copy. */ if( sMem.zMalloc ){ assert( sMem.z==sMem.zMalloc ); assert( !(pDest->flags & MEM_Dyn) ); assert( !(pDest->flags & (MEM_Blob|MEM_Str)) || pDest->z==sMem.z ); pDest->flags &= ~(MEM_Ephem|MEM_Static); pDest->flags |= MEM_Term; pDest->z = sMem.z; pDest->zMalloc = sMem.zMalloc; } } pDest->enc = encoding; op_column_out: rc = sqlite3VdbeMemMakeWriteable(pDest); op_column_error: UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); break; } /* Opcode: Affinity P1 P2 * P4 * ** Synopsis: affinity(r[P1@P2]) |
︙ | ︙ |
Changes to test/analyze9.test.
︙ | ︙ | |||
801 802 803 804 805 806 807 808 | reset_db execsql { CREATE TABLE t1(a, UNIQUE(a)); INSERT INTO t1 VALUES($two); ANALYZE; } set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] | > | | 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 | reset_db execsql { CREATE TABLE t1(a, UNIQUE(a)); INSERT INTO t1 VALUES($two); ANALYZE; } set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] puts -nonewline " (nByte=$nByte nByte2=$nByte2)" expr {$nByte2 > $nByte+900 && $nByte2 < $nByte+1100} } {1} #------------------------------------------------------------------------- # Test that stat4 data may be used with partial indexes. # do_test 17.1 { reset_db |
︙ | ︙ |