Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -1308,12 +1308,13 @@ ** index and making sure that duplicate entries do not already exist. ** Add the new records to the indices as we go. */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ int regIdx; +#ifndef SQLITE_OMIT_UNIQUE_ENFORCEMENT int regR; - +#endif if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ /* Create a key for accessing the index entry */ regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1); for(i=0; inColumn; i++){ @@ -1326,10 +1327,16 @@ } sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); + +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + pIdx->onError = OE_None; + sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); + continue; /* Treat pIdx as if it is not a UNIQUE index */ +#else /* Find out what action to take in case there is an indexing conflict */ onError = pIdx->onError; if( onError==OE_None ){ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); @@ -1400,10 +1407,11 @@ break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); +#endif } if( pbMayReplace ){ *pbMayReplace = seenReplace; } Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -472,10 +472,16 @@ #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif + +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "1", TCL_GLOBAL_ONLY); +#endif #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY); ADDED test/omitunique.test Index: test/omitunique.test ================================================================== --- /dev/null +++ test/omitunique.test @@ -0,0 +1,170 @@ +# 2011 March 10 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the SQLITE_OMIT_UNIQUE_ENFORCEMENT +# compiler option. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# table with UNIQUE keyword on column +do_test omitunique-1.1.1 { + catchsql {CREATE TABLE t1(a TEXT UNIQUE); } +} {0 {}} +do_test omitunique-1.1.2 { + catchsql {INSERT INTO t1(a) VALUES('abc'); } +} {0 {}} +do_test omitunique-1.1.3 { + catchsql {INSERT INTO t1(a) VALUES('123'); } +} {0 {}} + +# table with UNIQUE index on column +do_test omitunique-1.2.1 { + catchsql { + CREATE TABLE t2(a TEXT); + CREATE UNIQUE INDEX t2a ON t2(a); + } +} {0 {}} +do_test omitunique-1.2.2 { + catchsql {INSERT INTO t2(a) VALUES('abc'); } +} {0 {}} +do_test omitunique-1.2.3 { + catchsql {INSERT INTO t2(a) VALUES('123'); } +} {0 {}} + +# table with regular index on column +do_test omitunique-1.3.1 { + catchsql { + CREATE TABLE t3(a TEXT); + CREATE INDEX t3a ON t3(a); + } +} {0 {}} +do_test omitunique-1.3.2 { + catchsql {INSERT INTO t3(a) VALUES('abc'); } +} {0 {}} +do_test omitunique-1.3.3 { + catchsql {INSERT INTO t3(a) VALUES('123'); } +} {0 {}} + +# table with no index on column +do_test omitunique-1.4.1 { + catchsql { + CREATE TABLE t4(a TEXT); + } +} {0 {}} +do_test omitunique-1.4.2 { + catchsql {INSERT INTO t4(a) VALUES('abc'); } +} {0 {}} +do_test omitunique-1.4.3 { + catchsql {INSERT INTO t4(a) VALUES('123'); } +} {0 {}} + +# run our tests using several table/index forms +foreach {j tbl uniq cnt_enforce cnt_omit qp_est} { +1 {t1} 1 1 9 1 +2 {t2} 1 1 9 1 +3 {t3} 0 9 9 10 +4 {t4} 0 9 9 100000 +} { + + # check various INSERT commands + foreach {i cmd err} { + 1 {INSERT} 1 + 2 {INSERT OR IGNORE} 0 + 3 {INSERT OR REPLACE} 0 + 4 {REPLACE} 0 + 5 {INSERT OR FAIL} 1 + 6 {INSERT OR ABORT} 1 + 7 {INSERT OR ROLLBACK} 1 + } { + + if { $uniq==0 || $err==0 } { + set msg {0 {}} + } { + set msg {1 {column a is not unique}} + } + + ifcapable unique_enforcement { + ifcapable explain { + do_test omitunique-2.1.$j.$i.1 { + set x [execsql [ subst {EXPLAIN $cmd INTO $tbl (a) VALUES('abc')}]] + regexp { IsUnique } $x + } $uniq + } + do_test omitunique-2.1.$j.$i.2 { + catchsql [ subst {$cmd INTO $tbl (a) VALUES('abc')}] + } $msg + } + ifcapable !unique_enforcement { + ifcapable explain { + do_test omitunique-2.1.$j.$i.1 { + set x [execsql [ subst {EXPLAIN $cmd INTO $tbl (a) VALUES('abc')}]] + regexp { IsUnique } $x + } {0} + } + do_test omitunique-2.1.$j.$i.2 { + catchsql [ subst {$cmd INTO $tbl (a) VALUES('abc')}] + } {0 {}} + } + + } + # end foreach cmd + + # check UPDATE command + ifcapable unique_enforcement { + ifcapable explain { + do_test omitunique-2.2.$j.1 { + set x [execsql [ subst {EXPLAIN UPDATE $tbl SET a='abc'}]] + regexp { IsUnique } $x + } $uniq + } + do_test omitunique-2.2.$j.2 { + catchsql [ subst {UPDATE $tbl SET a='abc'}] + } $msg + } + ifcapable !unique_enforcement { + ifcapable explain { + do_test omitunique-2.2.$j.1 { + set x [execsql [ subst {EXPLAIN UPDATE $tbl SET a='abc'}]] + regexp { IsUnique } $x + } {0} + } + do_test omitunique-2.2.$j.2 { + catchsql [ subst {UPDATE $tbl SET a='abc' }] + } {0 {}} + } + + # check record counts + ifcapable unique_enforcement { + do_test omitunique-2.3.$j { + execsql [ subst {SELECT count(*) FROM $tbl WHERE a='abc' }] + } $cnt_enforce + } + ifcapable !unique_enforcement { + do_test omitunique-2.3.$j { + execsql [ subst {SELECT count(*) FROM $tbl WHERE a='abc' }] + } $cnt_omit + } + + # make sure the query planner row estimate not affected because of omit enforcement + ifcapable explain { + do_test omitunique-2.4.$j { + set x [execsql [ subst {EXPLAIN QUERY PLAN SELECT count(*) FROM $tbl WHERE a='abc' }]] + set y [ subst {~$qp_est row} ] + regexp $y $x + } {1} + } + +} +# end foreach tbl + +finish_test