From: drh Date: Thu, 27 Sep 2001 03:22:32 +0000 (+0000) Subject: Added basic support for enforcement of UNIQUE on indices and primary X-Git-Tag: version-3.6.10~5778 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=717e6402948a55c8501adcdedc0508a82f9b880d;p=thirdparty%2Fsqlite.git Added basic support for enforcement of UNIQUE on indices and primary keys. Support for addition constraints is to follow. (CVS 267) FossilOrigin-Name: 34c42967f3d52dfb65d9f31db4f6995d098ec1f7 --- diff --git a/manifest b/manifest index df3b92ed73..f36f581880 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sRCSID\sproblem.\s(CVS\s266) -D 2001-09-25T02:04:29 +C Added\sbasic\ssupport\sfor\senforcement\sof\sUNIQUE\son\sindices\sand\sprimary\nkeys.\s\sSupport\sfor\saddition\sconstraints\sis\sto\sfollow.\s(CVS\s267) +D 2001-09-27T03:22:33 F Makefile.in fe9d96d6a7b04b3000a24692c2a3761840bbbf97 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 F VERSION 17fadc361fb942d644f92116388409c937c9fa79 @@ -8,37 +8,37 @@ F configure.in 0000c0d62beb47cae1d2d81a197c7fe6efd56a45 F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 -F src/btree.c da9b60a0b94daac0b7a231f42cb79961bf06531d -F src/btree.h 5e5531869e53623aad5b32c22249c5743039251e -F src/build.c 6a27cbfabc956e89cf62358d368524429c1aec9c +F src/btree.c a4a88dfef2072cedfdac09f3a51b7d70b017b9b4 +F src/btree.h 57d653ef5137b91f2a068aaf71a2905468dd2cb7 +F src/build.c 501c96f8224f1dd6c87c53c1ca7de62c3a98d5bb F src/delete.c 81002d889aae874decf507627207c5d1b3599dc2 F src/expr.c 343a515a4abaf60e9e26c7412aa8c43fd3eae97d F src/hash.c bf36fb4cba114015123b0050f137d2c4553778a1 F src/hash.h 5f6e7c04c46ed015ab4e01797c2049b4af5b006d -F src/insert.c 061e531d19869e26ba9202c6d069385237b4c102 +F src/insert.c 0552c2a4b5fd359e9ed5d1e314d5e89093802c8e F src/main.c 00ff61d82189ad23fe2f2e6c355951f514cb1b5c F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c F src/os.c 45376582c41dc8829330816d56b8e9e6cd1b7972 F src/os.h 0f478e2fef5ec1612f94b59b163d4807d4c77d6d F src/pager.c 0fe02b63a89d8eebb42ad30529d0c7cc918ecb94 F src/pager.h a0d4c5ae271914aa07b62aee0707997d6932b6ca -F src/parse.y 2bcf47bb8e6afd8bc10aebd555fa07b73905bee4 +F src/parse.y a136e0a24ce434e52c79a7eaa669cf9c8249e4db F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9 F src/random.c 708a23f69f40d6f2ae5ce1a04e6a4055d4a6ecec F src/select.c 7d90a6464906419fde96c0707a4cf4f3280db318 F src/shell.c 8e573138074e0b9526fca59b3eac22bdf18ecc03 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e -F src/sqlite.h.in dbe7a1b1e1ab9bfce1a6983cfa6f53c5c2499305 -F src/sqliteInt.h 6bcdcfbc2b2ec7bb6ec96b3a31f4a0c74ee14c4f +F src/sqlite.h.in 08151912b382ded315b5c8fc6288d9d7a9332aa4 +F src/sqliteInt.h 35f42e624f11924a56a5501e30a670e1b92f62bb F src/table.c abd0adbe0fee39d995287b3bcccd908d174dfcac F src/tclsqlite.c 04a35d04f06046acc3944121dc6c36717f7f36d5 F src/test1.c e4b31f62ea71963cbae44338acf477a04fc8fc49 F src/test2.c 0168b39225b768cfdadd534406f9dec58c27879e F src/test3.c 4a0d7b882fdae731dbb759f512ad867122452f96 F src/tokenize.c 2ab07b85fde38d8fa2b4e73417b93e94f9cf8f5f -F src/update.c 8de22957017e17c5e751ba71c4ea76c60f93aa2f -F src/util.c 9c888445c1fd7896dab38fa62efc532f2364010a -F src/vdbe.c 13907958b90401d495132a17840979d58be45554 +F src/update.c 0449af173b5f2f0b26e2f0e4545ee0e0429763cb +F src/util.c 4da3be37d0fd3c640d2d3033503768afdc8e5387 +F src/vdbe.c d3cf685bd9c823445a4b4a57f7cb01718169f464 F src/vdbe.h dc1d441494ba560a1ff464e1c56beb8ca03844fc F src/where.c cce952b6a2459ac2296e3432876a4252d2fe3b87 F test/all.test a2320eb40b462f25bd3e33115b1cabf3791450dd @@ -88,7 +88,7 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4 F www/arch.tcl c1891efdc00cd76eec72bdbf8b446195902932a3 F www/c_interface.tcl 52ae81c89bf906b358e04857bd3f76b1a7f61c1b -F www/changes.tcl 7e5a04a59a417a9fd4c94ce38543adbca0e01937 +F www/changes.tcl 5d0c66ad2bcc9750c7cb6773052e0202a0e7b26d F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c F www/index.tcl fd8ef2d78f22022d2baea03371a1add75d98d236 @@ -99,7 +99,7 @@ F www/speed.tcl 91b53f9403a62bb322dc1f85a81531309bcfb41c F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P 9c9322eb46894860cd7c294cd19ce72614722a73 -R b873f99185874032fd1a88b4c0d82a0a +P 0e9cfcd53e16f96fc181def1d0b2d0ea7f7df73f +R d903e186383659ad85dc50397b26ad49 U drh -Z fd5ec8ff22e2ffe1e95452de93954184 +Z 4633481d5649b488b3df47e886de0708 diff --git a/manifest.uuid b/manifest.uuid index 4819e53c82..01640a3521 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0e9cfcd53e16f96fc181def1d0b2d0ea7f7df73f \ No newline at end of file +34c42967f3d52dfb65d9f31db4f6995d098ec1f7 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 909ead07d2..1808db33f7 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.30 2001/09/23 02:35:53 drh Exp $ +** $Id: btree.c,v 1.31 2001/09/27 03:22:33 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1070,26 +1070,35 @@ int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){ } /* -** Compare the key for the entry that pCur points to against the -** given key (pKey,nKeyOrig). Put the comparison result in *pResult. -** The result is negative if pCurpKey. +** Compare the first nKey bytes of the key of the entry that pCur +** points to against the first nKey bytes of pKey. Set *pRes to +** show the comparison results: ** -** SQLITE_OK is returned on success. If part of the cursor key -** is on overflow pages and we are unable to access those overflow -** pages, then some other value might be returned to indicate the -** reason for the error. +** *pRes<0 This means pCur0 This means pCur>pKey +** +** If pCur contains N bytes where N0 depending on the difference. +** +** If pCur contains M bytes where M>nKey then only the first nKey +** bytes of pCur are used in the comparison. The result is the same +** as it would be if pCur were truncated to nKey bytes. */ -static int compareKey( - BtCursor *pCur, /* Points to the entry against which we are comparing */ - const char *pKey, /* The comparison key */ - int nKeyOrig, /* Number of bytes in the comparison key */ - int *pResult /* Write the comparison results here */ +int sqliteBtreeKeyCompare( + BtCursor *pCur, + const void *pKey, + int nKey, + int *pResult ){ Pgno nextPage; - int nKey = nKeyOrig; int n, c, rc; Cell *pCell; + const char *zKey = (const char*)pKey; assert( pCur->pPage ); assert( pCur->idx>=0 && pCur->idxpPage->nCell ); @@ -1101,12 +1110,12 @@ static int compareKey( if( n>MX_LOCAL_PAYLOAD ){ n = MX_LOCAL_PAYLOAD; } - c = memcmp(pCell->aPayload, pKey, n); + c = memcmp(pCell->aPayload, zKey, n); if( c!=0 ){ *pResult = c; return SQLITE_OK; } - pKey += n; + zKey += n; nKey -= n; nextPage = pCell->ovfl; while( nKey>0 ){ @@ -1123,16 +1132,52 @@ static int compareKey( if( n>OVERFLOW_SIZE ){ n = OVERFLOW_SIZE; } - c = memcmp(pOvfl->aPayload, pKey, n); + c = memcmp(pOvfl->aPayload, zKey, n); sqlitepager_unref(pOvfl); if( c!=0 ){ *pResult = c; return SQLITE_OK; } nKey -= n; - pKey += n; + zKey += n; + } + *pResult = c; + return SQLITE_OK; +} + +/* +** Compare the key for the entry that pCur points to against the +** given key (pKey,nKeyOrig). Put the comparison result in *pResult. +** The result is negative if pCurpKey. +** +** Shorter strings are considered less than longer strings if they +** are otherwise equal. All bytes of both pCur and pKey are considered +** in this comparison. This is different from sqliteBtreeKeyCompare() +** which only considers the first nKeyOrig bytes of pCur. +** +** SQLITE_OK is returned on success. If part of the cursor key +** is on overflow pages and we are unable to access those overflow +** pages, then some other value might be returned to indicate the +** reason for the error. +*/ +static int compareKey( + BtCursor *pCur, /* Points to the entry against which we are comparing */ + const char *pKey, /* The comparison key */ + int nKeyOrig, /* Number of bytes in the comparison key */ + int *pResult /* Write the comparison results here */ +){ + int rc, c; + + rc = sqliteBtreeKeyCompare(pCur, pKey, nKeyOrig, &c); + if( rc!=SQLITE_OK ) return rc; + if( c==0 ){ + Cell *pCell; + assert( pCur->pPage ); + assert( pCur->pPage->nCell>pCur->idx && pCur->idx>=0 ); + pCell = pCur->pPage->apCell[pCur->idx]; + c = pCell->h.nKey - nKeyOrig; } - c = pCell->h.nKey - nKeyOrig; *pResult = c; return SQLITE_OK; } diff --git a/src/btree.h b/src/btree.h index 4efd718e63..0769e405ba 100644 --- a/src/btree.h +++ b/src/btree.h @@ -12,7 +12,7 @@ ** This header file defines the interface that the sqlite B-Tree file ** subsystem. ** -** @(#) $Id: btree.h,v 1.15 2001/09/23 02:35:53 drh Exp $ +** @(#) $Id: btree.h,v 1.16 2001/09/27 03:22:33 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -41,6 +41,7 @@ int sqliteBtreeFirst(BtCursor*, int *pRes); int sqliteBtreeNext(BtCursor*, int *pRes); int sqliteBtreeKeySize(BtCursor*, int *pSize); int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf); +int sqliteBtreeKeyCompare(BtCursor*, const void *pKey, int nKey, int *pRes); int sqliteBtreeDataSize(BtCursor*, int *pSize); int sqliteBtreeData(BtCursor*, int offset, int amt, char *zBuf); int sqliteBtreeCloseCursor(BtCursor*); diff --git a/src/build.c b/src/build.c index 6cd307228a..84e612ac41 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.41 2001/09/24 03:12:40 drh Exp $ +** $Id: build.c,v 1.42 2001/09/27 03:22:33 drh Exp $ */ #include "sqliteInt.h" #include @@ -645,6 +645,7 @@ void sqliteCreateIndex( Token *pName, /* Name of the index. May be NULL */ Token *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */ IdList *pList, /* A list of columns to be indexed */ + int isUnique, /* True if all entries in this index must be unique */ Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ Token *pEnd /* The ")" that closes the CREATE INDEX statement */ ){ @@ -724,6 +725,7 @@ void sqliteCreateIndex( strcpy(pIndex->zName, zName); pIndex->pTable = pTab; pIndex->nColumn = pList->nId; + pIndex->isUnique = isUnique; /* Scan the names of the columns of the table to be indexed and ** load the column indices into the Index structure. Report an error @@ -833,7 +835,7 @@ void sqliteCreateIndex( sqliteVdbeAddOp(v, OP_Column, 0, pIndex->aiColumn[i], 0, 0); } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0, 0, 0); - sqliteVdbeAddOp(v, OP_PutIdx, 1, 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); @@ -1106,7 +1108,7 @@ void sqliteCopy( sqliteVdbeAddOp(v, OP_FileColumn, pIdx->aiColumn[j], 0, 0, 0); } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); - sqliteVdbeAddOp(v, OP_PutIdx, i, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, i, pIdx->isUnique, 0, 0); } sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end); diff --git a/src/insert.c b/src/insert.c index 8de423f678..1c209be2d5 100644 --- a/src/insert.c +++ b/src/insert.c @@ -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.19 2001/09/23 02:35:53 drh Exp $ +** $Id: insert.c,v 1.20 2001/09/27 03:22:33 drh Exp $ */ #include "sqliteInt.h" @@ -231,7 +231,7 @@ void sqliteInsert( } } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); - sqliteVdbeAddOp(v, OP_PutIdx, idx+base, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, idx+base, pIdx->isUnique, 0, 0); } /* The bottom of the loop, if the data source is a SELECT statement diff --git a/src/parse.y b/src/parse.y index da0eafbec2..e07054ca58 100644 --- a/src/parse.y +++ b/src/parse.y @@ -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.30 2001/09/16 00:13:27 drh Exp $ +** @(#) $Id: parse.y,v 1.31 2001/09/27 03:22:33 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -124,7 +124,7 @@ carg ::= DEFAULT NULL. // In addition to the type name, we also care about the primary key. // ccons ::= NOT NULL. -ccons ::= PRIMARY KEY sortorder. {sqliteCreateIndex(pParse,0,0,0,0,0);} +ccons ::= PRIMARY KEY sortorder. {sqliteCreateIndex(pParse,0,0,0,1,0,0);} ccons ::= UNIQUE. ccons ::= CHECK LP expr RP. @@ -137,7 +137,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,0,0);} +tcons ::= PRIMARY KEY LP idxlist(X) RP. {sqliteCreateIndex(pParse,0,0,X,1,0,0);} tcons ::= UNIQUE LP idlist RP. tcons ::= CHECK expr. idlist ::= idlist COMMA ids. @@ -445,10 +445,12 @@ expritem(A) ::= expr(X). {A = X;} expritem(A) ::= . {A = 0;} -cmd ::= CREATE(S) uniqueflag INDEX ids(X) ON ids(Y) LP idxlist(Z) RP(E). - {sqliteCreateIndex(pParse, &X, &Y, Z, &S, &E);} -uniqueflag ::= UNIQUE. -uniqueflag ::= . +cmd ::= CREATE(S) uniqueflag(U) INDEX ids(X) ON ids(Y) LP idxlist(Z) RP(E). + {sqliteCreateIndex(pParse, &X, &Y, Z, U, &S, &E);} + +%type uniqueflag {int} +uniqueflag(A) ::= UNIQUE. { A = 1; } +uniqueflag(A) ::= . { A = 0; } %type idxlist {IdList*} %destructor idxlist {sqliteIdListDelete($$);} diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 6d56498b5f..9056ed4567 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.19 2001/09/23 02:35:53 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.20 2001/09/27 03:22:33 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -133,25 +133,32 @@ int sqlite_exec( /* ** Return values for sqlite_exec() */ -#define SQLITE_OK 0 /* Successful result */ -#define SQLITE_ERROR 1 /* SQL error or missing database */ -#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */ -#define SQLITE_PERM 3 /* Access permission denied */ -#define SQLITE_ABORT 4 /* Callback routine requested an abort */ -#define SQLITE_BUSY 5 /* The database file is locked */ -#define SQLITE_LOCKED 6 /* A table in the database is locked */ -#define SQLITE_NOMEM 7 /* A malloc() failed */ -#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ -#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */ -#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ -#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */ -#define SQLITE_FULL 13 /* Insertion failed because database is full */ -#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ -#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */ -#define SQLITE_SCHEMA 17 /* The database schema changed */ -#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */ +#define SQLITE_OK 0 /* Successful result */ +#define SQLITE_ERROR 1 /* SQL error or missing database */ +#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */ +#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */ + +/* If the parameter to this routine is one of the return value constants +** defined above, then this routine returns a constant text string which +** descripts (in English) the meaning of the return value. +*/ +const char *sqliteErrStr(int); /* This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1a5f05aabb..40323e4de8 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.54 2001/09/23 19:46:52 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.55 2001/09/27 03:22:33 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -190,9 +190,9 @@ struct Table { Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Page containing root for this table */ - int readOnly; /* True if this table should not be written by the user */ - int isCommit; /* True if creation of this table has been committed */ - int isDelete; /* True if this table is being deleted */ + 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 */ }; /* @@ -220,9 +220,9 @@ struct Index { int *aiColumn; /* Which columns are used by this index. 1st is 0 */ Table *pTable; /* The SQL table being indexed */ int tnum; /* Page containing root of this index in database file */ - int isUnique; /* True if keys must all be unique */ - int isCommit; /* True if creation of this index has been committed */ - int isDelete; /* True if deletion of this index has not been comitted */ + u8 isUnique; /* True if keys must all be unique */ + u8 isCommit; /* True if creation of this index has been committed */ + u8 isDelete; /* True if deletion of this index has not been comitted */ Index *pNext; /* The next index associated with the same table */ }; @@ -431,7 +431,7 @@ void sqliteInsert(Parse*, Token*, ExprList*, Select*, IdList*); IdList *sqliteIdListAppend(IdList*, Token*); void sqliteIdListAddAlias(IdList*, Token*); void sqliteIdListDelete(IdList*); -void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*); +void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, int, Token*, Token*); void sqliteDropIndex(Parse*, Token*); int sqliteSelect(Parse*, Select*, int, int); Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,int); @@ -463,4 +463,3 @@ void sqliteBeginTransaction(Parse*); void sqliteCommitTransaction(Parse*); void sqliteRollbackTransaction(Parse*); char *sqlite_mprintf(const char *, ...); -const char *sqliteErrStr(int); diff --git a/src/update.c b/src/update.c index fb67ac66bf..164a360c08 100644 --- a/src/update.c +++ b/src/update.c @@ -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.15 2001/09/23 02:35:53 drh Exp $ +** $Id: update.c,v 1.16 2001/09/27 03:22:34 drh Exp $ */ #include "sqliteInt.h" @@ -206,7 +206,7 @@ void sqliteUpdate( sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiColumn[j], 0, 0, 0); } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0, 0, 0); - sqliteVdbeAddOp(v, OP_PutIdx, base+i+1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, base+i+1, pIdx->isUnique, 0, 0); } /* Write the new data back into the database. diff --git a/src/util.c b/src/util.c index e12c0a9e9e..4d0205f63c 100644 --- a/src/util.c +++ b/src/util.c @@ -14,7 +14,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.28 2001/09/23 02:35:53 drh Exp $ +** $Id: util.c,v 1.29 2001/09/27 03:22:34 drh Exp $ */ #include "sqliteInt.h" #include @@ -987,6 +987,7 @@ const char *sqliteErrStr(int rc){ case SQLITE_EMPTY: z = "table contains no data"; break; case SQLITE_SCHEMA: z = "database schema has changed"; break; case SQLITE_TOOBIG: z = "too much data for one table row"; break; + case SQLITE_CONSTRAINT: z = "constraint failed"; break; default: z = "unknown error"; break; } return z; diff --git a/src/vdbe.c b/src/vdbe.c index 6c9d6a0338..49a1eb55cd 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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.77 2001/09/24 03:12:40 drh Exp $ +** $Id: vdbe.c,v 1.78 2001/09/27 03:22:34 drh Exp $ */ #include "sqliteInt.h" #include @@ -67,7 +67,6 @@ struct Cursor { Btree *pBt; /* Separate file holding temporary table */ char *zKey; /* Key used in BeginIdx and NextIdx operators */ int nKey; /* Number of bytes in zKey[] */ - char *zBuf; /* Buffer space used to hold a copy of zKey[] */ }; typedef struct Cursor Cursor; @@ -2619,9 +2618,8 @@ case OP_BeginIdx: { if( Stringify(p, tos) ) goto no_mem; if( pCrsr->zKey ) sqliteFree(pCrsr->zKey); pCrsr->nKey = aStack[tos].n; - pCrsr->zKey = sqliteMalloc( 2*pCrsr->nKey ); + pCrsr->zKey = sqliteMalloc( pCrsr->nKey ); if( pCrsr->zKey==0 ) goto no_mem; - pCrsr->zBuf = &pCrsr->zKey[pCrsr->nKey]; memcpy(pCrsr->zKey, zStack[tos], aStack[tos].n); pCrsr->zKey[aStack[tos].n] = 0; rx = sqliteBtreeMoveto(pCrsr->pCursor, zStack[tos], aStack[tos].n, &res); @@ -2662,8 +2660,8 @@ case OP_NextIdx: { } sqliteBtreeKeySize(pCur, &size); if( res>0 || size!=pCrsr->nKey+sizeof(u32) || - sqliteBtreeKey(pCur, 0, pCrsr->nKey, pCrsr->zBuf)!=pCrsr->nKey || - memcmp(pCrsr->zKey, pCrsr->zBuf, pCrsr->nKey)!=0 + sqliteBtreeKeyCompare(pCur, pCrsr->zKey, pCrsr->nKey, &res)!=SQLITE_OK || + res!=0 ){ pc = pOp->p2 - 1; POPSTACK; @@ -2679,11 +2677,16 @@ case OP_NextIdx: { break; } -/* Opcode: PutIdx P1 * * +/* Opcode: PutIdx P1 P2 P3 ** ** The top of the stack hold an SQL index key made using the ** MakeIdxKey instruction. This opcode writes that key into the ** index P1. Data for the entry is nil. +** +** If P2==1, then the key must be unique. If the key is not unique, +** the program aborts with a SQLITE_CONSTRAINT error and the database +** is rolled back. If P3 is not null, then it because part of the +** error message returned with the SQLITE_CONSTRAINT. */ case OP_PutIdx: { int i = pOp->p1; @@ -2691,7 +2694,35 @@ case OP_PutIdx: { BtCursor *pCrsr; VERIFY( if( tos<0 ) goto not_enough_stack; ) if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - rc = sqliteBtreeInsert(pCrsr, zStack[tos], aStack[tos].n, "", 0); + int nKey = aStack[tos].n; + const char *zKey = zStack[tos]; + if( pOp->p2 ){ + int res, n; + assert( aStack[tos].n >= 4 ); + rc = sqliteBtreeMoveto(pCrsr, zKey, nKey-4, &res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + while( res!=0 ){ + int c; + sqliteBtreeKeySize(pCrsr, &n); + if( n==nKey + && sqliteBtreeKeyCompare(pCrsr, zKey, nKey-4, &c)==SQLITE_OK + && c==0 + ){ + rc = SQLITE_CONSTRAINT; + if( pOp->p3 && pOp->p3[0] ){ + sqliteSetString(pzErrMsg, "duplicate index entry: ", pOp->p3,0); + } + goto abort_due_to_error; + } + if( res<0 ){ + sqliteBtreeNext(pCrsr, &res); + res = +1; + }else{ + break; + } + } + } + rc = sqliteBtreeInsert(pCrsr, zKey, nKey, "", 0); } POPSTACK; break; diff --git a/www/changes.tcl b/www/changes.tcl index 6824866279..5aa71ad8b0 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -18,6 +18,8 @@ proc chng {date desc} { } chng {2001 Sep 22 (2.0-beta-1)} { +
  • SQLite now honors the UNIQUE keyword in CREATE UNIQUE INDEX. Primary + keys are required to unique indices.
  • Fixes to the rollback and locking behavior
  • }