Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Where possible insert the set of new keys for each index in sorted order within "INSERT INTO ... SELECT" statements. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | insert-select-opt |
Files: | files | file ages | folders |
SHA1: |
0a7f2051b2cc9b4e060fd60815875021 |
User & Date: | dan 2015-03-20 20:30:26.758 |
Context
2015-03-21
| ||
07:03 | Disable the sorter optimization used by INSERT INTO SELECT statements if the statement explicitly specifies REPLACE, IGNORE or FAIL conflict handling. (check-in: d421594297 user: dan tags: insert-select-opt) | |
2015-03-20
| ||
20:30 | Where possible insert the set of new keys for each index in sorted order within "INSERT INTO ... SELECT" statements. (check-in: 0a7f2051b2 user: dan tags: insert-select-opt) | |
08:43 | Fix a problem causing collation sequence names to be dequoted multiple times under some circumstances. (check-in: eddc05e7bb user: dan tags: trunk) | |
Changes
Changes to src/insert.c.
︙ | |||
334 335 336 337 338 339 340 341 342 343 344 345 346 347 | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | + + + + + + + + + + + + + + + + + | static int xferOptimization( Parse *pParse, /* Parser context */ Table *pDest, /* The table we are inserting into */ Select *pSelect, /* A SELECT statement to use as the data source */ int onError, /* How to handle constraint errors */ int iDbDest /* The database of pDest */ ); /* ** Return the conflict handling mode that should be used for index pIdx ** if the statement specified conflict mode overrideError. ** ** If the index is not a UNIQUE index, then the conflict handling mode is ** always OE_None. Otherwise, it is one of OE_Abort, OE_Rollback, OE_Fail, ** OE_Ignore or OE_Replace. */ static u8 idxConflictMode(Index *pIdx, u8 overrideError){ u8 ret = pIdx->onError; if( ret!=OE_None ){ if( overrideError!=OE_Default ) ret = overrideError; if( ret==OE_Default ) ret = OE_Abort; } return ret; } /* ** This routine is called to handle SQL of the following forms: ** ** insert into TABLE (IDLIST) values(EXPRLIST) ** insert into TABLE (IDLIST) select ** |
︙ | |||
447 448 449 450 451 452 453 454 455 456 457 458 459 460 | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | + | int i, j, idx; /* Loop counters */ Vdbe *v; /* Generate code into this virtual machine */ Index *pIdx; /* For looping over indices of the table */ int nColumn; /* Number of columns in the data */ int nHidden = 0; /* Number of hidden columns if TABLE is virtual */ int iDataCur = 0; /* VDBE cursor that is the main data repository */ int iIdxCur = 0; /* First index cursor */ int iSortCur = 0; /* First sorter cursor (for INSERT INTO ... SELECT) */ int ipkColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ int endOfLoop; /* Label for the end of the insertion loop */ int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ int addrInsTop = 0; /* Jump to label "D" */ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */ int iDb; /* Index of database holding TABLE */ |
︙ | |||
751 752 753 754 755 756 757 758 759 760 761 762 763 764 | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1)); if( aRegIdx==0 ){ goto insert_cleanup; } for(i=0; i<nIdx; i++){ aRegIdx[i] = ++pParse->nMem; } /* If this is an INSERT INTO ... SELECT statement on a non-virtual table, ** check if it is possible to defer updating any indexes until after ** all rows have been processed. If it is, the index keys can be sorted ** before they are inserted into the index b-tree, which is more efficient ** for large inserts. It is possible to defer updating the indexes if: ** ** * there are no triggers to fire, and ** * no foreign key processing to perform, and ** * the on-conflict mode used for all UNIQUE indexes is either ** ROLLBACK or ABORT. */ if( pSelect && !IsVirtual(pTab) && pTrigger==0 && 0==sqlite3FkRequired(pParse, pTab, 0, 0) ){ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ u8 oe = idxConflictMode(pIdx, onError); if( oe==OE_Fail || oe==OE_Replace || oe==OE_Ignore ) break; assert( oe==OE_None||oe==OE_Abort||oe==OE_Rollback ); } if( pIdx==0 ){ /* This statement can sort the set of new keys for each index before ** writing them into the b-tree on disk. So open a sorter for each ** index on the table. */ iSortCur = pParse->nTab; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ sqlite3VdbeAddOp1(v, OP_SorterOpen, pParse->nTab++); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); } assert( iSortCur>0 ); } } } /* This is the top of the main insertion loop */ if( useTempTable ){ /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 4): ** |
︙ | |||
952 953 954 955 956 957 958 959 | 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | + + - + + + + + + - - + + + | sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite3MayAbort(pParse); }else #endif { int isReplace; /* Set to true if constraints may cause a replace */ int iIdxBase = iIdxCur; int op = OP_IdxInsert; sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, |
︙ | |||
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 | 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 | + + + + + + + + + + + + + + + + + + + + + + + + + | sqlite3VdbeAddOp1(v, OP_Close, srcTab); }else if( pSelect ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrCont); sqlite3VdbeJumpHere(v, addrInsTop); } if( !IsVirtual(pTab) && !isView ){ /* If new index keys were written into sorter objects instead of ** directly to the index b-trees, copy them from the sorters into the ** indexes now. And close all the sorters. */ if( iSortCur ){ int iTmp = sqlite3GetTempReg(pParse); for(idx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ int oe = idxConflictMode(pIdx, onError); int iCur = iSortCur + idx; int iIdx = iIdxCur + idx; int addr = sqlite3VdbeAddOp1(v, OP_SorterSort, iCur); sqlite3VdbeAddOp3(v, OP_SorterData, iCur, iTmp, iIdx); if( oe!=OE_None ){ int nField = -1 * pIdx->nKeyCol; int jmp = sqlite3VdbeCurrentAddr(v)+2; sqlite3VdbeAddOp4Int(v, OP_NoConflict, iIdx, jmp, iTmp, nField); sqlite3UniqueConstraint(pParse, oe, pIdx); } sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, iTmp); sqlite3VdbeAddOp2(v, OP_SorterNext, iCur, addr+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp1(v, OP_Close, iCur); } sqlite3ReleaseTempReg(pParse, iTmp); } /* Close all tables opened */ if( iDataCur<iIdxCur ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur); for(idx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ sqlite3VdbeAddOp1(v, OP_Close, idx+iIdxCur); } } |
︙ | |||
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 | 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 | + | int iDataCur, /* Canonical data cursor (main table or PK index) */ int iIdxCur, /* First index cursor */ int regNewData, /* First register in a range holding values to insert */ int regOldData, /* Previous content. 0 for INSERTs */ u8 pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */ u8 overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ int ignoreUnique, /* Do not enforce UNIQUE constraints */ int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ ){ Vdbe *v; /* VDBE under constrution */ Index *pIdx; /* Pointer to one of the indices */ Index *pPk = 0; /* The PRIMARY KEY index */ sqlite3 *db; /* Database connection */ int i; /* loop counter */ |
︙ | |||
1409 1410 1411 1412 1413 1414 1415 | 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 | - - + + - - - - - | ** logic below can all be skipped. */ if( isUpdate && pPk==pIdx && pkChng==0 ){ sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } /* Find out what action to take in case there is a uniqueness conflict */ |
︙ | |||
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 | 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 | + + + - + | */ void sqlite3CompleteInsertion( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int iDataCur, /* Cursor of the canonical data source */ int iIdxCur, /* First index cursor */ int regNewData, /* Range of content */ int idxop, /* Opcode to use to write to "indexes" */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ int isUpdate, /* True for UPDATE, False for INSERT */ int appendBias, /* True if this is likely to be an append */ int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */ ){ Vdbe *v; /* Prepared statements under construction */ Index *pIdx; /* An index being inserted or updated */ u8 pik_flags; /* flag values passed to the btree insert */ int regData; /* Content registers (after the rowid) */ int regRec; /* Register holding assembled record for the table */ int i; /* Loop counter */ u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */ assert( idxop==OP_IdxInsert || idxop==OP_SorterInsert ); v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( aRegIdx[i]==0 ) continue; bAffinityDone = 1; if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2); VdbeCoverage(v); } |
︙ |
Changes to src/sqliteInt.h.
︙ | |||
3327 3328 3329 3330 3331 3332 3333 | 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 | - - + + | int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); int sqlite3IsRowid(const char*); void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8); void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); void sqlite3ResolvePartIdxLabel(Parse*,int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, |
︙ |
Changes to src/update.c.
︙ | |||
563 564 565 566 567 568 569 | 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | - + | if( !isView ){ int j1 = 0; /* Address of jump instruction */ int bReplace = 0; /* True if REPLACE conflict resolution might happen */ /* Do constraint checks. */ assert( regOldRowid>0 ); sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, |
︙ | |||
595 596 597 598 599 600 601 | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 | - + | if( hasFK ){ sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey); } /* Insert the new index entries and the new record. */ sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, |
︙ |
Changes to src/vdbe.c.
︙ | |||
160 161 162 163 164 165 166 | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | - + | ** string that the register itself controls. In other words, it ** converts an MEM_Ephem string into a string with P.z==P.zMalloc. */ #define Deephemeralize(P) \ if( ((P)->flags&MEM_Ephem)!=0 \ && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} |
︙ | |||
3765 3766 3767 3768 3769 3770 3771 | 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 | - + + | ** See also: Found, NotExists, NoConflict */ /* Opcode: NoConflict P1 P2 P3 P4 * ** Synopsis: key=r[P3@P4] ** ** If P4==0 then register P3 holds a blob constructed by MakeRecord. If ** P4>0 then register P3 is the first of P4 registers that form an unpacked |
︙ | |||
3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 | 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 | + + + - + | pIdxKey = sqlite3VdbeAllocUnpackedRecord( pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree ); if( pIdxKey==0 ) goto no_mem; assert( pIn3->flags & MEM_Blob ); ExpandBlob(pIn3); sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); if( pOp->p4.i<0 ){ pIdxKey->nField = pOp->p4.i * -1; } } pIdxKey->default_rc = 0; if( pOp->opcode==OP_NoConflict ){ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not ** conflict */ for(ii=0; ii<pIdxKey->nField; ii++){ if( pIdxKey->aMem[ii].flags & MEM_Null ){ pc = pOp->p2 - 1; VdbeBranchTaken(1,2); break; } } } rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res); |
︙ |
Changes to test/e_vacuum.test.
︙ | |||
25 26 27 28 29 30 31 | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | - - - - - - - + + + + + + + - + | db transaction { execsql { PRAGMA page_size = 1024; } execsql $sql execsql { CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 VALUES(1, randomblob(400)); |
︙ | |||
123 124 125 126 127 128 129 | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | - + | # less fragmented. # ifcapable vtab&&compound { create_db register_dbstat_vtab db do_execsql_test e_vacuum-1.2.1 { DELETE FROM t1 WHERE a%2; |
︙ |
Added test/insert6.test.