mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
Progress toward registerification of the constraint checking logic for
INSERT and UPDATE. (CVS 4693) FossilOrigin-Name: b9bf509e39f5ac38c2149d2a648f68e5df5ae9e3
This commit is contained in:
116
src/insert.c
116
src/insert.c
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.215 2008/01/07 19:20:25 drh Exp $
|
||||
** $Id: insert.c,v 1.216 2008/01/08 02:57:56 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -373,6 +373,7 @@ void sqlite3Insert(
|
||||
int regRowid; /* registers holding insert rowid */
|
||||
int regData; /* register holding first column to insert */
|
||||
int regRecord; /* Holds the assemblied row record */
|
||||
int *aRegIdx = 0; /* One register allocated to each index */
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
@@ -631,8 +632,18 @@ void sqlite3Insert(
|
||||
|
||||
/* If this is not a view, open the table and and all indices */
|
||||
if( !isView ){
|
||||
int nIdx;
|
||||
int i;
|
||||
|
||||
base = pParse->nTab;
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
|
||||
nIdx = sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
|
||||
aRegIdx = sqlite3DbMallocZero(db, sizeof(int)*(nIdx+1));
|
||||
if( aRegIdx==0 ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
for(i=0; i<nIdx; i++){
|
||||
aRegIdx[i] = ++pParse->nMem;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the data source is a temporary table, then we have to create
|
||||
@@ -830,9 +841,9 @@ void sqlite3Insert(
|
||||
#endif
|
||||
{
|
||||
sqlite3RegToStack(pParse, regIns, pTab->nCol+1);
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0,
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, base, aRegIdx, keyColumn>=0,
|
||||
0, onError, endOfLoop);
|
||||
sqlite3CompleteInsertion(pParse, pTab, base, 0,0,0,
|
||||
sqlite3CompleteInsertion(pParse, pTab, base, aRegIdx,0,0,
|
||||
(triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1,
|
||||
appendFlag);
|
||||
}
|
||||
@@ -895,6 +906,7 @@ insert_cleanup:
|
||||
sqlite3ExprListDelete(pList);
|
||||
sqlite3SelectDelete(pSelect);
|
||||
sqlite3IdListDelete(pColumn);
|
||||
sqlite3_free(aRegIdx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -919,11 +931,11 @@ insert_cleanup:
|
||||
** and rowidChng are 1. isUpdate is true for UPDATEs and false for
|
||||
** INSERTs and rowidChng is true if the record number is being changed.
|
||||
**
|
||||
** The code generated by this routine pushes additional entries onto
|
||||
** the stack which are the keys for new index entries for the new record.
|
||||
** The order of index keys is the same as the order of the indices on
|
||||
** the pTable->pIndex list. A key is only created for index i if
|
||||
** aIdxUsed!=0 and aIdxUsed[i]!=0.
|
||||
** The code generated by this routine store new index entries into
|
||||
** registers identified by aRegIdx[]. No index entry is created for
|
||||
** indices where aRegIdx[i]==0. The order of indices in aRegIdx[] is
|
||||
** the same as the order of indices on the linked list of indices
|
||||
** attached to the table.
|
||||
**
|
||||
** This routine also generates code to check constraints. NOT NULL,
|
||||
** CHECK, and UNIQUE constraints are all checked. If a constraint fails,
|
||||
@@ -968,7 +980,7 @@ insert_cleanup:
|
||||
** cursor number "base". All indices of pTab must also have open
|
||||
** read/write cursors with cursor number base+i for the i-th cursor.
|
||||
** Except, if there is no possibility of a REPLACE action then
|
||||
** cursors do not need to be open for indices where aIdxUsed[i]==0.
|
||||
** cursors do not need to be open for indices where aRegIdx[i]==0.
|
||||
**
|
||||
** If the isUpdate flag is true, it means that the "base" cursor is
|
||||
** initially pointing to an entry that is being updated. The isUpdate
|
||||
@@ -980,7 +992,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
Parse *pParse, /* The parser context */
|
||||
Table *pTab, /* the table into which we are inserting */
|
||||
int base, /* Index of a read/write cursor pointing at pTab */
|
||||
char *aIdxUsed, /* Which indices are used. NULL means all are used */
|
||||
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
||||
int rowidChng, /* True if the record number will change */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int overrideError, /* Override onError to this if not OE_Default */
|
||||
@@ -990,19 +1002,26 @@ void sqlite3GenerateConstraintChecks(
|
||||
Vdbe *v;
|
||||
int nCol;
|
||||
int onError;
|
||||
int addr;
|
||||
int extra;
|
||||
int j1, j2, j3; /* Address of jump instructions */
|
||||
int iCur;
|
||||
Index *pIdx;
|
||||
int seenReplace = 0;
|
||||
int jumpInst1=0, jumpInst2;
|
||||
int hasTwoRowids = (isUpdate && rowidChng);
|
||||
|
||||
int regRowid, regData;
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
nCol = pTab->nCol;
|
||||
|
||||
/* Copy rowids and data into registers
|
||||
*/
|
||||
regRowid = sqlite3StackToReg(pParse, nCol+1+hasTwoRowids);
|
||||
sqlite3RegToStack(pParse, regRowid, nCol+1+hasTwoRowids);
|
||||
if( hasTwoRowids ) regRowid++;
|
||||
regData = regRowid+1;
|
||||
|
||||
/* Test all NOT NULL constraints.
|
||||
*/
|
||||
for(i=0; i<nCol; i++){
|
||||
@@ -1019,8 +1038,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(nCol-1-i));
|
||||
addr = sqlite3VdbeAddOp0(v, OP_NotNull);
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
|| onError==OE_Ignore || onError==OE_Replace );
|
||||
switch( onError ){
|
||||
@@ -1041,11 +1059,12 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
case OE_Replace: {
|
||||
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, 0, regData+i);
|
||||
sqlite3VdbeAddOp1(v, OP_Push, nCol-i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
}
|
||||
|
||||
/* Test all CHECK constraints
|
||||
@@ -1053,11 +1072,8 @@ void sqlite3GenerateConstraintChecks(
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){
|
||||
int allOk = sqlite3VdbeMakeLabel(v);
|
||||
assert( pParse->ckOffset==0 );
|
||||
pParse->ckOffset = nCol;
|
||||
pParse->ckBase = regData;
|
||||
sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, 1);
|
||||
assert( pParse->ckOffset==nCol );
|
||||
pParse->ckOffset = 0;
|
||||
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
|
||||
if( onError==OE_Ignore ){
|
||||
sqlite3VdbeAddOp2(v, OP_Pop, nCol+1+hasTwoRowids, 0);
|
||||
@@ -1082,12 +1098,11 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
|
||||
if( isUpdate ){
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(nCol+1));
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(nCol+1));
|
||||
jumpInst1 = sqlite3VdbeAddOp2(v, OP_Eq, 0, 0);
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid-1);
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid);
|
||||
j2 = sqlite3VdbeAddOp2(v, OP_Eq, 0, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -nCol);
|
||||
jumpInst2 = sqlite3VdbeAddOp2(v, OP_NotExists, base, 0);
|
||||
j3 = sqlite3VdbeAddOp3(v, OP_NotExists, base, 0, regRowid);
|
||||
switch( onError ){
|
||||
default: {
|
||||
onError = OE_Abort;
|
||||
@@ -1103,7 +1118,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
case OE_Replace: {
|
||||
sqlite3GenerateRowIndexDelete(v, pTab, base, 0);
|
||||
if( isUpdate ){
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(nCol+hasTwoRowids));
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid-hasTwoRowids);
|
||||
sqlite3VdbeAddOp2(v, OP_MoveGe, base, 0);
|
||||
}
|
||||
seenReplace = 1;
|
||||
@@ -1116,10 +1131,10 @@ void sqlite3GenerateConstraintChecks(
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, jumpInst2);
|
||||
sqlite3VdbeJumpHere(v, j3);
|
||||
if( isUpdate ){
|
||||
sqlite3VdbeJumpHere(v, jumpInst1);
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(nCol+1));
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid-1);
|
||||
sqlite3VdbeAddOp2(v, OP_MoveGe, base, 0);
|
||||
}
|
||||
}
|
||||
@@ -1128,22 +1143,20 @@ void sqlite3GenerateConstraintChecks(
|
||||
** index and making sure that duplicate entries do not already exist.
|
||||
** Add the new records to the indices as we go.
|
||||
*/
|
||||
extra = -1;
|
||||
for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
|
||||
if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */
|
||||
extra++;
|
||||
if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */
|
||||
|
||||
/* Create a key for accessing the index entry */
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(nCol+extra));
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid);
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int idx = pIdx->aiColumn[i];
|
||||
if( idx==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(i+extra+nCol+1));
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid);
|
||||
}else{
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(i+extra+nCol-idx));
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regData+idx);
|
||||
}
|
||||
}
|
||||
jumpInst1 = sqlite3VdbeAddOp2(v, OP_MakeIdxRec, pIdx->nColumn, 0);
|
||||
j2 = sqlite3VdbeAddOp3(v, OP_MakeIdxRec, pIdx->nColumn, 0, aRegIdx[iCur]);
|
||||
sqlite3IndexAffinityStr(v, pIdx);
|
||||
|
||||
/* Find out what action to take in case there is an indexing conflict */
|
||||
@@ -1161,8 +1174,9 @@ void sqlite3GenerateConstraintChecks(
|
||||
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(extra+nCol+1+hasTwoRowids));
|
||||
jumpInst2 = sqlite3VdbeAddOp2(v, OP_IsUnique, base+iCur+1, 0);
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, aRegIdx[iCur]);
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid-hasTwoRowids);
|
||||
j3 = sqlite3VdbeAddOp2(v, OP_IsUnique, base+iCur+1, 0);
|
||||
|
||||
/* Generate code that executes if the new index entry is not unique */
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
@@ -1199,7 +1213,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
case OE_Ignore: {
|
||||
assert( seenReplace==0 );
|
||||
sqlite3VdbeAddOp2(v, OP_Pop, nCol+extra+3+hasTwoRowids, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Pop, nCol+3+hasTwoRowids, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
|
||||
break;
|
||||
}
|
||||
@@ -1207,17 +1221,18 @@ void sqlite3GenerateConstraintChecks(
|
||||
int iRowid = sqlite3StackToReg(pParse, 1);
|
||||
sqlite3GenerateRowDelete(pParse->db, v, pTab, base, iRowid, 0);
|
||||
if( isUpdate ){
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, -(nCol+extra+1+hasTwoRowids));
|
||||
sqlite3VdbeAddOp1(v, OP_SCopy, regRowid-hasTwoRowids);
|
||||
sqlite3VdbeAddOp2(v, OP_MoveGe, base, 0);
|
||||
}
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, j3);
|
||||
sqlite3VdbeAddOp1(v, OP_Pop, 1);
|
||||
#if NULL_DISTINCT_FOR_UNIQUE
|
||||
sqlite3VdbeJumpHere(v, jumpInst1);
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
#endif
|
||||
sqlite3VdbeJumpHere(v, jumpInst2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1235,7 +1250,7 @@ void sqlite3CompleteInsertion(
|
||||
Parse *pParse, /* The parser context */
|
||||
Table *pTab, /* the table into which we are inserting */
|
||||
int base, /* Index of a read/write cursor pointing at pTab */
|
||||
char *aIdxUsed, /* Which indices are used. NULL means all are used */
|
||||
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
||||
int rowidChng, /* True if the record number will change */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int newIdx, /* Index of NEW table for triggers. -1 if none */
|
||||
@@ -1252,8 +1267,8 @@ void sqlite3CompleteInsertion(
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
|
||||
for(i=nIdx-1; i>=0; i--){
|
||||
if( aIdxUsed && aIdxUsed[i]==0 ) continue;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, base+i+1, 0);
|
||||
if( aRegIdx[i]==0 ) continue;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, base+i+1, aRegIdx[i]);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_MakeRecord, pTab->nCol, 0);
|
||||
sqlite3TableAffinityStr(v, pTab);
|
||||
@@ -1287,8 +1302,10 @@ void sqlite3CompleteInsertion(
|
||||
** Generate code that will open cursors for a table and for all
|
||||
** indices of that table. The "base" parameter is the cursor number used
|
||||
** for the table. Indices are opened on subsequent cursors.
|
||||
**
|
||||
** Return the number of indices on the table.
|
||||
*/
|
||||
void sqlite3OpenTableAndIndices(
|
||||
int sqlite3OpenTableAndIndices(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pTab, /* Table to be opened */
|
||||
int base, /* Cursor number assigned to the table */
|
||||
@@ -1299,7 +1316,7 @@ void sqlite3OpenTableAndIndices(
|
||||
Index *pIdx;
|
||||
Vdbe *v;
|
||||
|
||||
if( IsVirtual(pTab) ) return;
|
||||
if( IsVirtual(pTab) ) return 0;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
@@ -1314,6 +1331,7 @@ void sqlite3OpenTableAndIndices(
|
||||
if( pParse->nTab<=base+i ){
|
||||
pParse->nTab = base+i;
|
||||
}
|
||||
return i-1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1614,7 +1632,7 @@ static int xferOptimization(
|
||||
VdbeComment((v, "%s", pDestIdx->zName));
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, 0, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user