1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-19 21:43:15 +03:00

Fixed the support of UNIQUE and PRIMARY KEY. (CVS 268)

FossilOrigin-Name: 116fdad06868acf6aca9e75c2c3497c0511a42c3
This commit is contained in:
drh
2001-09-27 15:11:53 +00:00
parent 717e640294
commit adbca9cfde
13 changed files with 261 additions and 238 deletions

View File

@@ -25,7 +25,7 @@
** ROLLBACK
** PRAGMA
**
** $Id: build.c,v 1.42 2001/09/27 03:22:33 drh Exp $
** $Id: build.c,v 1.43 2001/09/27 15:11:54 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -343,6 +343,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){
Table *pTable;
char *zName;
sqlite *db = pParse->db;
Vdbe *v;
pParse->sFirstToken = *pStart;
zName = sqliteTableNameFromToken(pName);
@@ -370,13 +371,13 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){
pTable->pIndex = 0;
if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable);
pParse->pNewTable = pTable;
if( !pParse->initFlag && (db->flags & SQLITE_InTrans)==0 ){
Vdbe *v = sqliteGetVdbe(pParse);
if( v ){
if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){
if( (db->flags & SQLITE_InTrans)==0 ){
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0);
pParse->schemaVerified = 1;
}
sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0);
}
}
@@ -491,47 +492,28 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
*/
if( pParse->initFlag ){
p->tnum = pParse->newTnum;
if( p->pIndex ){
p->pIndex->tnum = pParse->newKnum;
}
}
/* If not initializing, then create a record for the new table
** in the SQLITE_MASTER table of the database.
*/
if( !pParse->initFlag ){
int n, base;
int n, addr;
Vdbe *v;
v = sqliteGetVdbe(pParse);
if( v==0 ) return;
n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1;
sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0);
sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_String, 0, 0, "table", 0);
sqliteVdbeAddOp(v, OP_String, 0, 0, p->zName, 0);
sqliteVdbeAddOp(v, OP_String, 0, 0, p->zName, 0);
sqliteVdbeAddOp(v, OP_CreateTable, 0, 0, 0, 0);
sqliteVdbeTableRootAddr(v, &p->tnum);
if( p->pIndex ){
/* If the table has a primary key, create an index in the database
** for that key and record the root page of the index in the "knum"
** column of of the SQLITE_MASTER table.
*/
Index *pIndex = p->pIndex;
assert( pIndex->pNext==0 );
assert( pIndex->tnum==0 );
sqliteVdbeAddOp(v, OP_CreateIndex, 0, 0, 0, 0),
sqliteVdbeIndexRootAddr(v, &pIndex->tnum);
}else{
/* If the table does not have a primary key, the "knum" column is
** fill with a NULL value.
*/
sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
}
base = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
sqliteVdbeChangeP3(v, base, pParse->sFirstToken.z, n);
sqliteVdbeAddOp(v, OP_MakeRecord, 6, 0, 0, 0);
addr = sqliteVdbeAddOp(v, OP_CreateTable, 0, 0, 0, 0);
sqliteVdbeChangeP3(v, addr, (char *)&p->tnum, -1);
p->tnum = 0;
addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
sqliteVdbeChangeP3(v, addr, pParse->sFirstToken.z, n);
sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
changeCookie(db);
sqliteVdbeAddOp(v, OP_SetCookie, db->next_cookie, 0, 0, 0);
@@ -634,11 +616,13 @@ void sqliteDropTable(Parse *pParse, Token *pName){
/*
** Create a new index for an SQL table. pIndex is the name of the index
** and pTable is the name of the table that is to be indexed. Both will
** be NULL for a primary key. In that case, use pParse->pNewTable as the
** table to be indexed.
** be NULL for a primary key or an index that is created to satisfy a
** UNIQUE constraint. If pTable and pIndex are NULL, use pParse->pNewTable
** as the table to be indexed.
**
** pList is a list of columns to be indexed. pList will be NULL if the
** most recently added column of the table is labeled as the primary key.
** most recently added column of the table is the primary key or has
** the UNIQUE constraint.
*/
void sqliteCreateIndex(
Parse *pParse, /* All information about this parse */
@@ -679,8 +663,8 @@ void sqliteCreateIndex(
/*
** Find the name of the index. Make sure there is not already another
** index or table with the same name. If pName==0 it means that we are
** dealing with a primary key, which has no name, so this step can be
** skipped.
** dealing with a primary key or UNIQUE constraint. We have to invent our
** own name.
*/
if( pName ){
zName = sqliteTableNameFromToken(pName);
@@ -698,8 +682,13 @@ void sqliteCreateIndex(
goto exit_create_index;
}
}else{
char zBuf[30];
int n;
Index *pLoop;
for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){}
sprintf(zBuf,"%d)",n);
zName = 0;
sqliteSetString(&zName, pTab->zName, " (primary key)", 0);
sqliteSetString(&zName, "(", pTab->zName, " autoindex ", zBuf, 0);
if( zName==0 ) goto exit_create_index;
}
@@ -746,16 +735,12 @@ void sqliteCreateIndex(
}
/* Link the new Index structure to its table and to the other
** in-memory database structures. Note that primary key indices
** do not appear in the index hash table.
** in-memory database structures.
*/
if( pParse->explain==0 ){
if( pName!=0 ){
char *zName = pIndex->zName;;
sqliteHashInsert(&db->idxHash, zName, strlen(zName)+1, pIndex);
}
pIndex->pNext = pTab->pIndex;
pTab->pIndex = pIndex;
pIndex->pNext = pTab->pIndex;
pTab->pIndex = pIndex;
if( !pParse->explain ){
sqliteHashInsert(&db->idxHash, pIndex->zName, strlen(zName)+1, pIndex);
db->flags |= SQLITE_InternChanges;
}
@@ -763,7 +748,7 @@ void sqliteCreateIndex(
** "sqlite_master" table on the disk. So do not write to the disk
** again. Extract the table number from the pParse->newTnum field.
*/
if( pParse->initFlag ){
if( pParse->initFlag && pTable!=0 ){
pIndex->tnum = pParse->newTnum;
}
@@ -778,79 +763,75 @@ void sqliteCreateIndex(
** we don't want to recreate it.
**
** If pTable==0 it means this index is generated as a primary key
** and those does not have a CREATE INDEX statement to add to the
** master table. Also, since primary keys are created at the same
** time as tables, the table will be empty so there is no need to
** initialize the index. Hence, skip all the code generation if
** pTable==0.
** or UNIQUE constraint of a CREATE TABLE statement. The code generator
** for CREATE TABLE will have already opened cursor 0 for writing to
** the sqlite_master table and will take care of closing that cursor
** for us in the end. So those steps are skipped when pTable==0
*/
else if( pParse->initFlag==0 && pTable!=0 ){
static VdbeOp addTable[] = {
{ OP_OpenWrite, 2, 2, MASTER_NAME},
{ OP_NewRecno, 2, 0, 0},
{ OP_String, 0, 0, "index"},
{ OP_String, 0, 0, 0}, /* 3 */
{ OP_String, 0, 0, 0}, /* 4 */
{ OP_CreateIndex, 1, 0, 0},
{ OP_Dup, 0, 0, 0},
{ OP_OpenWrite, 1, 0, 0}, /* 7 */
{ OP_Null, 0, 0, 0},
{ OP_String, 0, 0, 0}, /* 9 */
{ OP_MakeRecord, 6, 0, 0},
{ OP_Put, 2, 0, 0},
{ OP_SetCookie, 0, 0, 0}, /* 12 */
{ OP_Close, 2, 0, 0},
};
else if( pParse->initFlag==0 ){
int n;
Vdbe *v = pParse->pVdbe;
Vdbe *v;
int lbl1, lbl2;
int i;
int addr;
v = sqliteGetVdbe(pParse);
if( v==0 ) goto exit_create_index;
if( pTable!=0 && (db->flags & SQLITE_InTrans)==0 ){
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0);
pParse->schemaVerified = 1;
if( pTable!=0 ){
if( (db->flags & SQLITE_InTrans)==0 ){
sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0);
pParse->schemaVerified = 1;
}
sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0);
}
sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_String, 0, 0, "index", 0);
sqliteVdbeAddOp(v, OP_String, 0, 0, pIndex->zName, 0);
sqliteVdbeAddOp(v, OP_String, 0, 0, pTab->zName, 0);
addr = sqliteVdbeAddOp(v, OP_CreateIndex, 0, 0, 0, 0);
sqliteVdbeChangeP3(v, addr, (char*)&pIndex->tnum, -1);
pIndex->tnum = 0;
if( pTable ){
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0, 0, 0);
}
if( pStart && pEnd ){
int base;
n = (int)pEnd->z - (int)pStart->z + 1;
base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable);
sqliteVdbeChangeP3(v, base+3, pIndex->zName, 0);
sqliteVdbeChangeP3(v, base+4, pTab->zName, 0);
sqliteVdbeIndexRootAddr(v, &pIndex->tnum);
sqliteVdbeChangeP3(v, base+7, pIndex->zName, 0);
sqliteVdbeChangeP3(v, base+9, pStart->z, n);
changeCookie(db);
sqliteVdbeChangeP1(v, base+12, db->next_cookie);
addr = sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
sqliteVdbeChangeP3(v, addr, pStart->z, n);
}else{
sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
}
sqliteVdbeAddOp(v, OP_Open, 0, pTab->tnum, pTab->zName, 0);
lbl1 = sqliteVdbeMakeLabel(v);
lbl2 = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_Rewind, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Next, 0, lbl2, 0, lbl1);
sqliteVdbeAddOp(v, OP_Recno, 0, 0, 0, 0);
for(i=0; i<pIndex->nColumn; i++){
sqliteVdbeAddOp(v, OP_Column, 0, pIndex->aiColumn[i], 0, 0);
sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
if( pTable ){
sqliteVdbeAddOp(v, OP_Open, 2, pTab->tnum, pTab->zName, 0);
lbl1 = sqliteVdbeMakeLabel(v);
lbl2 = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_Rewind, 2, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Next, 2, lbl2, 0, lbl1);
sqliteVdbeAddOp(v, OP_Recno, 2, 0, 0, 0);
for(i=0; i<pIndex->nColumn; i++){
sqliteVdbeAddOp(v, OP_Column, 2, pIndex->aiColumn[i], 0, 0);
}
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0, 0, 0);
sqliteVdbeAddOp(v, OP_PutIdx, 1, pIndex->isUnique, 0, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0);
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2);
sqliteVdbeAddOp(v, OP_Close, 2, 0, 0, 0);
}
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0, 0, 0);
sqliteVdbeAddOp(v, OP_PutIdx, 1, pIndex->isUnique, 0, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0);
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2);
sqliteVdbeAddOp(v, OP_Close, 1, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
if( pTable!=0 && (db->flags & SQLITE_InTrans)==0 ){
sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0);
if( pTable!=0 ){
changeCookie(db);
sqliteVdbeAddOp(v, OP_SetCookie, db->next_cookie, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
if( (db->flags & SQLITE_InTrans)==0 ){
sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0);
}
}
}
/* Reclaim memory on an EXPLAIN call.
*/
if( pParse->explain ){
sqliteFree(pIndex);
}
/* Clean up before exiting */
exit_create_index:
sqliteIdListDelete(pList);