Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -1504,33 +1504,32 @@ VdbeComment((v, "%s.%s", pTab->zName, pTab->aCol[pPk->aiColumn[i]].zName)); } } if( isUpdate ){ - if( pIdx->autoIndex==2 ){ - /* For a PRIMARY KEY index on a WITHOUT ROWID table, always conflict - ** on an INSERT. On an UPDATE, only conflict if the PRIMARY KEY - ** has changed. */ - int addrPkConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; - for(i=0; inKeyCol-1; i++){ - x = pPk->aiColumn[i]; - sqlite3VdbeAddOp3(v, OP_Ne, regOldData+1+x, - addrPkConflict, regIdx+i); - } - x = pPk->aiColumn[i]; - sqlite3VdbeAddOp3(v, OP_Eq, regOldData+1+x, addrUniqueOk, regIdx+i); - }else{ - /* For a UNIQUE index on a WITHOUT ROWID table, conflict only if the - ** PRIMARY KEY value of the match is different from the old - ** PRIMARY KEY value from before the update. */ - int addrConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; - for(i=0; inKeyCol-1; i++){ - sqlite3VdbeAddOp3(v, OP_Ne, regOldData+pPk->aiColumn[i]+1, - addrConflict, regR+i); - } - sqlite3VdbeAddOp3(v, OP_Eq, regOldData+pPk->aiColumn[i]+1, - addrUniqueOk, regR+i); + /* If currently processing the PRIMARY KEY of a WITHOUT ROWID + ** table, only conflict if the new PRIMARY KEY values are actually + ** different from the old. + ** + ** For a UNIQUE index, only conflict if the PRIMARY KEY values + ** of the matched index row are different from the original PRIMARY + ** KEY values of this row before the update. */ + char *p4; + int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; + int op = OP_Ne; + int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); + + for(i=0; inKeyCol; i++){ + char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); + x = pPk->aiColumn[i]; + if( i==(pPk->nKeyCol-1) ){ + addrJump = addrUniqueOk; + op = OP_Eq; + } + sqlite3VdbeAddOp4(v, op, + regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ + ); } } } sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn); Index: test/tester.tcl ================================================================== --- test/tester.tcl +++ test/tester.tcl @@ -1020,10 +1020,54 @@ puts [format {%-4d %-12.12s %-6d %-6d %-6d % -17s %s %s} \ $addr $opcode $p1 $p2 $p3 $p4 $p5 $comment ] } } + +proc explain_i {sql {db db}} { + puts "" + puts "addr opcode p1 p2 p3 p4 p5 #" + puts "---- ------------ ------ ------ ------ ---------------- -- -" + + set addrTail 0 + + $db eval "explain $sql" {} { + set x($addr) 0 + set op($addr) $opcode + + if {$opcode == "Goto" && $addrTail==0} { + set addrTail $p2 + } + + if {$opcode == "Next"} { + for {set i $p2} {$i<$addr} {incr i} { + incr x($i) 2 + } + } + + if {$opcode == "Goto" && $p2<$addr && $op($p2)=="Yield"} { + for {set i [expr $p2+1]} {$i<$addr} {incr i} { + incr x($i) 2 + } + } + } + + $db eval "explain $sql" {} { + if {$addr == $addrTail} { + puts "" + } + set I [string repeat " " $x($addr)] + puts [format {%-4d %s%-12.12s %-6d %-6d %-6d % -17s %s %s} \ + $addr $I $opcode $p1 $p2 $p3 $p4 $p5 $comment + ] + + if {$opcode == "Halt" && $comment == "End of coroutine"} { + puts "" + } + } + puts "---- ------------ ------ ------ ------ ---------------- -- -" +} # Show the VDBE program for an SQL statement but omit the Trace # opcode at the beginning. This procedure can be used to prove # that different SQL statements generate exactly the same VDBE code. # Index: test/without_rowid1.test ================================================================== --- test/without_rowid1.test +++ test/without_rowid1.test @@ -13,10 +13,11 @@ # focus of this file is testing WITHOUT ROWID tables. # set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix without_rowid1 # Create and query a WITHOUT ROWID table. # do_execsql_test without_rowid1-1.0 { CREATE TABLE t1(a,b,c,d, PRIMARY KEY(c,a)) WITHOUT ROWID; @@ -105,7 +106,53 @@ ifcapable stat4 { do_execsql_test without_rowid1-1.52 { SELECT DISTINCT tbl, idx FROM sqlite_stat4 ORDER BY idx; } {t1 t1 t1 t1bd} } + +#---------- + +do_execsql_test 2.1.1 { + CREATE TABLE t4 (a COLLATE nocase PRIMARY KEY, b) WITHOUT ROWID; + INSERT INTO t4 VALUES('abc', 'def'); + SELECT * FROM t4; +} {abc def} +do_execsql_test 2.1.2 { + UPDATE t4 SET a = 'ABC'; + SELECT * FROM t4; +} {ABC def} + +do_execsql_test 2.2.1 { + DROP TABLE t4; + CREATE TABLE t4 (b, a COLLATE nocase PRIMARY KEY) WITHOUT ROWID; + INSERT INTO t4(a, b) VALUES('abc', 'def'); + SELECT * FROM t4; +} {def abc} + +do_execsql_test 2.2.2 { + UPDATE t4 SET a = 'ABC', b = 'xyz'; + SELECT * FROM t4; +} {xyz ABC} + +do_execsql_test 2.3.1 { + CREATE TABLE t5 (a, b, PRIMARY KEY(b, a)) WITHOUT ROWID; + INSERT INTO t5(a, b) VALUES('abc', 'def'); + UPDATE t5 SET a='abc', b='def'; +} {} + +do_execsql_test 2.4.1 { + CREATE TABLE t6 ( + a COLLATE nocase, b, c UNIQUE, PRIMARY KEY(b, a) + ) WITHOUT ROWID; + + INSERT INTO t6(a, b, c) VALUES('abc', 'def', 'ghi'); + UPDATE t6 SET a='ABC', c='ghi'; +} {} + +do_execsql_test 2.4.2 { + SELECT * FROM t6 ORDER BY b, a; + SELECT * FROM t6 ORDER BY c; +} {ABC def ghi ABC def ghi} + finish_test +