]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Added basic support for enforcement of UNIQUE on indices and primary
authordrh <drh@noemail.net>
Thu, 27 Sep 2001 03:22:32 +0000 (03:22 +0000)
committerdrh <drh@noemail.net>
Thu, 27 Sep 2001 03:22:32 +0000 (03:22 +0000)
keys.  Support for addition constraints is to follow. (CVS 267)

FossilOrigin-Name: 34c42967f3d52dfb65d9f31db4f6995d098ec1f7

13 files changed:
manifest
manifest.uuid
src/btree.c
src/btree.h
src/build.c
src/insert.c
src/parse.y
src/sqlite.h.in
src/sqliteInt.h
src/update.c
src/util.c
src/vdbe.c
www/changes.tcl

index df3b92ed7375d40f754f9b2f6b06daa531177ca0..f36f581880685f3d9857a464370458c5bb87279b 100644 (file)
--- 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
index 4819e53c828edff7ac0d555232be29c13584a709..01640a35211f282144e9b9bde1e5ca8e5858595c 100644 (file)
@@ -1 +1 @@
-0e9cfcd53e16f96fc181def1d0b2d0ea7f7df73f
\ No newline at end of file
+34c42967f3d52dfb65d9f31db4f6995d098ec1f7
\ No newline at end of file
index 909ead07d2189964418dc0a87e97319dd7657ea0..1808db33f7930e4b3a4accef35ef501d8ec7fab8 100644 (file)
@@ -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 pCur<pKey, zero if they are equal and
-** positive if pCur>pKey.
+** 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 pCur<pKey
+**
+**    *pRes==0   This means pCur==pKey for all nKey bytes
+**
+**    *pRes>0    This means pCur>pKey
+**
+** If pCur contains N bytes where N<nKey and the N bytes of pCur
+** match the first N bytes of pKey, then *pRes<0 is returned.
+** If pCur differs from pKey in the first N bytes, then *pRes<0
+** or *pRes>0 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->idx<pCur->pPage->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 pCur<pKey, zero if they are equal and
+** positive if pCur>pKey.
+**
+** 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;
 }
index 4efd718e63c699198a3768c7ebb8546ffec9b360..0769e405ba952c5341626adf9acaf9762f3d09fc 100644 (file)
@@ -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*);
index 6cd307228a07e0033a33b81bf8e6c26003298080..84e612ac41d1d4b69bc8abbe6f1fc61a8c060c39 100644 (file)
@@ -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 <ctype.h>
@@ -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);
index 8de423f678f6634be30a48d383fab7dbb39a600b..1c209be2d5b6e3c346b02ab4bf0ccc8dd3b0992c 100644 (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.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
index da0eafbec21defe8a37cd307a5debad2cc9a7547..e07054ca5845d7760b227a5510c88d8f0884b7e7 100644 (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.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($$);}
index 6d56498b5fb563a1bee7a0505f68f9d1b2ade3ee..9056ed456733c35e27a6efd68930c805c562ef77 100644 (file)
@@ -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
index 1a5f05aabb86f2a254d68cdb656ff7c01b0b3670..40323e4de837534073e92e0b6af1bc6243c521bc 100644 (file)
@@ -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);
index fb67ac66bfc045da80b8b18ff61b2a6f0cc39872..164a360c08ea11dfbc2c65640c437246f87a74ba 100644 (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.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.
index e12c0a9e9e5a51fd045495cab03dd6bead960439..4d0205f63c5782bdd9fee84845d9d2b839c4a3d6 100644 (file)
@@ -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 <stdarg.h>
@@ -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;
index 6c9d6a0338898f42f70ae58a9aee1d0b36c41f3f..49a1eb55cd8b30368b09808e8e7bf506656d1291 100644 (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.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 <ctype.h>
@@ -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 && i<p->nCursor && ) (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;
index 6824866279b7c60fc8a4a446f2e3059880cfd021..5aa71ad8b0461a5d0d831689df9567d931ff8dde 100644 (file)
@@ -18,6 +18,8 @@ proc chng {date desc} {
 }
 
 chng {2001 Sep 22 (2.0-beta-1)} {
+<li>SQLite now honors the UNIQUE keyword in CREATE UNIQUE INDEX.  Primary
+    keys are required to unique indices.</li>
 <li>Fixes to the rollback and locking behavior</li>
 }