@@ -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