Add a new row type to RBU (a peer of insert, update and delete) - "delete then insert".
FossilOrigin-Name: 1d9468d2427d2c9b7240b364554ac85a0b62fa44
This commit is contained in:
commit
d0b8b664bd
142
ext/rbu/rbuC.test
Normal file
142
ext/rbu/rbuC.test
Normal file
@ -0,0 +1,142 @@
|
||||
# 2016 March 7
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Tests for RBU focused on the REPLACE operation (rbu_control column
|
||||
# contains integer value 2).
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set ::testprefix rbuC
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This test is actually of an UPDATE directive. Just to establish that
|
||||
# these work with UNIQUE indexes before preceding to REPLACE.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, 'a', 'b', 'c');
|
||||
}
|
||||
|
||||
forcedelete rbu.db
|
||||
do_execsql_test 1.1 {
|
||||
ATTACH 'rbu.db' AS rbu;
|
||||
CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control);
|
||||
INSERT INTO data_t1 VALUES(1, 'a', 'b', 'c', '.xxx');
|
||||
}
|
||||
|
||||
do_test 1.2 {
|
||||
step_rbu test.db rbu.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 a b c
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn schema} {
|
||||
1 {
|
||||
CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
}
|
||||
2 {
|
||||
CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
}
|
||||
3 {
|
||||
CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE) WITHOUT ROWID;
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
}
|
||||
} {
|
||||
reset_db
|
||||
forcedelete rbu.db
|
||||
execsql $schema
|
||||
|
||||
do_execsql_test 2.$tn.0 {
|
||||
INSERT INTO t1 VALUES(1, 'a', 'b', 'c');
|
||||
INSERT INTO t1 VALUES(2, 'b', 'c', 'd');
|
||||
INSERT INTO t1 VALUES(3, 'c', 'd', 'e');
|
||||
}
|
||||
|
||||
do_execsql_test 2.$tn.1 {
|
||||
ATTACH 'rbu.db' AS rbu;
|
||||
CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control);
|
||||
INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2);
|
||||
INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2);
|
||||
INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2);
|
||||
}
|
||||
|
||||
do_test 2.$tn.2 {
|
||||
step_rbu test.db rbu.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 2.$tn.3 {
|
||||
SELECT * FROM t1 ORDER BY i
|
||||
} {
|
||||
1 1 2 3
|
||||
2 b c d
|
||||
3 c d e
|
||||
4 d e f
|
||||
}
|
||||
|
||||
integrity_check 2.$tn.4
|
||||
}
|
||||
|
||||
foreach {tn schema} {
|
||||
1 {
|
||||
CREATE TABLE t1(a, b, c UNIQUE);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
}
|
||||
|
||||
2 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
|
||||
}
|
||||
} {
|
||||
if {$tn==2} { ifcapable !fts5 break }
|
||||
reset_db
|
||||
forcedelete rbu.db
|
||||
execsql $schema
|
||||
|
||||
do_execsql_test 3.$tn.0 {
|
||||
INSERT INTO t1 VALUES('a', 'b', 'c');
|
||||
INSERT INTO t1 VALUES('b', 'c', 'd');
|
||||
INSERT INTO t1 VALUES('c', 'd', 'e');
|
||||
}
|
||||
|
||||
do_execsql_test 3.$tn.1 {
|
||||
ATTACH 'rbu.db' AS rbu;
|
||||
CREATE TABLE rbu.data_t1(rbu_rowid, a, b, c, rbu_control);
|
||||
INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2);
|
||||
INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2);
|
||||
INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2);
|
||||
}
|
||||
|
||||
do_test 3.$tn.2 {
|
||||
step_rbu test.db rbu.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 3.$tn.3 {
|
||||
SELECT rowid, * FROM t1 ORDER BY 1
|
||||
} {
|
||||
1 1 2 3
|
||||
2 b c d
|
||||
3 c d e
|
||||
4 d e f
|
||||
}
|
||||
|
||||
integrity_check 3.$tn.4
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -280,10 +280,11 @@ struct RbuObjIter {
|
||||
*/
|
||||
#define RBU_INSERT 1 /* Insert on a main table b-tree */
|
||||
#define RBU_DELETE 2 /* Delete a row from a main table b-tree */
|
||||
#define RBU_IDX_DELETE 3 /* Delete a row from an aux. index b-tree */
|
||||
#define RBU_IDX_INSERT 4 /* Insert on an aux. index b-tree */
|
||||
#define RBU_UPDATE 5 /* Update a row in a main table b-tree */
|
||||
#define RBU_REPLACE 3 /* Delete and then insert a row */
|
||||
#define RBU_IDX_DELETE 4 /* Delete a row from an aux. index b-tree */
|
||||
#define RBU_IDX_INSERT 5 /* Insert on an aux. index b-tree */
|
||||
|
||||
#define RBU_UPDATE 6 /* Update a row in a main table b-tree */
|
||||
|
||||
/*
|
||||
** A single step of an incremental checkpoint - frame iWalFrame of the wal
|
||||
@ -1909,13 +1910,13 @@ static int rbuObjIterPrepareAll(
|
||||
);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
|
||||
"UNION ALL "
|
||||
"SELECT %s, rbu_control FROM '%q' "
|
||||
"WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
|
||||
"UNION ALL "
|
||||
"SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
|
||||
"ORDER BY %s%s",
|
||||
zCollist, pIter->zDataTbl,
|
||||
zCollist, p->zStateDb, pIter->zDataTbl,
|
||||
zCollist, pIter->zDataTbl,
|
||||
zCollist, zLimit
|
||||
);
|
||||
}
|
||||
@ -1981,17 +1982,17 @@ static int rbuObjIterPrepareAll(
|
||||
rbuMPrintfExec(p, p->dbMain,
|
||||
"CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" "
|
||||
"BEGIN "
|
||||
" SELECT rbu_tmp_insert(2, %s);"
|
||||
" SELECT rbu_tmp_insert(3, %s);"
|
||||
"END;"
|
||||
|
||||
"CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" "
|
||||
"BEGIN "
|
||||
" SELECT rbu_tmp_insert(2, %s);"
|
||||
" SELECT rbu_tmp_insert(3, %s);"
|
||||
"END;"
|
||||
|
||||
"CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" "
|
||||
"BEGIN "
|
||||
" SELECT rbu_tmp_insert(3, %s);"
|
||||
" SELECT rbu_tmp_insert(4, %s);"
|
||||
"END;",
|
||||
zWrite, zTbl, zOldlist,
|
||||
zWrite, zTbl, zOldlist,
|
||||
@ -2509,14 +2510,12 @@ static int rbuStepType(sqlite3rbu *p, const char **pzMask){
|
||||
switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
|
||||
case SQLITE_INTEGER: {
|
||||
int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
|
||||
if( iVal==0 ){
|
||||
res = RBU_INSERT;
|
||||
}else if( iVal==1 ){
|
||||
res = RBU_DELETE;
|
||||
}else if( iVal==2 ){
|
||||
res = RBU_IDX_DELETE;
|
||||
}else if( iVal==3 ){
|
||||
res = RBU_IDX_INSERT;
|
||||
switch( iVal ){
|
||||
case 0: res = RBU_INSERT; break;
|
||||
case 1: res = RBU_DELETE; break;
|
||||
case 2: res = RBU_REPLACE; break;
|
||||
case 3: res = RBU_IDX_DELETE; break;
|
||||
case 4: res = RBU_IDX_INSERT; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2555,6 +2554,67 @@ static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
|
||||
# define assertColumnName(x,y,z)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Argument eType must be one of RBU_INSERT, RBU_DELETE, RBU_IDX_INSERT or
|
||||
** RBU_IDX_DELETE. This function performs the work of a single
|
||||
** sqlite3rbu_step() call for the type of operation specified by eType.
|
||||
*/
|
||||
static void rbuStepOneOp(sqlite3rbu *p, int eType){
|
||||
RbuObjIter *pIter = &p->objiter;
|
||||
sqlite3_value *pVal;
|
||||
sqlite3_stmt *pWriter;
|
||||
int i;
|
||||
|
||||
assert( p->rc==SQLITE_OK );
|
||||
assert( eType!=RBU_DELETE || pIter->zIdx==0 );
|
||||
|
||||
if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
|
||||
pWriter = pIter->pDelete;
|
||||
}else{
|
||||
pWriter = pIter->pInsert;
|
||||
}
|
||||
|
||||
for(i=0; i<pIter->nCol; i++){
|
||||
/* If this is an INSERT into a table b-tree and the table has an
|
||||
** explicit INTEGER PRIMARY KEY, check that this is not an attempt
|
||||
** to write a NULL into the IPK column. That is not permitted. */
|
||||
if( eType==RBU_INSERT
|
||||
&& pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i]
|
||||
&& sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
|
||||
){
|
||||
p->rc = SQLITE_MISMATCH;
|
||||
p->zErrmsg = sqlite3_mprintf("datatype mismatch");
|
||||
return;
|
||||
}
|
||||
|
||||
if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
pVal = sqlite3_column_value(pIter->pSelect, i);
|
||||
p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
|
||||
if( p->rc ) return;
|
||||
}
|
||||
if( pIter->zIdx==0
|
||||
&& (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
|
||||
){
|
||||
/* For a virtual table, or a table with no primary key, the
|
||||
** SELECT statement is:
|
||||
**
|
||||
** SELECT <cols>, rbu_control, rbu_rowid FROM ....
|
||||
**
|
||||
** Hence column_value(pIter->nCol+1).
|
||||
*/
|
||||
assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
|
||||
pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
|
||||
p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
|
||||
}
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3_step(pWriter);
|
||||
p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work for an sqlite3rbu_step() call.
|
||||
**
|
||||
@ -2569,78 +2629,32 @@ static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
|
||||
static int rbuStep(sqlite3rbu *p){
|
||||
RbuObjIter *pIter = &p->objiter;
|
||||
const char *zMask = 0;
|
||||
int i;
|
||||
int eType = rbuStepType(p, &zMask);
|
||||
|
||||
if( eType ){
|
||||
assert( eType==RBU_INSERT || eType==RBU_DELETE
|
||||
|| eType==RBU_REPLACE || eType==RBU_IDX_DELETE
|
||||
|| eType==RBU_IDX_INSERT || eType==RBU_UPDATE
|
||||
);
|
||||
assert( eType!=RBU_UPDATE || pIter->zIdx==0 );
|
||||
|
||||
if( pIter->zIdx==0 && eType==RBU_IDX_DELETE ){
|
||||
rbuBadControlError(p);
|
||||
}
|
||||
else if(
|
||||
eType==RBU_INSERT
|
||||
|| eType==RBU_DELETE
|
||||
|| eType==RBU_IDX_DELETE
|
||||
|| eType==RBU_IDX_INSERT
|
||||
){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3_stmt *pWriter;
|
||||
|
||||
assert( eType!=RBU_UPDATE );
|
||||
assert( eType!=RBU_DELETE || pIter->zIdx==0 );
|
||||
|
||||
if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
|
||||
pWriter = pIter->pDelete;
|
||||
}else{
|
||||
pWriter = pIter->pInsert;
|
||||
}
|
||||
|
||||
for(i=0; i<pIter->nCol; i++){
|
||||
/* If this is an INSERT into a table b-tree and the table has an
|
||||
** explicit INTEGER PRIMARY KEY, check that this is not an attempt
|
||||
** to write a NULL into the IPK column. That is not permitted. */
|
||||
if( eType==RBU_INSERT
|
||||
&& pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i]
|
||||
&& sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
|
||||
){
|
||||
p->rc = SQLITE_MISMATCH;
|
||||
p->zErrmsg = sqlite3_mprintf("datatype mismatch");
|
||||
goto step_out;
|
||||
}
|
||||
|
||||
if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
pVal = sqlite3_column_value(pIter->pSelect, i);
|
||||
p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
|
||||
if( p->rc ) goto step_out;
|
||||
}
|
||||
if( pIter->zIdx==0
|
||||
&& (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
|
||||
){
|
||||
/* For a virtual table, or a table with no primary key, the
|
||||
** SELECT statement is:
|
||||
**
|
||||
** SELECT <cols>, rbu_control, rbu_rowid FROM ....
|
||||
**
|
||||
** Hence column_value(pIter->nCol+1).
|
||||
*/
|
||||
assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
|
||||
pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
|
||||
p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
|
||||
}
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3_step(pWriter);
|
||||
p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
|
||||
}
|
||||
}else{
|
||||
else if( eType==RBU_REPLACE ){
|
||||
if( pIter->zIdx==0 ) rbuStepOneOp(p, RBU_DELETE);
|
||||
if( p->rc==SQLITE_OK ) rbuStepOneOp(p, RBU_INSERT);
|
||||
}
|
||||
else if( eType!=RBU_UPDATE ){
|
||||
rbuStepOneOp(p, eType);
|
||||
}
|
||||
else{
|
||||
sqlite3_value *pVal;
|
||||
sqlite3_stmt *pUpdate = 0;
|
||||
assert( eType==RBU_UPDATE );
|
||||
rbuGetUpdateStmt(p, pIter, zMask, &pUpdate);
|
||||
if( pUpdate ){
|
||||
int i;
|
||||
for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
|
||||
char c = zMask[pIter->aiSrcOrder[i]];
|
||||
pVal = sqlite3_column_value(pIter->pSelect, i);
|
||||
|
17
manifest
17
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sthe\sSQLITE_DEFAULT_SYNCHRONOUS\sand\sSQLITE_DEFAULT_WAL_SYNCHRONOUS\ncompile-time\soptions.\s\sAutomatically\sswitch\sto\sthe\sWAL_SYNCHRONOUS\ssetting\nwhen\sfirst\sopening\sa\sWAL-mode\sdatabase\sif\sthe\ssynchronous\ssetting\shas\snot\nbeen\spreviously\sset\sby\sthe\sapplication.
|
||||
D 2016-03-08T15:47:47.861
|
||||
C Add\sa\snew\srow\stype\sto\sRBU\s(a\speer\sof\sinsert,\supdate\sand\sdelete)\s-\s"delete\sthen\sinsert".
|
||||
D 2016-03-08T15:52:43.903
|
||||
F Makefile.in f53429fb2f313c099283659d0df6f20f932c861f
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc df0bf9ff7f8b3f4dd9fb4cc43f92fe58f6ec5c66
|
||||
@ -234,6 +234,7 @@ F ext/rbu/rbu8.test 3bbf2c35d71a843c463efe93946f14ad10c3ede0
|
||||
F ext/rbu/rbu9.test 0806d1772c9f4981774ff028de6656e4183082af
|
||||
F ext/rbu/rbuA.test c1a7b3e2d926b8f8448bb3b4ae787e314ee4b2b3
|
||||
F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2
|
||||
F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831
|
||||
F ext/rbu/rbu_common.tcl 0398545fed614f807d5f0ba55a85a51f08ba8f1a
|
||||
F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695
|
||||
F ext/rbu/rbudiff.test 6cc806dc36389292f2a8f5842d0103721df4a07d
|
||||
@ -241,7 +242,7 @@ F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
|
||||
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
|
||||
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
|
||||
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
|
||||
F ext/rbu/sqlite3rbu.c 371e8bf06cfb3f691adac47eb15ab1073ed92dcf
|
||||
F ext/rbu/sqlite3rbu.c 0d901d773bf4c9e7d101daaf545263044e7e6615
|
||||
F ext/rbu/sqlite3rbu.h 0bdeb3be211aaba7d85445fa36f4701a25a3dbde
|
||||
F ext/rbu/test_rbu.c 4a4cdcef4ef9379fc2a21f008805c80b27bcf573
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
@ -1454,8 +1455,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 64b3cb29159491cbfab7e01844b54408541ece5e 592d2104361500e5002783ba329a2609389c57b9
|
||||
R da9c8dcc67c43bdd497418f56500542e
|
||||
T +closed 592d2104361500e5002783ba329a2609389c57b9
|
||||
U drh
|
||||
Z 4fa68fdb2896879ae18878d725366e25
|
||||
P 5a847a676e756bbe33436596d4279f339bfb247c 169311c85b30f625bdb6986c9cd11db70942d73b
|
||||
R 43c0b748ba126143d737a43b13d0c445
|
||||
T +closed 169311c85b30f625bdb6986c9cd11db70942d73b
|
||||
U dan
|
||||
Z 99ed19537580c1e7328c1d8043178b0a
|
||||
|
@ -1 +1 @@
|
||||
5a847a676e756bbe33436596d4279f339bfb247c
|
||||
1d9468d2427d2c9b7240b364554ac85a0b62fa44
|
Loading…
x
Reference in New Issue
Block a user