Skip to content

Commit f60e962

Browse files
committed
move sqlcipher_export to crypto.c
1 parent 9aa1310 commit f60e962

2 files changed

Lines changed: 197 additions & 197 deletions

File tree

src/crypto.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,203 @@ void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) {
423423
}
424424
}
425425

426+
#ifndef OMIT_EXPORT
427+
428+
/*
429+
* Implementation of an "export" function that allows a caller
430+
* to duplicate the main database to an attached database. This is intended
431+
* as a conveneince for users who need to:
432+
*
433+
* 1. migrate from an non-encrypted database to an encrypted database
434+
* 2. move from an encrypted database to a non-encrypted database
435+
* 3. convert beween the various flavors of encrypted databases.
436+
*
437+
* This implementation is based heavily on the procedure and code used
438+
* in vacuum.c, but is exposed as a function that allows export to any
439+
* named attached database.
440+
*/
441+
442+
/*
443+
** Finalize a prepared statement. If there was an error, store the
444+
** text of the error message in *pzErrMsg. Return the result code.
445+
**
446+
** Based on vacuumFinalize from vacuum.c
447+
*/
448+
static int sqlcipher_finalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
449+
int rc;
450+
rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
451+
if( rc ){
452+
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
453+
}
454+
return rc;
455+
}
456+
457+
/*
458+
** Execute zSql on database db. Return an error code.
459+
**
460+
** Based on execSql from vacuum.c
461+
*/
462+
static int sqlcipher_execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
463+
sqlite3_stmt *pStmt;
464+
VVA_ONLY( int rc; )
465+
if( !zSql ){
466+
return SQLITE_NOMEM;
467+
}
468+
if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
469+
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
470+
return sqlite3_errcode(db);
471+
}
472+
VVA_ONLY( rc = ) sqlite3_step(pStmt);
473+
assert( rc!=SQLITE_ROW );
474+
return sqlcipher_finalize(db, pStmt, pzErrMsg);
475+
}
476+
477+
/*
478+
** Execute zSql on database db. The statement returns exactly
479+
** one column. Execute this as SQL on the same database.
480+
**
481+
** Based on execExecSql from vacuum.c
482+
*/
483+
static int sqlcipher_execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
484+
sqlite3_stmt *pStmt;
485+
int rc;
486+
487+
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
488+
if( rc!=SQLITE_OK ) return rc;
489+
490+
while( SQLITE_ROW==sqlite3_step(pStmt) ){
491+
rc = sqlcipher_execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
492+
if( rc!=SQLITE_OK ){
493+
sqlcipher_finalize(db, pStmt, pzErrMsg);
494+
return rc;
495+
}
496+
}
497+
498+
return sqlcipher_finalize(db, pStmt, pzErrMsg);
499+
}
500+
501+
/*
502+
* copy database and schema from the main database to an attached database
503+
*
504+
* Based on sqlite3RunVacuum from vacuum.c
505+
*/
506+
void sqlcipher_exportFunc(sqlite3_context *context, int argc, sqlite3_value **argv) {
507+
sqlite3 *db = sqlite3_context_db_handle(context);
508+
const char* attachedDb = (const char*) sqlite3_value_text(argv[0]);
509+
int saved_flags; /* Saved value of the db->flags */
510+
int saved_nChange; /* Saved value of db->nChange */
511+
int saved_nTotalChange; /* Saved value of db->nTotalChange */
512+
void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */
513+
int rc = SQLITE_OK; /* Return code from service routines */
514+
char *zSql = NULL; /* SQL statements */
515+
char *pzErrMsg = NULL;
516+
517+
saved_flags = db->flags;
518+
saved_nChange = db->nChange;
519+
saved_nTotalChange = db->nTotalChange;
520+
saved_xTrace = db->xTrace;
521+
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
522+
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
523+
db->xTrace = 0;
524+
525+
/* Query the schema of the main database. Create a mirror schema
526+
** in the temporary database.
527+
*/
528+
zSql = sqlite3_mprintf(
529+
"SELECT 'CREATE TABLE %s.' || substr(sql,14) "
530+
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
531+
" AND rootpage>0"
532+
, attachedDb);
533+
rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
534+
if( rc!=SQLITE_OK ) goto end_of_export;
535+
sqlite3_free(zSql);
536+
537+
zSql = sqlite3_mprintf(
538+
"SELECT 'CREATE INDEX %s.' || substr(sql,14)"
539+
" FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %%' "
540+
, attachedDb);
541+
rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
542+
if( rc!=SQLITE_OK ) goto end_of_export;
543+
sqlite3_free(zSql);
544+
545+
zSql = sqlite3_mprintf(
546+
"SELECT 'CREATE UNIQUE INDEX %s.' || substr(sql,21) "
547+
" FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %%'"
548+
, attachedDb);
549+
rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
550+
if( rc!=SQLITE_OK ) goto end_of_export;
551+
sqlite3_free(zSql);
552+
553+
/* Loop through the tables in the main database. For each, do
554+
** an "INSERT INTO rekey_db.xxx SELECT * FROM main.xxx;" to copy
555+
** the contents to the temporary database.
556+
*/
557+
zSql = sqlite3_mprintf(
558+
"SELECT 'INSERT INTO %s.' || quote(name) "
559+
"|| ' SELECT * FROM main.' || quote(name) || ';'"
560+
"FROM main.sqlite_master "
561+
"WHERE type = 'table' AND name!='sqlite_sequence' "
562+
" AND rootpage>0"
563+
, attachedDb);
564+
rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
565+
if( rc!=SQLITE_OK ) goto end_of_export;
566+
sqlite3_free(zSql);
567+
568+
/* Copy over the sequence table
569+
*/
570+
zSql = sqlite3_mprintf(
571+
"SELECT 'DELETE FROM %s.' || quote(name) || ';' "
572+
"FROM %s.sqlite_master WHERE name='sqlite_sequence' "
573+
, attachedDb, attachedDb);
574+
rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
575+
if( rc!=SQLITE_OK ) goto end_of_export;
576+
sqlite3_free(zSql);
577+
578+
zSql = sqlite3_mprintf(
579+
"SELECT 'INSERT INTO %s.' || quote(name) "
580+
"|| ' SELECT * FROM main.' || quote(name) || ';' "
581+
"FROM %s.sqlite_master WHERE name=='sqlite_sequence';"
582+
, attachedDb, attachedDb);
583+
rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
584+
if( rc!=SQLITE_OK ) goto end_of_export;
585+
sqlite3_free(zSql);
586+
587+
/* Copy the triggers, views, and virtual tables from the main database
588+
** over to the temporary database. None of these objects has any
589+
** associated storage, so all we have to do is copy their entries
590+
** from the SQLITE_MASTER table.
591+
*/
592+
zSql = sqlite3_mprintf(
593+
"INSERT INTO %s.sqlite_master "
594+
" SELECT type, name, tbl_name, rootpage, sql"
595+
" FROM main.sqlite_master"
596+
" WHERE type='view' OR type='trigger'"
597+
" OR (type='table' AND rootpage=0)"
598+
, attachedDb);
599+
rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execSql(db, &pzErrMsg, zSql);
600+
if( rc!=SQLITE_OK ) goto end_of_export;
601+
sqlite3_free(zSql);
602+
603+
zSql = NULL;
604+
end_of_export:
605+
db->flags = saved_flags;
606+
db->nChange = saved_nChange;
607+
db->nTotalChange = saved_nTotalChange;
608+
db->xTrace = saved_xTrace;
609+
610+
sqlite3_free(zSql);
611+
612+
if(rc) {
613+
if(pzErrMsg != NULL) {
614+
sqlite3_result_error(context, pzErrMsg, -1);
615+
sqlite3DbFree(db, pzErrMsg);
616+
} else {
617+
sqlite3_result_error(context, sqlite3ErrStr(rc), -1);
618+
}
619+
}
620+
}
621+
622+
#endif
426623

427624
/* END CRYPTO */
428625
#endif

0 commit comments

Comments
 (0)