From 87baa98f6766dc628a71767b195ed47c8ef6a583 Mon Sep 17 00:00:00 2001 From: Kenichi Ishigaki Date: Wed, 6 May 2009 07:55:35 +0000 Subject: [PATCH] DBD-SQLite: applied the online backup patch from Toby Corkindale (with a little modification) --- Changes | 1 + SQLite.xs | 23 ++++++++++++++ dbdimp.c | 60 +++++++++++++++++++++++++++++++++--- dbdimp.h | 2 ++ lib/DBD/SQLite.pm | 14 +++++++++ t/34_online_backup.t | 64 +++++++++++++++++++++++++++++++++++++++ t/34_online_backup_func.t | 58 +++++++++++++++++++++++++++++++++++ 7 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 t/34_online_backup.t create mode 100644 t/34_online_backup_func.t diff --git a/Changes b/Changes index add49d7..a660061 100644 --- a/Changes +++ b/Changes @@ -3,6 +3,7 @@ Changes for Perl extension DBD-SQLite 1.26_02 to be released - Started using install_method() suggested by TIMB (#44882) Ported last_insert_rowid as the first attempt (ISHIGAKI) + - Added access to Online Backup functionality. (TJC) 1.26_01 Tue 5 May 2009 - Added ORDINAL_POSITION support for $dbh->column_info (ADAMK) diff --git a/SQLite.xs b/SQLite.xs index 76aacf8..2bcafcb 100644 --- a/SQLite.xs +++ b/SQLite.xs @@ -94,6 +94,29 @@ busy_timeout(dbh, timeout=0) OUTPUT: RETVAL +static int +backup_from_file(dbh, filename) + SV *dbh + char *filename + ALIAS: + DBD::SQLite::db::sqlite_backup_from_file = 1 + CODE: + RETVAL = sqlite_db_backup_from_file(aTHX_ dbh, filename); + OUTPUT: + RETVAL + +static int +backup_to_file(dbh, filename) + SV *dbh + char *filename + ALIAS: + DBD::SQLite::db::sqlite_backup_to_file = 1 + CODE: + RETVAL = sqlite_db_backup_to_file(aTHX_ dbh, filename); + OUTPUT: + RETVAL + + MODULE = DBD::SQLite PACKAGE = DBD::SQLite::st PROTOTYPES: DISABLE diff --git a/dbdimp.c b/dbdimp.c index 89a531e..81d5671 100644 --- a/dbdimp.c +++ b/dbdimp.c @@ -1254,7 +1254,6 @@ sqlite_db_collation_dispatcher_utf8( return cmp; } - void sqlite3_db_create_collation(pTHX_ SV *dbh, const char *name, SV *func ) { @@ -1293,7 +1292,6 @@ sqlite3_db_create_collation(pTHX_ SV *dbh, const char *name, SV *func ) } } - static int sqlite_db_progress_handler_dispatcher( void *handler ) { @@ -1318,8 +1316,6 @@ sqlite_db_progress_handler_dispatcher( void *handler ) return retval; } - - void sqlite3_db_progress_handler(pTHX_ SV *dbh, int n_opcodes, SV *handler ) { @@ -1342,4 +1338,60 @@ sqlite3_db_progress_handler(pTHX_ SV *dbh, int n_opcodes, SV *handler ) } } +/* Accesses the SQLite Online Backup API, and fills the currently loaded + * database from the passed filename. + * Usual usage of this would be when you're operating on the :memory: + * special database connection and want to copy it in from a real db. + */ +int +sqlite_db_backup_from_file(pTHX_ SV *dbh, char *filename) +{ + int rc; + sqlite3 *pFrom; + sqlite3_backup *pBackup; + + D_imp_dbh(dbh); + + rc = sqlite3_open(filename, &pFrom); + if (rc==SQLITE_OK) { + + pBackup = sqlite3_backup_init(imp_dbh->db, "main", pFrom, "main"); + if (pBackup) { + (void)sqlite3_backup_step(pBackup, -1); + (void)sqlite3_backup_finish(pBackup); + } + rc = sqlite3_errcode(imp_dbh->db); + (void)sqlite3_close(pFrom); + } + return rc; +} + +/* Accesses the SQLite Online Backup API, and copies the currently loaded + * database into the passed filename. + * Usual usage of this would be when you're operating on the :memory: + * special database connection, and want to back it up to an on-disk file. + */ +int +sqlite_db_backup_to_file(pTHX_ SV *dbh, char *filename) +{ + int rc; + sqlite3 *pTo; + sqlite3_backup *pBackup; + + D_imp_dbh(dbh); + + rc = sqlite3_open(filename, &pTo); + if (rc==SQLITE_OK) { + + pBackup = sqlite3_backup_init(pTo, "main", imp_dbh->db, "main"); + if (pBackup) { + (void)sqlite3_backup_step(pBackup, -1); + (void)sqlite3_backup_finish(pBackup); + } + rc = sqlite3_errcode(pTo); + (void)sqlite3_close(pTo); + } + return rc; +} + /* end */ diff --git a/dbdimp.h b/dbdimp.h index 78f7c5e..311e26e 100644 --- a/dbdimp.h +++ b/dbdimp.h @@ -79,6 +79,8 @@ void sqlite3_db_progress_handler(pTHX_ SV *dbh, int n_opcodes, SV *handler); void sqlite_st_reset(pTHX_ SV *sth ); int sqlite_bind_col( SV *sth, imp_sth_t *imp_sth, SV *col, SV *ref, IV sql_type, SV *attribs ); int dbd_set_sqlite3_busy_timeout (pTHX_ SV *dbh, int timeout ); +int sqlite_db_backup_from_file(pTHX_ SV *dbh, char *filename); +int sqlite_db_backup_to_file(pTHX_ SV *dbh, char *filename); #ifdef SvUTF8_on diff --git a/lib/DBD/SQLite.pm b/lib/DBD/SQLite.pm index 2ccffae..f37ed14 100644 --- a/lib/DBD/SQLite.pm +++ b/lib/DBD/SQLite.pm @@ -32,6 +32,8 @@ sub driver { if (!$methods_are_installed && $DBI::VERSION >= 1.608) { DBI->setup_driver('DBD::SQLite'); DBD::SQLite::db->install_method('sqlite_last_insert_rowid'); + DBD::SQLite::db->install_method('sqlite_backup_from_file'); + DBD::SQLite::db->install_method('sqlite_backup_to_file'); $methods_are_installed++; } @@ -734,6 +736,18 @@ progress handler. =back +=head2 $dbh->sqlite_backup_from_file( $filename ) + +This method accesses the SQLite Online Backup API, and will take a backup of +the named database file, copying it to, and overwriting, your current database +connection. This can be particularly handy if your current connection is to the +special :memory: database, and you wish to populate it from an existing DB. + +=head2 $dbh->sqlite_backup_to_file( $filename ) + +This method accesses the SQLite Online Backup API, and will take a backup of +the currently connected database, and write it out to the named file. + =head1 BLOBS As of version 1.11, blobs should "just work" in SQLite as text columns. diff --git a/t/34_online_backup.t b/t/34_online_backup.t new file mode 100644 index 0000000..5be1146 --- /dev/null +++ b/t/34_online_backup.t @@ -0,0 +1,64 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; +use t::lib::Test; +use DBI; + +BEGIN { + plan skip_all => 'requires DBI v1.608' if $DBI::VERSION < 1.608; +} + +plan tests => 4; + +# Connect to the test db and add some stuff: +my $foo = connect_ok( RaiseError => 1 ); +$foo->do( + 'CREATE TABLE online_backup_test( id INTEGER PRIMARY KEY, foo INTEGER )' +); +$foo->do("INSERT INTO online_backup_test (foo) VALUES ($$)"); + +# That should be in the "foo" database on disk now, so disconnect and try to +# back it up: + +$foo->disconnect; + +my $dbh = DBI->connect( + 'dbi:SQLite:dbname=:memory:', + undef, undef, + { RaiseError => 1 } +); + +$dbh->sqlite_backup_from_file('foo'); + +{ + my ($count) = $dbh->selectrow_array( + "SELECT count(foo) FROM online_backup_test WHERE foo=$$" + ); + is($count, 1, "Found our process ID in backed-up table"); +} + +# Add more data then attempt to copy it back to file: +$dbh->do( + 'CREATE TABLE online_backup_test2 ( id INTEGER PRIMARY KEY, foo INTEGER )' +); +$dbh->do("INSERT INTO online_backup_test2 (foo) VALUES ($$)"); + +# backup to file (foo): +$dbh->sqlite_backup_to_file('foo'); + +$dbh->disconnect; + +# Reconnect to foo db and check data made it over: +{ + my $foo = connect_ok( RaiseError => 1 ); + + my ($count) = $foo->selectrow_array( + "SELECT count(foo) FROM online_backup_test2 WHERE foo=$$" + ); + is($count, 1, "Found our process ID in table back on disk"); + + $foo->disconnect; +} diff --git a/t/34_online_backup_func.t b/t/34_online_backup_func.t new file mode 100644 index 0000000..36c3163 --- /dev/null +++ b/t/34_online_backup_func.t @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More tests => 4; +use t::lib::Test; +use DBI; + +# Connect to the test db and add some stuff: +my $foo = connect_ok( RaiseError => 1 ); +$foo->do( + 'CREATE TABLE online_backup_test( id INTEGER PRIMARY KEY, foo INTEGER )' +); +$foo->do("INSERT INTO online_backup_test (foo) VALUES ($$)"); + +# That should be in the "foo" database on disk now, so disconnect and try to +# back it up: + +$foo->disconnect; + +my $dbh = DBI->connect( + 'dbi:SQLite:dbname=:memory:', + undef, undef, + { RaiseError => 1 } +); + +$dbh->func('foo', 'backup_from_file'); + +{ + my ($count) = $dbh->selectrow_array( + "SELECT count(foo) FROM online_backup_test WHERE foo=$$" + ); + is($count, 1, "Found our process ID in backed-up table"); +} + +# Add more data then attempt to copy it back to file: +$dbh->do( + 'CREATE TABLE online_backup_test2 ( id INTEGER PRIMARY KEY, foo INTEGER )' +); +$dbh->do("INSERT INTO online_backup_test2 (foo) VALUES ($$)"); + +# backup to file (foo): +$dbh->func('foo', 'backup_to_file'); + +$dbh->disconnect; + +# Reconnect to foo db and check data made it over: +{ + my $foo = connect_ok( RaiseError => 1 ); + + my ($count) = $foo->selectrow_array( + "SELECT count(foo) FROM online_backup_test2 WHERE foo=$$" + ); + is($count, 1, "Found our process ID in table back on disk"); + + $foo->disconnect; +}