Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add an experimental extension for applying bulk updates to databases. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | ota-update |
Files: | files | file ages | folders |
SHA1: |
2954ab501049968430011b63d046eb42 |
User & Date: | dan 2014-09-02 19:59:40.729 |
Context
2014-09-03
| ||
08:25 | Add a command line program that uses the extension. This serves as example code and is also useful for performance testing. (check-in: ffa1524ef2 user: dan tags: ota-update) | |
2014-09-02
| ||
19:59 | Add an experimental extension for applying bulk updates to databases. (check-in: 2954ab5010 user: dan tags: ota-update) | |
2014-09-01
| ||
23:06 | Update comments in the ANALYZE command that describe how the Stat4Accum objecct is passed around within the VDBE. No changes to functional code. (check-in: 9779c7a9eb user: drh tags: trunk) | |
Changes
Added ext/ota/ota1.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | # 2014 August 30 # # 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. # #*********************************************************************** # set testdir [file join [file dirname $argv0] .. .. test] source $testdir/tester.tcl set ::testprefix ota1 # Create a simple OTA database. That expects to write to a table: # # CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); # proc create_ota1 {filename} { forcedelete $filename sqlite3 ota1 $filename ota1 eval { CREATE TABLE data_t1(a, b, c, ota_control); INSERT INTO data_t1 VALUES(1, 2, 3, 0); INSERT INTO data_t1 VALUES(2, 'two', 'three', 0); INSERT INTO data_t1 VALUES(3, NULL, 8.2, 0); } ota1 close return $filename } # Create an empty target database suitable for the OTA created by # [create_ota1]. # # CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); # proc create_target1 {filename} { forcedelete $filename sqlite3 target1 $filename target1 eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) } target1 close return $filename } # Run the OTA in file $ota on target database $target until completion. # proc run_ota {target ota} { sqlite3ota ota $target $ota while { [ota step]=="SQLITE_OK" } {} ota close } proc step_ota {target ota} { while 1 { sqlite3ota ota $target $ota set rc [ota step] ota close if {$rc != "SQLITE_OK"} break } set rc } foreach {tn2 cmd} {1 step_ota 2 run_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 PRIMARY KEY, b, c) WITHOUT ROWID; } 4 { CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID; CREATE INDEX i1 ON t1(b); } 5 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c)) WITHOUT ROWID; CREATE INDEX i1 ON t1(b); } 6 { CREATE TABLE t1(a, b, c, PRIMARY KEY(c)) WITHOUT ROWID; CREATE INDEX i1 ON t1(b, a); } 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 } {SQLITE_DONE} do_execsql_test 1.$tn2.$tn.2 { SELECT * FROM t1 ORDER BY a ASC; } { 1 2 3 2 two three 3 {} 8.2 } 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}} 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_ERROR} do_test 2.6 { list [catch { ota close } msg] $msg } {1 {SQLITE_ERROR - table t1 has no PRIMARY KEY}} #------------------------------------------------------------------------- # 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'); } } { reset_db execsql $schema set cksum [dbcksum db main] do_test 3.$tn.1 { create_ota1 ota.db sqlite3ota ota test.db ota.db while {[set res [ota step]]=="SQLITE_OK"} {} set res } $errcode do_test 3.$tn.2 { ota step } $errcode do_test 3.$tn.3 { list [catch { ota close } msg] $msg } [list 1 "$errcode - $errmsg"] do_test 3.$tn.4 { dbcksum db main } $cksum } finish_test |
Added ext/ota/ota2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # 2014 August 30 # # 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. # #*********************************************************************** # set testdir [file join [file dirname $argv0] .. .. test] source $testdir/tester.tcl set ::testprefix ota2 do_execsql_test 1.0 { PRAGMA ota_mode = 1; PRAGMA journal_mode = wal; CREATE TABLE t1(a, b); BEGIN; INSERT INTO t1 VALUES(1, 2); } {wal} do_test 1.1 { set state [sqlite3_transaction_save db] db close file exists test.db-wal } {1} do_test 1.2 { sqlite3 db test.db db eval {SELECT * FROM t1} } {} do_test 1.3 { execsql {BEGIN IMMEDIATE} sqlite3_transaction_restore db $::state db eval {SELECT * FROM t1} } {1 2} do_test 1.4 { execsql { INSERT INTO t1 VALUES(3, 4); COMMIT; SELECT * FROM t1; } } {1 2 3 4} do_test 1.5 { db close file exists test.db-wal } {0} do_test 1.5 { sqlite3 db test.db db eval {SELECT * FROM t1} } {1 2 3 4} finish_test |
Added ext/ota/sqlite3ota.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 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 158 159 160 161 162 163 164 165 166 167 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 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 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 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 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 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 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 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 | /* ** 2014 August 30 ** ** 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. ** ************************************************************************* */ #include <assert.h> #include <string.h> #include "sqlite3.h" #include "sqlite3ota.h" /* ** The ota_state table is used to save the state of a partially applied ** update so that it can be resumed later. The table contains at most a ** single row: ** ** "wal_state" -> Blob to use with sqlite3_transaction_restore(). ** ** "tbl" -> Table currently being written (target database names). ** ** "idx" -> Index currently being written (target database names). ** Or, if the main table is being written, a NULL value. ** ** "row" -> Last rowid processed from ota database table (i.e. data_%). ** ** "progress" -> total number of key/value b-tree operations performed ** so far as part of this ota update. */ #define OTA_CREATE_STATE "CREATE TABLE IF NOT EXISTS ota_state" \ "(wal_state, tbl, idx, row, progress)" typedef struct OtaTblIter OtaTblIter; typedef struct OtaIdxIter OtaIdxIter; /* ** Iterator used to iterate through all data tables in the OTA. As follows: ** ** OtaTblIter iter; ** for(rc=tblIterFirst(db, &iter); ** rc==SQLITE_OK && iter.zTarget; ** rc=tblIterNext(&iter) ** ){ ** } */ struct OtaTblIter { sqlite3_stmt *pTblIter; /* Iterate through tables */ int iEntry; /* Index of current entry (from 1) */ /* Output varibles. zTarget==0 implies EOF. */ const char *zTarget; /* Name of target table */ const char *zSource; /* Name of source table */ /* Useful things populated by a call to tblIterPrepareAll() */ int nCol; /* Number of columns in this table */ char **azCol; /* Array of quoted column names */ sqlite3_stmt *pSelect; /* PK b-tree SELECT statement */ sqlite3_stmt *pInsert; /* PK b-tree INSERT statement */ }; /* ** API is: ** ** idxIterFirst() ** idxIterNext() ** idxIterFinalize() ** idxIterPrepareAll() */ struct OtaIdxIter { sqlite3_stmt *pIdxIter; /* Iterate through indexes */ int iEntry; /* Index of current entry (from 1) */ /* Output varibles. zTarget==0 implies EOF. */ const char *zIndex; /* Name of index */ int nCol; /* Number of columns in index */ int *aiCol; /* Array of column indexes */ sqlite3_stmt *pWriter; /* Index writer */ sqlite3_stmt *pSelect; /* Select to read values in index order */ }; struct sqlite3ota { sqlite3 *dbDest; /* Target db */ sqlite3 *dbOta; /* Ota db */ int rc; /* Value returned by last ota_step() call */ char *zErrmsg; /* Error message if rc!=SQLITE_OK */ OtaTblIter tbliter; /* Used to iterate through tables */ OtaIdxIter idxiter; /* Used to iterate through indexes */ }; static int prepareAndCollectError( sqlite3 *db, const char *zSql, sqlite3_stmt **ppStmt, char **pzErrmsg ){ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); if( rc!=SQLITE_OK ){ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db)); *ppStmt = 0; } return rc; } /* ** Unless it is NULL, argument zSql points to a buffer allocated using ** sqlite3_malloc containing an SQL statement. This function prepares the SQL ** statement against database db and frees the buffer. If statement ** compilation is successful, *ppStmt is set to point to the new statement ** handle and SQLITE_OK is returned. ** ** Otherwise, if an error occurs, *ppStmt is set to NULL and an error code ** returned. In this case, *pzErrmsg may also be set to point to an error ** message. It is the responsibility of the caller to free this error message ** buffer using sqlite3_free(). ** ** If argument zSql is NULL, this function assumes that an OOM has occurred. ** In this case SQLITE_NOMEM is returned and *ppStmt set to NULL. */ static int prepareFreeAndCollectError( sqlite3 *db, char *zSql, sqlite3_stmt **ppStmt, char **pzErrmsg ){ int rc; assert( *pzErrmsg==0 ); if( zSql==0 ){ rc = SQLITE_NOMEM; *ppStmt = 0; }else{ rc = prepareAndCollectError(db, zSql, ppStmt, pzErrmsg); sqlite3_free(zSql); } return rc; } static char *quoteSqlName(const char *zName){ int nName = strlen(zName); char *zRet = sqlite3_malloc(nName * 2 + 2 + 1); if( zRet ){ int i; char *p = zRet; *p++ = '"'; for(i=0; i<nName; i++){ if( zName[i]=='"' ) *p++ = '"'; *p++ = zName[i]; } *p++ = '"'; *p++ = '\0'; } return zRet; } static int tblIterPrepareAll(sqlite3ota *p){ OtaTblIter *pIter = &p->tbliter; int rc = SQLITE_OK; char *zCol = 0; char *zBindings = 0; char *zSql; sqlite3_stmt *pPragma = 0; int i; int bSeenPk = 0; /* Set to true once PK column seen */ /* Allocate and populate the azCol[] array */ zSql = sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTarget); rc = prepareFreeAndCollectError(p->dbDest, zSql, &pPragma, &p->zErrmsg); pIter->nCol = 0; if( rc==SQLITE_OK ){ while( SQLITE_ROW==sqlite3_step(pPragma) ){ const char *zName = (const char*)sqlite3_column_text(pPragma, 1); if( (pIter->nCol % 4)==0 ){ int nByte = sizeof(char*) * (pIter->nCol+4); char **azNew = (char**)sqlite3_realloc(pIter->azCol, nByte); if( azNew==0 ){ rc = SQLITE_NOMEM; break; } pIter->azCol = azNew; } pIter->azCol[pIter->nCol] = quoteSqlName(zName); if( pIter->azCol[pIter->nCol]==0 ){ rc = SQLITE_NOMEM; break; } pIter->nCol++; if( sqlite3_column_int(pPragma, 5) ) bSeenPk = 1; } if( rc==SQLITE_OK ){ rc = sqlite3_finalize(pPragma); }else{ sqlite3_finalize(pPragma); } } /* If the table has no PRIMARY KEY, throw an exception. */ if( bSeenPk==0 ){ p->zErrmsg = sqlite3_mprintf("table %s has no PRIMARY KEY", pIter->zTarget); rc = SQLITE_ERROR; } /* Populate the zCol variable */ for(i=0; rc==SQLITE_OK && i<pIter->nCol; i++){ zCol = sqlite3_mprintf("%z%s%s", zCol, (i==0?"":", "), pIter->azCol[i]); if( zCol==0 ){ rc = SQLITE_NOMEM; } } /* Allocate and populate zBindings */ if( rc==SQLITE_OK ){ zBindings = (char*)sqlite3_malloc(pIter->nCol * 2); if( zBindings==0 ){ rc = SQLITE_NOMEM; }else{ int i; for(i=0; i<pIter->nCol; i++){ zBindings[i*2] = '?'; zBindings[i*2+1] = ','; } zBindings[pIter->nCol*2-1] = '\0'; } } /* Create OtaTblIter.pSelect */ if( rc==SQLITE_OK ){ zSql = sqlite3_mprintf("SELECT rowid, %s FROM %Q", zCol, pIter->zSource); rc = prepareFreeAndCollectError(p->dbOta,zSql,&pIter->pSelect, &p->zErrmsg); } /* Create OtaTblIter.pInsert */ if( rc==SQLITE_OK ){ zSql = sqlite3_mprintf("INSERT INTO %Q(%s) VALUES(%s)", pIter->zTarget, zCol, zBindings ); rc = prepareFreeAndCollectError(p->dbDest,zSql,&pIter->pInsert,&p->zErrmsg); } sqlite3_free(zCol); sqlite3_free(zBindings); return rc; } static void tblIterFreeAll(OtaTblIter *pIter){ int i; sqlite3_finalize(pIter->pSelect); sqlite3_finalize(pIter->pInsert); for(i=0; i<pIter->nCol; i++) sqlite3_free(pIter->azCol[i]); sqlite3_free(pIter->azCol); pIter->azCol = 0; pIter->pSelect = 0; pIter->pInsert = 0; pIter->nCol = 0; } static int tblIterNext(OtaTblIter *pIter){ int rc; tblIterFreeAll(pIter); assert( pIter->pTblIter ); rc = sqlite3_step(pIter->pTblIter); if( rc==SQLITE_ROW ){ pIter->zSource = (const char*)sqlite3_column_text(pIter->pTblIter, 0); pIter->zTarget = &pIter->zSource[5]; assert( 5==strlen("data_") ); pIter->iEntry++; }else{ pIter->zSource = 0; pIter->zTarget = 0; } if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK; return rc; } static int tblIterFirst(sqlite3 *db, OtaTblIter *pIter){ int rc; /* return code */ memset(pIter, 0, sizeof(OtaTblIter)); rc = sqlite3_prepare_v2(db, "SELECT name FROM sqlite_master " "WHERE type='table' AND name LIKE 'data_%'", -1, &pIter->pTblIter, 0 ); if( rc==SQLITE_OK ){ rc = tblIterNext(pIter); } return rc; } static void tblIterFinalize(OtaTblIter *pIter){ tblIterFreeAll(pIter); sqlite3_finalize(pIter->pTblIter); memset(pIter, 0, sizeof(OtaTblIter)); } static void idxIterFreeAll(OtaIdxIter *pIter){ sqlite3_finalize(pIter->pWriter); sqlite3_finalize(pIter->pSelect); pIter->pWriter = 0; pIter->pSelect = 0; pIter->aiCol = 0; pIter->nCol = 0; } static int idxIterPrepareAll(sqlite3ota *p){ int rc; int i; /* Iterator variable */ char *zSql = 0; char *zCols = 0; /* Columns list */ OtaIdxIter *pIter = &p->idxiter; /* Prepare the writer statement to write (insert) entries into the index. */ rc = sqlite3_index_writer( p->dbDest, 0, pIter->zIndex, &pIter->pWriter, &pIter->aiCol, &pIter->nCol ); /* Prepare a SELECT statement to read values from the source table in ** the same order as they are stored in the current index. The statement ** is: ** ** SELECT rowid, <cols> FROM data_<tbl> ORDER BY <cols> */ for(i=0; rc==SQLITE_OK && i<pIter->nCol; i++){ const char *zQuoted = p->tbliter.azCol[ pIter->aiCol[i] ]; zCols = sqlite3_mprintf("%z%s%s", zCols, zCols?", ":"", zQuoted); if( !zCols ){ rc = SQLITE_NOMEM; } } if( rc==SQLITE_OK ){ const char *zFmt = "SELECT rowid, %s FROM %Q ORDER BY %s"; zSql = sqlite3_mprintf(zFmt, zCols, p->tbliter.zSource, zCols); if( zSql ){ sqlite3_stmt **pp = &p->idxiter.pSelect; rc = prepareFreeAndCollectError(p->dbOta, zSql, pp, &p->zErrmsg); }else{ rc = SQLITE_NOMEM; } } sqlite3_free(zCols); return rc; } static int idxIterNext(OtaIdxIter *pIter){ int rc; idxIterFreeAll(pIter); assert( pIter->pIdxIter ); rc = sqlite3_step(pIter->pIdxIter); if( rc==SQLITE_ROW ){ pIter->zIndex = (const char*)sqlite3_column_text(pIter->pIdxIter, 0); pIter->iEntry++; }else{ pIter->zIndex = 0; rc = sqlite3_finalize(pIter->pIdxIter); pIter->pIdxIter = 0; } if( rc==SQLITE_ROW ) rc = SQLITE_OK; return rc; } static int idxIterFirst(sqlite3 *db, const char *zTable, OtaIdxIter *pIter){ int rc; /* return code */ memset(pIter, 0, sizeof(OtaIdxIter)); rc = sqlite3_prepare_v2(db, "SELECT name FROM sqlite_master " "WHERE type='index' AND tbl_name = ?", -1, &pIter->pIdxIter, 0 ); if( rc==SQLITE_OK ){ rc = sqlite3_bind_text(pIter->pIdxIter, 1, zTable, -1, SQLITE_TRANSIENT); } if( rc==SQLITE_OK ){ rc = idxIterNext(pIter); } return rc; } static void idxIterFinalize(OtaIdxIter *pIter){ idxIterFreeAll(pIter); sqlite3_finalize(pIter->pIdxIter); memset(pIter, 0, sizeof(OtaIdxIter)); } /* ** Call sqlite3_reset() on the SQL statement passed as the second argument. ** If it returns anything other than SQLITE_OK, store the error code and ** error message in the OTA handle. */ static void otaResetStatement(sqlite3ota *p, sqlite3_stmt *pStmt){ assert( p->rc==SQLITE_OK ); assert( p->zErrmsg==0 ); p->rc = sqlite3_reset(pStmt); if( p->rc!=SQLITE_OK ){ sqlite3 *db = sqlite3_db_handle(pStmt); p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } } /* ** Check that all SQL statements required to process the current ** table and index have been prepared. If not, prepare them. If ** an error occurs, store the error code and message in the OTA ** handle before returning. */ static int otaPrepareAll(sqlite3ota *p){ assert( p->rc==SQLITE_OK ); assert( p->zErrmsg==0 ); assert( p->tbliter.zTarget ); if( p->tbliter.pSelect==0 ){ p->rc = tblIterPrepareAll(p); } if( p->rc==SQLITE_OK && p->idxiter.zIndex && 0==p->idxiter.pSelect ){ p->rc = idxIterPrepareAll(p); } return p->rc; } int sqlite3ota_step(sqlite3ota *p){ if( p ){ while( p && p->rc==SQLITE_OK && p->tbliter.zTarget ){ sqlite3_stmt *pSelect; int i; otaPrepareAll(p); pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect); /* Advance to the next input row. */ if( p->rc==SQLITE_OK ){ int rc = sqlite3_step(pSelect); if( rc!=SQLITE_ROW ){ otaResetStatement(p, pSelect); /* Go to the next index. */ if( p->rc==SQLITE_OK ){ if( p->idxiter.zIndex ){ p->rc = idxIterNext(&p->idxiter); }else{ p->rc = idxIterFirst(p->dbDest, p->tbliter.zTarget, &p->idxiter); } } /* If there is no next index, go to the next table. */ if( p->rc==SQLITE_OK && p->idxiter.zIndex==0 ){ p->rc = tblIterNext(&p->tbliter); } continue; } } /* Update the target database PK table according to the row that ** tbliter.pSelect currently points to. ** ** todo: For now, we assume all rows are INSERT commands - this will ** change. */ if( p->rc==SQLITE_OK ){ sqlite3_stmt *pInsert; int nCol; if( p->idxiter.zIndex ){ pInsert = p->idxiter.pWriter; nCol = p->idxiter.nCol; }else{ pInsert = p->tbliter.pInsert; nCol = p->tbliter.nCol; } for(i=0; i<nCol; i++){ sqlite3_value *pVal = sqlite3_column_value(pSelect, i+1); sqlite3_bind_value(pInsert, i+1, pVal); } sqlite3_step(pInsert); otaResetStatement(p, pInsert); } break; } if( p->rc==SQLITE_OK && p->tbliter.zTarget==0 ) p->rc = SQLITE_DONE; } return (p ? p->rc : SQLITE_NOMEM); } static void otaOpenDatabase(sqlite3ota *p, sqlite3 **pDb, const char *zFile){ if( p->rc==SQLITE_OK ){ p->rc = sqlite3_open(zFile, pDb); if( p->rc ){ const char *zErr = sqlite3_errmsg(*pDb); p->zErrmsg = sqlite3_mprintf("sqlite3_open(): %s", zErr); } } } static void otaSaveTransactionState(sqlite3ota *p){ sqlite3_stmt *pStmt = 0; void *pWalState = 0; int nWalState = 0; int rc; const char *zInsert = "INSERT INTO ota_state(wal_state, tbl, idx, row, progress)" "VALUES(:wal_state, :tbl, :idx, :row, :progress)"; rc = sqlite3_transaction_save(p->dbDest, &pWalState, &nWalState); if( rc==SQLITE_OK ){ rc = sqlite3_exec(p->dbOta, "DELETE FROM ota_state", 0, 0, 0); } if( rc==SQLITE_OK ){ rc = prepareAndCollectError(p->dbOta, zInsert, &pStmt, &p->zErrmsg); } if( rc==SQLITE_OK ){ sqlite3_stmt *pSelect; pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect); sqlite3_bind_blob(pStmt, 1, pWalState, nWalState, SQLITE_STATIC); sqlite3_bind_text(pStmt, 2, p->tbliter.zTarget, -1, SQLITE_STATIC); if( p->idxiter.zIndex ){ sqlite3_bind_text(pStmt, 3, p->idxiter.zIndex, -1, SQLITE_STATIC); } sqlite3_bind_int64(pStmt, 4, sqlite3_column_int64(pSelect, 0)); sqlite3_step(pStmt); rc = sqlite3_finalize(pStmt); if( rc==SQLITE_OK ){ rc = sqlite3_exec(p->dbOta, "COMMIT", 0, 0, 0); } if( rc!=SQLITE_OK ){ p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->dbOta)); } } sqlite3_free(pWalState); assert( p->rc==SQLITE_OK ); p->rc = rc; } static void otaLoadTransactionState(sqlite3ota *p){ sqlite3_stmt *pStmt = 0; int rc; const char *zSelect = "SELECT wal_state, tbl, idx, row, progress FROM ota_state"; rc = prepareAndCollectError(p->dbOta, zSelect, &pStmt, &p->zErrmsg); if( rc==SQLITE_OK ){ if( SQLITE_ROW==sqlite3_step(pStmt) ){ const void *pWalState = 0; int nWalState = 0; const char *zTbl; const char *zIdx; sqlite3_int64 iRowid; pWalState = sqlite3_column_blob(pStmt, 0); nWalState = sqlite3_column_bytes(pStmt, 0); zTbl = (const char*)sqlite3_column_text(pStmt, 1); zIdx = (const char*)sqlite3_column_text(pStmt, 2); iRowid = sqlite3_column_int64(pStmt, 3); rc = sqlite3_transaction_restore(p->dbDest, pWalState, nWalState); while( rc==SQLITE_OK && p->tbliter.zTarget && sqlite3_stricmp(p->tbliter.zTarget, zTbl) ){ rc = tblIterNext(&p->tbliter); } if( rc==SQLITE_OK && !p->tbliter.zTarget ){ rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf("ota_state mismatch error"); } if( rc==SQLITE_OK && zIdx ){ rc = idxIterFirst(p->dbDest, p->tbliter.zTarget, &p->idxiter); while( rc==SQLITE_OK && p->idxiter.zIndex && sqlite3_stricmp(p->idxiter.zIndex, zIdx) ){ rc = idxIterNext(&p->idxiter); } if( rc==SQLITE_OK && !p->idxiter.zIndex ){ rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf("ota_state mismatch error"); } } if( rc==SQLITE_OK ){ rc = otaPrepareAll(p); } if( rc==SQLITE_OK ){ sqlite3_stmt *pSelect; pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect); while( sqlite3_column_int64(pSelect, 0)!=iRowid ){ rc = sqlite3_step(pSelect); if( rc!=SQLITE_ROW ) break; } if( rc==SQLITE_ROW ){ rc = SQLITE_OK; }else{ rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf("ota_state mismatch error"); } } } if( rc==SQLITE_OK ){ rc = sqlite3_finalize(pStmt); }else{ sqlite3_finalize(pStmt); } } p->rc = rc; } /* ** Open and return a new OTA handle. */ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){ sqlite3ota *p; p = (sqlite3ota*)sqlite3_malloc(sizeof(sqlite3ota)); if( p ){ /* Open the target database */ memset(p, 0, sizeof(sqlite3ota)); otaOpenDatabase(p, &p->dbDest, zTarget); otaOpenDatabase(p, &p->dbOta, zOta); /* If it has not already been created, create the ota_state table */ if( p->rc==SQLITE_OK ){ p->rc = sqlite3_exec(p->dbOta, OTA_CREATE_STATE, 0, 0, &p->zErrmsg); } if( p->rc==SQLITE_OK ){ const char *zScript = "PRAGMA ota_mode=1;" "PRAGMA journal_mode=wal;" "BEGIN IMMEDIATE;" ; p->rc = sqlite3_exec(p->dbDest, zScript, 0, 0, &p->zErrmsg); } if( p->rc==SQLITE_OK ){ const char *zScript = "BEGIN IMMEDIATE"; p->rc = sqlite3_exec(p->dbOta, zScript, 0, 0, &p->zErrmsg); } /* Point the table iterator at the first table */ if( p->rc==SQLITE_OK ){ p->rc = tblIterFirst(p->dbOta, &p->tbliter); } if( p->rc==SQLITE_OK ){ otaLoadTransactionState(p); } } return p; } static void otaCloseHandle(sqlite3 *db){ int rc = sqlite3_close(db); assert( rc==SQLITE_OK ); } int sqlite3ota_close(sqlite3ota *p, char **pzErrmsg){ int rc; if( p ){ /* If the update has not been fully applied, save the state in ** the ota db. If successful, this call also commits the open ** transaction on the ota db. */ assert( p->rc!=SQLITE_ROW ); if( p->rc==SQLITE_OK ){ assert( p->zErrmsg==0 ); otaSaveTransactionState(p); } /* Close all open statement handles. */ tblIterFinalize(&p->tbliter); idxIterFinalize(&p->idxiter); /* If the ota update has been fully applied, commit the transaction ** on the target database. */ if( p->rc==SQLITE_DONE ){ rc = sqlite3_exec(p->dbDest, "COMMIT", 0, 0, &p->zErrmsg); if( rc!=SQLITE_OK ) p->rc = rc; } rc = p->rc; *pzErrmsg = p->zErrmsg; otaCloseHandle(p->dbDest); otaCloseHandle(p->dbOta); sqlite3_free(p); }else{ rc = SQLITE_NOMEM; *pzErrmsg = 0; } return rc; } /**************************************************************************/ #ifdef SQLITE_TEST #include <tcl.h> /* From main.c (apparently...) */ extern const char *sqlite3ErrName(int); static int test_sqlite3ota_cmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int ret = TCL_OK; sqlite3ota *pOta = (sqlite3ota*)clientData; const char *azMethod[] = { "step", "close", 0 }; int iMethod; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "METHOD"); return TCL_ERROR; } if( Tcl_GetIndexFromObj(interp, objv[1], azMethod, "method", 0, &iMethod) ){ return TCL_ERROR; } switch( iMethod ){ case 0: /* step */ { int rc = sqlite3ota_step(pOta); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); break; } case 1: /* close */ { char *zErrmsg = 0; int rc; Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); rc = sqlite3ota_close(pOta, &zErrmsg); if( rc==SQLITE_OK || rc==SQLITE_DONE ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); assert( zErrmsg==0 ); }else{ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); if( zErrmsg ){ Tcl_AppendResult(interp, " - ", zErrmsg, 0); sqlite3_free(zErrmsg); } ret = TCL_ERROR; } break; } default: /* seems unlikely */ assert( !"cannot happen" ); break; } return ret; } /* ** Tclcmd: sqlite3ota CMD <target-db> <ota-db> */ static int test_sqlite3ota( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3ota *pOta = 0; const char *zCmd; const char *zTarget; const char *zOta; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB OTA-DB"); return TCL_ERROR; } zCmd = Tcl_GetString(objv[1]); zTarget = Tcl_GetString(objv[2]); zOta = Tcl_GetString(objv[3]); pOta = sqlite3ota_open(zTarget, zOta); Tcl_CreateObjCommand(interp, zCmd, test_sqlite3ota_cmd, (ClientData)pOta, 0); Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } int SqliteOta_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "sqlite3ota", test_sqlite3ota, 0, 0); return TCL_OK; } #endif /* ifdef SQLITE_TEST */ |
Added ext/ota/sqlite3ota.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 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 158 159 160 161 162 163 164 165 166 167 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 | /* ** 2014 August 30 ** ** 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 contains the public interface for the OTA extension. */ /* ** SUMMARY ** ** Writing a transaction containing a large number of operations on ** b-tree indexes that are collectively larger than the available cache ** memory can be very inefficient. ** ** The problem is that in order to update a b-tree, the leaf page (at least) ** containing the entry being inserted or deleted must be modified. If the ** working set of leaves is larger than the available cache memory, then a ** single leaf that is modified more than once as part of the transaction ** may be loaded from or written to the persistent media more than once. ** Additionally, because the index updates are likely to be applied in ** random order, access to pages within the databse is also likely to be in ** random order, which is itself quite inefficient. ** ** One way to improve the situation is to sort the operations on each index ** by index key before applying them to the b-tree. This leads to an IO ** pattern that resembles a single linear scan through the index b-tree, ** and all but guarantees each modified leaf page is loaded and stored ** exactly once. SQLite uses this trick to improve the performance of ** CREATE INDEX commands. This extension allows it to be used to improve ** the performance of large transactions on existing databases. ** ** Additionally, this extension allows the work involved in writing the ** large transaction to be broken down into sub-transactions performed ** sequentially by separate processes. This is useful if the system cannot ** guarantee that a single update process may run for long enough to apply ** the entire update, for example because the update is running on a mobile ** device that is frequently rebooted. Even after the writer process has ** committed one or more sub-transactions, other database clients continue ** to read from the original database snapshot. In other words, partially ** applied transactions are not visible to other clients. ** ** "OTA" stands for "Over The Air" update. As in a large database update ** transmitted via a wireless network to a mobile device. A transaction ** applied using this extension is hence refered to as an "OTA update". ** ** ** LIMITATIONS ** ** An "OTA update" transaction is subject to the following limitations: ** ** * The transaction must consist of INSERT, UPDATE and DELETE operations ** only. ** ** * INSERT statements may not use any default values. ** ** * UPDATE and DELETE statements must identify their target rows by ** real PRIMARY KEY values - i.e. INTEGER PRIMARY KEY columns or ** by the PRIMARY KEY columns of WITHOUT ROWID tables. ** ** * UPDATE statements may not modify real PRIMARY KEY columns. ** ** * No triggers will be fired. ** ** * No foreign key violations are detected or reported. ** ** * No constraint handling mode except for "OR ROLLBACK" is supported. ** ** ** PREPARATION ** ** An "OTA update" is stored as a separate SQLite database. A database ** containing an OTA update is an "OTA database". For each table in the ** target database to be updated, the OTA database should contain a table ** named "data_<target name>" containing the same set of columns as the ** target table, and one more - "ota_control". The data_% table should ** have no PRIMARY KEY or UNIQUE constraints, but each column should have ** the same type as the corresponding column in the target database. ** The "ota_control" column should have no type at all. For example, if ** the target database contains: ** ** CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE); ** ** Then the OTA database should contain: ** ** CREATE TABLE data_t1(a INTEGER, b TEXT, c, ota_control); ** ** The order of the columns in the data_% table does not matter. ** ** For each row to INSERT into the target database as part of the OTA ** update, the corresponding data_% table should contain a single record ** with the "ota_control" column set to contain integer value 0. The ** other columns should be set to the values that make up the new record ** to insert. ** ** For each row to DELETE from the target database as part of the OTA ** update, the corresponding data_% table should contain a single record ** with the "ota_control" column set to contain integer value 1. The ** real primary key values of the row to delete should be stored in the ** corresponding columns of the data_% table. The values stored in the ** other columns are not used. ** ** For each row to DELETE from the target database as part of the OTA ** update, the corresponding data_% table should contain a single record ** with the "ota_control" column set to contain a value of type text. ** The real primary key values identifying the row to update should be ** stored in the corresponding columns of the data_% table row, as should ** the new values of all columns being update. The text value in the ** "ota_control" column must contain the same number of characters as ** there are column in the target database table, and must consist entirely ** of "x" and "." characters. For each column that is being updated, ** the corresponding character is set to "x". For those that remain as ** they are, the corresponding character of the ota_control value should ** be set to ".". For example, given the tables above, the update ** statement: ** ** UPDATE t1 SET c = 'usa' WHERE a = 4; ** ** is represented by the data_t1 row created by: ** ** INSERT INTO data_t1(a, b, c, ota_control) VALUES(4, NULL, 'usa', '..x'); ** ** ** USAGE ** ** The API declared below allows an application to apply an OTA update ** stored on disk to an existing target database. Essentially, the ** application: ** ** 1) Opens an OTA handle using the sqlite3ota_open() function. ** ** 2) Calls the sqlite3ota_step() function one or more times on ** the new handle. Each call to sqlite3ota_step() performs a single ** b-tree operation, so thousands of calls may be required to apply ** a complete update. ** ** 3) Calls sqlite3ota_close() to close the OTA update handle. If ** sqlite3ota_step() has been called enough times to completely ** apply the update to the target database, then it is committed ** and made visible to other database clients at this point. ** Otherwise, the state of the OTA update application is saved ** in the OTA database for later resumption. ** ** See comments below for more detail on APIs. ** ** If an update is only partially applied to the target database by the ** time sqlite3ota_close() is called, various state information is saved ** within the OTA database. This allows subsequent processes to automatically ** resume the OTA update from where it left off. ** ** To remove all OTA extension state information, returning an OTA database ** to its original contents, it is sufficient to drop all tables that begin ** with the prefix "ota_" */ #ifndef _SQLITE3OTA_H #define _SQLITE3OTA_H typedef struct sqlite3ota sqlite3ota; /* ** Open an OTA handle. ** ** Argument zTarget is the path to the target database. Argument zOta is ** the path to the OTA database. Each call to this function must be matched ** by a call to sqlite3ota_close(). */ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta); /* ** Do some work towards applying the OTA update to the target db. ** ** Return SQLITE_DONE if the update has been completely applied, or ** SQLITE_OK if no error occurs but there remains work to do to apply ** the OTA update. If an error does occur, some other error code is ** returned. ** ** Once a call to sqlite3ota_step() has returned a value other than ** SQLITE_OK, all subsequent calls on the same OTA handle are no-ops ** that immediately return the same value. */ int sqlite3ota_step(sqlite3ota *pOta); /* ** Close an OTA handle. ** ** If the OTA update has been completely applied, commit it to the target ** database. Otherwise, assuming no error has occurred, save the current ** state of the OTA update appliation to the OTA database. ** ** If an error has already occurred as part of an sqlite3ota_step() ** or sqlite3ota_open() call, or if one occurs within this function, an ** SQLite error code is returned. Additionally, *pzErrmsg may be set to ** point to a buffer containing a utf-8 formatted English language error ** message. It is the responsibility of the caller to eventually free any ** such buffer using sqlite3_free(). ** ** Otherwise, if no error occurs, this function returns SQLITE_OK if the ** update has been partially applied, or SQLITE_DONE if it has been ** completely applied. */ int sqlite3ota_close(sqlite3ota *pOta, char **pzErrmsg); #endif /* _SQLITE3OTA_H */ |
Changes to main.mk.
︙ | ︙ | |||
327 328 329 330 331 332 333 | $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ | | > | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c \ $(TOP)/ext/ota/sqlite3ota.c # Header files used by all library source files. # HDR = \ $(TOP)/src/btree.h \ $(TOP)/src/btreeInt.h \ $(TOP)/src/hash.h \ |
︙ | ︙ |
Changes to src/btree.c.
︙ | ︙ | |||
147 148 149 150 151 152 153 | Pgno iTab = 0; BtLock *pLock; /* If this database is not shareable, or if the client is reading ** and has the read-uncommitted flag set, then no lock is required. ** Return true immediately. */ | > | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | Pgno iTab = 0; BtLock *pLock; /* If this database is not shareable, or if the client is reading ** and has the read-uncommitted flag set, then no lock is required. ** Return true immediately. */ if( (pBtree->db->flags & SQLITE_OtaMode) || (pBtree->sharable==0) || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted)) ){ return 1; } /* If the client is reading or writing an index and the schema is ** not loaded, then it is too difficult to actually check to see if |
︙ | ︙ | |||
2041 2042 2043 2044 2045 2046 2047 | } #endif *ppBtree = p; btree_open_out: if( rc!=SQLITE_OK ){ if( pBt && pBt->pPager ){ | | | 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 | } #endif *ppBtree = p; btree_open_out: if( rc!=SQLITE_OK ){ if( pBt && pBt->pPager ){ sqlite3PagerClose(pBt->pPager, 0); } sqlite3_free(pBt); sqlite3_free(p); *ppBtree = 0; }else{ /* If the B-Tree was successfully opened, set the pager-cache size to the ** default value. Except, when opening on an existing shared pager-cache, |
︙ | ︙ | |||
2170 2171 2172 2173 2174 2175 2176 | if( !p->sharable || removeFromSharingList(pBt) ){ /* The pBt is no longer on the sharing list, so we can access ** it without having to hold the mutex. ** ** Clean out and delete the BtShared object. */ assert( !pBt->pCursor ); | | | 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 | if( !p->sharable || removeFromSharingList(pBt) ){ /* The pBt is no longer on the sharing list, so we can access ** it without having to hold the mutex. ** ** Clean out and delete the BtShared object. */ assert( !pBt->pCursor ); sqlite3PagerClose(pBt->pPager, (p->db->flags & SQLITE_OtaMode)!=0); if( pBt->xFreeSchema && pBt->pSchema ){ pBt->xFreeSchema(pBt->pSchema); } sqlite3DbFree(0, pBt->pSchema); freeTempSpace(pBt); sqlite3_free(pBt); } |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 | ** WITHOUT ROWID table. */ for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ int regIdx; /* Range of registers hold conent for pIdx */ int regR; /* Range of registers holding conflicting PK */ int iThisCur; /* Cursor for this UNIQUE index */ int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ if( bAffinityDone==0 ){ sqlite3TableAffinity(v, pTab, regNewData+1); bAffinityDone = 1; } iThisCur = iIdxCur+ix; | > > > > | 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 | ** WITHOUT ROWID table. */ for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ int regIdx; /* Range of registers hold conent for pIdx */ int regR; /* Range of registers holding conflicting PK */ int iThisCur; /* Cursor for this UNIQUE index */ int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ /* If the "ota_mode" flag is set, ignore all indexes except the PK ** index of WITHOUT ROWID tables. */ if( (db->flags & SQLITE_OtaMode) && pIdx!=pPk) continue; if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ if( bAffinityDone==0 ){ sqlite3TableAffinity(v, pTab, regNewData+1); bAffinityDone = 1; } iThisCur = iIdxCur+ix; |
︙ | ︙ | |||
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 | u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */ v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( aRegIdx[i]==0 ) continue; bAffinityDone = 1; if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2); VdbeCoverage(v); } sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]); pik_flags = 0; | > > > > > > > > > | 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 | u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */ v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( aRegIdx[i]==0 ) continue; /* If the "ota_mode" flag is set, ignore all indexes except the PK ** index of WITHOUT ROWID tables. */ if( (pParse->db->flags & SQLITE_OtaMode) && (pTab->iPKey>=0 || pIdx->idxType!=SQLITE_IDXTYPE_PRIMARYKEY) ){ continue; } bAffinityDone = 1; if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2); VdbeCoverage(v); } sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]); pik_flags = 0; |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
3466 3467 3468 3469 3470 3471 3472 | ** Return 1 if database is read-only or 0 if read/write. Return -1 if ** no such database exists. */ int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ Btree *pBt = sqlite3DbNameToBtree(db, zDbName); return pBt ? sqlite3BtreeIsReadonly(pBt) : -1; } | > > > > > > > > > > > > | 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 | ** Return 1 if database is read-only or 0 if read/write. Return -1 if ** no such database exists. */ int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ Btree *pBt = sqlite3DbNameToBtree(db, zDbName); return pBt ? sqlite3BtreeIsReadonly(pBt) : -1; } int sqlite3_transaction_save(sqlite3 *db, void **ppState, int *pnState){ Pager *pPager = sqlite3BtreePager(db->aDb[0].pBt); return sqlite3PagerSaveState(pPager, ppState, pnState); } int sqlite3_transaction_restore(sqlite3 *db, const void *pState, int nState){ Pager *pPager = sqlite3BtreePager(db->aDb[0].pBt); return sqlite3PagerRestoreState(pPager, pState, nState); } |
Changes to src/pager.c.
︙ | ︙ | |||
3943 3944 3945 3946 3947 3948 3949 | ** result in a coredump. ** ** This function always succeeds. If a transaction is active an attempt ** is made to roll it back. If an error occurs during the rollback ** a hot journal may be left in the filesystem but no error is returned ** to the caller. */ | | > | > | 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 | ** result in a coredump. ** ** This function always succeeds. If a transaction is active an attempt ** is made to roll it back. If an error occurs during the rollback ** a hot journal may be left in the filesystem but no error is returned ** to the caller. */ int sqlite3PagerClose(Pager *pPager, int bOtaMode){ u8 *pTmp = (u8 *)pPager->pTmpSpace; assert( assert_pager_state(pPager) ); disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pagerFreeMapHdrs(pPager); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL sqlite3WalClose( pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, (bOtaMode?0:pTmp) ); pPager->pWal = 0; #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* If it is open, sync the journal file before calling UnlockAndRollback. |
︙ | ︙ | |||
7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 | if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); pPager->pWal = 0; pagerFixMaplimit(pPager); } } return rc; } #endif /* !SQLITE_OMIT_WAL */ #ifdef SQLITE_ENABLE_ZIPVFS /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 | if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); pPager->pWal = 0; pagerFixMaplimit(pPager); } } return rc; } int sqlite3PagerSaveState(Pager *pPager, void **ppState, int *pnState){ int rc = SQLITE_OK; *ppState = 0; *pnState = 0; if( pPager->pWal==0 || pPager->eState<PAGER_WRITER_LOCKED ){ rc = SQLITE_ERROR; }else{ /* Flush all dirty pages to the wal. */ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); rc = sqlite3WalFrames(pPager->pWal, pPager->pageSize, pList, 0, 0, pPager->walSyncFlags ); if( rc==SQLITE_OK ){ rc = sqlite3WalSaveState(pPager->pWal, ppState, pnState); } } return rc; } int sqlite3PagerRestoreState(Pager *pPager, const void *pState, int nState){ int rc = SQLITE_OK; if( pPager->pWal==0 || pPager->eState<PAGER_WRITER_LOCKED || sqlite3PcacheDirtyList(pPager->pPCache) ){ rc = SQLITE_ERROR; }else{ sqlite3PcacheTruncate(pPager->pPCache, 1); rc = sqlite3WalRestoreState(pPager->pWal, pState, nState); pPager->eState = PAGER_WRITER_CACHEMOD; } return rc; } #endif /* !SQLITE_OMIT_WAL */ #ifdef SQLITE_ENABLE_ZIPVFS /* |
︙ | ︙ |
Changes to src/pager.h.
︙ | ︙ | |||
108 109 110 111 112 113 114 | Pager **ppPager, const char*, int, int, int, void(*)(DbPage*) ); | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | Pager **ppPager, const char*, int, int, int, void(*)(DbPage*) ); int sqlite3PagerClose(Pager *pPager, int); int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u32*, int); int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); |
︙ | ︙ | |||
203 204 205 206 207 208 209 210 | void disable_simulated_io_errors(void); void enable_simulated_io_errors(void); #else # define disable_simulated_io_errors() # define enable_simulated_io_errors() #endif #endif /* _PAGER_H_ */ | > > > | 203 204 205 206 207 208 209 210 211 212 213 | void disable_simulated_io_errors(void); void enable_simulated_io_errors(void); #else # define disable_simulated_io_errors() # define enable_simulated_io_errors() #endif int sqlite3PagerSaveState(Pager *pPager, void **ppState, int *pnState); int sqlite3PagerRestoreState(Pager *pPager, const void *pState, int nState); #endif /* _PAGER_H_ */ |
Changes to src/pragma.c.
︙ | ︙ | |||
304 305 306 307 308 309 310 311 312 313 314 315 316 317 | /* ePragTyp: */ PragTyp_PAGE_COUNT, /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, { /* zName: */ "mmap_size", /* ePragTyp: */ PragTyp_MMAP_SIZE, /* ePragFlag: */ 0, /* iArg: */ 0 }, { /* zName: */ "page_count", /* ePragTyp: */ PragTyp_PAGE_COUNT, /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, { /* zName: */ "page_size", /* ePragTyp: */ PragTyp_PAGE_SIZE, /* ePragFlag: */ 0, | > > > > > > | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | /* ePragTyp: */ PragTyp_PAGE_COUNT, /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, { /* zName: */ "mmap_size", /* ePragTyp: */ PragTyp_MMAP_SIZE, /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif { /* zName: */ "ota_mode", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_OtaMode }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) { /* zName: */ "page_count", /* ePragTyp: */ PragTyp_PAGE_COUNT, /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, { /* zName: */ "page_size", /* ePragTyp: */ PragTyp_PAGE_SIZE, /* ePragFlag: */ 0, |
︙ | ︙ | |||
1463 1464 1465 1466 1467 1468 1469 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); } if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ k = 0; }else if( pPk==0 ){ k = 1; }else{ | > > > | > | 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 | 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) ){ k = 0; }else{ for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} } } sqlite3VdbeAddOp2(v, OP_Integer, k, 6); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); } } } break; |
︙ | ︙ |
Changes to src/prepare.c.
︙ | ︙ | |||
791 792 793 794 795 796 797 | const char **pzTail /* OUT: End of parsed string */ ){ int rc; rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } | < | 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | const char **pzTail /* OUT: End of parsed string */ ){ int rc; rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } #ifndef SQLITE_OMIT_UTF16 /* ** Compile the UTF-16 encoded SQL statement zSql into a statement handle. */ static int sqlite3Prepare16( sqlite3 *db, /* Database handle. */ |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 | */ #define SQLITE_ROLLBACK 1 /* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ #define SQLITE_FAIL 3 /* #define SQLITE_ABORT 4 // Also an error code */ #define SQLITE_REPLACE 5 /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 | */ #define SQLITE_ROLLBACK 1 /* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ #define SQLITE_FAIL 3 /* #define SQLITE_ABORT 4 // Also an error code */ #define SQLITE_REPLACE 5 /* ** Allocate a statement handle that may be used to write directly to an ** index b-tree. This allows the user to create a corrupt database. Once ** the statement handle is allocated, it may be used with the same APIs ** as any statement handle created with sqlite3_prepare(). ** ** The statement writes to the index specified by parameter zIndex, which ** must be in the "main" database. If argument bDelete is false, then each ** time the statement is sqlite3_step()ed, an entry is inserted into the ** b-tree index. If it is true, then an entry may be deleted (or may not, if ** the specified key is not found) each time the statement is ** sqlite3_step()ed. ** ** If statement compilation is successful, *ppStmt is set to point to the ** new statement handle and SQLITE_OK is returned. Otherwise, if an error ** occurs, *ppStmt is set to NULL and an error code returned. An error ** message may be left in the database handle in this case. ** ** If statement compilation succeeds, output variable *pnCol is set to the ** total number of columns in the index, including the primary key columns ** at the end. Variable *paiCol is set to point to an array *pnCol entries ** in size. Each entry is the table column index, numbered from zero from left ** to right, of the corresponding index column. For example, if: ** ** CREATE TABLE t1(a, b, c, d); ** CREATE INDEX i1 ON t1(b, c); ** ** then *pnCol is 3 and *paiCol points to an array containing {1, 2, -1}. ** If table t1 had an explicit INTEGER PRIMARY KEY, then the "-1" in the ** *paiCol array would be replaced by its column index. Or if: ** ** CREATE TABLE t2(a, b, c, d, PRIMARY KEY(d, c)) WITHOUT ROWID; ** CREATE INDEX i2 ON t2(a); ** ** then (*pnCol) is 3 and *paiCol points to an array containing {0, 3, 2}. ** ** The lifetime of the array is the same as that of the statement handle - ** it is automatically freed when the statement handle is passed to ** sqlite3_finalize(). ** ** The statement has (*pnCol) SQL variables that values may be bound to. ** They correspond to the values used to create the index key that is ** inserted or deleted when the statement is stepped. ** ** If the index is a UNIQUE index, the usual checking and error codes apply ** to insert operations. */ int sqlite3_index_writer( sqlite3 *db, int bDelete, /* Zero for insert, non-zero for delete */ const char *zIndex, /* Index to write to */ sqlite3_stmt**, /* OUT: New statement handle */ int **paiCol, int *pnCol /* OUT: See above */ ); /* ** This function is used to save the state of an ongoing WAL mode write ** transaction on the "main" database of the supplied database handle. ** ** If successful, SQLITE_OK is returned and output variable (*ppState) ** is set to point to a buffer containing the transaction state data. ** (*pnState) is set to the size of that buffer in bytes. Otherwise, if ** an error occurs, an SQLite error code is returned and both output ** variables are zeroed. ** ** A transaction state may be saved if: ** ** * the transaction does not contain any schema modifications. ** * there are no open sub-transactions. */ int sqlite3_transaction_save(sqlite3 *db, void **ppState, int *pnState); int sqlite3_transaction_restore(sqlite3 *db, const void *pState, int nState); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 | #define SQLITE_PreferBuiltin 0x00200000 /* Preference to built-in funcs */ #define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */ #define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */ #define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */ #define SQLITE_QueryOnly 0x02000000 /* Disable database changes */ #define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */ /* ** Bits of the sqlite3.dbOptFlags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to ** selectively disable various optimizations. */ #define SQLITE_QueryFlattener 0x0001 /* Query flattening */ | > > | 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 | #define SQLITE_PreferBuiltin 0x00200000 /* Preference to built-in funcs */ #define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */ #define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */ #define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */ #define SQLITE_QueryOnly 0x02000000 /* Disable database changes */ #define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */ #define SQLITE_OtaMode 0x08000000 /* True in "ota mode" */ /* ** Bits of the sqlite3.dbOptFlags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to ** selectively disable various optimizations. */ #define SQLITE_QueryFlattener 0x0001 /* Query flattening */ |
︙ | ︙ | |||
3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 | # define sqlite3MemdebugNoType(X,Y) 1 #endif #define MEMTYPE_HEAP 0x01 /* General heap allocations */ #define MEMTYPE_LOOKASIDE 0x02 /* Might have been lookaside memory */ #define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */ #define MEMTYPE_PCACHE 0x08 /* Page cache allocations */ #define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */ /* ** Threading interface */ #if SQLITE_MAX_WORKER_THREADS>0 int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*); int sqlite3ThreadJoin(SQLiteThread*, void**); #endif #endif /* _SQLITEINT_H_ */ | > | 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 | # define sqlite3MemdebugNoType(X,Y) 1 #endif #define MEMTYPE_HEAP 0x01 /* General heap allocations */ #define MEMTYPE_LOOKASIDE 0x02 /* Might have been lookaside memory */ #define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */ #define MEMTYPE_PCACHE 0x08 /* Page cache allocations */ #define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */ /* ** Threading interface */ #if SQLITE_MAX_WORKER_THREADS>0 int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*); int sqlite3ThreadJoin(SQLiteThread*, void**); #endif #endif /* _SQLITEINT_H_ */ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 | extern int Sqlitetestintarray_Init(Tcl_Interp*); extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int Sqlitetestrtree_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); #endif #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); | > > | 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 | extern int Sqlitetestintarray_Init(Tcl_Interp*); extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int Sqlitetestrtree_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); extern int SqliteOta_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); #endif #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); |
︙ | ︙ | |||
3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 | Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); Sqlitetestrtree_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); #endif Tcl_CreateObjCommand( interp, "load_testfixture_extensions", init_all_cmd, 0, 0 | > | 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 | Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); Sqlitetestrtree_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); SqliteOta_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); #endif Tcl_CreateObjCommand( interp, "load_testfixture_extensions", init_all_cmd, 0, 0 |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 | return TCL_OK; sql_error: Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0); return TCL_ERROR; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 | return TCL_OK; sql_error: Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0); return TCL_ERROR; } /* ** tclcmd: sqlite3_transaction_save DB */ static int testTransactionSave( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ void *pState; int nState; sqlite3 *db; int rc; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_transaction_save(db, &pState, &nState); if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pState, nState)); }else{ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; } sqlite3_free(pState); return TCL_OK; } /* ** tclcmd: sqlite3_transaction_restore DB BLOB */ static int testTransactionRestore( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ void *pState; int nState; sqlite3 *db; int rc; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB BLOB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; pState = (void*)Tcl_GetByteArrayFromObj(objv[2], &nState); rc = sqlite3_transaction_restore(db, pState, nState); if( rc==SQLITE_OK ){ Tcl_ResetResult(interp); }else{ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; } return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; |
︙ | ︙ | |||
6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 | { "sqlite3_test_control", test_test_control }, #if SQLITE_OS_UNIX { "getrusage", test_getrusage }, #endif { "load_static_extension", tclLoadStaticExtensionCmd }, { "sorter_test_fakeheap", sorter_test_fakeheap }, { "sorter_test_sort4_helper", sorter_test_sort4_helper }, }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; | > > | 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 | { "sqlite3_test_control", test_test_control }, #if SQLITE_OS_UNIX { "getrusage", test_getrusage }, #endif { "load_static_extension", tclLoadStaticExtensionCmd }, { "sorter_test_fakeheap", sorter_test_fakeheap }, { "sorter_test_sort4_helper", sorter_test_sort4_helper }, { "sqlite3_transaction_save", testTransactionSave }, { "sqlite3_transaction_restore", testTransactionRestore }, }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; |
︙ | ︙ |
Changes to src/test2.c.
︙ | ︙ | |||
85 86 87 88 89 90 91 | int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pPager = sqlite3TestTextToPtr(argv[1]); | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pPager = sqlite3TestTextToPtr(argv[1]); rc = sqlite3PagerClose(pPager, 0); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; } |
︙ | ︙ |
Changes to src/vdbeblob.c.
︙ | ︙ | |||
458 459 460 461 462 463 464 465 466 | } rc = sqlite3ApiExit(db, rc); assert( rc==SQLITE_OK || p->pStmt==0 ); sqlite3_mutex_leave(db->mutex); return rc; } #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 | } rc = sqlite3ApiExit(db, rc); assert( rc==SQLITE_OK || p->pStmt==0 ); sqlite3_mutex_leave(db->mutex); return rc; } int sqlite3_index_writer( sqlite3 *db, int bDelete, const char *zIndex, sqlite3_stmt **ppStmt, int **paiCol, int *pnCol ){ int rc = SQLITE_OK; Parse *pParse = 0; Index *pIdx = 0; /* The index to write to */ Table *pTab; int i; /* Used to iterate through index columns */ Vdbe *v = 0; int regRec; /* Register to assemble record in */ int *aiCol = 0; sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); /* Allocate the parse context */ pParse = sqlite3StackAllocRaw(db, sizeof(*pParse)); if( !pParse ) goto index_writer_out; memset(pParse, 0, sizeof(Parse)); pParse->db = db; /* Allocate the Vdbe */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto index_writer_out; /* Find the index to write to */ pIdx = sqlite3FindIndex(db, zIndex, "main"); if( pIdx==0 ){ sqlite3ErrorMsg(pParse, "no such index: %s", zIndex); goto index_writer_out; } pTab = pIdx->pTable; /* Populate the two output variables, *pnCol and *pnAiCol. */ *pnCol = pIdx->nColumn; *paiCol = aiCol = sqlite3DbMallocZero(db, sizeof(int) * pIdx->nColumn); if( aiCol==0 ){ rc = SQLITE_NOMEM; goto index_writer_out; } for(i=0; i<pIdx->nKeyCol; i++){ aiCol[i] = pIdx->aiColumn[i]; } if( !HasRowid(pTab) ){ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); assert( pIdx->nColumn==pIdx->nKeyCol+pPk->nKeyCol ); if( pPk==pIdx ){ rc = SQLITE_ERROR; goto index_writer_out; } for(i=0; i<pPk->nKeyCol; i++){ aiCol[pIdx->nKeyCol+i] = pPk->aiColumn[i]; } }else{ assert( pIdx->nColumn==pIdx->nKeyCol+1 ); aiCol[i] = pTab->iPKey; } /* Add an OP_Noop to the VDBE program. Then store a pointer to the ** output array *paiCol as its P4 value. This is so that the array ** is automatically deleted when the user finalizes the statement. The ** OP_Noop serves no other purpose. */ sqlite3VdbeAddOp0(v, OP_Noop); sqlite3VdbeChangeP4(v, -1, (const char*)aiCol, P4_INTARRAY); sqlite3BeginWriteOperation(pParse, 0, 0); /* Open a write cursor on the index */ pParse->nTab = 1; sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, pIdx->tnum, 0); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); /* Create the record to insert into the index. Store it in register regRec. */ pParse->nVar = pIdx->nColumn; pParse->nMem = pIdx->nColumn; for(i=1; i<=pIdx->nColumn; i++){ sqlite3VdbeAddOp2(v, OP_Variable, i, i); } regRec = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_MakeRecord, 1, pIdx->nColumn, regRec); /* If this is a UNIQUE index, check the constraint. */ if( pIdx->onError ){ int addr = sqlite3VdbeAddOp4Int(v, OP_NoConflict, 0, 0, 1, pIdx->nKeyCol); sqlite3UniqueConstraint(pParse, SQLITE_ABORT, pIdx); sqlite3VdbeJumpHere(v, addr); } /* Code the IdxInsert to write to the b-tree index. */ sqlite3VdbeAddOp2(v, OP_IdxInsert, 0, regRec); sqlite3FinishCoding(pParse); index_writer_out: if( rc==SQLITE_OK && db->mallocFailed==0 ){ *ppStmt = (sqlite3_stmt*)v; }else{ *ppStmt = 0; if( v ) sqlite3VdbeFinalize(v); } sqlite3ParserReset(pParse); sqlite3StackFree(db, pParse); sqlite3BtreeLeaveAll(db); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ |
Changes to src/wal.c.
︙ | ︙ | |||
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | #endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ } return rc; } /* ** Recover the wal-index by reading the write-ahead log file. ** ** This routine first tries to establish an exclusive lock on the ** wal-index to prevent other threads/processes from doing anything ** with the WAL or wal-index while recovery is running. The | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 | #endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ } return rc; } static int walFileReadHdr(Wal *pWal, int *pbValid){ u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ int rc; /* Return code */ u32 magic; /* Magic value read from WAL header */ int szPage; /* Page size according to the log */ u32 version; /* Magic value read from WAL header */ *pbValid = 0; /* Read in the WAL header. */ rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ return rc; } /* If the database page size is not a power of two, or is greater than ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid ** data. Similarly, if the 'magic' value is invalid, ignore the whole ** WAL file. */ magic = sqlite3Get4byte(&aBuf[0]); szPage = sqlite3Get4byte(&aBuf[8]); if( (magic&0xFFFFFFFE)!=WAL_MAGIC || szPage&(szPage-1) || szPage>SQLITE_MAX_PAGE_SIZE || szPage<512 ){ return SQLITE_OK; } pWal->hdr.bigEndCksum = (u8)(magic&0x00000001); pWal->szPage = szPage; pWal->nCkpt = sqlite3Get4byte(&aBuf[12]); memcpy(&pWal->hdr.aSalt, &aBuf[16], 8); /* Verify that the WAL header checksum is correct */ walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum ); if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24]) || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28]) ){ return SQLITE_OK; } /* Verify that the version number on the WAL format is one that ** are able to understand */ version = sqlite3Get4byte(&aBuf[4]); if( version!=WAL_MAX_VERSION ){ return SQLITE_CANTOPEN_BKPT; } *pbValid = 1; return SQLITE_OK; } /* ** Recover the wal-index by reading the write-ahead log file. ** ** This routine first tries to establish an exclusive lock on the ** wal-index to prevent other threads/processes from doing anything ** with the WAL or wal-index while recovery is running. The |
︙ | ︙ | |||
1086 1087 1088 1089 1090 1091 1092 | rc = sqlite3OsFileSize(pWal->pWalFd, &nSize); if( rc!=SQLITE_OK ){ goto recovery_error; } if( nSize>WAL_HDRSIZE ){ | < < < | < | < < < < < < < < < < < < < < < | < < | < < < < < < < < < < < < < < < < < < < < | 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | rc = sqlite3OsFileSize(pWal->pWalFd, &nSize); if( rc!=SQLITE_OK ){ goto recovery_error; } if( nSize>WAL_HDRSIZE ){ u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ int szFrame; /* Number of bytes in buffer aFrame[] */ u8 *aData; /* Pointer to data part of aFrame buffer */ int iFrame; /* Index of last frame read */ i64 iOffset; /* Next offset to read from log file */ int szPage; /* Page size according to the log */ int isValid; /* True if this frame is valid */ rc = walFileReadHdr(pWal, &isValid); if( rc!=SQLITE_OK ) goto recovery_error; if( isValid==0 ) goto finished; szPage = pWal->szPage; /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)sqlite3_malloc(szFrame); if( !aFrame ){ rc = SQLITE_NOMEM; goto recovery_error; |
︙ | ︙ | |||
1833 1834 1835 1836 1837 1838 1839 | ** ordinary, rollback-mode locking methods, this guarantees that the ** connection associated with this log file is the only connection to ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ | > | | | | | | | | | | | | | | | | | | | | | | | | | | > | 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 | ** ordinary, rollback-mode locking methods, this guarantees that the ** connection associated with this log file is the only connection to ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ if( zBuf ){ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } rc = sqlite3WalCheckpoint( pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 ); if( rc==SQLITE_OK ){ int bPersist = -1; sqlite3OsFileControlHint( pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist ); if( bPersist!=1 ){ /* Try to delete the WAL file if the checkpoint completed and ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal ** mode (!bPersist) */ isDelete = 1; }else if( pWal->mxWalSize>=0 ){ /* Try to truncate the WAL file to zero bytes if the checkpoint ** completed and fsynced (rc==SQLITE_OK) and we are in persistent ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a ** non-negative value (pWal->mxWalSize>=0). Note that we truncate ** to zero bytes as truncating to the journal_size_limit might ** leave a corrupt WAL file on disk. */ walLimitSize(pWal, 0); } } } } walIndexClose(pWal, isDelete); sqlite3OsClose(pWal->pWalFd); if( isDelete ){ |
︙ | ︙ | |||
3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 | ** Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal){ return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); } #ifdef SQLITE_ENABLE_ZIPVFS /* ** If the argument is not NULL, it points to a Wal object that holds a ** read-lock. This function returns the database page-size if it is known, ** or zero if it is not (or if pWal is NULL). */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 | ** Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal){ return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); } /* ** Save current transaction state. ** ** The transaction state consists of a series of 32-bit big-endian integers: ** ** * initial number of frames in WAL file. ** * initial checksum values (2 integers). ** * current number of frames. ** * current checksum values (2 integers). */ int sqlite3WalSaveState(Wal *pWal, void **ppState, int *pnState){ int rc = SQLITE_OK; *ppState = 0; *pnState = 0; if( pWal->writeLock==0 ){ /* Must be in a write transaction to call this function. */ rc = SQLITE_ERROR; }else{ WalIndexHdr *pOrig = (WalIndexHdr*)walIndexHdr(pWal); int nBuf = 6 * 4; /* Bytes of space to allocate */ u8 *aBuf; aBuf = sqlite3_malloc(nBuf); if( aBuf==0 ){ rc = SQLITE_NOMEM; }else{ sqlite3Put4byte(&aBuf[0], pOrig->mxFrame); sqlite3Put4byte(&aBuf[4], pOrig->aFrameCksum[0]); sqlite3Put4byte(&aBuf[8], pOrig->aFrameCksum[1]); sqlite3Put4byte(&aBuf[12], pWal->hdr.mxFrame); sqlite3Put4byte(&aBuf[16], pWal->hdr.aFrameCksum[0]); sqlite3Put4byte(&aBuf[20], pWal->hdr.aFrameCksum[1]); *ppState = (void*)aBuf; *pnState = nBuf; } } return rc; } static int walUndoNoop(void *pUndoCtx, Pgno pgno){ UNUSED_PARAMETER(pUndoCtx); UNUSED_PARAMETER(pgno); return SQLITE_OK; } /* ** If possible, restore the state of the curent transaction to that ** described by the second and third arguments. */ int sqlite3WalRestoreState(Wal *pWal, const void *pState, int nState){ int rc = SQLITE_OK; if( pWal->writeLock==0 ){ /* Must have opened a write transaction to call this */ rc = SQLITE_ERROR; }else{ u8 *aBuf = (u8*)pState; int szFrame; /* Size of each frame in WAL file */ u8 *aFrame = 0; /* Buffer to read data into */ u8 *aData; /* Data part of aFrame[] buffer */ u32 mxFrame; /* Maximum frame following restoration */ int i; /* Iterator variable */ WalIndexHdr *pOrig = (WalIndexHdr*)walIndexHdr(pWal); /* Check that no dirty pages have been written to the WAL file since ** the current transaction was opened. */ if( pOrig->mxFrame!=pWal->hdr.mxFrame || pOrig->aFrameCksum[0]!=pWal->hdr.aFrameCksum[0] || pOrig->aFrameCksum[1]!=pWal->hdr.aFrameCksum[1] ){ rc = SQLITE_ERROR; } /* Check that the WAL file is in the same state that it was when the ** transaction was saved. If not, return SQLITE_MISMATCH - cannot ** resume this transaction */ if( rc==SQLITE_OK && ( pWal->hdr.mxFrame!=sqlite3Get4byte(&aBuf[0]) || pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[4]) || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[8]) )){ rc = SQLITE_MISMATCH; } if( rc==SQLITE_OK && pWal->readLock==0 ){ int cnt = 0; walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; do{ int notUsed; rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); }while( rc==WAL_RETRY ); if( rc==SQLITE_OK ){ int bValid; rc = walFileReadHdr(pWal, &bValid); if( rc==SQLITE_OK && bValid==0 ) rc = SQLITE_MISMATCH; pWal->hdr.szPage = (u16)((pWal->szPage&0xff00) | (pWal->szPage>>16)); } } /* Malloc a buffer to read frames into. */ if( rc==SQLITE_OK ){ szFrame = pWal->szPage + WAL_FRAME_HDRSIZE; aFrame = (u8*)sqlite3_malloc(szFrame); if( !aFrame ){ rc = SQLITE_NOMEM; }else{ aData = &aFrame[WAL_FRAME_HDRSIZE]; } } mxFrame = sqlite3Get4byte(&aBuf[12]); for(i=pWal->hdr.mxFrame+1; rc==SQLITE_OK && i<=mxFrame; i++){ sqlite3_int64 iOff = walFrameOffset(i, pWal->szPage); rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOff); if( rc==SQLITE_OK ){ u32 iPg; u32 dummy; if( 0==walDecodeFrame(pWal, &iPg, &dummy, aData, aFrame) ){ rc = SQLITE_MISMATCH; }else{ rc = walIndexAppend(pWal, i, iPg); if( iPg>pWal->hdr.nPage ) pWal->hdr.nPage = iPg; } pWal->hdr.mxFrame = i; } } sqlite3_free(aFrame); if( rc==SQLITE_OK ){ assert( pWal->hdr.mxFrame==mxFrame ); if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[16]) || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[20]) ){ rc = SQLITE_MISMATCH; } } if( rc!=SQLITE_OK ){ sqlite3WalUndo(pWal, walUndoNoop, 0); } } return rc; } #ifdef SQLITE_ENABLE_ZIPVFS /* ** If the argument is not NULL, it points to a Wal object that holds a ** read-lock. This function returns the database page-size if it is known, ** or zero if it is not (or if pWal is NULL). */ |
︙ | ︙ |
Changes to src/wal.h.
︙ | ︙ | |||
122 123 124 125 126 127 128 129 130 131 132 133 134 135 | /* Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal); #ifdef SQLITE_ENABLE_ZIPVFS /* If the WAL file is not empty, return the number of bytes of content ** stored in each frame (i.e. the db page-size when the WAL was created). */ int sqlite3WalFramesize(Wal *pWal); #endif | > > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /* Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal); int sqlite3WalSaveState(Wal *pWal, void **ppState, int *pnState); int sqlite3WalRestoreState(Wal *pWal, const void *pState, int nState); #ifdef SQLITE_ENABLE_ZIPVFS /* If the WAL file is not empty, return the number of bytes of content ** stored in each frame (i.e. the db page-size when the WAL was created). */ int sqlite3WalFramesize(Wal *pWal); #endif |
︙ | ︙ |