summaryrefslogtreecommitdiff
path: root/ext/sqlite/libsqlite/src/insert.c
diff options
context:
space:
mode:
authorWez Furlong <wez@php.net>2003-06-04 22:40:00 +0000
committerWez Furlong <wez@php.net>2003-06-04 22:40:00 +0000
commit80e7f7001d39add9010ba78be636245410b79c24 (patch)
tree23ae7f9f01ea9bb1add35b1ff85efa2421d05142 /ext/sqlite/libsqlite/src/insert.c
parent82a1818fdec3afe8e3a5cc8aa7171f4472ea1e4a (diff)
downloadphp-git-80e7f7001d39add9010ba78be636245410b79c24.tar.gz
Update bundled library to version 2.8.2.
Make OnUpdateInt compatible with ZE2. Fix the makefile fragment for non-gnu makes
Diffstat (limited to 'ext/sqlite/libsqlite/src/insert.c')
-rw-r--r--ext/sqlite/libsqlite/src/insert.c228
1 files changed, 134 insertions, 94 deletions
diff --git a/ext/sqlite/libsqlite/src/insert.c b/ext/sqlite/libsqlite/src/insert.c
index f625ffc942..313764f51e 100644
--- a/ext/sqlite/libsqlite/src/insert.c
+++ b/ext/sqlite/libsqlite/src/insert.c
@@ -85,22 +85,22 @@
*/
void sqliteInsert(
Parse *pParse, /* Parser context */
- Token *pTableName, /* Name of table into which we are inserting */
+ SrcList *pTabList, /* Name of table into which we are inserting */
ExprList *pList, /* List of values to be inserted */
Select *pSelect, /* A SELECT statement to use as the data source */
IdList *pColumn, /* Column names corresponding to IDLIST. */
int onError /* How to handle constraint errors */
){
Table *pTab; /* The table to insert into */
- char *zTab = 0; /* Name of the table into which we are inserting */
+ char *zTab; /* Name of the table into which we are inserting */
+ const char *zDb; /* Name of the database holding this table */
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 base; /* First available cursor */
+ int base; /* VDBE Cursor number for pTab */
int iCont, iBreak; /* Beginning and end of the loop over srcTab */
sqlite *db; /* The main database structure */
- int openOp; /* Opcode used to open cursors */
int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */
int endOfLoop; /* Label for the end of the insertion loop */
int useTempTable; /* Store SELECT results in intermediate table */
@@ -109,24 +109,28 @@ void sqliteInsert(
int iCleanup; /* Address of the cleanup code */
int iInsertBlock; /* Address of the subroutine used to insert data */
int iCntMem; /* Memory cell used for the row counter */
+ int isView; /* True if attempting to insert into a view */
int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
- int newIdx = -1;
+ int before_triggers; /* True if there are BEFORE triggers */
+ int after_triggers; /* True if there are AFTER triggers */
+ int newIdx = -1; /* Cursor for the NEW table */
if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
db = pParse->db;
/* Locate the table into which we will be inserting new information.
*/
- zTab = sqliteTableNameFromToken(pTableName);
+ assert( pTabList->nSrc==1 );
+ zTab = pTabList->a[0].zName;
if( zTab==0 ) goto insert_cleanup;
- pTab = sqliteFindTable(pParse->db, zTab);
+ pTab = sqliteSrcListLookup(pParse, pTabList);
if( pTab==0 ){
- sqliteSetString(&pParse->zErrMsg, "no such table: ", zTab, 0);
- pParse->nErr++;
goto insert_cleanup;
}
- if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0) ){
+ assert( pTab->iDb<db->nDb );
+ zDb = db->aDb[pTab->iDb].zName;
+ if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
goto insert_cleanup;
}
@@ -134,37 +138,28 @@ void sqliteInsert(
* (a) the table is not read-only,
* (b) that if it is a view then ON INSERT triggers exist
*/
- row_triggers_exist =
- sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
- TK_BEFORE, TK_ROW, 0) ||
- sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT, TK_AFTER, TK_ROW, 0);
- if( pTab->readOnly || (pTab->pSelect && !row_triggers_exist) ){
- sqliteSetString(&pParse->zErrMsg,
- pTab->pSelect ? "view " : "table ",
- zTab,
- " may not be modified", 0);
- pParse->nErr++;
+ before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
+ TK_BEFORE, TK_ROW, 0);
+ after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
+ TK_AFTER, TK_ROW, 0);
+ row_triggers_exist = before_triggers || after_triggers;
+ isView = pTab->pSelect!=0;
+ if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
goto insert_cleanup;
}
- sqliteFree(zTab);
- zTab = 0;
-
if( pTab==0 ) goto insert_cleanup;
/* If pTab is really a view, make sure it has been initialized.
*/
- if( pTab->pSelect ){
- if( sqliteViewGetColumnNames(pParse, pTab) ){
- goto insert_cleanup;
- }
+ if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
+ goto insert_cleanup;
}
/* Allocate a VDBE
*/
v = sqliteGetVdbe(pParse);
if( v==0 ) goto insert_cleanup;
- sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist,
- !row_triggers_exist && pTab->isTemp);
+ sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb);
/* if there are row triggers, allocate a temp table for new.* references. */
if( row_triggers_exist ){
@@ -183,7 +178,6 @@ void sqliteInsert(
/* Data is coming from a SELECT. Generate code to implement that SELECT
*/
int rc, iInitCode;
- int opCode;
iInitCode = sqliteVdbeAddOp(v, OP_Goto, 0, 0);
iSelectLoop = sqliteVdbeCurrentAddr(v);
iInsertBlock = sqliteVdbeMakeLabel(v);
@@ -197,9 +191,23 @@ void sqliteInsert(
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table. Set to FALSE if each
** row of the SELECT can be written directly into the result table.
+ **
+ ** A temp table must be used if the table being updated is also one
+ ** of the tables being read by the SELECT statement. Also use a
+ ** temp table in the case of row triggers.
*/
- opCode = pTab->isTemp ? OP_OpenTemp : OP_Open;
- useTempTable = row_triggers_exist || sqliteVdbeFindOp(v,opCode,pTab->tnum);
+ if( row_triggers_exist ){
+ useTempTable = 1;
+ }else{
+ int addr = sqliteVdbeFindOp(v, OP_OpenRead, pTab->tnum);
+ useTempTable = 0;
+ if( addr>0 ){
+ VdbeOp *pOp = sqliteVdbeGetOp(v, addr-2);
+ if( pOp->opcode==OP_Integer && pOp->p1==pTab->iDb ){
+ useTempTable = 1;
+ }
+ }
+ }
if( useTempTable ){
/* Generate the subroutine that SELECT calls to process each row of
@@ -236,7 +244,7 @@ void sqliteInsert(
nColumn = pList->nExpr;
dummy.nSrc = 0;
for(i=0; i<nColumn; i++){
- if( sqliteExprResolveIds(pParse, 0, &dummy, 0, pList->a[i].pExpr) ){
+ if( sqliteExprResolveIds(pParse, &dummy, 0, pList->a[i].pExpr) ){
goto insert_cleanup;
}
if( sqliteExprCheck(pParse, pList->a[i].pExpr, 0, 0) ){
@@ -249,24 +257,13 @@ void sqliteInsert(
** of columns to be inserted into the table.
*/
if( pColumn==0 && nColumn!=pTab->nCol ){
- char zNum1[30];
- char zNum2[30];
- sprintf(zNum1,"%d", nColumn);
- sprintf(zNum2,"%d", pTab->nCol);
- sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
- " has ", zNum2, " columns but ",
- zNum1, " values were supplied", 0);
- pParse->nErr++;
+ sqliteErrorMsg(pParse,
+ "table %S has %d columns but %d values were supplied",
+ pTabList, 0, pTab->nCol, nColumn);
goto insert_cleanup;
}
if( pColumn!=0 && nColumn!=pColumn->nId ){
- char zNum1[30];
- char zNum2[30];
- sprintf(zNum1,"%d", nColumn);
- sprintf(zNum2,"%d", pColumn->nId);
- sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
- zNum2, " columns", 0);
- pParse->nErr++;
+ sqliteErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
goto insert_cleanup;
}
@@ -296,8 +293,8 @@ void sqliteInsert(
}
}
if( j>=pTab->nCol ){
- sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
- " has no column named ", pColumn->a[i].zName, 0);
+ sqliteErrorMsg(pParse, "table %S has no column named %s",
+ pTabList, 0, pColumn->a[i].zName);
pParse->nErr++;
goto insert_cleanup;
}
@@ -315,7 +312,7 @@ void sqliteInsert(
/* Open the temp table for FOR EACH ROW triggers
*/
if( row_triggers_exist ){
- sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
+ sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
}
/* Initialize the count of rows to be inserted
@@ -329,11 +326,12 @@ void sqliteInsert(
/* Open tables and indices if there are no row triggers */
if( !row_triggers_exist ){
base = pParse->nTab;
- openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
- sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
+ sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
- sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum);
+ sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
}
pParse->nTab += idx;
@@ -353,11 +351,33 @@ void sqliteInsert(
sqliteVdbeResolveLabel(v, iInsertBlock);
}
+ /* Run the BEFORE and INSTEAD OF triggers, if there are any
+ */
endOfLoop = sqliteVdbeMakeLabel(v);
- if( row_triggers_exist ){
+ if( before_triggers ){
+
+ /* build the NEW.* reference row. Note that if there is an INTEGER
+ ** PRIMARY KEY into which a NULL is being inserted, that NULL will be
+ ** translated into a unique ID for the row. But on a BEFORE trigger,
+ ** we do not know what the unique ID will be (because the insert has
+ ** not happened yet) so we substitute a rowid of -1
+ */
+ if( keyColumn<0 ){
+ sqliteVdbeAddOp(v, OP_Integer, -1, 0);
+ }else if( useTempTable ){
+ sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
+ }else if( pSelect ){
+ sqliteVdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
+ }else{
+ sqliteExprCode(pParse, pList->a[keyColumn].pExpr);
+ sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3);
+ sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+ sqliteVdbeAddOp(v, OP_Integer, -1, 0);
+ sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
+ }
- /* build the new.* reference row */
- sqliteVdbeAddOp(v, OP_Integer, 13, 0);
+ /* Create the new column data
+ */
for(i=0; i<pTab->nCol; i++){
if( pColumn==0 ){
j = i;
@@ -379,26 +399,28 @@ void sqliteInsert(
}
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
- sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
- /* Fire BEFORE triggers */
- if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1,
- onError, endOfLoop) ){
+ /* Fire BEFORE or INSTEAD OF triggers */
+ if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab,
+ newIdx, -1, onError, endOfLoop) ){
goto insert_cleanup;
}
+ }
- /* Open the tables and indices for the INSERT */
- if( !pTab->pSelect ){
- base = pParse->nTab;
- openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
- sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
- sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
- for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
- sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum);
- sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
- }
- pParse->nTab += idx;
+ /* If any triggers exists, the opening of tables and indices is deferred
+ ** until now.
+ */
+ if( row_triggers_exist && !isView ){
+ base = pParse->nTab;
+ sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
+ sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
+ for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
+ sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
+ sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
}
+ pParse->nTab += idx;
}
/* Push the record number for the new entry onto the stack. The
@@ -406,7 +428,7 @@ void sqliteInsert(
** except when the table has an INTEGER PRIMARY KEY column, in which
** case the record number is the same as that column.
*/
- if( !pTab->pSelect ){
+ if( !isView ){
if( keyColumn>=0 ){
if( useTempTable ){
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
@@ -461,18 +483,19 @@ void sqliteInsert(
** do the insertion.
*/
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
- sqliteCompleteInsertion(pParse, pTab, base, 0,0,0);
+ sqliteCompleteInsertion(pParse, pTab, base, 0,0,0,
+ after_triggers ? newIdx : -1);
+ }
- /* Update the count of rows that are inserted
- */
- if( (db->flags & SQLITE_CountRows)!=0 ){
- sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0);
- }
+ /* Update the count of rows that are inserted
+ */
+ if( (db->flags & SQLITE_CountRows)!=0 ){
+ sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0);
}
if( row_triggers_exist ){
/* Close all tables opened */
- if( !pTab->pSelect ){
+ if( !isView ){
sqliteVdbeAddOp(v, OP_Close, base, 0);
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
@@ -520,9 +543,9 @@ void sqliteInsert(
}
insert_cleanup:
+ sqliteSrcListDelete(pTabList);
if( pList ) sqliteExprListDelete(pList);
if( pSelect ) sqliteSelectDelete(pSelect);
- if ( zTab ) sqliteFree(zTab);
sqliteIdListDelete(pColumn);
}
@@ -532,7 +555,7 @@ insert_cleanup:
** When this routine is called, the stack contains (from bottom to top)
** the following values:
**
-** 1. The recno of the row to be updated before it is updated. This
+** 1. The recno of the row to be updated before the update. This
** value is omitted unless we are doing an UPDATE that involves a
** change to the record number.
**
@@ -644,9 +667,10 @@ void sqliteGenerateConstraintChecks(
if( onError==OE_None ) continue;
if( overrideError!=OE_Default ){
onError = overrideError;
- }else if( onError==OE_Default ){
+ }else if( pParse->db->onError!=OE_Default ){
onError = pParse->db->onError;
- if( onError==OE_Default ) onError = OE_Abort;
+ }else if( onError==OE_Default ){
+ onError = OE_Abort;
}
if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){
onError = OE_Abort;
@@ -694,9 +718,10 @@ void sqliteGenerateConstraintChecks(
onError = pTab->keyConf;
if( overrideError!=OE_Default ){
onError = overrideError;
- }else if( onError==OE_Default ){
+ }else if( pParse->db->onError!=OE_Default ){
onError = pParse->db->onError;
- if( onError==OE_Default ) onError = OE_Abort;
+ }else if( onError==OE_Default ){
+ onError = OE_Abort;
}
if( onError!=OE_Replace ){
if( isUpdate ){
@@ -735,10 +760,12 @@ void sqliteGenerateConstraintChecks(
** index and making sure that duplicate entries do not already exist.
** Add the new records to the indices as we go.
*/
- extra = 0;
- for(extra=(-1), iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
- if( aIdxUsed && aIdxUsed[iCur]==0 ) continue;
- extra++;
+ extra = -1;
+ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
+ if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */
+ extra++;
+
+ /* Create a key for accessing the index entry */
sqliteVdbeAddOp(v, OP_Dup, nCol+extra, 1);
for(i=0; i<pIdx->nColumn; i++){
int idx = pIdx->aiColumn[i];
@@ -750,16 +777,23 @@ void sqliteGenerateConstraintChecks(
}
jumpInst1 = sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
if( pParse->db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx);
+
+ /* Find out what action to take in case there is an indexing conflict */
onError = pIdx->onError;
- if( onError==OE_None ) continue;
+ if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */
if( overrideError!=OE_Default ){
onError = overrideError;
- }else if( onError==OE_Default ){
+ }else if( pParse->db->onError!=OE_Default ){
onError = pParse->db->onError;
- if( onError==OE_Default ) onError = OE_Abort;
+ }else if( onError==OE_Default ){
+ onError = OE_Abort;
}
+
+ /* Check to see if the new index entry will be unique */
sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1);
jumpInst2 = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
+
+ /* Generate code that executes if the new index entry is not unique */
switch( onError ){
case OE_Rollback:
case OE_Abort:
@@ -809,7 +843,8 @@ void sqliteCompleteInsertion(
int base, /* Index of a read/write cursor pointing at pTab */
char *aIdxUsed, /* Which indices are used. NULL means all are used */
int recnoChng, /* True if the record number will change */
- int isUpdate /* True for UPDATE, False for INSERT */
+ int isUpdate, /* True for UPDATE, False for INSERT */
+ int newIdx /* Index of NEW table for triggers. -1 if none */
){
int i;
Vdbe *v;
@@ -825,6 +860,11 @@ void sqliteCompleteInsertion(
sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0);
}
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+ if( newIdx>=0 ){
+ sqliteVdbeAddOp(v, OP_Dup, 1, 0);
+ sqliteVdbeAddOp(v, OP_Dup, 1, 0);
+ sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
+ }
sqliteVdbeAddOp(v, OP_PutIntKey, base, pParse->trigStack?0:1);
if( isUpdate && recnoChng ){
sqliteVdbeAddOp(v, OP_Pop, 1, 0);