1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Added support for the INTEGER PRIMARY KEY column type. (CVS 333)

FossilOrigin-Name: 236a54d289e858a1e0505a20d907a2a40c01b521
This commit is contained in:
drh
2001-12-21 14:30:42 +00:00
parent 7c917d196f
commit 4a32431ce7
14 changed files with 432 additions and 112 deletions

View File

@ -25,7 +25,7 @@
** ROLLBACK
** PRAGMA
**
** $Id: build.c,v 1.59 2001/12/15 02:35:59 drh Exp $
** $Id: build.c,v 1.60 2001/12/21 14:30:43 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -268,7 +268,7 @@ void sqliteCommitInternalChanges(sqlite *db){
}
sqliteHashClear(&toDelete);
for(pElem=sqliteHashFirst(&db->idxHash); pElem; pElem=sqliteHashNext(pElem)){
Table *pIndex = sqliteHashData(pElem);
Index *pIndex = sqliteHashData(pElem);
if( pIndex->isDelete ){
sqliteHashInsert(&toDelete, pIndex, 0, pIndex);
}else{
@ -311,7 +311,7 @@ void sqliteRollbackInternalChanges(sqlite *db){
}
sqliteHashClear(&toDelete);
for(pElem=sqliteHashFirst(&db->idxHash); pElem; pElem=sqliteHashNext(pElem)){
Table *pIndex = sqliteHashData(pElem);
Index *pIndex = sqliteHashData(pElem);
if( !pIndex->isCommit ){
sqliteHashInsert(&toDelete, pIndex, 0, pIndex);
}else{
@ -425,6 +425,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){
pTable->zName = zName;
pTable->nCol = 0;
pTable->aCol = 0;
pTable->iPKey = -1;
pTable->pIndex = 0;
pTable->isTemp = isTemp;
if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable);
@ -436,6 +437,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){
pParse->schemaVerified = 1;
}
if( !isTemp ){
sqliteVdbeAddOp(v, OP_SetCookie, db->file_format, 1);
sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);
sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC);
}
@ -534,6 +536,56 @@ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){
sqliteDequote(*pz);
}
/*
** Designate the PRIMARY KEY for the table. pList is a list of names
** of columns that form the primary key. If pList is NULL, then the
** most recently added column of the table is the primary key.
**
** A table can have at most one primary key. If the table already has
** a primary key (and this is the second primary key) then create an
** error.
**
** If the PRIMARY KEY is on a single column whose datatype is INTEGER,
** then we will try to use that column as the row id. (Exception:
** For backwards compatibility with older databases, do not do this
** if the file format version number is less than 1.) Set the Table.iPKey
** field of the table under construction to be the index of the
** INTEGER PRIMARY KEY column. Table.iPKey is set to -1 if there is
** no INTEGER PRIMARY KEY.
**
** If the key is not an INTEGER PRIMARY KEY, then create a unique
** index for the key. No index is created for INTEGER PRIMARY KEYs.
*/
void sqliteAddPrimaryKey(Parse *pParse, IdList *pList){
Table *pTab = pParse->pNewTable;
char *zType = 0;
int iCol = -1;
if( pTab==0 ) return;
if( pTab->hasPrimKey ){
sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName,
"\" has more than one primary key", 0);
pParse->nErr++;
return;
}
pTab->hasPrimKey = 1;
if( pList==0 ){
iCol = pTab->nCol - 1;
}else if( pList->nId==1 ){
for(iCol=0; iCol<pTab->nCol; iCol++){
if( sqliteStrICmp(pList->a[0].zName, pTab->aCol[iCol].zName)==0 ) break;
}
}
if( iCol>=0 && iCol<pTab->nCol ){
zType = pTab->aCol[iCol].zType;
}
if( pParse->db->file_format>=1 &&
zType && sqliteStrICmp(zType, "INTEGER")==0 ){
pTab->iPKey = iCol;
}else{
sqliteCreateIndex(pParse, 0, 0, pList, 1, 0, 0);
}
}
/*
** Come up with a new random value for the schema cookie. Make sure
** the new value is different from the old.
@ -787,8 +839,8 @@ void sqliteCreateIndex(
/* If this index is created while re-reading the schema from sqlite_master
** but the table associated with this index is a temporary table, it can
** only mean that the table this index is really associated with is one
** whose name is hidden behind a temporary table with the same name.
** only mean that the table that this index is really associated with is
** one whose name is hidden behind a temporary table with the same name.
** Since its table has been suppressed, we need to also suppress the
** index.
*/

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.21 2001/11/07 16:48:27 drh Exp $
** $Id: delete.c,v 1.22 2001/12/21 14:30:43 drh Exp $
*/
#include "sqliteInt.h"
@ -157,7 +157,12 @@ void sqliteDeleteFrom(
int j;
sqliteVdbeAddOp(v, OP_Recno, base, 0);
for(j=0; j<pIdx->nColumn; j++){
sqliteVdbeAddOp(v, OP_Column, base, pIdx->aiColumn[j]);
int idx = pIdx->aiColumn[j];
if( idx==pTab->iPKey ){
sqliteVdbeAddOp(v, OP_Dup, j, 0);
}else{
sqliteVdbeAddOp(v, OP_Column, base, idx);
}
}
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
sqliteVdbeAddOp(v, OP_IdxDelete, base+i, 0);

View File

@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.34 2001/11/24 00:31:46 drh Exp $
** $Id: expr.c,v 1.35 2001/12/21 14:30:43 drh Exp $
*/
#include "sqliteInt.h"
@ -127,7 +127,12 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
cnt++;
pExpr->iTable = i + pParse->nTab;
pExpr->iColumn = j;
if( j==pTab->iPKey ){
/* Substitute the record number for the INTEGER PRIMARY KEY */
pExpr->iColumn = -1;
}else{
pExpr->iColumn = j;
}
}
}
}
@ -190,7 +195,12 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
cnt++;
pExpr->iTable = i + pParse->nTab;
pExpr->iColumn = j;
if( j==pTab->iPKey ){
/* Substitute the record number for the INTEGER PRIMARY KEY */
pExpr->iColumn = -1;
}else{
pExpr->iColumn = j;
}
}
}
}

View File

@ -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.26 2001/11/07 16:48:27 drh Exp $
** $Id: insert.c,v 1.27 2001/12/21 14:30:43 drh Exp $
*/
#include "sqliteInt.h"
@ -49,6 +49,7 @@ void sqliteInsert(
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 */
if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
db = pParse->db;
@ -140,6 +141,9 @@ void sqliteInsert(
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 = j;
}
break;
}
}
@ -152,6 +156,13 @@ void sqliteInsert(
}
}
/* If there is not IDLIST term but the table has an integer primary
** key, the set the keyColumn variable to the primary key column.
*/
if( pColumn==0 ){
keyColumn = pTab->iPKey;
}
/* Open cursors into the table that is received the new data and
** all indices of that table.
*/
@ -178,13 +189,41 @@ void sqliteInsert(
iCont = sqliteVdbeCurrentAddr(v);
}
/* Create a new entry in the table and fill it with data.
/* Push the record number for the new entry onto the stack. The
** record number is a randomly generate integer created by NewRecno
** except when the table has an INTEGER PRIMARY KEY column, in which
** case the record number is the same as that column.
*/
if( keyColumn>=0 ){
if( srcTab>=0 ){
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
}else{
sqliteExprCode(pParse, pList->a[keyColumn].pExpr);
}
sqliteVdbeAddOp(v, OP_AddImm, 0, 0); /* Make sure ROWID is an integer */
}else{
sqliteVdbeAddOp(v, OP_NewRecno, base, 0);
}
/* If there are indices, we'll need this record number again, so make
** a copy.
*/
sqliteVdbeAddOp(v, OP_NewRecno, base, 0);
if( pTab->pIndex ){
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
}
/* Push onto the stack data for all columns of the new entry, beginning
** with the first column.
*/
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
/* The value of the INTEGER PRIMARY KEY column is always a NULL.
** Whenever this column is used, the record number will be substituted
** in its place, so there is no point it it taking up space in
** the data record. */
sqliteVdbeAddOp(v, OP_String, 0, 0);
continue;
}
if( pColumn==0 ){
j = i;
}else{
@ -201,10 +240,12 @@ void sqliteInsert(
sqliteExprCode(pParse, pList->a[j].pExpr);
}
}
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_Put, base, 0);
/* Create the new record and put it into the database.
*/
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_Put, base, keyColumn>=0);
/* Create appropriate entries for the new data row in all indices
** of the table.
*/
@ -214,6 +255,11 @@ void sqliteInsert(
}
for(i=0; i<pIdx->nColumn; i++){
int idx = pIdx->aiColumn[i];
if( idx==pTab->iPKey ){
/* Copy the record number in place of the INTEGER PRIMARY KEY column */
sqliteVdbeAddOp(v, OP_Dup, i, 0);
continue;
}
if( pColumn==0 ){
j = idx;
}else{

View File

@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.51 2001/12/05 00:21:20 drh Exp $
** $Id: main.c,v 1.52 2001/12/21 14:30:43 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@ -25,7 +25,7 @@
**
** Each callback contains the following information:
**
** argv[0] = "meta" or "table" or "index"
** argv[0] = "file-format" or "schema-cookie" or "table" or "index"
** argv[1] = table or index name or meta statement type.
** argv[2] = root page number for table or index. NULL for meta.
** argv[3] = SQL create statement for the table or index
@ -42,13 +42,13 @@ static int sqliteOpenCb(void *pDb, int argc, char **argv, char **azColName){
assert( argc==4 );
switch( argv[0][0] ){
case 'm': { /* Meta information */
if( strcmp(argv[1],"file-format")==0 ){
db->file_format = atoi(argv[3]);
}else if( strcmp(argv[1],"schema-cookie")==0 ){
db->schema_cookie = atoi(argv[3]);
db->next_cookie = db->schema_cookie;
}
case 'f': { /* File format */
db->file_format = atoi(argv[3]);
break;
}
case 's': { /* Schema cookie */
db->schema_cookie = atoi(argv[3]);
db->next_cookie = db->schema_cookie;
break;
}
case 'i':
@ -156,44 +156,39 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
** database scheme.
*/
static VdbeOp initProg[] = {
{ OP_Open, 0, 2, 0},
{ OP_Rewind, 0, 31, 0},
{ OP_Column, 0, 0, 0}, /* 2 */
{ OP_String, 0, 0, "meta"},
{ OP_Ne, 0, 10, 0},
{ OP_Column, 0, 0, 0},
{ OP_Column, 0, 1, 0},
{ OP_Column, 0, 3, 0},
{ OP_Column, 0, 4, 0},
{ OP_Callback, 4, 0, 0},
{ OP_Next, 0, 2, 0}, /* 10 */
{ OP_Rewind, 0, 31, 0}, /* 11 */
{ OP_Column, 0, 0, 0}, /* 12 */
{ OP_String, 0, 0, "table"},
{ OP_Ne, 0, 20, 0},
{ OP_Column, 0, 0, 0},
{ OP_Column, 0, 1, 0},
{ OP_Column, 0, 3, 0},
{ OP_Column, 0, 4, 0},
{ OP_Callback, 4, 0, 0},
{ OP_Next, 0, 12, 0}, /* 20 */
{ OP_Rewind, 0, 31, 0}, /* 21 */
{ OP_Column, 0, 0, 0}, /* 22 */
{ OP_String, 0, 0, "index"},
{ OP_Ne, 0, 30, 0},
{ OP_Column, 0, 0, 0},
{ OP_Column, 0, 1, 0},
{ OP_Column, 0, 3, 0},
{ OP_Column, 0, 4, 0},
{ OP_Callback, 4, 0, 0},
{ OP_Next, 0, 22, 0}, /* 30 */
{ OP_String, 0, 0, "meta"}, /* 31 */
{ OP_String, 0, 0, "schema-cookie"},
{ OP_String, 0, 0, 0},
{ OP_ReadCookie,0,0, 0},
{ OP_Callback, 4, 0, 0},
{ OP_Close, 0, 0, 0},
{ OP_Halt, 0, 0, 0},
{ OP_Open, 0, 2, 0},
{ OP_String, 0, 0, "file-format"},
{ OP_String, 0, 0, 0},
{ OP_String, 0, 0, 0},
{ OP_ReadCookie, 0, 1, 0},
{ OP_Callback, 4, 0, 0},
{ OP_String, 0, 0, "schema_cookie"},
{ OP_String, 0, 0, 0},
{ OP_String, 0, 0, 0},
{ OP_ReadCookie, 0, 0, 0},
{ OP_Callback, 4, 0, 0},
{ OP_Rewind, 0, 31, 0},
{ OP_Column, 0, 0, 0}, /* 12 */
{ OP_String, 0, 0, "table"},
{ OP_Ne, 0, 20, 0},
{ OP_Column, 0, 0, 0},
{ OP_Column, 0, 1, 0},
{ OP_Column, 0, 3, 0},
{ OP_Column, 0, 4, 0},
{ OP_Callback, 4, 0, 0},
{ OP_Next, 0, 12, 0}, /* 20 */
{ OP_Rewind, 0, 31, 0}, /* 21 */
{ OP_Column, 0, 0, 0}, /* 22 */
{ OP_String, 0, 0, "index"},
{ OP_Ne, 0, 30, 0},
{ OP_Column, 0, 0, 0},
{ OP_Column, 0, 1, 0},
{ OP_Column, 0, 3, 0},
{ OP_Column, 0, 4, 0},
{ OP_Callback, 4, 0, 0},
{ OP_Next, 0, 22, 0}, /* 30 */
{ OP_Close, 0, 0, 0}, /* 31 */
{ OP_Halt, 0, 0, 0},
};
/* Create a virtual machine to run the initialization program. Run
@ -208,7 +203,10 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg,
db->pBusyArg, db->xBusyCallback);
sqliteVdbeDelete(vdbe);
if( rc==SQLITE_OK && db->file_format>1 && db->nTable>0 ){
if( rc==SQLITE_OK && db->nTable==0 ){
db->file_format = FILE_FORMAT;
}
if( rc==SQLITE_OK && db->file_format>FILE_FORMAT ){
sqliteSetString(pzErrMsg, "unsupported file format", 0);
rc = SQLITE_ERROR;
}
@ -282,9 +280,6 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
return 0;
}
/* Assume file format 1 unless the database says otherwise */
db->file_format = 1;
/* Attempt to read the schema */
rc = sqliteInit(db, pzErrMsg);
if( sqlite_malloc_failed ){

View File

@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.39 2001/12/16 20:05:06 drh Exp $
** @(#) $Id: parse.y,v 1.40 2001/12/21 14:30:43 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@ -138,7 +138,7 @@ carg ::= DEFAULT NULL.
// UNIQUE constraints.
//
ccons ::= NOT NULL. {sqliteAddNotNull(pParse);}
ccons ::= PRIMARY KEY sortorder. {sqliteCreateIndex(pParse,0,0,0,1,0,0);}
ccons ::= PRIMARY KEY sortorder. {sqliteAddPrimaryKey(pParse, 0);}
ccons ::= UNIQUE. {sqliteCreateIndex(pParse,0,0,0,1,0,0);}
ccons ::= CHECK LP expr RP.
@ -151,7 +151,7 @@ conslist ::= conslist COMMA tcons.
conslist ::= conslist tcons.
conslist ::= tcons.
tcons ::= CONSTRAINT ids.
tcons ::= PRIMARY KEY LP idxlist(X) RP. {sqliteCreateIndex(pParse,0,0,X,1,0,0);}
tcons ::= PRIMARY KEY LP idxlist(X) RP. {sqliteAddPrimaryKey(pParse,X);}
tcons ::= UNIQUE LP idxlist(X) RP. {sqliteCreateIndex(pParse,0,0,X,1,0,0);}
tcons ::= CHECK expr.

View File

@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.72 2001/12/05 00:21:20 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.73 2001/12/21 14:30:43 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@ -30,6 +30,11 @@
#define MAX_PAGES 100
#define TEMP_PAGES 25
/*
** File format version number
*/
#define FILE_FORMAT 1
/*
** Integers of known sizes. These typedefs might change for architectures
** where the sizes very. Preprocessor macros are available so that the
@ -213,7 +218,8 @@ struct Column {
char *zName; /* Name of this column */
char *zDflt; /* Default value of this column */
char *zType; /* Data type for this column */
int notNull; /* True if there is a NOT NULL constraint */
u8 notNull; /* True if there is a NOT NULL constraint */
u8 isPrimKey; /* True if this column is an INTEGER PRIMARY KEY */
};
/*
@ -224,12 +230,14 @@ struct Table {
char *zName; /* Name of the table */
int nCol; /* Number of columns in this table */
Column *aCol; /* Information about each column */
int iPKey; /* Use this column as the record-number for each row */
Index *pIndex; /* List of SQL indexes on this table. */
int tnum; /* Page containing root for this table */
u8 readOnly; /* True if this table should not be written by the user */
u8 isCommit; /* True if creation of this table has been committed */
u8 isDelete; /* True if this table is being deleted */
u8 isTemp; /* True if stored in db->pBeTemp instead of db->pBe */
u8 hasPrimKey; /* True if there exists a primary key */
};
/*

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.22 2001/11/21 02:21:12 drh Exp $
** $Id: update.c,v 1.23 2001/12/21 14:30:43 drh Exp $
*/
#include "sqliteInt.h"
@ -40,6 +40,8 @@ void sqliteUpdate(
** an expression for the i-th column of the table.
** aXRef[i]==-1 if the i-th column is not changed. */
int openOp; /* Opcode used to open tables */
int chngRecno; /* True if the record number is being changed */
Expr *pRecnoExpr; /* Expression defining the new record number */
if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup;
db = pParse->db;
@ -89,6 +91,7 @@ void sqliteUpdate(
goto update_cleanup;
}
}
chngRecno = 0;
for(i=0; i<pChanges->nExpr; i++){
if( sqliteExprResolveIds(pParse, pTabList, pChanges->a[i].pExpr) ){
goto update_cleanup;
@ -98,6 +101,10 @@ void sqliteUpdate(
}
for(j=0; j<pTab->nCol; j++){
if( sqliteStrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
if( i==pTab->iPKey ){
chngRecno = 1;
pRecnoExpr = pChanges->a[i].pExpr;
}
aXRef[j] = i;
break;
}
@ -115,8 +122,12 @@ void sqliteUpdate(
** key includes one of the columns named in pChanges.
*/
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
for(i=0; i<pIdx->nColumn; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
if( chngRecno ){
i = 0;
}else {
for(i=0; i<pIdx->nColumn; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
}
}
if( i<pIdx->nColumn ) nIdx++;
}
@ -125,8 +136,12 @@ void sqliteUpdate(
if( apIdx==0 ) goto update_cleanup;
}
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
for(i=0; i<pIdx->nColumn; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
if( chngRecno ){
i = 0;
}else{
for(i=0; i<pIdx->nColumn; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
}
}
if( i<pIdx->nColumn ) apIdx[nIdx++] = pIdx;
}
@ -175,6 +190,7 @@ void sqliteUpdate(
** the old data for each record to be updated because some columns
** might not change and we will need to copy the old value.
** Also, the old data is needed to delete the old index entires.
** So make the cursor point at the old record.
*/
end = sqliteVdbeMakeLabel(v);
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
@ -193,9 +209,22 @@ void sqliteUpdate(
sqliteVdbeAddOp(v, OP_IdxDelete, base+i+1, 0);
}
/* If changing the record number, remove the old record number
** from the top of the stack and replace it with the new one.
*/
if( chngRecno ){
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
sqliteExprCode(pParse, pRecnoExpr);
sqliteVdbeAddOp(v, OP_AddImm, 0, 0);
}
/* Compute new data for this record.
*/
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
sqliteVdbeAddOp(v, OP_Dup, i, 0);
continue;
}
j = aXRef[i];
if( j<0 ){
sqliteVdbeAddOp(v, OP_Column, base, i);
@ -204,13 +233,24 @@ void sqliteUpdate(
}
}
/* If changing the record number, delete the hold record.
*/
if( chngRecno ){
sqliteVdbeAddOp(v, OP_Delete, 0, 0);
}
/* Insert new index entries that correspond to the new data
*/
for(i=0; i<nIdx; i++){
sqliteVdbeAddOp(v, OP_Dup, pTab->nCol, 0); /* The KEY */
pIdx = apIdx[i];
for(j=0; j<pIdx->nColumn; j++){
sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiColumn[j], 0);
int idx = pIdx->aiColumn[j];
if( idx==pTab->iPKey ){
sqliteVdbeAddOp(v, OP_Dup, j, 0);
}else{
sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-idx, 0);
}
}
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, pIdx->isUnique);

View File

@ -30,7 +30,7 @@
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
** $Id: vdbe.c,v 1.100 2001/11/13 19:35:15 drh Exp $
** $Id: vdbe.c,v 1.101 2001/12/21 14:30:43 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -1654,7 +1654,10 @@ case OP_ShiftRight: {
/* Opcode: AddImm P1 * *
**
** Add the value P1 to whatever is on top of the stack.
** Add the value P1 to whatever is on top of the stack. The result
** is always an integer.
**
** To force the top of the stack to be an integer, just add 0.
*/
case OP_AddImm: {
int tos = p->tos;
@ -2269,15 +2272,20 @@ case OP_Rollback: {
break;
}
/* Opcode: ReadCookie * * *
/* Opcode: ReadCookie * P2 *
**
** Read the schema cookie from the database file and push it onto the
** When P2==0,
** read the schema cookie from the database file and push it onto the
** stack. The schema cookie is an integer that is used like a version
** number for the database schema. Everytime the schema changes, the
** cookie changes to a new random value. This opcode is used during
** initialization to read the initial cookie value so that subsequent
** database accesses can verify that the cookie has not changed.
**
** If P2>0, then read global database parameter number P2. There is
** a small fixed number of global database parameters. P2==1 is the
** database version number. Other parameters are currently unused.
**
** There must be a read-lock on the database (either a transaction
** must be started or there must be an open cursor) before
** executing this instruction.
@ -2285,17 +2293,21 @@ case OP_Rollback: {
case OP_ReadCookie: {
int i = ++p->tos;
int aMeta[SQLITE_N_BTREE_META];
assert( pOp->p2<SQLITE_N_BTREE_META );
VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
rc = sqliteBtreeGetMeta(pBt, aMeta);
aStack[i].i = aMeta[1];
aStack[i].i = aMeta[1+pOp->p2];
aStack[i].flags = STK_Int;
break;
}
/* Opcode: SetCookie P1 * *
/* Opcode: SetCookie P1 P2 *
**
** This operation changes the value of the schema cookie on the database.
** The new value is P1.
** When P2==0,
** this operation changes the value of the schema cookie on the database.
** The new value is P1. When P2>0, the value of global database parameter
** number P2 is changed. See ReadCookie for more information about
** global database parametes.
**
** The schema cookie changes its value whenever the database schema changes.
** That way, other processes can recognize when the schema has changed
@ -2305,18 +2317,21 @@ case OP_ReadCookie: {
*/
case OP_SetCookie: {
int aMeta[SQLITE_N_BTREE_META];
assert( pOp->p2<SQLITE_N_BTREE_META );
rc = sqliteBtreeGetMeta(pBt, aMeta);
if( rc==SQLITE_OK ){
aMeta[1] = pOp->p1;
aMeta[1+pOp->p2] = pOp->p1;
rc = sqliteBtreeUpdateMeta(pBt, aMeta);
}
break;
}
/* Opcode: VerifyCookie P1 * *
/* Opcode: VerifyCookie P1 P2 *
**
** Check the current value of the schema cookie and make sure it is
** equal to P1. If it is not, abort with an SQLITE_SCHEMA error.
** Check the value of global database parameter number P2 and make
** sure it is equal to P1. P2==0 is the schema cookie. P1==1 is
** the database version. If the values do not match, abort with
** an SQLITE_SCHEMA error.
**
** The cookie changes its value whenever the database schema changes.
** This operation is used to detect when that the cookie has changed
@ -2328,8 +2343,9 @@ case OP_SetCookie: {
*/
case OP_VerifyCookie: {
int aMeta[SQLITE_N_BTREE_META];
assert( pOp->p2<SQLITE_N_BTREE_META );
rc = sqliteBtreeGetMeta(pBt, aMeta);
if( rc==SQLITE_OK && aMeta[1]!=pOp->p1 ){
if( rc==SQLITE_OK && aMeta[1+pOp->p2]!=pOp->p1 ){
sqliteSetString(pzErrMsg, "database schema has changed", 0);
rc = SQLITE_SCHEMA;
}
@ -2613,7 +2629,7 @@ case OP_Found: {
**
** Get a new integer record number used as the key to a table.
** The record number is not previously used as a key in the database
** table that cursor P1 points to. The new record number pushed
** table that cursor P1 points to. The new record number is pushed
** onto the stack.
*/
case OP_NewRecno: {
@ -2666,13 +2682,16 @@ case OP_NewRecno: {
break;
}
/* Opcode: Put P1 * *
/* Opcode: Put P1 P2 *
**
** Write an entry into the database file P1. A new entry is
** created if it doesn't already exist or the data for an existing
** entry is overwritten. The data is the value on the top of the
** stack. The key is the next value down on the stack. The stack
** is popped twice by this instruction.
**
** If P2==1 then overwriting is prohibited. If a prior entry with
** the same key exists, an SQLITE_CONSTRAINT exception is raised.
*/
case OP_Put: {
int tos = p->tos;
@ -2691,6 +2710,16 @@ case OP_Put: {
iKey = bigEndian(aStack[nos].i);
zKey = (char*)&iKey;
}
if( pOp->p2 ){
int res;
rc = sqliteBtreeMoveto(p->aCsr[i].pCursor, zKey, nKey, &res);
if( res==0 && rc==SQLITE_OK ){
rc = SQLITE_CONSTRAINT;
}
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
}
rc = sqliteBtreeInsert(p->aCsr[i].pCursor, zKey, nKey,
zStack[tos], aStack[tos].n);
}