mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Add support for delete operations to the ota extension.
FossilOrigin-Name: f988234ba54d7c667f7deef1d04beed4e7fe6182
This commit is contained in:
@@ -32,16 +32,21 @@ proc create_ota1 {filename} {
|
|||||||
return $filename
|
return $filename
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create an empty target database suitable for the OTA created by
|
# Create a simple OTA database. That expects to write to a table:
|
||||||
# [create_ota1].
|
|
||||||
#
|
#
|
||||||
# CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
# CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||||
#
|
#
|
||||||
proc create_target1 {filename} {
|
proc create_ota4 {filename} {
|
||||||
forcedelete $filename
|
forcedelete $filename
|
||||||
sqlite3 target1 $filename
|
sqlite3 ota1 $filename
|
||||||
target1 eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) }
|
ota1 eval {
|
||||||
target1 close
|
CREATE TABLE data_t1(a, b, c, ota_control);
|
||||||
|
INSERT INTO data_t1 VALUES(1, 2, 3, 0);
|
||||||
|
INSERT INTO data_t1 VALUES(2, NULL, 5, 1);
|
||||||
|
INSERT INTO data_t1 VALUES(3, 8, 9, 0);
|
||||||
|
INSERT INTO data_t1 VALUES(4, NULL, 11, 1);
|
||||||
|
}
|
||||||
|
ota1 close
|
||||||
return $filename
|
return $filename
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +181,49 @@ foreach {tn errcode errmsg schema} {
|
|||||||
do_test 3.$tn.4 { dbcksum db main } $cksum
|
do_test 3.$tn.4 { dbcksum db main } $cksum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
foreach {tn2 cmd} {1 run_ota 2 step_ota} {
|
||||||
|
foreach {tn schema} {
|
||||||
|
1 {
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||||
|
}
|
||||||
|
2 {
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||||
|
CREATE INDEX i1 ON t1(b);
|
||||||
|
}
|
||||||
|
3 {
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||||
|
CREATE INDEX i1 ON t1(b);
|
||||||
|
CREATE INDEX i2 ON t1(c, b);
|
||||||
|
CREATE INDEX i3 ON t1(c, b, c);
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
reset_db
|
||||||
|
execsql $schema
|
||||||
|
execsql {
|
||||||
|
INSERT INTO t1 VALUES(2, 'hello', 'world');
|
||||||
|
INSERT INTO t1 VALUES(4, 'hello', 'planet');
|
||||||
|
INSERT INTO t1 VALUES(6, 'hello', 'xyz');
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test 4.$tn2.$tn.1 {
|
||||||
|
create_ota4 ota.db
|
||||||
|
$cmd test.db ota.db
|
||||||
|
} {SQLITE_DONE}
|
||||||
|
|
||||||
|
do_execsql_test 4.$tn2.$tn.2 {
|
||||||
|
SELECT * FROM t1 ORDER BY a ASC;
|
||||||
|
} {
|
||||||
|
1 2 3
|
||||||
|
3 8 9
|
||||||
|
6 hello xyz
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 4.$tn2.$tn.3 { PRAGMA integrity_check } ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@@ -77,6 +77,7 @@ struct OtaObjIter {
|
|||||||
int nCol; /* Number of columns in current object */
|
int nCol; /* Number of columns in current object */
|
||||||
sqlite3_stmt *pSelect; /* Source data */
|
sqlite3_stmt *pSelect; /* Source data */
|
||||||
sqlite3_stmt *pInsert; /* Statement for INSERT operations */
|
sqlite3_stmt *pInsert; /* Statement for INSERT operations */
|
||||||
|
sqlite3_stmt *pDelete; /* Statement for DELETE ops */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -189,6 +190,7 @@ static void otaObjIterFinalize(OtaObjIter *pIter){
|
|||||||
sqlite3_finalize(pIter->pIdxIter);
|
sqlite3_finalize(pIter->pIdxIter);
|
||||||
sqlite3_finalize(pIter->pSelect);
|
sqlite3_finalize(pIter->pSelect);
|
||||||
sqlite3_finalize(pIter->pInsert);
|
sqlite3_finalize(pIter->pInsert);
|
||||||
|
sqlite3_finalize(pIter->pDelete);
|
||||||
otaObjIterFreeCols(pIter);
|
otaObjIterFreeCols(pIter);
|
||||||
memset(pIter, 0, sizeof(OtaObjIter));
|
memset(pIter, 0, sizeof(OtaObjIter));
|
||||||
}
|
}
|
||||||
@@ -208,8 +210,10 @@ static int otaObjIterNext(sqlite3ota *p, OtaObjIter *pIter){
|
|||||||
/* Free any SQLite statements used while processing the previous object */
|
/* Free any SQLite statements used while processing the previous object */
|
||||||
sqlite3_finalize(pIter->pSelect);
|
sqlite3_finalize(pIter->pSelect);
|
||||||
sqlite3_finalize(pIter->pInsert);
|
sqlite3_finalize(pIter->pInsert);
|
||||||
|
sqlite3_finalize(pIter->pDelete);
|
||||||
pIter->pSelect = 0;
|
pIter->pSelect = 0;
|
||||||
pIter->pInsert = 0;
|
pIter->pInsert = 0;
|
||||||
|
pIter->pDelete = 0;
|
||||||
pIter->nCol = 0;
|
pIter->nCol = 0;
|
||||||
|
|
||||||
if( pIter->bCleanup ){
|
if( pIter->bCleanup ){
|
||||||
@@ -305,6 +309,32 @@ static char *otaQuoteName(const char *zName){
|
|||||||
return zRet;
|
return zRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
|
||||||
|
** arguments are the usual subsitution values. This function performs
|
||||||
|
** the printf() style substitutions and executes the result as an SQL
|
||||||
|
** statement on the OTA handles database.
|
||||||
|
**
|
||||||
|
** If an error occurs, an error code and error message is stored in the
|
||||||
|
** OTA handle. If an error has already occurred when this function is
|
||||||
|
** called, it is a no-op.
|
||||||
|
*/
|
||||||
|
static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, zFmt);
|
||||||
|
if( p->rc==SQLITE_OK ){
|
||||||
|
char *zSql = sqlite3_vmprintf(zFmt, ap);
|
||||||
|
if( zSql==0 ){
|
||||||
|
p->rc = SQLITE_NOMEM;
|
||||||
|
}else{
|
||||||
|
p->rc = sqlite3_exec(p->db, zSql, 0, 0, &p->zErrmsg);
|
||||||
|
sqlite3_free(zSql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
return p->rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** If they are not already populated, populate the pIter->azTblCol[],
|
** If they are not already populated, populate the pIter->azTblCol[],
|
||||||
** pIter->abTblPk[] and pIter->nTblCol variables according to the table
|
** pIter->abTblPk[] and pIter->nTblCol variables according to the table
|
||||||
@@ -380,6 +410,48 @@ static char *otaObjIterGetCollist(
|
|||||||
return zList;
|
return zList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *otaObjIterGetOldlist(
|
||||||
|
sqlite3ota *p,
|
||||||
|
OtaObjIter *pIter
|
||||||
|
){
|
||||||
|
char *zList = 0;
|
||||||
|
if( p->rc==SQLITE_OK ){
|
||||||
|
const char *zSep = "";
|
||||||
|
int i;
|
||||||
|
for(i=0; i<pIter->nTblCol; i++){
|
||||||
|
zList = sqlite3_mprintf("%z%sold.%s", zList, zSep, pIter->azTblCol[i]);
|
||||||
|
zSep = ", ";
|
||||||
|
if( zList==0 ){
|
||||||
|
p->rc = SQLITE_NOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zList;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *otaObjIterGetWhere(
|
||||||
|
sqlite3ota *p,
|
||||||
|
OtaObjIter *pIter
|
||||||
|
){
|
||||||
|
char *zList = 0;
|
||||||
|
if( p->rc==SQLITE_OK ){
|
||||||
|
const char *zSep = "";
|
||||||
|
int i;
|
||||||
|
for(i=0; i<pIter->nTblCol; i++){
|
||||||
|
if( pIter->abTblPk[i] ){
|
||||||
|
zList = sqlite3_mprintf("%z%s%s=?", zList, zSep, pIter->azTblCol[i]);
|
||||||
|
zSep = " AND ";
|
||||||
|
if( zList==0 ){
|
||||||
|
p->rc = SQLITE_NOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zList;
|
||||||
|
}
|
||||||
|
|
||||||
static char *otaObjIterGetBindlist(sqlite3ota *p, int nBind){
|
static char *otaObjIterGetBindlist(sqlite3ota *p, int nBind){
|
||||||
char *zRet = 0;
|
char *zRet = 0;
|
||||||
if( p->rc==SQLITE_OK ){
|
if( p->rc==SQLITE_OK ){
|
||||||
@@ -423,25 +495,37 @@ static int otaObjIterPrepareAll(
|
|||||||
if( zIdx ){
|
if( zIdx ){
|
||||||
int *aiCol; /* Column map */
|
int *aiCol; /* Column map */
|
||||||
|
|
||||||
/* Create the index writer */
|
/* Create the index writers */
|
||||||
if( p->rc==SQLITE_OK ){
|
if( p->rc==SQLITE_OK ){
|
||||||
p->rc = sqlite3_index_writer(
|
p->rc = sqlite3_index_writer(
|
||||||
p->db, 0, zIdx, &pIter->pInsert, &aiCol, &pIter->nCol
|
p->db, 0, zIdx, &pIter->pInsert, &aiCol, &pIter->nCol
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if( p->rc==SQLITE_OK ){
|
||||||
|
p->rc = sqlite3_index_writer(
|
||||||
|
p->db, 1, zIdx, &pIter->pDelete, &aiCol, &pIter->nCol
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/* Create the SELECT statement to read keys in sorted order */
|
/* Create the SELECT statement to read keys in sorted order */
|
||||||
zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol);
|
zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol);
|
||||||
if( p->rc==SQLITE_OK ){
|
if( p->rc==SQLITE_OK ){
|
||||||
p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
|
p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
|
||||||
sqlite3_mprintf(
|
sqlite3_mprintf(
|
||||||
"SELECT %s FROM ota.'data_%q' ORDER BY %s%s",
|
"SELECT %s, ota_control FROM ota.'data_%q' "
|
||||||
zCollist, pIter->zTbl, zCollist, zLimit
|
"UNION ALL "
|
||||||
|
"SELECT %s, ota_control FROM ota.'ota_tmp_%q' "
|
||||||
|
"ORDER BY %s%s",
|
||||||
|
zCollist, pIter->zTbl,
|
||||||
|
zCollist, pIter->zTbl,
|
||||||
|
zCollist, zLimit
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol);
|
char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol);
|
||||||
|
char *zWhere = otaObjIterGetWhere(p, pIter);
|
||||||
|
char *zOldlist = otaObjIterGetOldlist(p, pIter);
|
||||||
zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0);
|
zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0);
|
||||||
pIter->nCol = pIter->nTblCol;
|
pIter->nCol = pIter->nTblCol;
|
||||||
|
|
||||||
@@ -449,7 +533,7 @@ static int otaObjIterPrepareAll(
|
|||||||
if( p->rc==SQLITE_OK ){
|
if( p->rc==SQLITE_OK ){
|
||||||
p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
|
p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
|
||||||
sqlite3_mprintf(
|
sqlite3_mprintf(
|
||||||
"SELECT %s FROM ota.'data_%q'%s",
|
"SELECT %s, ota_control FROM ota.'data_%q'%s",
|
||||||
zCollist, pIter->zTbl, zLimit)
|
zCollist, pIter->zTbl, zLimit)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -463,6 +547,31 @@ static int otaObjIterPrepareAll(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create the DELETE statement to write to the target PK b-tree */
|
||||||
|
if( p->rc==SQLITE_OK ){
|
||||||
|
p->rc = prepareFreeAndCollectError(p->db, &pIter->pDelete, pz,
|
||||||
|
sqlite3_mprintf(
|
||||||
|
"DELETE FROM main.%Q WHERE %s", pIter->zTbl, zWhere
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( p->rc==SQLITE_OK ){
|
||||||
|
otaMPrintfExec(p,
|
||||||
|
"CREATE TABLE IF NOT EXISTS ota.'ota_tmp_%q' AS "
|
||||||
|
"SELECT * FROM ota.'data_%q' WHERE 0;"
|
||||||
|
"CREATE TEMP TRIGGER ota_delete_%q BEFORE DELETE ON main.%Q "
|
||||||
|
"BEGIN "
|
||||||
|
" INSERT INTO 'ota_tmp_%q'(ota_control, %s) VALUES(2, %s);"
|
||||||
|
"END;"
|
||||||
|
, pIter->zTbl, pIter->zTbl, pIter->zTbl, pIter->zTbl, pIter->zTbl,
|
||||||
|
zCollist, zOldlist
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_free(zWhere);
|
||||||
|
sqlite3_free(zOldlist);
|
||||||
sqlite3_free(zBindings);
|
sqlite3_free(zBindings);
|
||||||
}
|
}
|
||||||
sqlite3_free(zCollist);
|
sqlite3_free(zCollist);
|
||||||
@@ -472,6 +581,72 @@ static int otaObjIterPrepareAll(
|
|||||||
return p->rc;
|
return p->rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define OTA_INSERT 1
|
||||||
|
#define OTA_DELETE 2
|
||||||
|
#define OTA_IDX_DELETE 3
|
||||||
|
#define OTA_UPDATE 4
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The SELECT statement iterating through the keys for the current object
|
||||||
|
** (p->objiter.pSelect) currently points to a valid row. However, there
|
||||||
|
** is something wrong with the ota_control value in the ota_control value
|
||||||
|
** stored in the (p->nCol+1)'th column. Set the error code and error message
|
||||||
|
** of the OTA handle to something reflecting this.
|
||||||
|
*/
|
||||||
|
static void otaBadControlError(sqlite3ota *p){
|
||||||
|
p->rc = SQLITE_ERROR;
|
||||||
|
p->zErrmsg = sqlite3_mprintf("Invalid ota_control value");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The SELECT statement iterating through the keys for the current object
|
||||||
|
** (p->objiter.pSelect) currently points to a valid row. This function
|
||||||
|
** determines the type of operation requested by this row and returns
|
||||||
|
** one of the following values to indicate the result:
|
||||||
|
**
|
||||||
|
** * OTA_INSERT
|
||||||
|
** * OTA_DELETE
|
||||||
|
** * OTA_IDX_DELETE
|
||||||
|
** * OTA_UPDATE
|
||||||
|
**
|
||||||
|
** If OTA_UPDATE is returned, then output variable *pzMask is set to
|
||||||
|
** point to the text value indicating the columns to update.
|
||||||
|
**
|
||||||
|
** If the ota_control field contains an invalid value, an error code and
|
||||||
|
** message are left in the OTA handle and zero returned.
|
||||||
|
*/
|
||||||
|
static int otaStepType(sqlite3ota *p, const char **pzMask){
|
||||||
|
int iCol = p->objiter.nCol; /* Index of ota_control column */
|
||||||
|
int res = 0; /* Return value */
|
||||||
|
|
||||||
|
switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
|
||||||
|
case SQLITE_INTEGER: {
|
||||||
|
int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
|
||||||
|
if( iVal==0 ){
|
||||||
|
res = OTA_INSERT;
|
||||||
|
}else if( iVal==1 ){
|
||||||
|
res = OTA_DELETE;
|
||||||
|
}else if( iVal==2 ){
|
||||||
|
res = OTA_IDX_DELETE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SQLITE_TEXT:
|
||||||
|
*pzMask = (const char*)sqlite3_column_text(p->objiter.pSelect, iCol);
|
||||||
|
res = OTA_UPDATE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( res==0 ){
|
||||||
|
otaBadControlError(p);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** This function does the work for an sqlite3ota_step() call.
|
** This function does the work for an sqlite3ota_step() call.
|
||||||
**
|
**
|
||||||
@@ -485,15 +660,49 @@ static int otaObjIterPrepareAll(
|
|||||||
*/
|
*/
|
||||||
static int otaStep(sqlite3ota *p){
|
static int otaStep(sqlite3ota *p){
|
||||||
OtaObjIter *pIter = &p->objiter;
|
OtaObjIter *pIter = &p->objiter;
|
||||||
|
const char *zMask = 0;
|
||||||
int i;
|
int i;
|
||||||
|
int eType = otaStepType(p, &zMask);
|
||||||
|
|
||||||
|
if( eType ){
|
||||||
|
assert( eType!=OTA_UPDATE || pIter->zIdx==0 );
|
||||||
|
|
||||||
|
if( pIter->zIdx==0 && eType==OTA_IDX_DELETE ){
|
||||||
|
otaBadControlError(p);
|
||||||
|
}
|
||||||
|
else if( eType==OTA_INSERT || eType==OTA_IDX_DELETE ){
|
||||||
|
sqlite3_stmt *pWriter;
|
||||||
|
assert( eType!=OTA_UPDATE );
|
||||||
|
|
||||||
|
pWriter = (eType==OTA_INSERT)?pIter->pInsert:pIter->pDelete;
|
||||||
for(i=0; i<pIter->nCol; i++){
|
for(i=0; i<pIter->nCol; i++){
|
||||||
sqlite3_value *pVal = sqlite3_column_value(pIter->pSelect, i);
|
sqlite3_value *pVal = sqlite3_column_value(pIter->pSelect, i);
|
||||||
sqlite3_bind_value(pIter->pInsert, i+1, pVal);
|
sqlite3_bind_value(pWriter, i+1, pVal);
|
||||||
|
}
|
||||||
|
sqlite3_step(pWriter);
|
||||||
|
p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
|
||||||
|
}
|
||||||
|
else if( eType==OTA_DELETE && pIter->zIdx==0 ){
|
||||||
|
int iVar = 1;
|
||||||
|
assert( pIter->zIdx==0 );
|
||||||
|
assert( pIter->nCol==pIter->nTblCol );
|
||||||
|
for(i=0; i<pIter->nCol; i++){
|
||||||
|
if( pIter->abTblPk[i] ){
|
||||||
|
sqlite3_value *pVal = sqlite3_column_value(pIter->pSelect, i);
|
||||||
|
sqlite3_bind_value(pIter->pDelete, iVar++, pVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_step(pIter->pDelete);
|
||||||
|
p->rc = resetAndCollectError(pIter->pDelete, &p->zErrmsg);
|
||||||
|
}else if( eType==OTA_UPDATE ){
|
||||||
|
p->rc = SQLITE_ERROR;
|
||||||
|
p->zErrmsg = sqlite3_mprintf("not yet");
|
||||||
|
}else{
|
||||||
|
/* no-op */
|
||||||
|
assert( eType==OTA_DELETE && pIter->zIdx );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_step(pIter->pInsert);
|
|
||||||
p->rc = resetAndCollectError(pIter->pInsert, &p->zErrmsg);
|
|
||||||
return p->rc;
|
return p->rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +715,10 @@ int sqlite3ota_step(sqlite3ota *p){
|
|||||||
while( p && p->rc==SQLITE_OK && pIter->zTbl ){
|
while( p && p->rc==SQLITE_OK && pIter->zTbl ){
|
||||||
|
|
||||||
if( pIter->bCleanup ){
|
if( pIter->bCleanup ){
|
||||||
/* this is where cleanup of the ota_xxx table will happen... */
|
/* Clean up the ota_tmp_xxx table for the previous table. It
|
||||||
|
** cannot be dropped as there are currently active SQL statements.
|
||||||
|
** But the contents can be deleted. */
|
||||||
|
otaMPrintfExec(p, "DELETE FROM ota.'ota_tmp_%q'", pIter->zTbl);
|
||||||
}else{
|
}else{
|
||||||
otaObjIterPrepareAll(p, pIter, 0);
|
otaObjIterPrepareAll(p, pIter, 0);
|
||||||
|
|
||||||
@@ -532,32 +744,6 @@ int sqlite3ota_step(sqlite3ota *p){
|
|||||||
return p->rc;
|
return p->rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
|
|
||||||
** arguments are the usual subsitution values. This function performs
|
|
||||||
** the printf() style substitutions and executes the result as an SQL
|
|
||||||
** statement on the OTA handles database.
|
|
||||||
**
|
|
||||||
** If an error occurs, an error code and error message is stored in the
|
|
||||||
** OTA handle. If an error has already occurred when this function is
|
|
||||||
** called, it is a no-op.
|
|
||||||
*/
|
|
||||||
static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, zFmt);
|
|
||||||
if( p->rc==SQLITE_OK ){
|
|
||||||
char *zSql = sqlite3_vmprintf(zFmt, ap);
|
|
||||||
if( zSql==0 ){
|
|
||||||
p->rc = SQLITE_NOMEM;
|
|
||||||
}else{
|
|
||||||
p->rc = sqlite3_exec(p->db, zSql, 0, 0, &p->zErrmsg);
|
|
||||||
sqlite3_free(zSql);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
va_end(ap);
|
|
||||||
return p->rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void otaSaveTransactionState(sqlite3ota *p){
|
static void otaSaveTransactionState(sqlite3ota *p){
|
||||||
otaMPrintfExec(p,
|
otaMPrintfExec(p,
|
||||||
"INSERT OR REPLACE INTO ota.ota_state(rowid, tbl, idx, row, progress)"
|
"INSERT OR REPLACE INTO ota.ota_state(rowid, tbl, idx, row, progress)"
|
||||||
|
18
manifest
18
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Switch\sback\sto\susing\sa\ssingle\sdatabase\sconnection\sin\ssqlite3ota.c.
|
C Add\ssupport\sfor\sdelete\soperations\sto\sthe\sota\sextension.
|
||||||
D 2014-09-05T19:52:42.359
|
D 2014-09-06T20:19:38.006
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@@ -122,9 +122,9 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
|||||||
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
||||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||||
F ext/ota/ota.c d37097e92a005d3915883adefbb93019ea6f8841
|
F ext/ota/ota.c d37097e92a005d3915883adefbb93019ea6f8841
|
||||||
F ext/ota/ota1.test 0bbdffa5cb4c4bc26be5dae55c834830c7e8e5e3
|
F ext/ota/ota1.test 47317179125b5e65289a9f59753c9f895186e6d5
|
||||||
F ext/ota/ota2.test 13f76922446c62ed96192e938b8e625ebf0142fa
|
F ext/ota/ota2.test 13f76922446c62ed96192e938b8e625ebf0142fa
|
||||||
F ext/ota/sqlite3ota.c 3ddf5f8122f9ab3270541f61bde5d95ef7b631d5
|
F ext/ota/sqlite3ota.c ceb0f77dc6a958d299f532319f6477e5599dc59d
|
||||||
F ext/ota/sqlite3ota.h 545f0008b5f02f2595899cb9841caddada5c17c0
|
F ext/ota/sqlite3ota.h 545f0008b5f02f2595899cb9841caddada5c17c0
|
||||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||||
F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
|
F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
|
||||||
@@ -181,7 +181,7 @@ F src/callback.c b97d0695ffcf6a8710ee445ffe56ee387d4d8a6f
|
|||||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||||
F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a
|
F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a
|
||||||
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
|
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
|
||||||
F src/delete.c 5adcd322c6b08fc25d215d780ca62cebce66304d
|
F src/delete.c 3c2a375c0329247d01222170ae19ad8a52ecbf9a
|
||||||
F src/expr.c e1691ab0fe6be7247ef073b0038fb8ecd9944fad
|
F src/expr.c e1691ab0fe6be7247ef073b0038fb8ecd9944fad
|
||||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||||
F src/fkey.c 8d81a780ad78d16ec9082585758a8f1d6bf02ca3
|
F src/fkey.c 8d81a780ad78d16ec9082585758a8f1d6bf02ca3
|
||||||
@@ -295,7 +295,7 @@ F src/vdbe.h c63fad052c9e7388d551e556e119c0bcf6bebdf8
|
|||||||
F src/vdbeInt.h cdc8e421f85beb1ac9b4669ec5beadab6faa15e0
|
F src/vdbeInt.h cdc8e421f85beb1ac9b4669ec5beadab6faa15e0
|
||||||
F src/vdbeapi.c 09677a53dd8c71bcd670b0bd073bb9aefa02b441
|
F src/vdbeapi.c 09677a53dd8c71bcd670b0bd073bb9aefa02b441
|
||||||
F src/vdbeaux.c cef5d34a64ae3a65b56d96d3fd663246ec8e1c36
|
F src/vdbeaux.c cef5d34a64ae3a65b56d96d3fd663246ec8e1c36
|
||||||
F src/vdbeblob.c 0bc9d22578d87ad9ff1c16e20a36863326f34fd7
|
F src/vdbeblob.c b1b8b2cd86617db009f027f116b335f86e95b617
|
||||||
F src/vdbemem.c 921d5468a68ac06f369810992e84ca22cc730a62
|
F src/vdbemem.c 921d5468a68ac06f369810992e84ca22cc730a62
|
||||||
F src/vdbesort.c 7c45bfcd823f30d172bbbc1b9f51ef4402fbfe8d
|
F src/vdbesort.c 7c45bfcd823f30d172bbbc1b9f51ef4402fbfe8d
|
||||||
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
|
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
|
||||||
@@ -1198,7 +1198,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||||
P 98387f05697526c7740e91d8a846a31f77639406
|
P 3c2f4a078132992e33cc675173c84f8385af9cb5
|
||||||
R cd4982d2f447b7a01026d09e8721c98f
|
R eeedfe1c92df64758f3e97de2d55d43b
|
||||||
U dan
|
U dan
|
||||||
Z 303c253ac0315bace60d825e70dc4fdd
|
Z 3317be7135040205f21e9375db1283ea
|
||||||
|
@@ -1 +1 @@
|
|||||||
3c2f4a078132992e33cc675173c84f8385af9cb5
|
f988234ba54d7c667f7deef1d04beed4e7fe6182
|
@@ -730,6 +730,9 @@ void sqlite3GenerateRowIndexDelete(
|
|||||||
Vdbe *v; /* The prepared statement under construction */
|
Vdbe *v; /* The prepared statement under construction */
|
||||||
Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */
|
Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */
|
||||||
|
|
||||||
|
/* Skip this if we are in OTA mode */
|
||||||
|
if( pParse->db->flags & SQLITE_OtaMode ) return;
|
||||||
|
|
||||||
v = pParse->pVdbe;
|
v = pParse->pVdbe;
|
||||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||||
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||||
|
@@ -546,6 +546,8 @@ int sqlite3_index_writer(
|
|||||||
sqlite3VdbeAddOp2(v, OP_Variable, i, i);
|
sqlite3VdbeAddOp2(v, OP_Variable, i, i);
|
||||||
}
|
}
|
||||||
regRec = ++pParse->nMem;
|
regRec = ++pParse->nMem;
|
||||||
|
|
||||||
|
if( bDelete==0 ){
|
||||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, 1, pIdx->nColumn, regRec);
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, 1, pIdx->nColumn, regRec);
|
||||||
|
|
||||||
/* If this is a UNIQUE index, check the constraint. */
|
/* If this is a UNIQUE index, check the constraint. */
|
||||||
@@ -557,6 +559,10 @@ int sqlite3_index_writer(
|
|||||||
|
|
||||||
/* Code the IdxInsert to write to the b-tree index. */
|
/* Code the IdxInsert to write to the b-tree index. */
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, 0, regRec);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, 0, regRec);
|
||||||
|
}else{
|
||||||
|
/* Code the IdxDelete to remove the entry from the b-tree index. */
|
||||||
|
sqlite3VdbeAddOp3(v, OP_IdxDelete, 0, 1, pIdx->nColumn);
|
||||||
|
}
|
||||||
sqlite3FinishCoding(pParse);
|
sqlite3FinishCoding(pParse);
|
||||||
|
|
||||||
index_writer_out:
|
index_writer_out:
|
||||||
|
Reference in New Issue
Block a user