Enhance the code generator for INSERT INTO ... SELECT so that the SELECT
generates output directly in the registers that INSERT INTO will be using, in many cases, and OP_SCopy operations can thus be avoided. FossilOrigin-Name: aa2d8b0e8154dd2f5e2c837dc11ab362b083495b
This commit is contained in:
parent
cfc6ca4179
commit
05a86c5c0f
27
manifest
27
manifest
@ -1,5 +1,5 @@
|
||||
C Seek\spast\sNULLs\sin\sa\stop-constrained\ssearch.\s\sAvoid\schecking\sfor\sNULLs\sin\nthe\sbody\sof\sthe\ssearch.
|
||||
D 2014-02-14T23:49:13.552
|
||||
C Enhance\sthe\scode\sgenerator\sfor\sINSERT\sINTO\s...\sSELECT\sso\sthat\sthe\sSELECT\ngenerates\soutput\sdirectly\sin\sthe\sregisters\sthat\sINSERT\sINTO\swill\sbe\susing,\nin\smany\scases,\sand\sOP_SCopy\soperations\scan\sthus\sbe\savoided.
|
||||
D 2014-02-16T01:55:49.753
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -172,7 +172,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 77779efbe78dd678d84bfb4fc2e87b6b6ad8dccd
|
||||
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
|
||||
F src/delete.c 6765a421f08adbedc5d52d21760ec6dbe5123fd3
|
||||
F src/expr.c 9bea427f95665c1aa8fdc87b7678546eef50c296
|
||||
F src/expr.c 90bba0ca6ec97d6857458f08a8ad2820130e13cf
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 2ab0f5384b70594468ef3ac5c7ed8ca24bfd17d5
|
||||
F src/func.c f4499b39d66b71825514334ce67b32ff14bd19f5
|
||||
@ -180,7 +180,7 @@ F src/global.c 1d7bb7ea8254ae6a68ed9bfaf65fcb3d1690b486
|
||||
F src/hash.c d139319967164f139c8d1bb8a11b14db9c4ba3cd
|
||||
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c 89526b031cf8f0e2306f02099a73cee56110c580
|
||||
F src/insert.c 36e61dd2201c34a11886487e7afb86f3451ffc52
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
@ -216,12 +216,12 @@ F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269
|
||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||
F src/resolve.c ca8b99d894164435f5c55cb304c1b8121705c51e
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c 50961f0d0ab8f2d45ff29ec5f91d8db221330ca7
|
||||
F src/select.c ebec4d3fad7fd5aa33cd69e2f50e9c109285dc73
|
||||
F src/shell.c 3dd86bf73ccd079f0e32ef5069600586085e8239
|
||||
F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b
|
||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h 5b1f4e30c8332270e4c14254ef1fbcbd92a4a4c7
|
||||
F src/sqliteInt.h 82aa6a9b068b5a827e85af0b7fa12661f5874459
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@ -277,7 +277,7 @@ F src/update.c a7df6fffce6bfedc578fda6136dd33e34a63f8ee
|
||||
F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
|
||||
F src/util.c c46c90459ef9bdc0c6c73803cf4c55425b4771cf
|
||||
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
|
||||
F src/vdbe.c 543ed4ed0c41b34affad239374d4c07e6e5b2401
|
||||
F src/vdbe.c 7b74ce685d05c1123b0360ce6bc377df114b8533
|
||||
F src/vdbe.h e6c4c610fcabad4fa80ebb1efc6822a9367e2b26
|
||||
F src/vdbeInt.h 5286af9067cabdb8ba57b87c0c988a931be6c6c8
|
||||
F src/vdbeapi.c 5bc41aaea448a7fc250902c418f1795859be3820
|
||||
@ -613,10 +613,10 @@ F test/index7.test a3baf9a625bda7fd49471e99aeae04095fbfeecf
|
||||
F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
F test/insert.test 489aa12a027c83d291f5034a83c8c32e6be1dca2
|
||||
F test/insert.test c120294273b18cf9c8b1a3158131f7a825bdba41
|
||||
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
|
||||
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
|
||||
F test/insert4.test b00ddf82d6d4f0a6f3999f42bb6a3c813ea70707
|
||||
F test/insert4.test 4791662c50518bdd37d394cae9a7a8014e845bb3
|
||||
F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
|
||||
F test/instr.test 737bbf80685232033f3abedc6ae92f75860b5dd2
|
||||
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
|
||||
@ -1150,7 +1150,10 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||
P 3c1ae447dec8fc2af1c5105134061717594ac0e0
|
||||
R 20e62eafcdc3ac9a7a16fa36432e6177
|
||||
P e07a32f30862acf3b322d4d8deb015846d6f8f5f
|
||||
R 4c464651f87f73c0925f058052453922
|
||||
T *branch * insert-optimization
|
||||
T *sym-insert-optimization *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z f35a13e794e2689c6821aa3d892fcd6f
|
||||
Z fb4eb5dbe58efaaeaac33be3bebd9bdd
|
||||
|
@ -1 +1 @@
|
||||
e07a32f30862acf3b322d4d8deb015846d6f8f5f
|
||||
aa2d8b0e8154dd2f5e2c837dc11ab362b083495b
|
42
src/expr.c
42
src/expr.c
@ -3112,7 +3112,7 @@ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
|
||||
** results in register target. The results are guaranteed to appear
|
||||
** in register target.
|
||||
*/
|
||||
int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
|
||||
void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
|
||||
int inReg;
|
||||
|
||||
assert( target>0 && target<=pParse->nMem );
|
||||
@ -3125,7 +3125,20 @@ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will evaluate expression pExpr and store the
|
||||
** results in register target. The results are guaranteed to appear
|
||||
** in register target. If the expression is constant, then this routine
|
||||
** might choose to code the expression at initialization time.
|
||||
*/
|
||||
void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
|
||||
if( pParse->okConstFactor && sqlite3ExprIsConstant(pExpr) ){
|
||||
sqlite3ExprCodeAtInit(pParse, pExpr, target, 0);
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pExpr, target);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3140,25 +3153,16 @@ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
|
||||
** times. They are evaluated once and the results of the expression
|
||||
** are reused.
|
||||
*/
|
||||
int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
|
||||
void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int inReg;
|
||||
inReg = sqlite3ExprCode(pParse, pExpr, target);
|
||||
int iMem;
|
||||
|
||||
assert( target>0 );
|
||||
/* The only place, other than this routine, where expressions can be
|
||||
** converted to TK_REGISTER is internal subexpressions in BETWEEN and
|
||||
** CASE operators. Neither ever calls this routine. And this routine
|
||||
** is never called twice on the same expression. Hence it is impossible
|
||||
** for the input to this routine to already be a register. Nevertheless,
|
||||
** it seems prudent to keep the ALWAYS() in case the conditions above
|
||||
** change with future modifications or enhancements. */
|
||||
if( ALWAYS(pExpr->op!=TK_REGISTER) ){
|
||||
int iMem;
|
||||
iMem = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem);
|
||||
exprToRegister(pExpr, iMem);
|
||||
}
|
||||
return inReg;
|
||||
assert( pExpr->op!=TK_REGISTER );
|
||||
sqlite3ExprCode(pParse, pExpr, target);
|
||||
iMem = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, target, iMem);
|
||||
exprToRegister(pExpr, iMem);
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
|
232
src/insert.c
232
src/insert.c
@ -148,7 +148,7 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
|
||||
** a statement of the form "INSERT INTO <iDb, pTab> SELECT ..." can
|
||||
** run without using temporary table for the results of the SELECT.
|
||||
*/
|
||||
static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){
|
||||
static int readsTable(Parse *p, int iDb, Table *pTab){
|
||||
Vdbe *v = sqlite3GetVdbe(p);
|
||||
int i;
|
||||
int iEnd = sqlite3VdbeCurrentAddr(v);
|
||||
@ -156,7 +156,7 @@ static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){
|
||||
VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0;
|
||||
#endif
|
||||
|
||||
for(i=iStartAddr; i<iEnd; i++){
|
||||
for(i=1; i<iEnd; i++){
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(v, i);
|
||||
assert( pOp!=0 );
|
||||
if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){
|
||||
@ -335,79 +335,6 @@ void sqlite3AutoincrementEnd(Parse *pParse){
|
||||
#endif /* SQLITE_OMIT_AUTOINCREMENT */
|
||||
|
||||
|
||||
/*
|
||||
** Generate code for a co-routine that will evaluate a subquery one
|
||||
** row at a time.
|
||||
**
|
||||
** The pSelect parameter is the subquery that the co-routine will evaluation.
|
||||
** Information about the location of co-routine and the registers it will use
|
||||
** is returned by filling in the pDest object.
|
||||
**
|
||||
** Registers are allocated as follows:
|
||||
**
|
||||
** pDest->iSDParm The register holding the next entry-point of the
|
||||
** co-routine. Run the co-routine to its next breakpoint
|
||||
** by calling "OP_Yield $X" where $X is pDest->iSDParm.
|
||||
**
|
||||
** pDest->iSdst First result register.
|
||||
**
|
||||
** pDest->nSdst Number of result registers.
|
||||
**
|
||||
** At EOF the first result register will be marked as "undefined" so that
|
||||
** the caller can know when to stop reading results.
|
||||
**
|
||||
** This routine handles all of the register allocation and fills in the
|
||||
** pDest structure appropriately.
|
||||
**
|
||||
** Here is a schematic of the generated code assuming that X is the
|
||||
** co-routine entry-point register reg[pDest->iSDParm], that EOF is the
|
||||
** completed flag reg[pDest->iSDParm+1], and R and S are the range of
|
||||
** registers that hold the result set, reg[pDest->iSdst] through
|
||||
** reg[pDest->iSdst+pDest->nSdst-1]:
|
||||
**
|
||||
** X <- A
|
||||
** goto B
|
||||
** A: setup for the SELECT
|
||||
** loop rows in the SELECT
|
||||
** load results into registers R..S
|
||||
** yield X
|
||||
** end loop
|
||||
** cleanup after the SELECT
|
||||
** end co-routine R
|
||||
** B:
|
||||
**
|
||||
** To use this subroutine, the caller generates code as follows:
|
||||
**
|
||||
** [ Co-routine generated by this subroutine, shown above ]
|
||||
** S: yield X, at EOF goto E
|
||||
** if skip this row, goto C
|
||||
** if terminate loop, goto E
|
||||
** deal with this row
|
||||
** C: goto S
|
||||
** E:
|
||||
*/
|
||||
int sqlite3CodeCoroutine(Parse *pParse, Select *pSelect, SelectDest *pDest){
|
||||
int regYield; /* Register holding co-routine entry-point */
|
||||
int addrTop; /* Top of the co-routine */
|
||||
int rc; /* Result code */
|
||||
Vdbe *v; /* VDBE under construction */
|
||||
|
||||
regYield = ++pParse->nMem;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
|
||||
sqlite3SelectDestInit(pDest, SRT_Coroutine, regYield);
|
||||
rc = sqlite3Select(pParse, pSelect, pDest);
|
||||
assert( pParse->nErr==0 || rc );
|
||||
if( pParse->db->mallocFailed && rc==SQLITE_OK ) rc = SQLITE_NOMEM;
|
||||
if( rc ) return rc;
|
||||
sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
|
||||
sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
static int xferOptimization(
|
||||
Parse *pParse, /* Parser context */
|
||||
@ -531,16 +458,16 @@ void sqlite3Insert(
|
||||
int iIdxCur = 0; /* First index cursor */
|
||||
int ipkColumn = -1; /* Column that is the INTEGER PRIMARY KEY */
|
||||
int endOfLoop; /* Label for the end of the insertion loop */
|
||||
int useTempTable = 0; /* Store SELECT results in intermediate table */
|
||||
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 */
|
||||
int addrSelect = 0; /* Address of coroutine that implements the SELECT */
|
||||
SelectDest dest; /* Destination for SELECT on rhs of INSERT */
|
||||
int iDb; /* Index of database holding TABLE */
|
||||
Db *pDb; /* The database containing table being inserted into */
|
||||
int appendFlag = 0; /* True if the insert is likely to be an append */
|
||||
int withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
|
||||
u8 useTempTable = 0; /* Store SELECT results in intermediate table */
|
||||
u8 appendFlag = 0; /* True if the insert is likely to be an append */
|
||||
u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
|
||||
u8 bIdListInOrder = 1; /* True if IDLIST is in table order */
|
||||
ExprList *pList = 0; /* List of VALUES() to be inserted */
|
||||
|
||||
/* Register allocations */
|
||||
@ -652,6 +579,56 @@ void sqlite3Insert(
|
||||
*/
|
||||
regAutoinc = autoIncBegin(pParse, iDb, pTab);
|
||||
|
||||
/* Allocate registers for holding the rowid of the new row,
|
||||
** the content of the new row, and the assemblied row record.
|
||||
*/
|
||||
regRowid = regIns = pParse->nMem+1;
|
||||
pParse->nMem += pTab->nCol + 1;
|
||||
if( IsVirtual(pTab) ){
|
||||
regRowid++;
|
||||
pParse->nMem++;
|
||||
}
|
||||
regData = regRowid+1;
|
||||
|
||||
/* If the INSERT statement included an IDLIST term, then make sure
|
||||
** all elements of the IDLIST really are columns of the table and
|
||||
** remember the column indices.
|
||||
**
|
||||
** If the table has an INTEGER PRIMARY KEY column and that column
|
||||
** is named in the IDLIST, then record in the ipkColumn variable
|
||||
** the index into IDLIST of the primary key column. ipkColumn is
|
||||
** the index of the primary key as it appears in IDLIST, not as
|
||||
** is appears in the original table. (The index of the INTEGER
|
||||
** PRIMARY KEY in the original table is pTab->iPKey.)
|
||||
*/
|
||||
if( pColumn ){
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
pColumn->a[i].idx = -1;
|
||||
}
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
|
||||
pColumn->a[i].idx = j;
|
||||
if( i!=j ) bIdListInOrder = 0;
|
||||
if( j==pTab->iPKey ){
|
||||
ipkColumn = i; assert( !withoutRowid );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){
|
||||
ipkColumn = i;
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "table %S has no column named %s",
|
||||
pTabList, 0, pColumn->a[i].zName);
|
||||
pParse->checkSchema = 1;
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out how many columns of data are supplied. If the data
|
||||
** is coming from a SELECT statement, then generate a co-routine that
|
||||
** produces a single row of the SELECT on each invocation. The
|
||||
@ -659,13 +636,24 @@ void sqlite3Insert(
|
||||
*/
|
||||
if( pSelect ){
|
||||
/* Data is coming from a SELECT. Generate a co-routine to run the SELECT */
|
||||
int rc = sqlite3CodeCoroutine(pParse, pSelect, &dest);
|
||||
if( rc ) goto insert_cleanup;
|
||||
int regYield; /* Register holding co-routine entry-point */
|
||||
int addrTop; /* Top of the co-routine */
|
||||
int rc; /* Result code */
|
||||
|
||||
regYield = ++pParse->nMem;
|
||||
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
|
||||
dest.iSdst = bIdListInOrder ? regData : 0;
|
||||
dest.nSdst = pTab->nCol;
|
||||
rc = sqlite3Select(pParse, pSelect, &dest);
|
||||
regFromSelect = dest.iSdst;
|
||||
assert( pParse->nErr==0 || rc );
|
||||
if( rc || db->mallocFailed ) goto insert_cleanup;
|
||||
sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
|
||||
sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
|
||||
assert( pSelect->pEList );
|
||||
nColumn = pSelect->pEList->nExpr;
|
||||
assert( dest.nSdst==nColumn );
|
||||
|
||||
/* Set useTempTable to TRUE if the result of the SELECT statement
|
||||
** should be written into a temporary table (template 4). Set to
|
||||
@ -676,7 +664,7 @@ void sqlite3Insert(
|
||||
** of the tables being read by the SELECT statement. Also use a
|
||||
** temp table in the case of row triggers.
|
||||
*/
|
||||
if( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){
|
||||
if( pTrigger || readsTable(pParse, iDb, pTab) ){
|
||||
useTempTable = 1;
|
||||
}
|
||||
|
||||
@ -725,6 +713,14 @@ void sqlite3Insert(
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is no IDLIST term but the table has an integer primary
|
||||
** key, the set the ipkColumn variable to the integer primary key
|
||||
** column index in the original table definition.
|
||||
*/
|
||||
if( pColumn==0 && nColumn>0 ){
|
||||
ipkColumn = pTab->iPKey;
|
||||
}
|
||||
|
||||
/* Make sure the number of columns in the source data matches the number
|
||||
** of columns to be inserted into the table.
|
||||
*/
|
||||
@ -743,52 +739,6 @@ void sqlite3Insert(
|
||||
sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
|
||||
/* If the INSERT statement included an IDLIST term, then make sure
|
||||
** all elements of the IDLIST really are columns of the table and
|
||||
** remember the column indices.
|
||||
**
|
||||
** If the table has an INTEGER PRIMARY KEY column and that column
|
||||
** is named in the IDLIST, then record in the ipkColumn variable
|
||||
** the index into IDLIST of the primary key column. ipkColumn is
|
||||
** the index of the primary key as it appears in IDLIST, not as
|
||||
** is appears in the original table. (The index of the INTEGER
|
||||
** PRIMARY KEY in the original table is pTab->iPKey.)
|
||||
*/
|
||||
if( pColumn ){
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
pColumn->a[i].idx = -1;
|
||||
}
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
|
||||
pColumn->a[i].idx = j;
|
||||
if( j==pTab->iPKey ){
|
||||
ipkColumn = i; assert( !withoutRowid );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){
|
||||
ipkColumn = i;
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "table %S has no column named %s",
|
||||
pTabList, 0, pColumn->a[i].zName);
|
||||
pParse->checkSchema = 1;
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is no IDLIST term but the table has an integer primary
|
||||
** key, the set the ipkColumn variable to the integer primary key
|
||||
** column index in the original table definition.
|
||||
*/
|
||||
if( pColumn==0 && nColumn>0 ){
|
||||
ipkColumn = pTab->iPKey;
|
||||
}
|
||||
|
||||
/* Initialize the count of rows to be inserted
|
||||
*/
|
||||
@ -836,17 +786,6 @@ void sqlite3Insert(
|
||||
addrInsTop = addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
|
||||
}
|
||||
|
||||
/* Allocate registers for holding the rowid of the new row,
|
||||
** the content of the new row, and the assemblied row record.
|
||||
*/
|
||||
regRowid = regIns = pParse->nMem+1;
|
||||
pParse->nMem += pTab->nCol + 1;
|
||||
if( IsVirtual(pTab) ){
|
||||
regRowid++;
|
||||
pParse->nMem++;
|
||||
}
|
||||
regData = regRowid+1;
|
||||
|
||||
/* Run the BEFORE and INSTEAD OF triggers, if there are any
|
||||
*/
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
@ -930,7 +869,7 @@ void sqlite3Insert(
|
||||
if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regRowid);
|
||||
}else if( pSelect ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+ipkColumn, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid);
|
||||
}else{
|
||||
VdbeOp *pOp;
|
||||
sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid);
|
||||
@ -976,8 +915,9 @@ void sqlite3Insert(
|
||||
/* The value of the INTEGER PRIMARY KEY column is always a NULL.
|
||||
** Whenever this column is read, the rowid will be substituted
|
||||
** in its place. Hence, fill this column with a NULL to avoid
|
||||
** taking up data space with information that will never be used. */
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRegStore);
|
||||
** taking up data space with information that will never be used.
|
||||
** As there may be shallow copies of this value, make it a soft-NULL */
|
||||
sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
|
||||
continue;
|
||||
}
|
||||
if( pColumn==0 ){
|
||||
@ -994,11 +934,13 @@ void sqlite3Insert(
|
||||
}
|
||||
}
|
||||
if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){
|
||||
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, iRegStore);
|
||||
sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
|
||||
}else if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore);
|
||||
}else if( pSelect ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
|
||||
if( regFromSelect!=regData ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
|
||||
}
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore);
|
||||
}
|
||||
|
12
src/select.c
12
src/select.c
@ -585,13 +585,19 @@ static void selectInnerLoop(
|
||||
/* Pull the requested columns.
|
||||
*/
|
||||
nResultCol = pEList->nExpr;
|
||||
|
||||
if( pDest->iSdst==0 ){
|
||||
pDest->iSdst = pParse->nMem+1;
|
||||
pDest->nSdst = nResultCol;
|
||||
pParse->nMem += nResultCol;
|
||||
}else{
|
||||
assert( pDest->nSdst==nResultCol );
|
||||
}else if( pDest->iSdst+nResultCol > pParse->nMem ){
|
||||
/* This is an error condition that can result, for example, when a SELECT
|
||||
** on the right-hand side of an INSERT contains more result columns than
|
||||
** there are columns in the table on the left. The error will be caught
|
||||
** and reported later. But we need to make sure enough memory is allocated
|
||||
** to avoid other spurious errors in the meantime. */
|
||||
pParse->nMem += nResultCol;
|
||||
}
|
||||
pDest->nSdst = nResultCol;
|
||||
regResult = pDest->iSdst;
|
||||
if( srcTab>=0 ){
|
||||
for(i=0; i<nResultCol; i++){
|
||||
|
@ -2990,7 +2990,6 @@ void sqlite3DeleteTable(sqlite3*, Table*);
|
||||
# define sqlite3AutoincrementBegin(X)
|
||||
# define sqlite3AutoincrementEnd(X)
|
||||
#endif
|
||||
int sqlite3CodeCoroutine(Parse*, Select*, SelectDest*);
|
||||
void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int);
|
||||
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
|
||||
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
|
||||
@ -3038,11 +3037,12 @@ void sqlite3ExprCachePop(Parse*, int);
|
||||
void sqlite3ExprCacheRemove(Parse*, int, int);
|
||||
void sqlite3ExprCacheClear(Parse*);
|
||||
void sqlite3ExprCacheAffinityChange(Parse*, int, int);
|
||||
int sqlite3ExprCode(Parse*, Expr*, int);
|
||||
void sqlite3ExprCode(Parse*, Expr*, int);
|
||||
void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
|
||||
void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8);
|
||||
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
|
||||
int sqlite3ExprCodeTarget(Parse*, Expr*, int);
|
||||
int sqlite3ExprCodeAndCache(Parse*, Expr*, int);
|
||||
void sqlite3ExprCodeAndCache(Parse*, Expr*, int);
|
||||
int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8);
|
||||
#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
|
||||
#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
|
||||
|
39
src/vdbe.c
39
src/vdbe.c
@ -994,6 +994,20 @@ case OP_Null: { /* out2-prerelease */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SoftNull P1 * * * *
|
||||
** Synopsis: r[P1]=NULL
|
||||
**
|
||||
** Set register P1 to have the value NULL as seen by the OP_MakeRecord
|
||||
** instruction, but do not free any string or blob memory associated with
|
||||
** the register, so that if the value was a string or blob that was
|
||||
** previously copied using OP_SCopy, the copies will continue to be valid.
|
||||
*/
|
||||
case OP_SoftNull: {
|
||||
assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
|
||||
pOut = &aMem[pOp->p1];
|
||||
pOut->flags = (pOut->flags|MEM_Null)&~MEM_Undefined;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Blob P1 P2 * P4 *
|
||||
** Synopsis: r[P2]=P4 (len=P1)
|
||||
@ -2909,23 +2923,17 @@ case OP_AutoCommit: {
|
||||
|
||||
/* Opcode: Transaction P1 P2 P3 P4 P5
|
||||
**
|
||||
** Begin a transaction. The transaction ends when a Commit or Rollback
|
||||
** opcode is encountered. Depending on the ON CONFLICT setting, the
|
||||
** transaction might also be rolled back if an error is encountered.
|
||||
** Begin a transaction on database P1 if a transaction is not already
|
||||
** active.
|
||||
** If P2 is non-zero, then a write-transaction is started, or if a
|
||||
** read-transaction is already active, it is upgraded to a write-transaction.
|
||||
** If P2 is zero, then a read-transaction is started.
|
||||
**
|
||||
** P1 is the index of the database file on which the transaction is
|
||||
** started. Index 0 is the main database file and index 1 is the
|
||||
** file used for temporary tables. Indices of 2 or more are used for
|
||||
** attached databases.
|
||||
**
|
||||
** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is
|
||||
** obtained on the database file when a write-transaction is started. No
|
||||
** other process can start another write transaction while this transaction is
|
||||
** underway. Starting a write transaction also creates a rollback journal. A
|
||||
** write transaction must be started before any changes can be made to the
|
||||
** database. If P2 is greater than or equal to 2 then an EXCLUSIVE lock is
|
||||
** also obtained on the file.
|
||||
**
|
||||
** If a write-transaction is started and the Vdbe.usesStmtJournal flag is
|
||||
** true (this flag is set if the Vdbe may modify more than one row and may
|
||||
** throw an ABORT exception), a statement transaction may also be opened.
|
||||
@ -2936,13 +2944,16 @@ case OP_AutoCommit: {
|
||||
** entire transaction. If no error is encountered, the statement transaction
|
||||
** will automatically commit when the VDBE halts.
|
||||
**
|
||||
** If P2 is zero, then a read-lock is obtained on the database file.
|
||||
**
|
||||
** If P5!=0 then this opcode also checks the schema cookie against P3
|
||||
** and the schema generation counter against P4.
|
||||
** The cookie changes its value whenever the database schema changes.
|
||||
** This operation is used to detect when that the cookie has changed
|
||||
** and that the current process needs to reread the schema.
|
||||
** and that the current process needs to reread the schema. If the schema
|
||||
** cookie in P3 differs from the schema cookie in the database header or
|
||||
** if the schema generation counter in P4 differs from the current
|
||||
** generation counter, then an SQLITE_SCHEMA error is raised and execution
|
||||
** halts. The sqlite3_step() wrapper function might then reprepare the
|
||||
** statement and rerun it from the beginning.
|
||||
*/
|
||||
case OP_Transaction: {
|
||||
Btree *pBt;
|
||||
|
@ -398,7 +398,7 @@ ifcapable compound {
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
do_test insert-10.2 {
|
||||
catchsql {
|
||||
INSERT INTO t10 VALUES(11,12,13), (14,15);
|
||||
INSERT INTO t10 VALUES(11,12,13), (14,15), (16,17,28);
|
||||
}
|
||||
} {1 {all VALUES must have the same number of terms}}
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ ifcapable vacuum {
|
||||
#
|
||||
do_test insert4-5.1 {
|
||||
# Table does not exist.
|
||||
catchsql { INSERT INTO t2 SELECT * FROM nosuchtable }
|
||||
catchsql { INSERT INTO t2 SELECT a, b FROM nosuchtable }
|
||||
} {1 {no such table: nosuchtable}}
|
||||
do_test insert4-5.2 {
|
||||
# Number of columns does not match.
|
||||
|
Loading…
x
Reference in New Issue
Block a user