Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Update the ota extension so that it can be used to update tables with external PRIMARY KEY indexes. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | ota-update |
Files: | files | file ages | folders |
SHA1: |
55066a1171cbd3077f5e6c8ceb2745e8 |
User & Date: | dan 2014-11-20 17:37:08.997 |
Context
2014-11-20
| ||
19:19 | Add the "ota_delta()" feature for delta-compressed updates. (check-in: c64dcd1788 user: dan tags: ota-update) | |
17:37 | Update the ota extension so that it can be used to update tables with external PRIMARY KEY indexes. (check-in: 55066a1171 user: dan tags: ota-update) | |
15:11 | Updates to support zipvfs in pass-through mode. (check-in: 556c3de53a user: dan tags: ota-update) | |
Changes
Changes to ext/ota/ota1.test.
︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 144 145 | } 7 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); CREATE INDEX i1 ON t1(b, c); CREATE INDEX i2 ON t1(c, b); CREATE INDEX i3 ON t1(a, b, c, a, b, c); } } { reset_db execsql $schema do_test 1.$tn2.$tn.1 { create_ota1 ota.db $cmd test.db ota.db | > > > > > > > > > > > > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | } 7 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); CREATE INDEX i1 ON t1(b, c); CREATE INDEX i2 ON t1(c, b); CREATE INDEX i3 ON t1(a, b, c, a, b, c); } 8 { CREATE TABLE t1(a PRIMARY KEY, b, c); CREATE INDEX i1 ON t1(b, c); CREATE INDEX i2 ON t1(c, b); CREATE INDEX i3 ON t1(a, b, c, a, b, c); } 9 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c)); CREATE INDEX i1 ON t1(b); } } { reset_db execsql $schema do_test 1.$tn2.$tn.1 { create_ota1 ota.db $cmd test.db ota.db |
︙ | ︙ | |||
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | do_execsql_test 1.$tn2.$tn.3 { PRAGMA integrity_check } ok } } #------------------------------------------------------------------------- # Check that an OTA cannot be applied to a table that has no PK. # reset_db create_ota1 ota.db do_execsql_test 2.1 { CREATE TABLE t1(a, b, c) } do_test 2.2 { sqlite3ota ota test.db ota.db ota step } {SQLITE_ERROR} do_test 2.3 { list [catch { ota close } msg] $msg } {1 {SQLITE_ERROR - table t1 has no PRIMARY KEY}} | > > > > > < | | > > > > > > > > > > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | do_execsql_test 1.$tn2.$tn.3 { PRAGMA integrity_check } ok } } #------------------------------------------------------------------------- # Check that an OTA cannot be applied to a table that has no PK. # # UPDATE: At one point OTA required that all tables featured either # explicit IPK columns or were declared WITHOUT ROWID. This has been # relaxed so that external PRIMARY KEYs on tables with automatic rowids # are now allowed. # reset_db create_ota1 ota.db do_execsql_test 2.1 { CREATE TABLE t1(a, b, c) } do_test 2.2 { sqlite3ota ota test.db ota.db ota step } {SQLITE_ERROR} do_test 2.3 { list [catch { ota close } msg] $msg } {1 {SQLITE_ERROR - table t1 has no PRIMARY KEY}} reset_db do_execsql_test 2.4 { CREATE TABLE t1(a PRIMARY KEY, b, c) } do_test 2.5 { sqlite3ota ota test.db ota.db ota step } {SQLITE_OK} do_test 2.6 { list [catch { ota close } msg] $msg } {0 SQLITE_OK} #------------------------------------------------------------------------- # Check that if a UNIQUE constraint is violated the current and all # subsequent [ota step] calls return SQLITE_CONSTRAINT. And that the OTA # transaction is rolled back by the [ota close] that deletes the ota # handle. # foreach {tn errcode errmsg schema} { 1 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); INSERT INTO t1 VALUES(3, 2, 1); } 2 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE); INSERT INTO t1 VALUES(4, 2, 'three'); } 3 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" { CREATE TABLE t1(a PRIMARY KEY, b, c); INSERT INTO t1 VALUES(3, 2, 1); } 4 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" { CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE); INSERT INTO t1 VALUES(4, 2, 'three'); } } { reset_db execsql $schema set cksum [dbcksum db main] do_test 3.$tn.1 { |
︙ | ︙ | |||
235 236 237 238 239 240 241 242 243 244 245 246 247 248 | CREATE INDEX i1 ON t1(b); CREATE INDEX i2 ON t1(c, b); CREATE INDEX i3 ON t1(c, b, c); } 4 { CREATE TABLE t1(a INT PRIMARY KEY, b, c) WITHOUT ROWID; 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 { | > > > > > > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | CREATE INDEX i1 ON t1(b); CREATE INDEX i2 ON t1(c, b); CREATE INDEX i3 ON t1(c, b, c); } 4 { CREATE TABLE t1(a INT PRIMARY KEY, b, c) WITHOUT ROWID; CREATE INDEX i1 ON t1(b); CREATE INDEX i2 ON t1(c, b); CREATE INDEX i3 ON t1(c, b, c); } 5 { CREATE TABLE t1(a INT 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 { |
︙ | ︙ | |||
283 284 285 286 287 288 289 290 291 292 293 294 295 296 | CREATE INDEX i4 ON t1(b); CREATE INDEX i5 ON t1(c); CREATE INDEX i6 ON t1(c, b); } 3 { CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID; CREATE INDEX i1 ON t1(d); CREATE INDEX i2 ON t1(d, c); CREATE INDEX i3 ON t1(d, c, b); CREATE INDEX i4 ON t1(b); CREATE INDEX i5 ON t1(c); CREATE INDEX i6 ON t1(c, b); } } { | > > > > > > > > > | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | CREATE INDEX i4 ON t1(b); CREATE INDEX i5 ON t1(c); CREATE INDEX i6 ON t1(c, b); } 3 { CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID; CREATE INDEX i1 ON t1(d); CREATE INDEX i2 ON t1(d, c); CREATE INDEX i3 ON t1(d, c, b); CREATE INDEX i4 ON t1(b); CREATE INDEX i5 ON t1(c); CREATE INDEX i6 ON t1(c, b); } 4 { CREATE TABLE t1(a PRIMARY KEY, b, c, d); CREATE INDEX i1 ON t1(d); CREATE INDEX i2 ON t1(d, c); CREATE INDEX i3 ON t1(d, c, b); CREATE INDEX i4 ON t1(b); CREATE INDEX i5 ON t1(c); CREATE INDEX i6 ON t1(c, b); } } { |
︙ | ︙ |
Changes to ext/ota/ota7.test.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | SELECT * FROM t1 } {2 def} #------------------------------------------------------------------------- # foreach {tn tbl} { 1 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)) WITHOUT ROWID } } { reset_db execsql $tbl do_execsql_test 2.$tn.1 { CREATE INDEX t1c ON t1(c); INSERT INTO t1 VALUES(1, 1, 'a'); | > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | SELECT * FROM t1 } {2 def} #------------------------------------------------------------------------- # foreach {tn tbl} { 1 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)) WITHOUT ROWID } 2 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)) } } { reset_db execsql $tbl do_execsql_test 2.$tn.1 { CREATE INDEX t1c ON t1(c); INSERT INTO t1 VALUES(1, 1, 'a'); |
︙ | ︙ |
Changes to ext/ota/sqlite3ota.c.
︙ | ︙ | |||
98 99 100 101 102 103 104 105 106 107 108 109 110 111 | */ struct OtaObjIter { sqlite3_stmt *pTblIter; /* Iterate through tables */ sqlite3_stmt *pIdxIter; /* Index iterator */ int nTblCol; /* Size of azTblCol[] array */ char **azTblCol; /* Array of quoted column names */ unsigned char *abTblPk; /* Array of flags - true for PK columns */ /* Output variables. zTbl==0 implies EOF. */ int bCleanup; /* True in "cleanup" state */ const char *zTbl; /* Name of target db table */ const char *zIdx; /* Name of target db index (or null) */ int iVisit; /* Number of points visited, incl. current */ | > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | */ struct OtaObjIter { sqlite3_stmt *pTblIter; /* Iterate through tables */ sqlite3_stmt *pIdxIter; /* Index iterator */ int nTblCol; /* Size of azTblCol[] array */ char **azTblCol; /* Array of quoted column names */ unsigned char *abTblPk; /* Array of flags - true for PK columns */ unsigned char bRowid; /* True for implicit IPK tables */ /* Output variables. zTbl==0 implies EOF. */ int bCleanup; /* True in "cleanup" state */ const char *zTbl; /* Name of target db table */ const char *zIdx; /* Name of target db index (or null) */ int iVisit; /* Number of points visited, incl. current */ |
︙ | ︙ | |||
221 222 223 224 225 226 227 228 229 230 231 232 233 234 | sqlite3_free(pIter->azTblCol); sqlite3_free(pIter->abTblPk); pIter->azTblCol = 0; pIter->abTblPk = 0; pIter->nTblCol = 0; sqlite3_free(pIter->zMask); pIter->zMask = 0; } /* ** Finalize all statements and free all allocations that are specific to ** the current object (table/index pair). */ static void otaObjIterClearStatements(OtaObjIter *pIter){ | > | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | sqlite3_free(pIter->azTblCol); sqlite3_free(pIter->abTblPk); pIter->azTblCol = 0; pIter->abTblPk = 0; pIter->nTblCol = 0; sqlite3_free(pIter->zMask); pIter->zMask = 0; pIter->bRowid = 0; } /* ** Finalize all statements and free all allocations that are specific to ** the current object (table/index pair). */ static void otaObjIterClearStatements(OtaObjIter *pIter){ |
︙ | ︙ | |||
387 388 389 390 391 392 393 | } va_end(ap); return p->rc; } /* ** If they are not already populated, populate the pIter->azTblCol[], | | | > | | > > | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | } va_end(ap); return p->rc; } /* ** If they are not already populated, populate the pIter->azTblCol[], ** pIter->abTblPk[], pIter->nTblCol and pIter->bRowid variables according to ** the table that the iterator currently points to. ** ** Return SQLITE_OK if successful, or an SQLite error code otherwise. If ** an error does occur, an error code and error message are also left in ** the OTA handle. */ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){ if( pIter->azTblCol==0 ){ sqlite3_stmt *pStmt; char *zSql; int nCol = 0; int bSeenPk = 0; int rc2; /* sqlite3_finalize() return value */ assert( pIter->bRowid==0 ); zSql = sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTbl); p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg, zSql); while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ if( (nCol % 8)==0 ){ unsigned char *abNew; int nByte = sizeof(char*) * (nCol+8); char **azNew = (char**)sqlite3_realloc(pIter->azTblCol, nByte); abNew = (unsigned char*)sqlite3_realloc(pIter->abTblPk, nCol+8); if( azNew ) pIter->azTblCol = azNew; if( abNew ) pIter->abTblPk = abNew; if( azNew==0 || abNew==0 ) p->rc = SQLITE_NOMEM; } if( p->rc==SQLITE_OK ){ const char *zName = (const char*)sqlite3_column_text(pStmt, 1); int iPk = sqlite3_column_int(pStmt, 5); pIter->abTblPk[nCol] = (iPk!=0); if( iPk ) bSeenPk = 1; if( iPk<0 ) pIter->bRowid = 1; pIter->azTblCol[nCol] = otaQuoteName(zName); if( pIter->azTblCol[nCol]==0 ) p->rc = SQLITE_NOMEM; nCol++; } } pIter->nTblCol = nCol; rc2 = sqlite3_finalize(pStmt); |
︙ | ︙ | |||
485 486 487 488 489 490 491 | ){ char *zList = 0; if( p->rc==SQLITE_OK ){ const char *zSep = ""; int i; for(i=0; i<nCol; i++){ int iCol = aiCol ? aiCol[i] : i; | > | | 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | ){ char *zList = 0; if( p->rc==SQLITE_OK ){ const char *zSep = ""; int i; for(i=0; i<nCol; i++){ int iCol = aiCol ? aiCol[i] : i; char *zCol = (iCol>=0 ? pIter->azTblCol[iCol] : "ota_rowid"); zList = sqlite3_mprintf("%z%s%s", zList, zSep, zCol); if( zList && azCollate ){ zList = sqlite3_mprintf("%z COLLATE %Q", zList, azCollate[i]); } zSep = ", "; if( zList==0 ){ p->rc = SQLITE_NOMEM; break; |
︙ | ︙ | |||
516 517 518 519 520 521 522 523 524 525 526 527 528 529 | zList = sqlite3_mprintf("%z%s%s.%s", zList, zS, zObj, pIter->azTblCol[i]); zS = ", "; if( zList==0 ){ p->rc = SQLITE_NOMEM; break; } } } return zList; } static char *otaObjIterGetWhere( sqlite3ota *p, OtaObjIter *pIter | > > > > > | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | zList = sqlite3_mprintf("%z%s%s.%s", zList, zS, zObj, pIter->azTblCol[i]); zS = ", "; if( zList==0 ){ p->rc = SQLITE_NOMEM; break; } } /* For a table with implicit rowids, append "old._rowid_" to the list. */ if( pIter->bRowid ){ zList = sqlite3_mprintf("%z, %s._rowid_", zList, zObj); } } return zList; } static char *otaObjIterGetWhere( sqlite3ota *p, OtaObjIter *pIter |
︙ | ︙ | |||
644 645 646 647 648 649 650 | p->db, 1, zIdx, &pIter->pDelete, &azColl, &aiCol, &pIter->nCol ); } /* Create the SELECT statement to read keys in sorted order */ zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol, azColl); if( p->rc==SQLITE_OK ){ | > | | > > > > > > | < | | > > > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 | p->db, 1, zIdx, &pIter->pDelete, &azColl, &aiCol, &pIter->nCol ); } /* Create the SELECT statement to read keys in sorted order */ zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol, azColl); if( p->rc==SQLITE_OK ){ char *zSql; if( pIter->bRowid ){ zSql = sqlite3_mprintf( "SELECT %s, ota_control FROM ota.'ota_tmp_%q' ORDER BY %s%s", zCollist, pIter->zTbl, zCollist, zLimit ); }else{ zSql = sqlite3_mprintf( "SELECT %s, ota_control FROM ota.'data_%q' " "WHERE typeof(ota_control)='integer' AND ota_control!=1 " "UNION ALL " "SELECT %s, ota_control FROM ota.'ota_tmp_%q' " "ORDER BY %s%s", zCollist, pIter->zTbl, zCollist, pIter->zTbl, zCollist, zLimit ); } p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz, zSql); } }else{ const char *zOtaRowid = (pIter->bRowid ? ", ota_rowid" : ""); char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol); char *zWhere = otaObjIterGetWhere(p, pIter); char *zOldlist = otaObjIterGetOldlist(p, pIter, "old"); char *zNewlist = otaObjIterGetOldlist(p, pIter, "new"); zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0, 0); pIter->nCol = pIter->nTblCol; |
︙ | ︙ | |||
693 694 695 696 697 698 699 | p->rc = prepareFreeAndCollectError(p->db, &pIter->pDelete, pz, sqlite3_mprintf( "DELETE FROM main.%Q WHERE %s", pIter->zTbl, zWhere ) ); } | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > | > > | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | p->rc = prepareFreeAndCollectError(p->db, &pIter->pDelete, pz, sqlite3_mprintf( "DELETE FROM main.%Q WHERE %s", pIter->zTbl, zWhere ) ); } /* Create the ota_tmp_xxx table and the triggers to populate it. */ otaMPrintfExec(p, "CREATE TABLE IF NOT EXISTS ota.'ota_tmp_%q' AS " "SELECT *%s 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%s) VALUES(2, %s);" "END;" "CREATE TEMP TRIGGER ota_update1_%q BEFORE UPDATE ON main.%Q " "BEGIN " " INSERT INTO 'ota_tmp_%q'(ota_control, %s%s) VALUES(2, %s);" "END;" "CREATE TEMP TRIGGER ota_update2_%q AFTER UPDATE ON main.%Q " "BEGIN " " INSERT INTO 'ota_tmp_%q'(ota_control, %s%s) VALUES(3, %s);" "END;" , pIter->zTbl, (pIter->bRowid ? ", 0 AS ota_rowid" : ""), pIter->zTbl, pIter->zTbl, pIter->zTbl, pIter->zTbl, zCollist, zOtaRowid, zOldlist, pIter->zTbl, pIter->zTbl, pIter->zTbl, zCollist, zOtaRowid, zOldlist, pIter->zTbl, pIter->zTbl, pIter->zTbl, zCollist, zOtaRowid, zNewlist ); if( pIter->bRowid ){ otaMPrintfExec(p, "CREATE TEMP TRIGGER ota_insert_%q AFTER INSERT ON main.%Q " "BEGIN " " INSERT INTO 'ota_tmp_%q'(ota_control, %s, ota_rowid)" " VALUES(0, %s);" "END;" , pIter->zTbl, pIter->zTbl, pIter->zTbl, zCollist, zNewlist ); } /* Allocate space required for the zMask field. */ if( p->rc==SQLITE_OK ){ int nMask = pIter->nTblCol+1; pIter->zMask = (char*)sqlite3_malloc(nMask); if( pIter->zMask==0 ){ p->rc = SQLITE_NOMEM; |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 1529 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); } if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ k = 0; }else if( pPk==0 ){ k = 1; }else{ if( (db->flags & SQLITE_OtaMode) && HasRowid(pTab) ){ | > | < < | 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); } if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ k = 0; }else if( pPk==0 ){ k = 1; }else{ for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} if( (db->flags & SQLITE_OtaMode) && HasRowid(pTab) ){ k = -1 * k; } } sqlite3VdbeAddOp2(v, OP_Integer, k, 6); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); } } } |
︙ | ︙ |