From 0d65dc0e90581d8ad5cc83ff1de10fe568286a24 Mon Sep 17 00:00:00 2001
From: drh
Date: Sun, 3 Feb 2002 00:56:09 +0000
Subject: [PATCH] Five-algorithm conflict resolution appears to be working.
(CVS 363)
FossilOrigin-Name: 0115518f8e4591123582e3d2bb67282111ebcf60
---
manifest | 26 +--
manifest.uuid | 2 +-
src/btree.c | 8 +-
src/build.c | 5 +-
src/insert.c | 57 ++---
src/parse.y | 7 +-
src/vdbe.c | 9 +-
test/conflict.test | 567 +++++++++++++++++++++++++++------------------
www/conflict.tcl | 208 +++++------------
www/lang.tcl | 356 +++++++++++++++++-----------
10 files changed, 678 insertions(+), 567 deletions(-)
diff --git a/manifest b/manifest
index f37bfe4807..511dafb31d 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Get\sthe\sABORT\sconflict\sresolution\salgorithm\sworking.\s(CVS\s362)
-D 2002-02-02T18:49:20
+C Five-algorithm\sconflict\sresolution\sappears\sto\sbe\sworking.\s(CVS\s363)
+D 2002-02-03T00:56:10
F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -19,21 +19,21 @@ F ltmain.sh e9ed72eb1d690f447c13945eaf69e28af531eda1
F publish.sh 5b59f4aff037aafa0e4a3b6fa599495dbd73f360
F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
-F src/btree.c 94deba286af8e1f665d98378c099da2f7455f291
+F src/btree.c ba5712cf620f80055948cc41157e14eab6ceee86
F src/btree.h a94bef69f5174461331b6b9ae45a2d84f05af6db
-F src/build.c 397d78ce466e3c22d56de85ae23d8a2777d3b9e6
+F src/build.c 0c7346d0522e59be67a4bb841020540d8ba5d136
F src/delete.c f8ad71be53cf18656b6573de65395852fe817f0c
F src/expr.c a2a87dbd411a508ff89dffa90505ad42dac2f920
F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
F src/hash.h a5f5b3ce2d086a172c5879b0b06a27a82eac9fac
-F src/insert.c 051e909cf4c8505aae930dcd773215404e187f23
+F src/insert.c 173da7b06fe9282e0f529ff1e599460304ceeb23
F src/main.c 300320ba68d3e5b22c2c5b2c07fa884878202181
F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
F src/os.c 1953080d14098cd45e5bde88941567688efb72b1
F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6
F src/pager.c 4059bda97a7e10083b77b7d347fea45426b08589
F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283
-F src/parse.y 88856227ae8472d0f4ae8514bc9561a6ca060690
+F src/parse.y f081d7d4ef17deb2e20511addd32d07e277edc25
F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
F src/random.c f6b36bec5ebd3edb3440224bf5bf811fe4ac9a1b
F src/select.c fc11d5a8c2bae1b62d8028ffb111c773ad6bf161
@@ -49,7 +49,7 @@ F src/test3.c d6775f95fd91f5b3cf0e2382a28e5aaeb68f745b
F src/tokenize.c 01a09db6adf933e941db1b781789a0c175be6504
F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103
F src/util.c 8f8973dd55a6ec63be9632fc5de86965c99d6327
-F src/vdbe.c 3e9d9dba06fb7f6fe85ca3c345eedd32bf27f3a7
+F src/vdbe.c df1c920e74b2cd76d763833a655fbabb67f17237
F src/vdbe.h 3791edabb212038ae5fbcfa72580204596be01a7
F src/where.c 2dda39367f193194e4c7d2e0dcab31527d9d8aba
F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe
@@ -57,7 +57,7 @@ F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/btree.test 6ab4dc5f595905a276ef588fad3c9236dc07a47b
F test/btree2.test 08e9485619265cbaf5d11bd71f357cdc26bb87e0
F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895
-F test/conflict.test 70d40d77bb83f326574488d3cde1d0f3c51a0949
+F test/conflict.test dd1b380595bb48b90289d761f4d11a46d7c60178
F test/copy.test 9ff0063c0b95b3d51b8d0c7fe0ff51dabaa66549
F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
F test/expr.test c8a495050dcec3f9e68538c3ef466726933302c1
@@ -108,21 +108,21 @@ F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
F www/c_interface.tcl 82a026b1681757f13b3f62e035f3a31407c1d353
F www/changes.tcl 5c3b5b80d7144d46f100f333d4cab6184828a6c2
-F www/conflict.tcl 3f70c01680b8d763bf3305eb67f6d85fdf83b497
+F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
F www/faq.tcl 32cbc134879871604070d4cc3a32e73fb22a35f9
F www/formatchng.tcl 2d9a35c787823b48d72a5c64bb5414a43e26d5ad
F www/index.tcl 748614d8208c761ed3840e7958b8eed04de81822
-F www/lang.tcl 7ad595247fd81f394012a0cfd84ccd6241b9e59a
+F www/lang.tcl 260f3f9015344634ed9c2518f9a856462d37d6a6
F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc
F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc
F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P aaa53e113ef849e34883ead8ae584c722ad967db
-R f2763ccecef4a6c8cd264dd83b3c6a22
+P 9be4d4c6f12056782966396dca0b8e2d384d0cf2
+R 60fe69f0169f5844176ca5b7853e2e65
U drh
-Z fb37183391242f0b96b036b1bf3ee1ea
+Z fa5a72d3a4d7961b510d6a8d5ba99c2c
diff --git a/manifest.uuid b/manifest.uuid
index 50d1d7fd51..7e943ff703 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-9be4d4c6f12056782966396dca0b8e2d384d0cf2
\ No newline at end of file
+0115518f8e4591123582e3d2bb67282111ebcf60
\ No newline at end of file
diff --git a/src/btree.c b/src/btree.c
index aea7123904..2aaee41752 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.47 2002/02/02 18:49:20 drh Exp $
+** $Id: btree.c,v 1.48 2002/02/03 00:56:10 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -816,7 +816,9 @@ int sqliteBtreeRollback(Btree *pBt){
*/
int sqliteBtreeBeginCkpt(Btree *pBt){
int rc;
- if( !pBt->inTrans || pBt->inCkpt ) return SQLITE_ERROR;
+ if( !pBt->inTrans || pBt->inCkpt ){
+ return SQLITE_ERROR;
+ }
rc = sqlitepager_ckpt_begin(pBt->pPager);
pBt->inCkpt = 1;
return rc;
@@ -834,6 +836,7 @@ int sqliteBtreeCommitCkpt(Btree *pBt){
}else{
rc = SQLITE_OK;
}
+ pBt->inCkpt = 0;
return rc;
}
@@ -856,6 +859,7 @@ int sqliteBtreeRollbackCkpt(Btree *pBt){
}
}
rc = sqlitepager_ckpt_rollback(pBt->pPager);
+ pBt->inCkpt = 0;
return rc;
}
diff --git a/src/build.c b/src/build.c
index e45008c7fd..a9fcf22603 100644
--- a/src/build.c
+++ b/src/build.c
@@ -25,7 +25,7 @@
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.71 2002/02/02 18:49:20 drh Exp $
+** $Id: build.c,v 1.72 2002/02/03 00:56:10 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -518,7 +518,6 @@ void sqliteAddNotNull(Parse *pParse, int onError){
int i;
if( (p = pParse->pNewTable)==0 ) return;
i = p->nCol-1;
- if( onError==OE_Default ) onError = OE_Abort;
if( i>=0 ) p->aCol[i].notNull = onError;
}
@@ -618,7 +617,6 @@ void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){
if( iCol>=0 && iColnCol ){
zType = pTab->aCol[iCol].zType;
}
- if( onError==OE_Default ) onError = OE_Abort;
if( pParse->db->file_format>=1 &&
zType && sqliteStrICmp(zType, "INTEGER")==0 ){
pTab->iPKey = iCol;
@@ -854,7 +852,6 @@ void sqliteCreateIndex(
int hideName = 0; /* Do not put table name in the hash table */
if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
- if( onError==OE_Default ) onError = OE_Abort;
/*
** Find the table that is to be indexed. Return early if not found.
diff --git a/src/insert.c b/src/insert.c
index 3e92c26d65..e387150da3 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.40 2002/02/02 18:49:20 drh Exp $
+** $Id: insert.c,v 1.41 2002/02/03 00:56:10 drh Exp $
*/
#include "sqliteInt.h"
@@ -396,9 +396,6 @@ void sqliteGenerateConstraintChecks(
v = sqliteGetVdbe(pParse);
assert( v!=0 );
nCol = pTab->nCol;
- if( overrideError==OE_Default ){
- overrideError = pParse->db->onError;
- }
/* Test all NOT NULL constraints.
*/
@@ -412,7 +409,8 @@ void sqliteGenerateConstraintChecks(
if( overrideError!=OE_Default ){
onError = overrideError;
}else if( onError==OE_Default ){
- onError = OE_Abort;
+ onError = pParse->db->onError;
+ if( onError==OE_Default ) onError = OE_Abort;
}
if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){
onError = OE_Abort;
@@ -447,35 +445,37 @@ void sqliteGenerateConstraintChecks(
/* Test all UNIQUE constraints. Add index records as we go.
*/
- if( (recnoChng || !isUpdate) && pTab->iPKey>=0 && pTab->keyConf!=OE_Replace
- && overrideError!=OE_Replace ){
- sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
- jumpInst = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
+ if( (recnoChng || !isUpdate) && pTab->iPKey>=0 ){
onError = pTab->keyConf;
if( overrideError!=OE_Default ){
onError = overrideError;
}else if( onError==OE_Default ){
- onError = OE_Abort;
+ onError = pParse->db->onError;
+ if( onError==OE_Default ) onError = OE_Abort;
}
- switch( onError ){
- case OE_Rollback:
- case OE_Abort:
- case OE_Fail: {
- sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
- break;
+ if( onError!=OE_Replace ){
+ sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
+ jumpInst = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
+ switch( onError ){
+ case OE_Rollback:
+ case OE_Abort:
+ case OE_Fail: {
+ sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
+ break;
+ }
+ case OE_Ignore: {
+ sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
+ break;
+ }
+ default: assert(0);
}
- case OE_Ignore: {
- sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
- sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
- break;
+ contAddr = sqliteVdbeCurrentAddr(v);
+ sqliteVdbeChangeP2(v, jumpInst, contAddr);
+ if( isUpdate ){
+ sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
+ sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
}
- default: assert(0);
- }
- contAddr = sqliteVdbeCurrentAddr(v);
- sqliteVdbeChangeP2(v, jumpInst, contAddr);
- if( isUpdate ){
- sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
- sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
}
}
extra = 0;
@@ -497,7 +497,8 @@ void sqliteGenerateConstraintChecks(
if( overrideError!=OE_Default ){
onError = overrideError;
}else if( onError==OE_Default ){
- onError = OE_Abort;
+ onError = pParse->db->onError;
+ if( onError==OE_Default ) onError = OE_Abort;
}
sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1);
jumpInst = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
diff --git a/src/parse.y b/src/parse.y
index 545a8c662b..800853f1f9 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.46 2002/02/02 15:01:16 drh Exp $
+** @(#) $Id: parse.y,v 1.47 2002/02/03 00:56:10 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -57,10 +57,7 @@ explain ::= EXPLAIN. {pParse->explain = 1;}
///////////////////// Begin and end transactions. ////////////////////////////
//
-// For now, disable the ability to change the default conflict resolution
-// algorithm in a transaction. We made add it back later.
-// cmd ::= BEGIN trans_opt onconf(R). {sqliteBeginTransaction(pParse,R);}
-cmd ::= BEGIN trans_opt. {sqliteBeginTransaction(pParse, OE_Default);}
+cmd ::= BEGIN trans_opt onconf(R). {sqliteBeginTransaction(pParse,R);}
trans_opt ::= .
trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION ids.
diff --git a/src/vdbe.c b/src/vdbe.c
index 59ec69ddb5..0fc2a0c4e3 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.114 2002/02/02 18:49:21 drh Exp $
+** $Id: vdbe.c,v 1.115 2002/02/03 00:56:10 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -4511,10 +4511,9 @@ cleanup:
break;
}
}
- }else{
- sqliteBtreeCommitCkpt(pBt);
- if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp);
- }
+ }
+ sqliteBtreeCommitCkpt(pBt);
+ if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp);
return rc;
/* Jump to here if a malloc() fails. It's hard to get a malloc()
diff --git a/test/conflict.test b/test/conflict.test
index 0c91c769d3..ff4b19d28d 100644
--- a/test/conflict.test
+++ b/test/conflict.test
@@ -13,263 +13,368 @@
# This file implements tests for the conflict resolution extension
# to SQLite.
#
-# $Id: conflict.test,v 1.4 2002/01/31 15:54:23 drh Exp $
+# $Id: conflict.test,v 1.5 2002/02/03 00:56:11 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-# Create a table with three fields, two of which must be
-# UNIQUE.
+# Create tables for the first group of tests.
#
-do_test conflict-1.1 {
+do_test conflict-1.0 {
execsql {
CREATE TABLE t1(a, b, c, UNIQUE(a,b));
- INSERT INTO t1 VALUES(1,2,3);
+ CREATE TABLE t2(x);
SELECT c FROM t1 ORDER BY c;
}
-} {3}
-do_test conflict-1.2 {
- catchsql {
- INSERT INTO t1 VALUES(1,2,4);
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}}
-do_test conflict-1.3 {
- catchsql {
- INSERT OR IGNORE INTO t1 VALUES(1,2,4);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 3}
-do_test conflict-1.4 {
- catchsql {
- INSERT OR REPLACE INTO t1 VALUES(1,2,4);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 4}
-do_test conflict-1.5 {
- catchsql {
- INSERT OR ABORT INTO t1 VALUES(1,2,5);
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}}
-do_test conflict-1.6 {
- catchsql {
- INSERT OR IGNORE INTO t1 VALUES(1,2,5);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 4}
-do_test conflict-1.7 {
- catchsql {
- INSERT OR REPLACE INTO t1 VALUES(1,2,5);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 5}
-do_test conflict-1.8 {
- catchsql {
- INSERT OR ABORT INTO t1 VALUES(1,2,6);
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}}
+} {}
-do_test conflict-1.9 {
- execsql {
- BEGIN;
- CREATE TABLE t2(a,b,c);
- INSERT INTO t2 VALUES(1,2,11);
- INSERT INTO t2 VALUES(1,2,12);
- INSERT INTO t2 VALUES(1,2,13);
- INSERT INTO t2 VALUES(1,2,14);
- INSERT INTO t2 VALUES(1,3,21);
- INSERT INTO t2 VALUES(1,3,22);
- INSERT INTO t2 VALUES(1,3,23);
- INSERT INTO t2 VALUES(1,3,24);
- COMMIT;
- SELECT count(*) FROM t2;
- }
-} 8
-do_test conflict-1.10 {
- catchsql {
- INSERT OR IGNORE INTO t1 SELECT a,b,c FROM t2 ORDER BY c;
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 {5 21}}
-do_test conflict-1.11 {
- catchsql {
- INSERT OR REPLACE INTO t1 SELECT a,b,c FROM t2 ORDER BY c;
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 {14 24}}
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf The conflict resolution algorithm on the BEGIN statement
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf cmd t0 t1 t2} {
+ 1 {} INSERT 1 {} 1
+ 2 {} {INSERT OR IGNORE} 0 3 1
+ 3 {} {INSERT OR REPLACE} 0 4 1
+ 4 {} REPLACE 0 4 1
+ 5 {} {INSERT OR FAIL} 1 {} 1
+ 6 {} {INSERT OR ABORT} 1 {} 1
+ 7 {} {INSERT OR ROLLBACK} 1 {} {}
+ 8 IGNORE INSERT 0 3 1
+ 9 IGNORE {INSERT OR IGNORE} 0 3 1
+ 10 IGNORE {INSERT OR REPLACE} 0 4 1
+ 11 IGNORE REPLACE 0 4 1
+ 12 IGNORE {INSERT OR FAIL} 1 {} 1
+ 13 IGNORE {INSERT OR ABORT} 1 {} 1
+ 14 IGNORE {INSERT OR ROLLBACK} 1 {} {}
+ 15 REPLACE INSERT 0 4 1
+ 16 FAIL INSERT 1 {} 1
+ 17 ABORT INSERT 1 {} 1
+ 18 ROLLBACK INSERT 1 {} {}
+} {
+ do_test conflict-1.$i {
+ if {$conf!=""} {set conf "ON CONFLICT $conf"}
+ set r0 [catch {execsql [subst {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN $conf;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ execsql {COMMIT}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
-###### Fix me!
-do_test conflict-1.12 {
- catchsql {
- INSERT OR REPLACE INTO t1 SELECT a,b,c FROM t2 ORDER BY c DESC;
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 {14 24}}
-
-do_test conflict-1.13 {
- execsql {
- BEGIN;
- DELETE FROM t1;
- INSERT INTO t1 VALUES(1,2,3);
- INSERT INTO t1 VALUES(1,3,4);
- INSERT INTO t1 VALUES(2,3,5);
- COMMIT;
- SELECT * FROM t1 ORDER BY c;
- }
-} {1 2 3 1 3 4 2 3 5}
-do_test conflict-1.14 {
- catchsql {
- UPDATE OR ABORT t1 SET b=3 WHERE b=2;
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}};
-do_test conflict-1.15 {
- catchsql {
- UPDATE t1 SET b=3 WHERE b=2;
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}};
-do_test conflict-1.16 {
- catchsql {
- UPDATE OR IGNORE t1 SET b=3 WHERE b=2;
- SELECT * FROM t1 ORDER BY c;
- }
-} {0 {1 2 3 1 3 4 2 3 5}}
-do_test conflict-1.17 {
- catchsql {
- UPDATE OR REPLACE t1 SET b=3 WHERE b=2;
- SELECT * FROM t1 ORDER BY c;
- }
-} {0 {1 3 3 2 3 5}}
-
-do_test conflict-2.1 {
+# Create tables for the first group of tests.
+#
+do_test conflict-2.0 {
execsql {
DROP TABLE t1;
DROP TABLE t2;
- CREATE TABLE t1(a integer primary key, b, c, UNIQUE(a,b));
- CREATE INDEX t1b ON t1(b);
- CREATE INDEX t1bc ON t1(b,c);
- INSERT INTO t1 VALUES(1,2,3);
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(a,b));
+ CREATE TABLE t2(x);
SELECT c FROM t1 ORDER BY c;
}
-} {3}
-do_test conflict-2.2 {
- catchsql {
- INSERT INTO t1 VALUES(1,2,4);
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}}
-do_test conflict-2.3 {
- catchsql {
- INSERT OR IGNORE INTO t1 VALUES(1,2,4);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 3}
-do_test conflict-2.4 {
- catchsql {
- INSERT OR REPLACE INTO t1 VALUES(1,2,4);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 4}
-do_test conflict-2.5 {
- catchsql {
- INSERT OR ABORT INTO t1 VALUES(1,2,5);
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}}
-do_test conflict-2.6 {
- catchsql {
- INSERT OR IGNORE INTO t1 VALUES(1,2,5);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 4}
-do_test conflict-2.7 {
- catchsql {
- INSERT OR REPLACE INTO t1 VALUES(1,2,5);
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 5}
-do_test conflict-2.8 {
- catchsql {
- INSERT OR ABORT INTO t1 VALUES(1,2,6);
- SELECT c FROM t1 ORDER BY c;
- }
-} {1 {constraint failed}}
+} {}
-do_test conflict-2.9 {
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf The conflict resolution algorithm on the BEGIN statement
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf cmd t0 t1 t2} {
+ 1 {} INSERT 1 {} 1
+ 2 {} {INSERT OR IGNORE} 0 3 1
+ 3 {} {INSERT OR REPLACE} 0 4 1
+ 4 {} REPLACE 0 4 1
+ 5 {} {INSERT OR FAIL} 1 {} 1
+ 6 {} {INSERT OR ABORT} 1 {} 1
+ 7 {} {INSERT OR ROLLBACK} 1 {} {}
+ 8 IGNORE INSERT 0 3 1
+ 9 IGNORE {INSERT OR IGNORE} 0 3 1
+ 10 IGNORE {INSERT OR REPLACE} 0 4 1
+ 11 IGNORE REPLACE 0 4 1
+ 12 IGNORE {INSERT OR FAIL} 1 {} 1
+ 13 IGNORE {INSERT OR ABORT} 1 {} 1
+ 14 IGNORE {INSERT OR ROLLBACK} 1 {} {}
+ 15 REPLACE INSERT 0 4 1
+ 16 FAIL INSERT 1 {} 1
+ 17 ABORT INSERT 1 {} 1
+ 18 ROLLBACK INSERT 1 {} {}
+} {
+ do_test conflict-2.$i {
+ if {$conf!=""} {set conf "ON CONFLICT $conf"}
+ set r0 [catch {execsql [subst {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN $conf;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ execsql {COMMIT}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+# Create tables for the first group of tests.
+#
+do_test conflict-3.0 {
execsql {
- BEGIN;
- CREATE TABLE t2(a,b,c INTEGER PRIMARY KEY);
- INSERT INTO t2 VALUES(1,2,11);
- INSERT INTO t2 VALUES(1,2,12);
- INSERT INTO t2 VALUES(1,2,13);
- INSERT INTO t2 VALUES(1,2,14);
- INSERT INTO t2 VALUES(2,2,21);
- INSERT INTO t2 VALUES(2,2,22);
- INSERT INTO t2 VALUES(2,2,23);
- INSERT INTO t2 VALUES(2,2,24);
- COMMIT;
- SELECT count(*) FROM t2;
- }
-} 8
-do_test conflict-2.10 {
- catchsql {
- INSERT OR IGNORE INTO t1 SELECT a,b,c FROM t2 ORDER BY c;
+ DROP TABLE t1;
+ DROP TABLE t2;
+ CREATE TABLE t1(a, b, c INTEGER PRIMARY KEY, UNIQUE(a,b));
+ CREATE TABLE t2(x);
SELECT c FROM t1 ORDER BY c;
}
-} {0 {5 21}}
-do_test conflict-2.11 {
- catchsql {
- INSERT OR REPLACE INTO t1 SELECT a,b,c FROM t2 ORDER BY c;
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 {14 24}}
+} {}
-###### Fix me!
-do_test conflict-2.12 {
- catchsql {
- INSERT OR REPLACE INTO t1 SELECT a,b,c FROM t2 ORDER BY c DESC;
- SELECT c FROM t1 ORDER BY c;
- }
-} {0 {14 24}}
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf The conflict resolution algorithm on the BEGIN statement
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf cmd t0 t1 t2} {
+ 1 {} INSERT 1 {} 1
+ 2 {} {INSERT OR IGNORE} 0 3 1
+ 3 {} {INSERT OR REPLACE} 0 4 1
+ 4 {} REPLACE 0 4 1
+ 5 {} {INSERT OR FAIL} 1 {} 1
+ 6 {} {INSERT OR ABORT} 1 {} 1
+ 7 {} {INSERT OR ROLLBACK} 1 {} {}
+ 8 IGNORE INSERT 0 3 1
+ 9 IGNORE {INSERT OR IGNORE} 0 3 1
+ 10 IGNORE {INSERT OR REPLACE} 0 4 1
+ 11 IGNORE REPLACE 0 4 1
+ 12 IGNORE {INSERT OR FAIL} 1 {} 1
+ 13 IGNORE {INSERT OR ABORT} 1 {} 1
+ 14 IGNORE {INSERT OR ROLLBACK} 1 {} {}
+ 15 REPLACE INSERT 0 4 1
+ 16 FAIL INSERT 1 {} 1
+ 17 ABORT INSERT 1 {} 1
+ 18 ROLLBACK INSERT 1 {} {}
+} {
+ do_test conflict-3.$i {
+ if {$conf!=""} {set conf "ON CONFLICT $conf"}
+ set r0 [catch {execsql [subst {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN $conf;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ execsql {COMMIT}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
-do_test conflict-2.13 {
+do_test conflict-4.0 {
execsql {
- BEGIN;
- DELETE FROM t1;
- INSERT INTO t1 VALUES(1,2,3);
- INSERT INTO t1 VALUES(2,3,4);
- INSERT INTO t1 VALUES(3,3,5);
- COMMIT;
- SELECT * FROM t1 ORDER BY c;
+ DROP TABLE t2;
+ CREATE TABLE t2(x);
+ SELECT x FROM t2;
}
-} {1 2 3 2 3 4 3 3 5}
-do_test conflict-2.14 {
- catchsql {
- UPDATE OR ABORT t1 SET a=2, b=3 WHERE b=2;
- SELECT c FROM t1 ORDER BY c;
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf1 The conflict resolution algorithm on the UNIQUE constraint
+# conf2 The conflict resolution algorithm on the BEGIN statement
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf1 conf2 cmd t0 t1 t2} {
+ 1 {} {} INSERT 1 {} 1
+ 2 REPLACE {} INSERT 0 4 1
+ 3 IGNORE {} INSERT 0 3 1
+ 4 FAIL {} INSERT 1 {} 1
+ 5 ABORT {} INSERT 1 {} 1
+ 6 ROLLBACK {} INSERT 1 {} {}
+ 7 REPLACE {} {INSERT OR IGNORE} 0 3 1
+ 8 IGNORE {} {INSERT OR REPLACE} 0 4 1
+ 9 FAIL {} {INSERT OR IGNORE} 0 3 1
+ 10 ABORT {} {INSERT OR REPLACE} 0 4 1
+ 11 ROLLBACK {} {INSERT OR IGNORE } 0 3 1
+ 12 REPLACE IGNORE INSERT 0 4 1
+ 13 IGNORE REPLACE INSERT 0 3 1
+ 14 FAIL IGNORE INSERT 1 {} 1
+ 15 ABORT REPLACE INSERT 1 {} 1
+ 16 ROLLBACK IGNORE INSERT 1 {} {}
+} {
+ do_test conflict-4.$i {
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,UNIQUE(a,b) $conf1);
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES(1,2,3);
+ BEGIN $conf2;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,4);
+ }]} r1]
+ execsql {COMMIT}
+ if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+do_test conflict-5.0 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(x);
+ SELECT x FROM t2;
}
-} {1 {constraint failed}};
-do_test conflict-2.15 {
- catchsql {
- UPDATE t1 SET a=2, b=3 WHERE b=2;
- SELECT c FROM t1 ORDER BY c;
+} {}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf1 The conflict resolution algorithm on the NOT NULL constraint
+# conf2 The conflict resolution algorithm on the BEGIN statement
+# cmd An INSERT or REPLACE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "c" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t2
+#
+foreach {i conf1 conf2 cmd t0 t1 t2} {
+ 1 {} {} INSERT 1 {} 1
+ 2 REPLACE {} INSERT 0 5 1
+ 3 IGNORE {} INSERT 0 {} 1
+ 4 FAIL {} INSERT 1 {} 1
+ 5 ABORT {} INSERT 1 {} 1
+ 6 ROLLBACK {} INSERT 1 {} {}
+ 7 REPLACE {} {INSERT OR IGNORE} 0 {} 1
+ 8 IGNORE {} {INSERT OR REPLACE} 0 5 1
+ 9 FAIL {} {INSERT OR IGNORE} 0 {} 1
+ 10 ABORT {} {INSERT OR REPLACE} 0 5 1
+ 11 ROLLBACK {} {INSERT OR IGNORE} 0 {} 1
+ 12 {} {} {INSERT OR IGNORE} 0 {} 1
+ 13 {} {} {INSERT OR REPLACE} 0 5 1
+ 14 {} {} {INSERT OR FAIL} 1 {} 1
+ 15 {} {} {INSERT OR ABORT} 1 {} 1
+ 16 {} {} {INSERT OR ROLLBACK} 1 {} {}
+ 17 {} IGNORE INSERT 0 {} 1
+ 18 {} REPLACE INSERT 0 5 1
+ 19 {} FAIL INSERT 1 {} 1
+ 20 {} ABORT INSERT 1 {} 1
+ 21 {} ROLLBACK INSERT 1 {} {}
+ 22 REPLACE FAIL INSERT 0 5 1
+ 23 IGNORE ROLLBACK INSERT 0 {} 1
+} {
+ if {$t0} {set t1 {constraint failed}}
+ do_test conflict-5.$i {
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c NOT NULL $conf1 DEFAULT 5);
+ DELETE FROM t2;
+ BEGIN $conf2;
+ INSERT INTO t2 VALUES(1);
+ $cmd INTO t1 VALUES(1,2,NULL);
+ }]} r1]
+ execsql {COMMIT}
+ if {!$r0} {set r1 [execsql {SELECT c FROM t1}]}
+ set r2 [execsql {SELECT x FROM t2}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
+
+do_test conflict-6.0 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(a,b,c);
+ INSERT INTO t2 VALUES(1,2,1);
+ INSERT INTO t2 VALUES(2,3,2);
+ INSERT INTO t2 VALUES(3,4,1);
+ INSERT INTO t2 VALUES(4,5,4);
+ SELECT c FROM t2 ORDER BY b;
+ CREATE TABLE t3(x);
+ INSERT INTO t3 VALUES(1);
}
-} {1 {constraint failed}};
-do_test conflict-2.16 {
- catchsql {
- UPDATE OR IGNORE t1 SET a=2, b=3 WHERE b=2;
- SELECT * FROM t1 ORDER BY c;
- }
-} {0 {1 2 3 2 3 4 3 3 5}}
-do_test conflict-2.17 {
- catchsql {
- UPDATE OR REPLACE t1 SET a=2, b=3 WHERE b=2;
- SELECT * FROM t1 ORDER BY c;
- }
-} {0 {2 3 3 3 3 5}}
+} {1 2 1 4}
+
+# Six columns of configuration data as follows:
+#
+# i The reference number of the test
+# conf1 The conflict resolution algorithm on the UNIQUE constraint
+# conf2 The conflict resolution algorithm on the BEGIN statement
+# cmd An UPDATE command to execute against table t1
+# t0 True if there is an error from $cmd
+# t1 Content of "b" column of t1 assuming no error in $cmd
+# t2 Content of "x" column of t3
+#
+foreach {i conf1 conf2 cmd t0 t1 t2} {
+ 1 {} {} UPDATE 1 {6 7 8 9} 1
+ 2 REPLACE {} UPDATE 0 {7 6 9} 1
+ 3 IGNORE {} UPDATE 0 {6 7 3 9} 1
+ 4 FAIL {} UPDATE 1 {6 7 3 4} 1
+ 5 ABORT {} UPDATE 1 {1 2 3 4} 1
+ 6 ROLLBACK {} UPDATE 1 {1 2 3 4} 0
+ 7 REPLACE {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 8 IGNORE {} {UPDATE OR REPLACE} 0 {7 6 9} 1
+ 9 FAIL {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 10 ABORT {} {UPDATE OR REPLACE} 0 {7 6 9} 1
+ 11 ROLLBACK {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 12 {} {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1
+ 13 {} {} {UPDATE OR REPLACE} 0 {7 6 9} 1
+ 14 {} {} {UPDATE OR FAIL} 1 {6 7 3 4} 1
+ 15 {} {} {UPDATE OR ABORT} 1 {1 2 3 4} 1
+ 16 {} {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0
+ 17 {} IGNORE UPDATE 0 {6 7 3 9} 1
+ 18 {} REPLACE UPDATE 0 {7 6 9} 1
+ 19 {} FAIL UPDATE 1 {6 7 3 4} 1
+ 20 {} ABORT UPDATE 1 {1 2 3 4} 1
+ 21 {} ROLLBACK UPDATE 1 {1 2 3 4} 0
+ 22 REPLACE FAIL UPDATE 0 {7 6 9} 1
+ 23 IGNORE ROLLBACK UPDATE 0 {6 7 3 9} 1
+} {
+ if {$t0} {set t1 {constraint failed}}
+ do_test conflict-6.$i {
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
+ INSERT INTO t1 SELECT * FROM t2;
+ UPDATE t3 SET x=0;
+ BEGIN $conf2;
+ $cmd t3 SET x=1;
+ $cmd t1 SET a=c+5;
+ }]} r1]
+ execsql {COMMIT}
+ if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]}
+ set r2 [execsql {SELECT x FROM t3}]
+ list $r0 $r1 $r2
+ } [list $t0 $t1 $t2]
+}
finish_test
diff --git a/www/conflict.tcl b/www/conflict.tcl
index 7316219830..c5200c22a5 100644
--- a/www/conflict.tcl
+++ b/www/conflict.tcl
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the constraint.html file.
#
-set rcsid {$Id: conflict.tcl,v 1.1 2002/01/30 16:17:25 drh Exp $ }
+set rcsid {$Id: conflict.tcl,v 1.2 2002/02/03 00:56:11 drh Exp $ }
puts {
@@ -22,7 +22,8 @@ puts {
In most SQL databases, if you have a UNIQUE constraint on
a table and you try to do an UPDATE or INSERT that violates
that constraint, the database will aborts the operation in
-progress and rolls back the current transaction.
+progress, back out any prior changes associated with that
+one UPDATE or INSERT command, and return an error.
This is the default behavior of SQLite.
Beginning with version 2.3.0, though, SQLite allows you to
define alternative ways for dealing with constraint violations.
@@ -32,154 +33,71 @@ This article describes those alternatives and how to use them.
Conflict Resolution Algorithms
-The default conflict resolution algorithm is to abort the
-operation in progress, rollback all changes, and cancel the
-current transaction. Call this algorithm "ABORT". Abort
-is the standard way of dealing with a constraint error
-in most SQL databases.
+SQLite defines five constraint conflict resolution algorithms
+as follows:
-
-Sometimes ABORT is not the most helpful way of dealing
-with constraint violations. Suppose, for example, you are
+
+- ROLLBACK
+When a constraint violation occurs, an immediate ROLLBACK
+occurs, thus ending the current transaction, and the command aborts
+with a return code of SQLITE_CONSTRAINT. If no transaction is
+active (other than the implied transaction that is created on every
+command) then this algorithm works the same as ABORT.
+
+- ABORT
+When a constraint violation occurs, the command backs out
+any prior changes it might have made and aborts with a return code
+of SQLITE_CONSTRAINT. But no ROLLBACK is executed so changes
+from prior commands within the same transaction
+are preserved. This is the default behavior for SQLite.
+
+- FAIL
+When a constraint violation occurs, the command aborts with a
+return code SQLITE_CONSTRAINT. But any changes to the database that
+the command made prior to encountering the constraint violation
+are preserved and are not backed out. For example, if an UPDATE
+statement encountered a constraint violation on the 100th row that
+it attempts to update, then the first 99 row changes are preserved
+by change to rows 100 and beyond never occur.
+
+- IGNORE
+When a constraint violation occurs, the one row that contains
+the constraint violation is not inserted or changed. But the command
+continues executing normally. Other rows before and after the row that
+contained the constraint violation continue to be inserted or updated
+normally. No error is returned.
+
+- REPLACE
+When a UNIQUE constraint violation occurs, the pre-existing row
+that caused the constraint violation is removed prior to inserting
+or updating the current row. Thus the insert or update always occurs.
+The command continues executing normally. No error is returned.
+
+
+Why So Many Choices?
+
+SQLite provides multiple conflict resolution algorithms for a
+couple of reasons. First, SQLite tries to be roughly compatible with as
+many other SQL databases as possible, but different SQL database
+engines exhibit different conflict resolution strategies. For
+example, PostgreSQL always uses ROLLBACK, Oracle always uses ABORT, and
+MySQL usually uses FAIL but can be instructed to use IGNORE or REPLACE.
+By supporting all five alternatives, SQLite provides maximum
+portability.
+
+Another reason for supporing multiple algorithms is that sometimes
+it is useful to use an algorithm other than the default.
+Suppose, for example, you are
inserting 1000 records into a database, all within a single
transaction, but one of those records is malformed and causes
-a constraint error. With the default ABORT behavior, none
-of the 1000 records gets inserted. But sometimes it is
-desirable to just omit the single malformed insert and
-finish the other 999.
-
+a constraint error. Under PostgreSQL or Oracle, none of the
+1000 records would get inserted. In MySQL, some subset of the
+records that appeared before the malformed record would be inserted
+but the rest would not. Neither behavior is espeically helpful.
+What you really want is to use the IGNORE algorithm to insert
+all but the malformed record.
-
-SQLite defines two addition conflict resolution algorithms
-called "IGNORE" and "REPLACE".
-If you are trying to do multiple INSERTs or UPDATEs when a constraint
-fails for a single row and the conflict behavior is IGNORE, then
-that row remains uninserted or unmodified. But the overall operation
-is not aborted and no rollback occurs. If a constraint
-fails and the behavior is REPLACE, then SQLite tries to
-delete other rows in the table in order to eliminate the
-constraint problem. Again, the overall operation continues
-and no rollback occurs.
-
-
-
-The default conflict resolution algorithm is always ABORT
-but you can specify an alternative algorithm using special
-(non-standard) syntax on the INSERT and UPDATE commands.
-You can add the clause "ON CONFLICT " immediately
-after the "INSERT" or "UPDATE" keywords to specify the
-conflict resolution algorithm to use for that one operation.
-(Substitute "ABORT", "IGNORE", or "REPLACE" for ,
-of course.)
-
-
-Consider this example:
-
-
- BEGIN;
- CREATE TABLE t1(
- a INTEGER,
- b INTEGER,
- c INTEGER,
- UNIQUE(a,b)
- );
- INSERT INTO a VALUES(1,2,3);
- COMMIT;
-
- BEGIN;
- INSERT INTO a VALUES(2,3,4);
- INSERT INTO a VALUES(1,2,5);
-
-
-
-In the last instruction, the UNIQUE constraint fails
-and the entire transaction is rolled back. The database
-now contains a single entry: {1,2,3}.
-
-
-
- BEGIN;
- INSERT ON CONFLICT IGNORE INTO a VALUES(2,3,4);
- INSERT ON CONFLICT IGNORE INTO a VALUES(1,2,5);
- COMMIT;
-
-
-This time the "ON CONFLICT IGNORE" clause tells SQLite to use
-IGNORE semantics when a constraint fails. The second
-INSERT statement fails, but the database is
-not rolled back and there is no failure. The database
-now contains two rows: {1,2,3} and {2,3,4}.
-
-
- BEGIN;
- INSERT ON CONFLICT REPLACE INTO a VALUES(1,2,5);
- COMMIT;
-
-
-Here the "ON CONFLICT REPLACE" clause tells SQLite to use REPLACE
-semantics. The {1,2,3} is deleted when the {1,2,5} row
-is inserted in order to satisfy the constraint. After
-the above, the database contains {1,2,5} and {2,3,4}.
-
-A Syntactic Shortcut
-
-On an INSERT, the "ON CONFLICT" keywords may be omitted for brevity.
-So you can say
-
-
- INSERT IGNORE INTO a VALUES(1,2,5);
-
-
-Instead of the more wordy:
-
-
- INSERT ON CONFLICT IGNORE INTO a VALUES(1,2,5);
-
-
-Unfortunately, you cannot do this with an UPDATE.
-
-Changing The Default Conflict Resolution Algorithm
-
-You can change the default conflict resolution algorithm
-on a constraint-by-constraint basis using special (non-standard)
-syntax in CREATE TABLE and CREATE INDEX statements. The
-same "ON CONFLICT" clause that appears in INSERT and UPDATE
-statements is used but the clause is attached to the constraint
-in the CREATE TABLE statement. Like this:
-
-
- CREATE TABLE t1 (
- a INTEGER,
- b INTEGER,
- c INTEGER,
- UNIQUE(a,b) ON CONFLICT REPLACE
- );
-
-
-The ON CONFLICT clause in the above table definition says that
-the default conflict resolution algorithm is REPLACE instead
-of ABORT. REPLACE will always be used unless you override
-this by saying "INSERT IGNORE" or "INSERT ABORT".
-
-The ON CONFLICT clause can also appear on a NOT NULL constraint,
-a PRIMARY KEY constraint, and a CHECK constraint.
-(Note, however, that CHECK constraints are not currently enforced
-so the ON CONFLICT clause has no effect there.)
-
-A NOT NULL constraint will normally ABORT if you try to insert
-a NULL. But if you substitute the REPLACE algorithm, it tries to insert
-the default value in place of the NULL. If there is no default value,
-then REPLACE is the same as ABORT for NOT NULL constraints.
-With the IGNORE algorithm on a NOT NULL, the INSERT or UPDATE
-is suppressed if the value is NULL.
-
-Portability
-
-The ON CONFLICT syntax is not standard SQL and will not
-(as far as is known) work on any other database product. Furthermore,
-the syntax might change in future versions of SQLite. So use it
-with appropriate discretion.
}
puts {
diff --git a/www/lang.tcl b/www/lang.tcl
index 2bb0104248..39ed433008 100644
--- a/www/lang.tcl
+++ b/www/lang.tcl
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
-set rcsid {$Id: lang.tcl,v 1.20 2002/01/31 15:54:23 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.21 2002/02/03 00:56:11 drh Exp $}
puts {
@@ -42,6 +42,7 @@ foreach {section} [lsort -index 0 -dictionary {
{{DROP TABLE} droptable}
{{DROP INDEX} dropindex}
{INSERT insert}
+ {REPLACE replace}
{DELETE delete}
{UPDATE update}
{SELECT select}
@@ -61,7 +62,7 @@ the sequel.
}
proc Syntax {args} {
- puts {}
+ puts {}
foreach {rule body} $args {
puts ""
puts "$rule ::= | "
@@ -105,7 +106,7 @@ proc Example {text} {
Section {BEGIN TRANSACTION} createindex
Syntax {sql-statement} {
-BEGIN [TRANSACTION []]
+BEGIN [TRANSACTION []] [ON CONFLICT ]
}
Syntax {sql-statement} {
END [TRANSACTION []]
@@ -119,9 +120,7 @@ ROLLBACK [TRANSACTION []]
puts {
Beginning in version 2.0, SQLite supports transactions with
-rollback and atomic commit. However, only a single level of
-transaction is allowed. Transactions may not be nested.
-
+rollback and atomic commit.
No changes can be made to the database except within a transaction.
@@ -132,52 +131,32 @@ are committed at the conclusion of the command.
-Transactions can be started manually using the BEGIN TRANSACTION
-command. Such transactions persist until a COMMIT or ROLLBACK
-or until an error occurs or the database is closed. If an
-error is encountered or the database is closed, the transaction
-is automatically rolled back. The END TRANSACTION command is
-a alias for COMMIT.
+Transactions can be started manually using the BEGIN
+command. Such transactions usually persist until the next
+COMMIT or ROLLBACK command. But a transaction will also
+ROLLBACK if the database is closed or if an error occurs
+and the ROLLBACK conflict resolution algorithm is specified.
+See the documention on the ON CONFLICT
+clause for additional information about the ROLLBACK
+conflict resolution algorithm.
-}
-Section {ON CONFLICT clause} conflict
-
-Syntax {conflict-clause} {
-ON CONFLICT
-} {algorithm} {
-ABORT | IGNORE | REPLACE
-}
-
-puts {
-The ON CONFLICT clause is not a separate SQL command. It is a
-non-standard clause that can appear in many other SQL commands.
-It is given its own section in this document because it is not
-part of standard SQL and therefore might not be familiar.
-
-The ON CONFLICT clause specifies an algorithm used to resolve
-constraint conflicts. The default algorithm is ABORT. When the
-ABORT algorithm is in use, any constraint violation causes the
-command to abort and the current transaction to be rolled back.
-This is the only behavior exhibited by most SQL engines. But
-SQLite allows two alternative behaviors: IGNORE and REPLACE.
-The IGNORE algorithm means that when a constraint violation occurs
-on a COPY, INSERT or UPDATE, the particular row that caused the constraint
-violation is not inserted or changed, but other rows effected by the
-COPY, INSERT, or UPDATE are insert or changed as usual.
-The command is not aborted and no rollback occurs.
-If the algorithm is REPLACE, then SQLite tries to
-delete preexisting rows from the table to remove the constraint
-violation before inserting or changing the row.
-
-For additional information, see
-conflict.html.
+
+The optional ON CONFLICT clause at the end of a BEGIN statement
+can be used to changed the default conflict resolution algorithm.
+The normal default is ABORT. If an alternative is specified by
+the ON CONFLICT clause of a BEGIN, then that alternative is used
+as the default for all commands within the transaction. The default
+algorithm is overridden by ON CONFLICT clauses on individual
+constraints within the CREATE TABLE or CREATE INDEX statements
+and by the OR clauses on COPY, INSERT, and UPDATE commands.
+
}
Section COPY copy
Syntax {sql-statement} {
-COPY [ ] FROM
+COPY [ OR ] FROM
[ USING DELIMITERS ]
}
@@ -219,7 +198,7 @@ Section {CREATE INDEX} createindex
Syntax {sql-statement} {
CREATE [UNIQUE] INDEX
ON ( [, ]* )
-[ ]
+[ ON CONFLICT ]
} {column-name} {
[ ASC | DESC ]
}
@@ -282,6 +261,8 @@ DEFAULT
PRIMARY KEY ( [, ]* ) [ ]|
UNIQUE ( [, ]* ) [ ] |
CHECK ( ) [ ]
+} {conflict-clause} {
+ON CONFLICT
}
puts {
@@ -563,8 +544,8 @@ The "count(*)" syntax is supported but
Section INSERT insert
Syntax {sql-statement} {
-INSERT [ ] INTO [( )] VALUES ( ) |
-INSERT [ ] INTO [( )]
+INSERT [OR ] INTO [()] VALUES() |
+INSERT [OR ] INTO [()]
}
puts {
@@ -590,7 +571,199 @@ the ORDER BY is ignored.
The optional conflict-clause allows the specification of an alternative
constraint conflict resolution algorithm to use during this one command.
See the section titled
-ON CONFLICT for additional information.
+ON CONFLICT for additional information.
+For compatibility with MySQL, the parser allows the use of the
+single keyword "REPLACE" as an alias for "INSERT OR REPLACE".
+
+}
+
+Section {ON CONFLICT clause} conflict
+
+Syntax {conflict-clause} {
+ON CONFLICT
+} {conflict-algorithm} {
+ROLLBACK | ABORT | FAIL | IGNORE | REPLACE
+}
+
+puts {
+The ON CONFLICT clause is not a separate SQL command. It is a
+non-standard clause that can appear in many other SQL commands.
+It is given its own section in this document because it is not
+part of standard SQL and therefore might not be familiar.
+
+The syntax for the ON CONFLICT clause is as shown above for
+the CREATE TABLE, CREATE INDEX, and BEGIN TRANSACTION commands.
+For the COPY, INSERT, and UPDATE commands, the keywords
+"ON CONFLICT" are replaced by "OR", to make the syntax seem more
+natural. But the meaning of the clause is the same either way.
+
+The ON CONFLICT clause specifies an algorithm used to resolve
+constraint conflicts. There are five choices: ROLLBACK, ABORT,
+FAIL, IGNORE, and REPLACE. The default algorithm is ABORT. This
+is what they mean:
+
+
+- ROLLBACK
+When a constraint violation occurs, an immediate ROLLBACK
+occurs, thus ending the current transaction, and the command aborts
+with a return code of SQLITE_CONSTRAINT. If no transaction is
+active (other than the implied transaction that is created on every
+command) then this algorithm works the same as ABORT.
+
+- ABORT
+When a constraint violation occurs, the command backs out
+any prior changes it might have made and aborts with a return code
+of SQLITE_CONSTRAINT. But no ROLLBACK is executed so changes
+from prior commands within the same transaction
+are preserved. This is the default behavior.
+
+- FAIL
+When a constraint violation occurs, the command aborts with a
+return code SQLITE_CONSTRAINT. But any changes to the database that
+the command made prior to encountering the constraint violation
+are preserved and are not backed out. For example, if an UPDATE
+statement encountered a constraint violation on the 100th row that
+it attempts to update, then the first 99 row changes are preserved
+by change to rows 100 and beyond never occur.
+
+- IGNORE
+When a constraint violation occurs, the one row that contains
+the constraint violation is not inserted or changed. But the command
+continues executing normally. Other rows before and after the row that
+contained the constraint violation continue to be inserted or updated
+normally. No error is returned.
+
+- REPLACE
+When a UNIQUE constraint violation occurs, the pre-existing row
+that is causing the constraint violation is removed prior to inserting
+or updating the current row. Thus the insert or update always occurs.
+The command continues executing normally. No error is returned.
+
+
+
+The conflict resolution algorithm can be specified in three places,
+in order from lowest to highest precedence:
+
+
+
+
+On a BEGIN TRANSACTION command.
+
+
+
+On individual constraints within a CREATE TABLE or CREATE INDEX
+statement.
+
+
+
+In the OR clause of a COPY, INSERT, or UPDATE command.
+
+
+
+The algorithm specified in the OR clause of a COPY, INSERT, or UPDATE
+overrides any algorithm specified by a CREATE TABLE or CREATE INDEX.
+The algorithm specified within a CREATE TABLE or CREATE INDEX will, in turn,
+override the algorithm specified by a BEGIN TRANSACTION command.
+If no algorithm is specified anywhere, the ABORT algorithm is used.
+
+}
+# For additional information, see
+# conflict.html.
+
+
+Section PRAGMA pragma
+
+Syntax {sql-statement} {
+PRAGMA = |
+PRAGMA ()
+}
+
+puts {
+The PRAGMA command is used to modify the operation of the SQLite library.
+The pragma command is experimental and specific pragma statements may
+removed or added in future releases of SQLite. Use this command
+with caution.
+
+The current implementation supports the following pragmas:
+
+
+PRAGMA cache_size = Number-of-pages;
+ Change the maximum number of database disk pages that SQLite
+ will hold in memory at once. Each page uses about 1.5K of RAM.
+ The default cache size is 100. If you are doing UPDATEs or DELETEs
+ that change many rows of a database and you do not mind if SQLite
+ uses more memory, you can increase the cache size for a possible speed
+ improvement.
+
+PRAGMA count_changes = ON;
+
PRAGMA count_changes = OFF;
+ When on, the COUNT_CHANGES pragma causes the callback function to
+ be invoked once for each DELETE, INSERT, or UPDATE operation. The
+ argument is the number of rows that were changed.
+
+PRAGMA empty_result_callbacks = ON;
+
PRAGMA empty_result_callbacks = OFF;
+ When on, the EMPTY_RESULT_CALLBACKS pragma causes the callback
+ function to be invoked once for each query that has an empty result
+ set. The third "argv" parameter to the callback is set to NULL
+ because there is no data to report. But the second "argc" and
+ fourth "columnNames" parameters are valid and can be used to
+ determine the number and names of the columns that would have been in
+ the result set had the set not been empty.
+
+PRAGMA full_column_names = ON;
+
PRAGMA full_column_names = OFF;
+ The column names reported in an SQLite callback are normally just
+ the name of the column itself, except for joins when "TABLE.COLUMN"
+ is used. But when full_column_names is turned on, column names are
+ always reported as "TABLE.COLUMN" even for simple queries.
+
+PRAGMA index_info(index-name);
+ For each column that the named index references, invoke the
+ callback function
+ once with information about that column, including the column name,
+ and the column number.
+
+PRAGMA index_list(table-name);
+ For each index on the named table, invoke the callback function
+ once with information about that index. Arguments include the
+ index name and a flag to indicate whether or not the index must be
+ unique.
+
+PRAGMA parser_trace = ON;
PRAGMA parser_trace = OFF;
+ Turn tracing of the SQL parser inside of the
+ SQLite library on and off. This is used for debugging.
+ This only works if the library is compiled without the NDEBUG macro.
+
+
+PRAGMA table_info(table-name);
+ For each column in the named table, invoke the callback function
+ once with information about that column, including the column name,
+ data type, whether or not the column can be NULL, and the default
+ value for the column.
+
+PRAGMA vdbe_trace = ON;
PRAGMA vdbe_trace = OFF;
+ Turn tracing of the virtual database engine inside of the
+ SQLite library on and off. This is used for debugging.
+
+
+No error message is generated if an unknown pragma is issued.
+Unknown pragmas are ignored.
+}
+
+Section REPLACE replace
+
+Syntax {sql-statement} {
+REPLACE INTO [( )] VALUES ( ) |
+REPLACE INTO [( )]
+}
+
+puts {
+The REPLACE command is an alias for the "INSERT OR REPLACE" variant
+of the INSERT command. This alias is provided for
+compatibility with MySQL. See the
+INSERT command documentation for additional
+information.
}
Section SELECT select
@@ -675,7 +848,7 @@ are connected into a compound, they group from left to right.
Section UPDATE update
Syntax {sql-statement} {
-UPDATE [ ]
+UPDATE [ OR ]
SET [, ]
[WHERE ]
} {assignment} {
@@ -713,89 +886,6 @@ the database backend and VACUUM has become a no-op.
}
-Section PRAGMA pragma
-
-Syntax {sql-statement} {
-PRAGMA = |
-PRAGMA ()
-}
-
-puts {
-The PRAGMA command is used to modify the operation of the SQLite library.
-The pragma command is experimental and specific pragma statements may
-removed or added in future releases of SQLite. Use this command
-with caution.
-
-The current implementation supports the following pragmas:
-
-
-PRAGMA cache_size = Number-of-pages;
- Change the maximum number of database disk pages that SQLite
- will hold in memory at once. Each page uses about 1.5K of RAM.
- The default cache size is 100. If you are doing UPDATEs or DELETEs
- that change many rows of a database and you do not mind if SQLite
- uses more memory, you can increase the cache size for a possible speed
- improvement.
-
-PRAGMA count_changes = ON;
-
PRAGMA count_changes = OFF;
- When on, the COUNT_CHANGES pragma causes the callback function to
- be invoked once for each DELETE, INSERT, or UPDATE operation. The
- argument is the number of rows that were changed.
-
-PRAGMA empty_result_callbacks = ON;
-
PRAGMA empty_result_callbacks = OFF;
- When on, the EMPTY_RESULT_CALLBACKS pragma causes the callback
- function to be invoked once for each query that has an empty result
- set. The third "argv" parameter to the callback is set to NULL
- because there is no data to report. But the second "argc" and
- fourth "columnNames" parameters are valid and can be used to
- determine the number and names of the columns that would have been in
- the result set had the set not been empty.
-
-PRAGMA full_column_names = ON;
-
PRAGMA full_column_names = OFF;
- The column names reported in an SQLite callback are normally just
- the name of the column itself, except for joins when "TABLE.COLUMN"
- is used. But when full_column_names is turned on, column names are
- always reported as "TABLE.COLUMN" even for simple queries.
-
-PRAGMA index_info(index-name);
- For each column that the named index references, invoke the
- callback function
- once with information about that column, including the column name,
- and the column number.
-
-PRAGMA index_list(table-name);
- For each index on the named table, invoke the callback function
- once with information about that index. Arguments include the
- index name and a flag to indicate whether or not the index must be
- unique.
-
-PRAGMA parser_trace = ON;
PRAGMA parser_trace = OFF;
- Turn tracing of the SQL parser inside of the
- SQLite library on and off. This is used for debugging.
- This only works if the library is compiled without the NDEBUG macro.
-
-
-PRAGMA table_info(table-name);
- For each column in the named table, invoke the callback function
- once with information about that column, including the column name,
- data type, whether or not the column can be NULL, and the default
- value for the column.
-
-PRAGMA vdbe_trace = ON;
PRAGMA vdbe_trace = OFF;
- Turn tracing of the virtual database engine inside of the
- SQLite library on and off. This is used for debugging.
-
-
-No error message is generated if an unknown pragma is issued.
-Unknown pragmas are ignored.
-}
-
-puts {
-
-}
puts {