/ Check-in [3c2f4a07]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Switch back to using a single database connection in sqlite3ota.c.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | ota-update
Files: files | file ages | folders
SHA1:3c2f4a078132992e33cc675173c84f8385af9cb5
User & Date: dan 2014-09-05 19:52:42
Context
2014-09-06
20:19
Add support for delete operations to the ota extension. check-in: f988234b user: dan tags: ota-update
2014-09-05
19:52
Switch back to using a single database connection in sqlite3ota.c. check-in: 3c2f4a07 user: dan tags: ota-update
19:31
Reorganize the code in sqlite3ota.c in preparation for adding support for update and delete operations. check-in: 98387f05 user: dan tags: ota-update
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/ota/sqlite3ota.c.

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
...
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
...
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
...
696
697
698
699
700
701
702
703
704



705
706
707
708
709
710
711
712
713
714
715
...
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
**                  Or, if the main table is being written, a NULL value.
**
**   "row"       -> Number of rows for this object already processed
**
**   "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"        \
                             "(tbl, idx, row, progress)"

typedef struct OtaState OtaState;
typedef struct OtaObjIter OtaObjIter;
typedef unsigned char u8;

/*
................................................................................
  sqlite3_stmt *pInsert;          /* Statement for INSERT operations */
};

/*
** OTA handle.
*/
struct sqlite3ota {
  sqlite3 *dbDest;                /* Target db */
  sqlite3 *dbOta;                 /* Ota db */
  char *zTarget;                  /* Path to target db */

  int rc;                         /* Value returned by last ota_step() call */
  char *zErrmsg;                  /* Error message if rc!=SQLITE_OK */

  int nStep;                      /* Rows processed for current object */
  OtaObjIter objiter;
};

/*
** Prepare the SQL statement in buffer zSql against database handle db.
** If successful, set *ppStmt to point to the new statement and return
................................................................................
** left in the OTA handle passed as the first argument. A copy of the 
** error code is returned.
*/
static int otaObjIterFirst(sqlite3ota *p, OtaObjIter *pIter){
  int rc;
  memset(pIter, 0, sizeof(OtaObjIter));

  rc = prepareAndCollectError(p->dbOta, &pIter->pTblIter, &p->zErrmsg, 
      "SELECT substr(name, 6) FROM sqlite_master "
      "WHERE type='table' AND name LIKE 'data_%'"
  );

  if( rc==SQLITE_OK ){
    rc = prepareAndCollectError(p->dbDest, &pIter->pIdxIter, &p->zErrmsg,
        "SELECT name FROM sqlite_master "
        "WHERE type='index' AND tbl_name = ?"
    );
  }

  pIter->bCleanup = 1;
  p->rc = rc;
  return otaObjIterNext(p, pIter);
................................................................................
  if( pIter->azTblCol==0 ){
    sqlite3_stmt *pStmt;
    char *zSql;
    int nCol = 0;
    int bSeenPk = 0;
    int rc2;                      /* sqlite3_finalize() return value */

    zSql = sqlite3_mprintf("PRAGMA table_info(%Q)", pIter->zTbl);
    p->rc = prepareFreeAndCollectError(p->dbDest, &pStmt, &p->zErrmsg, zSql);
    while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
      if( (nCol % 8)==0 ){
        int nByte = sizeof(char*) * (nCol+8);
        char **azNew = (char**)sqlite3_realloc(pIter->azTblCol, nByte);
        u8 *abNew = (u8*)sqlite3_realloc(pIter->azTblCol, nCol+8);

        if( azNew ) pIter->azTblCol = azNew;
................................................................................

    if( zIdx ){
      int *aiCol;                 /* Column map */

      /* Create the index writer */
      if( p->rc==SQLITE_OK ){
        p->rc = sqlite3_index_writer(
            p->dbDest, 0, zIdx, &pIter->pInsert, &aiCol, &pIter->nCol
        );
      }

      /* Create the SELECT statement to read keys in sorted order */
      zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol);
      if( p->rc==SQLITE_OK ){
        p->rc = prepareFreeAndCollectError(p->dbOta, &pIter->pSelect, pz,
            sqlite3_mprintf(
              "SELECT %s FROM 'data_%q' ORDER BY %s%s",
              zCollist, pIter->zTbl, zCollist, zLimit
            )
        );
      }
    }else{
      char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol);
      zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0);
      pIter->nCol = pIter->nTblCol;

      /* Create the SELECT statement to read keys from data_xxx */
      if( p->rc==SQLITE_OK ){
        p->rc = prepareFreeAndCollectError(p->dbOta, &pIter->pSelect, pz,
            sqlite3_mprintf(
              "SELECT %s FROM 'data_%q'%s", 
              zCollist, pIter->zTbl, zLimit)
        );
      }

      /* Create the INSERT statement to write to the target PK b-tree */
      if( p->rc==SQLITE_OK ){
        p->rc = prepareFreeAndCollectError(p->dbDest, &pIter->pInsert, pz,
            sqlite3_mprintf(

              "INSERT INTO %Q(%s) VALUES(%s)", pIter->zTbl, zCollist, zBindings
            )
        );
      }
      sqlite3_free(zBindings);
    }
    sqlite3_free(zCollist);
    sqlite3_free(zLimit);
................................................................................
    if( p->rc==SQLITE_OK && pIter->zTbl==0 ){
      p->rc = SQLITE_DONE;
    }
  }
  return p->rc;
}

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){
  char *zInsert;

  zInsert = sqlite3_mprintf(
    "INSERT OR REPLACE INTO ota_state(rowid, tbl, idx, row, progress)"
    "VALUES(1, %Q, %Q, %d, NULL)",
    p->objiter.zTbl, p->objiter.zIdx, p->nStep
  );

  if( zInsert==0 ){
    p->rc = SQLITE_NOMEM;
  }else{
    p->rc = sqlite3_exec(p->dbOta, zInsert, 0, 0, &p->zErrmsg);
    if( p->rc==SQLITE_OK ){
      p->rc = sqlite3_exec(p->dbOta, "COMMIT", 0, 0, &p->zErrmsg);
    }
  }

  sqlite3_free(zInsert);
}

/*
** Allocate an OtaState object and load the contents of the ota_state 
** table into it. Return a pointer to the new object. It is the 
** responsibility of the caller to eventually free the object using
** sqlite3_free().
**
** If an error occurs, leave an error code and message in the ota handle
** and return NULL.
*/
static OtaState *otaLoadState(sqlite3ota *p){
  const char *zSelect = "SELECT tbl, idx, row, progress FROM ota_state";
  OtaState *pRet = 0;
  sqlite3_stmt *pStmt;
  int rc;

  assert( p->rc==SQLITE_OK );
  rc = prepareAndCollectError(p->dbOta, &pStmt, &p->zErrmsg, zSelect);
  if( rc==SQLITE_OK ){
    if( sqlite3_step(pStmt)==SQLITE_ROW ){
      const char *zIdx = (const char*)sqlite3_column_text(pStmt, 1);
      const char *zTbl = (const char*)sqlite3_column_text(pStmt, 0);
      int nIdx = zIdx ? (strlen(zIdx) + 1) : 0;
      int nTbl = strlen(zTbl) + 1;
      int nByte = sizeof(OtaState) + nTbl + nIdx;
................................................................................
  if( p ){
    OtaState *pState = 0;

    /* Open the target database */
    memset(p, 0, sizeof(sqlite3ota));
    p->zTarget = (char*)&p[1];
    memcpy(p->zTarget, zTarget, nTarget+1);
    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 ){
      pState = otaLoadState(p);
      if( pState && pState->zTbl==0 ){
        otaDeleteOalFile(p);
      }
................................................................................
    if( p->rc==SQLITE_OK ){
      const char *zScript =
        "PRAGMA journal_mode=off;"
        "PRAGMA pager_ota_mode=1;"
        "PRAGMA ota_mode=1;"
        "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 object iterator at the first object */
    if( p->rc==SQLITE_OK ){
      p->rc = otaObjIterFirst(p, &p->objiter);
    }

................................................................................
    }

    /* Close all open statement handles. */
    otaObjIterFinalize(&p->objiter);

    /* Commit the transaction to the *-oal file. */
    if( p->rc==SQLITE_OK || p->rc==SQLITE_DONE ){
      rc = sqlite3_exec(p->dbDest, "COMMIT", 0, 0, &p->zErrmsg);
      if( rc!=SQLITE_OK ) p->rc = rc;
    }

    assert( sqlite3_next_stmt(p->dbDest, 0)==0 );
    assert( sqlite3_next_stmt(p->dbOta, 0)==0 );

    /* Close the open database handles */
    sqlite3_close(p->dbDest);
    sqlite3_close(p->dbOta);

    /* If the OTA has been completely applied and no error occurred, move
    ** the *-oal file to *-wal. */
    if( p->rc==SQLITE_DONE ){
      otaMoveOalFile(p);
    }








|







 







|
<

<


<







 







|
|




|
|







 







|
|







 







|






|

|











|

|






|

>
|







 







<
>
>
>
>
>
>
>
>
>
>
>
>
>

<
>
>
|
<
<
>
>
>


>
>



|
<
<
|



<
<
<
<
<
<
<
<
<
<
<












|





|







 







|
|
>
>
>



|







 







<
<
<
<
<
|







 







|



<
<
<

<
|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
79
80
81
82
83
84
85
86

87

88
89

90
91
92
93
94
95
96
...
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
...
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
...
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
...
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
...
722
723
724
725
726
727
728





729
730
731
732
733
734
735
736
...
761
762
763
764
765
766
767
768
769
770
771



772

773
774
775
776
777
778
779
780
**                  Or, if the main table is being written, a NULL value.
**
**   "row"       -> Number of rows for this object already processed
**
**   "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.ota_state"        \
                             "(tbl, idx, row, progress)"

typedef struct OtaState OtaState;
typedef struct OtaObjIter OtaObjIter;
typedef unsigned char u8;

/*
................................................................................
  sqlite3_stmt *pInsert;          /* Statement for INSERT operations */
};

/*
** OTA handle.
*/
struct sqlite3ota {
  sqlite3 *db;                    /* "main" -> target db, "ota" -> ota db */

  char *zTarget;                  /* Path to target db */

  int rc;                         /* Value returned by last ota_step() call */
  char *zErrmsg;                  /* Error message if rc!=SQLITE_OK */

  int nStep;                      /* Rows processed for current object */
  OtaObjIter objiter;
};

/*
** Prepare the SQL statement in buffer zSql against database handle db.
** If successful, set *ppStmt to point to the new statement and return
................................................................................
** left in the OTA handle passed as the first argument. A copy of the 
** error code is returned.
*/
static int otaObjIterFirst(sqlite3ota *p, OtaObjIter *pIter){
  int rc;
  memset(pIter, 0, sizeof(OtaObjIter));

  rc = prepareAndCollectError(p->db, &pIter->pTblIter, &p->zErrmsg, 
      "SELECT substr(name, 6) FROM ota.sqlite_master "
      "WHERE type='table' AND name LIKE 'data_%'"
  );

  if( rc==SQLITE_OK ){
    rc = prepareAndCollectError(p->db, &pIter->pIdxIter, &p->zErrmsg,
        "SELECT name FROM main.sqlite_master "
        "WHERE type='index' AND tbl_name = ?"
    );
  }

  pIter->bCleanup = 1;
  p->rc = rc;
  return otaObjIterNext(p, pIter);
................................................................................
  if( pIter->azTblCol==0 ){
    sqlite3_stmt *pStmt;
    char *zSql;
    int nCol = 0;
    int bSeenPk = 0;
    int rc2;                      /* sqlite3_finalize() return value */

    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 ){
        int nByte = sizeof(char*) * (nCol+8);
        char **azNew = (char**)sqlite3_realloc(pIter->azTblCol, nByte);
        u8 *abNew = (u8*)sqlite3_realloc(pIter->azTblCol, nCol+8);

        if( azNew ) pIter->azTblCol = azNew;
................................................................................

    if( zIdx ){
      int *aiCol;                 /* Column map */

      /* Create the index writer */
      if( p->rc==SQLITE_OK ){
        p->rc = sqlite3_index_writer(
            p->db, 0, zIdx, &pIter->pInsert, &aiCol, &pIter->nCol
        );
      }

      /* Create the SELECT statement to read keys in sorted order */
      zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol);
      if( p->rc==SQLITE_OK ){
        p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
            sqlite3_mprintf(
              "SELECT %s FROM ota.'data_%q' ORDER BY %s%s",
              zCollist, pIter->zTbl, zCollist, zLimit
            )
        );
      }
    }else{
      char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol);
      zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0);
      pIter->nCol = pIter->nTblCol;

      /* Create the SELECT statement to read keys from data_xxx */
      if( p->rc==SQLITE_OK ){
        p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
            sqlite3_mprintf(
              "SELECT %s FROM ota.'data_%q'%s", 
              zCollist, pIter->zTbl, zLimit)
        );
      }

      /* Create the INSERT statement to write to the target PK b-tree */
      if( p->rc==SQLITE_OK ){
        p->rc = prepareFreeAndCollectError(p->db, &pIter->pInsert, pz,
            sqlite3_mprintf(
              "INSERT INTO main.%Q(%s) VALUES(%s)", 
              pIter->zTbl, zCollist, zBindings
            )
        );
      }
      sqlite3_free(zBindings);
    }
    sqlite3_free(zCollist);
    sqlite3_free(zLimit);
................................................................................
    if( p->rc==SQLITE_OK && pIter->zTbl==0 ){
      p->rc = SQLITE_DONE;
    }
  }
  return p->rc;
}


/*
** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
** arguments are the usual subsitution values. This function performs
** the printf() style substitutions and executes the result as an SQL
** statement on the OTA handles database.
**
** If an error occurs, an error code and error message is stored in the
** OTA handle. If an error has already occurred when this function is
** called, it is a no-op.
*/
static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){
  va_list ap;
  va_start(ap, zFmt);
  if( p->rc==SQLITE_OK ){

    char *zSql = sqlite3_vmprintf(zFmt, ap);
    if( zSql==0 ){
      p->rc = SQLITE_NOMEM;


    }else{
      p->rc = sqlite3_exec(p->db, zSql, 0, 0, &p->zErrmsg);
      sqlite3_free(zSql);
    }
  }
  va_end(ap);
  return p->rc;
}

static void otaSaveTransactionState(sqlite3ota *p){
  otaMPrintfExec(p, 


    "INSERT OR REPLACE INTO ota.ota_state(rowid, tbl, idx, row, progress)"
    "VALUES(1, %Q, %Q, %d, NULL)",
    p->objiter.zTbl, p->objiter.zIdx, p->nStep
  );











}

/*
** Allocate an OtaState object and load the contents of the ota_state 
** table into it. Return a pointer to the new object. It is the 
** responsibility of the caller to eventually free the object using
** sqlite3_free().
**
** If an error occurs, leave an error code and message in the ota handle
** and return NULL.
*/
static OtaState *otaLoadState(sqlite3ota *p){
  const char *zSelect = "SELECT tbl, idx, row, progress FROM ota.ota_state";
  OtaState *pRet = 0;
  sqlite3_stmt *pStmt;
  int rc;

  assert( p->rc==SQLITE_OK );
  rc = prepareAndCollectError(p->db, &pStmt, &p->zErrmsg, zSelect);
  if( rc==SQLITE_OK ){
    if( sqlite3_step(pStmt)==SQLITE_ROW ){
      const char *zIdx = (const char*)sqlite3_column_text(pStmt, 1);
      const char *zTbl = (const char*)sqlite3_column_text(pStmt, 0);
      int nIdx = zIdx ? (strlen(zIdx) + 1) : 0;
      int nTbl = strlen(zTbl) + 1;
      int nByte = sizeof(OtaState) + nTbl + nIdx;
................................................................................
  if( p ){
    OtaState *pState = 0;

    /* Open the target database */
    memset(p, 0, sizeof(sqlite3ota));
    p->zTarget = (char*)&p[1];
    memcpy(p->zTarget, zTarget, nTarget+1);
    p->rc = sqlite3_open(zTarget, &p->db);
    if( p->rc ){
      p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
    }
    otaMPrintfExec(p, "ATTACH %Q AS ota", zOta);

    /* If it has not already been created, create the ota_state table */
    if( p->rc==SQLITE_OK ){
      p->rc = sqlite3_exec(p->db, OTA_CREATE_STATE, 0, 0, &p->zErrmsg);
    }

    if( p->rc==SQLITE_OK ){
      pState = otaLoadState(p);
      if( pState && pState->zTbl==0 ){
        otaDeleteOalFile(p);
      }
................................................................................
    if( p->rc==SQLITE_OK ){
      const char *zScript =
        "PRAGMA journal_mode=off;"
        "PRAGMA pager_ota_mode=1;"
        "PRAGMA ota_mode=1;"
        "BEGIN IMMEDIATE;"
      ;





      p->rc = sqlite3_exec(p->db, zScript, 0, 0, &p->zErrmsg);
    }

    /* Point the object iterator at the first object */
    if( p->rc==SQLITE_OK ){
      p->rc = otaObjIterFirst(p, &p->objiter);
    }

................................................................................
    }

    /* Close all open statement handles. */
    otaObjIterFinalize(&p->objiter);

    /* Commit the transaction to the *-oal file. */
    if( p->rc==SQLITE_OK || p->rc==SQLITE_DONE ){
      rc = sqlite3_exec(p->db, "COMMIT", 0, 0, &p->zErrmsg);
      if( rc!=SQLITE_OK ) p->rc = rc;
    }




    /* Close the open database handles */

    sqlite3_close(p->db);

    /* If the OTA has been completely applied and no error occurred, move
    ** the *-oal file to *-wal. */
    if( p->rc==SQLITE_DONE ){
      otaMoveOalFile(p);
    }