/*
** This routine generates VDBE code that causes a single row of a
-** single table to be deleted.
+** single table to be deleted. Both the original table entry and
+** all indices are removed.
**
-** The VDBE must be in a particular state when this routine is called.
-** These are the requirements:
+** Preconditions:
**
-** 1. iDataCur is an open cursor on the btree that is the primary data
-** repository for the table. This will be either the table itself,
+** 1. iDataCur is an open cursor on the btree that is the canonical data
+** store for the table. (This will be either the table itself,
** in the case of a rowid table, or the PRIMARY KEY index in the case
-** of a WITHOUT ROWID table.
+** of a WITHOUT ROWID table.)
**
** 2. Read/write cursors for all indices of pTab must be open as
** cursor number iIdxCur+i for the i-th index.
**
** 3. The primary key for the row to be deleted must be stored in a
** sequence of nPk memory cells starting at iPk.
-**
-** This routine generates code to remove both the table record and all
-** index entries that point to that record.
*/
void sqlite3GenerateRowDelete(
Parse *pParse, /* Parsing context */
/* Vdbe is guaranteed to have been allocated by this stage. */
assert( v );
- VdbeModuleComment((v, "BEGIN: GenerateRowDelete(%d,%d,%d,%d)",
+ VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)",
iDataCur, iIdxCur, iPk, (int)nPk));
/* Seek cursor iCur to the row to delete. If this row no longer exists
** trigger programs were invoked. Or if a trigger program throws a
** RAISE(IGNORE) exception. */
sqlite3VdbeResolveLabel(v, iLabel);
- VdbeModuleComment((v, "END: GenerateRowDelete()"));
+ VdbeModuleComment((v, "END: GenRowDel()"));
}
/*
** This routine generates VDBE code that causes the deletion of all
-** index entries associated with a single row of a single table.
+** index entries associated with a single row of a single table, pTab
**
-** The VDBE must be in a particular state when this routine is called.
-** These are the requirements:
+** Preconditions:
**
-** 1. A read/write cursor "iDataCur" pointing to canonical storage
-** tree for the table pTab, which will be either the table itself
+** 1. A read/write cursor "iDataCur" must be open on the canonical storage
+** btree for the table pTab. (This will be either the table itself
** for rowid tables or to the primary key index for WITHOUT ROWID
-** tables.
+** tables.)
**
** 2. Read/write cursors for all indices of pTab must be open as
-** cursor number iIdxCur+i for the i-th index.
+** cursor number iIdxCur+i for the i-th index. (The pTab->pIndex
+** index is the 0-th index.)
**
-** 3. The "iDataCur" cursor must be pointing to the row that is to be
-** deleted.
+** 3. The "iDataCur" cursor must be already be positioned on the row
+** that is to be deleted.
*/
void sqlite3GenerateRowIndexDelete(
Parse *pParse, /* Parsing and code generating context */
int iIdxCur, /* First index cursor */
int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
){
- int i;
- Index *pIdx;
- int r1;
- int iPartIdxLabel;
- Vdbe *v = pParse->pVdbe;
- Index *pPk;
-
+ int i; /* Index loop counter */
+ int r1; /* Register holding an index key */
+ int iPartIdxLabel; /* Jump destination for skipping partial index entries */
+ Index *pIdx; /* Current index */
+ Vdbe *v; /* The prepared statement under construction */
+ Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */
+
+ v = pParse->pVdbe;
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ assert( iIdxCur+i!=iDataCur || pPk==pIdx );
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
if( pIdx==pPk ) continue;
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, &iPartIdxLabel);
#endif
/*
-** Generate code to do constraint checks prior to an INSERT or an UPDATE.
-**
-** The input is a range of consecutive registers as follows:
-**
-** 1. The rowid of the row after the update, or NULL
-** for WITHOUT ROWID tables.
-**
-** 2. The data in the first column of the entry after the update.
-**
-** i. Data from middle columns...
-**
-** N. The data in the last column of the entry after the update.
-**
-** The regRowid parameter is the index of the register containing (1).
-**
-** If isUpdate is true and pkChng is non-zero, then pkChng contains
-** the address of a range of registers containing the rowid or PRIMARY KEY
-** value before the update takes place. isUpdate is true for UPDATEs and
-** false for INSERTs. If isUpdate is false then a non-zero pkChng
-** indicates that the rowid was explicitly specified as part of the
-** INSERT statement. If pkChng is false, it means that the rowid is
-** computed automatically in an insert and is therefore guaranteed to
-** be unique. The pkChng parameter is always false for inserts
-** into a WITHOUT ROWID table.
-**
-** The code generated by this routine should store new index entries into
+** Generate code to do constraint checks prior to an INSERT or an UPDATE
+** on table pTab.
+**
+** The regNewData parameter is the first register in a range that contains
+** the data to be inserted or the data after the update. There will be
+** pTab->nCol+1 registers in this range. The first register (the one
+** that regNewData points to) will contain the new rowid, or NULL in the
+** case of a WITHOUT ROWID table. The second register in the range will
+** contain the content of the first table column. The third register will
+** contain the content of the second table column. And so forth.
+**
+** For an UPDATE (isUpdate!=0), if pkChng is non-zero then it contains
+** the address of a range of registers containing the rowid and table
+** data from before the change. In other words, pkChng is like
+** regNewData except that it describes the row before the update rather
+** than afterwards. If pkChng is zero, that means that the rowid does
+** not change (for a normal rowid table) or the PRIMARY KEY does not
+** change (for a WITHOUT ROWID table) in which case the old data is
+** not needed.
+**
+** For an INSERT (isUpdate==0), pkChng is just a boolean that indicates
+** whether or not the rowid was explicitly specified as part of the
+** INSERT statement. If pkChng is zero, it means that the either rowid
+** is computed automatically or that the table is a WITHOUT ROWID table
+** and has no rowid. On an INSERT, pkChng will only be true if the
+** INSERT statement provides an integer value for either the rowid
+** column or its INTEGER PRIMARY KEY alias.
+**
+** The code generated by this routine will 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.
+** at pTab->pIndex.
+**
+** The caller must have already opened writeable cursors on the main
+** table and all applicable indices (that is to say, all indices for which
+** aRegIdx[] is not zero). iDataCur is the cursor for the main table when
+** inserting or updating a rowid table, or the cursor for the PRIMARY KEY
+** index when operating on a WITHOUT ROWID table. iIdxCur is the cursor
+** for the first index in the pTab->pIndex list. Cursors for other indices
+** are at iIdxCur+N for the N-th element of the pTab->pIndex list.
**
** This routine also generates code to check constraints. NOT NULL,
** CHECK, and UNIQUE constraints are all checked. If a constraint fails,
** Constraint type Action What Happens
** --------------- ---------- ----------------------------------------
** any ROLLBACK The current transaction is rolled back and
-** sqlite3_exec() returns immediately with a
+** sqlite3_step() returns immediately with a
** return code of SQLITE_CONSTRAINT.
**
** any ABORT Back out changes from the current command
** only (do not do a complete rollback) then
-** cause sqlite3_exec() to return immediately
+** cause sqlite3_step() to return immediately
** with SQLITE_CONSTRAINT.
**
-** any FAIL Sqlite3_exec() returns immediately with a
+** any FAIL Sqlite3_step() returns immediately with a
** return code of SQLITE_CONSTRAINT. The
** transaction is not rolled back and any
-** prior changes are retained.
+** changes to prior rows are retained.
**
-** any IGNORE The record number and data is popped from
-** the stack and there is an immediate jump
-** to label ignoreDest.
+** any IGNORE The attempt in insert or update the current
+** row is skipped, without throwing an error.
+** Processing continues with the next row.
+** (There is an immediate jump to ignoreDest.)
**
** NOT NULL REPLACE The NULL value is replace by the default
** value for that column. If the default value
** Or if overrideError==OE_Default, then the pParse->onError parameter
** is used. Or if pParse->onError==OE_Default then the onError value
** for the constraint is used.
-**
-** The calling routine must open a read/write cursor for pTab with
-** cursor number "baseCur". All indices of pTab must also have open
-** read/write cursors with cursor number baseCur+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 aRegIdx[i]==0.
*/
void sqlite3GenerateConstraintChecks(
- Parse *pParse, /* The parser context */
- Table *pTab, /* the table into which we are inserting */
- int iDataCur, /* Cursor of the canonical data tree */
- int iIdxCur, /* First index cursor */
- int regRowid, /* First register in a range holding values to insert */
- int *aRegIdx, /* Register used by each index. 0 for unused indices */
- int pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */
- int isUpdate, /* True for UPDATE, False for INSERT */
- int overrideError, /* Override onError to this if not OE_Default */
- int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
- int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
+ Parse *pParse, /* The parser context */
+ Table *pTab, /* The table being inserted or updated */
+ 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 *aRegIdx, /* Register used by each index. 0 for unused indices */
+ int pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */
+ int isUpdate, /* True for UPDATE, False for INSERT */
+ int overrideError, /* Override onError to this if not OE_Default */
+ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
+ int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
){
- int i; /* loop counter */
- Vdbe *v; /* VDBE under constrution */
- int nCol; /* Number of columns */
- int onError; /* Conflict resolution strategy */
- int j1; /* Addresss of jump instruction */
- int ix; /* Index loop counter */
- int regData; /* Register containing first data column */
+ int i; /* loop counter */
+ Vdbe *v; /* VDBE under constrution */
+ int nCol; /* Number of columns */
+ int onError; /* Conflict resolution strategy */
+ int j1; /* Addresss of jump instruction */
+ int ix; /* Index loop counter */
Index *pIdx; /* Pointer to one of the indices */
Index *pPk = 0; /* The PRIMARY KEY index */
sqlite3 *db; /* Database connection */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
- int regOldPk; /* Previous rowid or PRIMARY KEY value */
+ int regOldData; /* Previous rowid and table data */
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
- regOldPk = (pkChng && isUpdate) ? pkChng : regRowid;
+ regOldData = (pkChng && isUpdate) ? pkChng : regNewData;
db = pParse->db;
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
nCol = pTab->nCol;
- regData = regRowid + 1;
-
- /* For WITHOUT ROWID tables, we'll need to know the Index and the cursor
- ** number for the PRIMARY KEY index */
+
+ /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for
+ ** normal rowid tables. nPkField is the number of key fields in the
+ ** pPk index or 1 for a rowid table. In other words, nPkField is the
+ ** number of fields in the true primary key of the table. */
if( HasRowid(pTab) ){
pPk = 0;
nPkField = 1;
/* Record that this module has started */
VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",
- iDataCur, iIdxCur, regRowid, pkChng, regOldPk));
+ iDataCur, iIdxCur, regNewData, regOldData, pkChng));
/* Test all NOT NULL constraints.
*/
case OE_Fail: {
char *zMsg;
sqlite3VdbeAddOp3(v, OP_HaltIfNull,
- SQLITE_CONSTRAINT_NOTNULL, onError, regData+i);
+ SQLITE_CONSTRAINT_NOTNULL, onError, regNewData+1+i);
zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL",
pTab->zName, pTab->aCol[i].zName);
sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
break;
}
case OE_Ignore: {
- sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest);
break;
}
default: {
assert( onError==OE_Replace );
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i);
+ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i);
sqlite3VdbeJumpHere(v, j1);
break;
}
#ifndef SQLITE_OMIT_CHECK
if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
ExprList *pCheck = pTab->pCheck;
- pParse->ckBase = regData;
+ pParse->ckBase = regNewData+1;
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
for(i=0; i<pCheck->nExpr; i++){
int allOk = sqlite3VdbeMakeLabel(v);
/* If there is an INTEGER PRIMARY KEY, make sure the primary key
** of the new record does not previously exist. Except, if this
- ** is an UPDATE and the primary key is not changing, that is OK.
+ ** is an UPDATE and the primary key is not changing, then obviously
+ ** it is OK for the previous rowid to exist in that case.
**
** This block only runs for tables that have a rowid.
*/
}
if( isUpdate ){
- sqlite3VdbeAddOp3(v, OP_Eq, regRowid, addrRowidOk, pkChng);
+ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData);
}
- sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regRowid);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
switch( onError ){
default: {
onError = OE_Abort;
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
sqlite3MultiWrite(pParse);
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
- regRowid, 1, 0, OE_Replace);
+ regNewData, 1, 0, OE_Replace);
}else if( pTab->pIndex ){
sqlite3MultiWrite(pParse);
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
** Compute the revised record entries for indices as we go.
*/
for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
- int regIdx;
- int regR;
- int iThisCur = iIdxCur+ix;
- int addrUniqueOk = sqlite3VdbeMakeLabel(v);
+ int regIdx; /* Range of registers hold conent for pIdx */
+ int regR; /* Range of registers holding conflicting PK */
+ int iThisCur; /* Cursor for this UNIQUE index */
+ int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
+ iThisCur = iIdxCur+ix;
+ addrUniqueOk = sqlite3VdbeMakeLabel(v);
if( pIdx->pPartIdxWhere ){
sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
- pParse->ckBase = regData;
+ pParse->ckBase = regNewData+1;
sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrUniqueOk,
SQLITE_JUMPIFNULL);
pParse->ckBase = 0;
}
- /* Create a key for accessing the index entry */
+ /* Create a record for this index entry as it should appear after
+ ** the insert or update. */
regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
for(i=0; i<pIdx->nColumn; i++){
- i16 iField = pIdx->aiColumn[i];
+ int iField = pIdx->aiColumn[i];
if( iField<0 || iField==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
+ iField = regNewData;
}else{
- sqlite3VdbeAddOp2(v, OP_SCopy, regData+iField, regIdx+i);
+ iField += regNewData + 1;
}
+ sqlite3VdbeAddOp2(v, OP_SCopy, iField, regIdx+i);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
VdbeComment((v, "for %s", pIdx->zName));
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);
- /* Find out what action to take in case there is an indexing conflict */
+ /* Find out what action to take in case there is a uniqueness conflict */
onError = pIdx->onError;
if( onError==OE_None ){
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
regIdx, pIdx->nKeyCol);
if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
/* Conflict only if the rowid of the existing index entry
** is different from old-rowid */
- sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
- sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldPk);
+ sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
}else{
/* Extract the PRIMARY KEY from the end of the index entry and
** store it in register regR..regR+nPk-1 */
if( isUpdate ){
int addrPkConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
for(i=0; i<pPk->nKeyCol-1; i++){
- sqlite3VdbeAddOp3(v, OP_Ne, regOldPk+pPk->aiColumn[i]+1,
+ sqlite3VdbeAddOp3(v, OP_Ne, regOldData+pPk->aiColumn[i]+1,
addrPkConflict, regIdx+i);
}
- sqlite3VdbeAddOp3(v, OP_Eq, regOldPk+pPk->aiColumn[i]+1,
+ sqlite3VdbeAddOp3(v, OP_Eq, regOldData+pPk->aiColumn[i]+1,
addrUniqueOk, regIdx+i);
}
}else{
assert( pIdx->nKeyCol + pPk->nKeyCol == pIdx->nColumn );
for(i=0; i<pPk->nKeyCol-1; i++){
sqlite3VdbeAddOp3(v, OP_Ne,
- regOldPk+pPk->aiColumn[i], addrConflict, regR+i);
+ regOldData+pPk->aiColumn[i]+1, addrConflict, regR+i);
}
sqlite3VdbeAddOp3(v, OP_Eq,
- regOldPk+pPk->aiColumn[i], addrUniqueOk, regR+i);
+ regOldData+pPk->aiColumn[i]+1, addrUniqueOk, regR+i);
}
}
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
/*
** This routine generates code to finish the INSERT or UPDATE operation
** that was started by a prior call to sqlite3GenerateConstraintChecks.
-** A consecutive range of registers starting at regRowid contains the
+** A consecutive range of registers starting at regNewData contains the
** rowid and the content to be inserted.
**
** The arguments to this routine should be the same as the first six
Table *pTab, /* the table into which we are inserting */
int iDataCur, /* Cursor of the canonical data source */
int iIdxCur, /* First index cursor */
- int regRowid, /* Range of content */
+ int regNewData, /* Range of content */
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 */
){
- int i;
- Vdbe *v;
- Index *pIdx;
- u8 pik_flags;
- int regData;
- int regRec;
+ 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 assemblied record for the table */
+ int i; /* Loop counter */
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
}
}
if( !HasRowid(pTab) ) return;
- regData = regRowid + 1;
+ regData = regNewData + 1;
regRec = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
sqlite3TableAffinityStr(v, pTab);
if( useSeekResult ){
pik_flags |= OPFLAG_USESEEKRESULT;
}
- sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
if( !pParse->nested ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
}