📄 insert.c
字号:
/*** 2001 September 15**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.***************************************************************************** This file contains C code routines that are called by the parser** to handle INSERT statements in SQLite.**** $Id: insert.c,v 1.94 2004/02/24 01:05:33 drh Exp $*/#include "sqliteInt.h"/*** This routine is call to handle SQL of the following forms:**** insert into TABLE (IDLIST) values(EXPRLIST)** insert into TABLE (IDLIST) select**** The IDLIST following the table name is always optional. If omitted,** then a list of all columns for the table is substituted. The IDLIST** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted.**** The pList parameter holds EXPRLIST in the first form of the INSERT** statement above, and pSelect is NULL. For the second form, pList is** NULL and pSelect is a pointer to the select statement used to generate** data for the insert.**** The code generated follows one of three templates. For a simple** select with data coming from a VALUES clause, the code executes** once straight down through. The template looks like this:**** open write cursor to <table> and its indices** puts VALUES clause expressions onto the stack** write the resulting record into <table>** cleanup**** If the statement is of the form**** INSERT INTO <table> SELECT ...**** And the SELECT clause does not read from <table> at any time, then** the generated code follows this template:**** goto B** A: setup for the SELECT** loop over the tables in the SELECT** gosub C** end loop** cleanup after the SELECT** goto D** B: open write cursor to <table> and its indices** goto A** C: insert the select result into <table>** return** D: cleanup**** The third template is used if the insert statement takes its** values from a SELECT but the data is being inserted into a table** that is also read as part of the SELECT. In the third form,** we have to use a intermediate table to store the results of** the select. The template is like this:**** goto B** A: setup for the SELECT** loop over the tables in the SELECT** gosub C** end loop** cleanup after the SELECT** goto D** C: insert the select result into the intermediate table** return** B: open a cursor to an intermediate table** goto A** D: open write cursor to <table> and its indices** loop over the intermediate table** transfer values form intermediate table into <table>** end the loop** cleanup*/void sqliteInsert( Parse *pParse, /* Parser context */ 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; /* 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; /* VDBE Cursor number for pTab */ int iCont, iBreak; /* Beginning and end of the loop over srcTab */ sqlite *db; /* The main database structure */ 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 */ int srcTab; /* Data comes from this temporary cursor if >=0 */ int iSelectLoop; /* Address of code that implements the SELECT */ 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 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. */ assert( pTabList->nSrc==1 ); zTab = pTabList->a[0].zName; if( zTab==0 ) goto insert_cleanup; pTab = sqliteSrcListLookup(pParse, pTabList); if( pTab==0 ){ goto insert_cleanup; } assert( pTab->iDb<db->nDb ); zDb = db->aDb[pTab->iDb].zName; if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ goto insert_cleanup; } /* Ensure that: * (a) the table is not read-only, * (b) that if it is a view then ON INSERT triggers exist */ 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; } if( pTab==0 ) goto insert_cleanup; /* If pTab is really a view, make sure it has been initialized. */ 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, pTab->iDb); /* if there are row triggers, allocate a temp table for new.* references. */ if( row_triggers_exist ){ newIdx = pParse->nTab++; } /* Figure out how many columns of data are supplied. If the data ** is coming from a SELECT statement, then this step also generates ** all the code to implement the SELECT statement and invoke a subroutine ** to process each row of the result. (Template 2.) If the SELECT ** statement uses the the table that is being inserted into, then the ** subroutine is also coded here. That subroutine stores the SELECT ** results in a temporary table. (Template 3.) */ if( pSelect ){ /* Data is coming from a SELECT. Generate code to implement that SELECT */ int rc, iInitCode; iInitCode = sqliteVdbeAddOp(v, OP_Goto, 0, 0); iSelectLoop = sqliteVdbeCurrentAddr(v); iInsertBlock = sqliteVdbeMakeLabel(v); rc = sqliteSelect(pParse, pSelect, SRT_Subroutine, iInsertBlock, 0,0,0); if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; iCleanup = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Goto, 0, iCleanup); assert( pSelect->pEList ); nColumn = pSelect->pEList->nExpr; /* 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. */ 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 ** the result. Store the result in a temporary table */ srcTab = pParse->nTab++; sqliteVdbeResolveLabel(v, iInsertBlock); sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0); sqliteVdbeAddOp(v, OP_NewRecno, srcTab, 0); sqliteVdbeAddOp(v, OP_Pull, 1, 0); sqliteVdbeAddOp(v, OP_PutIntKey, srcTab, 0); sqliteVdbeAddOp(v, OP_Return, 0, 0); /* The following code runs first because the GOTO at the very top ** of the program jumps to it. Create the temporary table, then jump ** back up and execute the SELECT code above. */ sqliteVdbeChangeP2(v, iInitCode, sqliteVdbeCurrentAddr(v)); sqliteVdbeAddOp(v, OP_OpenTemp, srcTab, 0); sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop); sqliteVdbeResolveLabel(v, iCleanup); }else{ sqliteVdbeChangeP2(v, iInitCode, sqliteVdbeCurrentAddr(v)); } }else{ /* This is the case if the data for the INSERT is coming from a VALUES ** clause */ SrcList dummy; assert( pList!=0 ); srcTab = -1; useTempTable = 0; assert( pList ); nColumn = pList->nExpr; dummy.nSrc = 0; for(i=0; i<nColumn; i++){ if( sqliteExprResolveIds(pParse, &dummy, 0, pList->a[i].pExpr) ){ goto insert_cleanup; } if( sqliteExprCheck(pParse, pList->a[i].pExpr, 0, 0) ){ goto insert_cleanup; } } } /* Make sure the number of columns in the source data matches the number ** of columns to be inserted into the table. */ if( pColumn==0 && nColumn!=pTab->nCol ){ 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 ){ sqliteErrorMsg(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 keyColumn variable ** the index into IDLIST of the primary key column. keyColumn is ** the index of the primary key as it appears in IDLIST, not as ** is appears in the original table. (The index of the 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( sqliteStrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ pColumn->a[i].idx = j; if( j==pTab->iPKey ){ keyColumn = i; } break; } } if( j>=pTab->nCol ){ if( sqliteIsRowid(pColumn->a[i].zName) ){ keyColumn = i; }else{ sqliteErrorMsg(pParse, "table %S has no column named %s", pTabList, 0, pColumn->a[i].zName); pParse->nErr++; goto insert_cleanup; } } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -