Skip to content

Commit

Permalink
Implement SQLite3 backup API
Browse files Browse the repository at this point in the history
  • Loading branch information
BohwaZ authored and cmb69 committed Jun 17, 2019
1 parent d924b42 commit ce22ccc
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ PHP NEWS
. Fixed bug #78106 (Path resolution fails if opcache disabled during request).
(Nikita)

- SQLite3:
. Implement FR ##70950 (Make SQLite3 Online Backup API available). (BohwaZ)

13 Jun 2019, PHP 7.4.0alpha1

- Core:
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ PHP 7.4 UPGRADE NOTES
. Added SQLite3Stmt::getSQL() to retrieve the SQL of the statement. If TRUE is
passed as parameter, query parameters will be replaced in the return value
by their currently bound value, if libsqlite ≥ 3.14 is used.
. Added SQLite3::backup() to create database backups via the SQLite3 online
backup API.

- Standard
. bool sapi_windows_set_ctrl_handler(callable handler, [, bool add = true]) -
Expand Down
68 changes: 68 additions & 0 deletions ext/sqlite3/sqlite3.c
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,63 @@ PHP_METHOD(sqlite3, enableExceptions)
}
/* }}} */

#if SQLITE_VERSION_NUMBER >= 3006011
/* {{{ proto bool SQLite3::backup(SQLite3 destination_db[, string source_dbname = "main"[, string destination_dbname = "main"]])
Backups the current database to another one. */
PHP_METHOD(sqlite3, backup)
{
php_sqlite3_db_object *source_obj;
php_sqlite3_db_object *destination_obj;
char *source_dbname = "main", *destination_dbname = "main";
size_t source_dbname_length, destination_dbname_length;
zval *source_zval = ZEND_THIS;
zval *destination_zval;
sqlite3_backup *dbBackup;
int rc; // Return code

source_obj = Z_SQLITE3_DB_P(source_zval);
SQLITE3_CHECK_INITIALIZED(source_obj, source_obj->initialised, SQLite3)

if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|ss", &destination_zval, php_sqlite3_sc_entry, &source_dbname, &source_dbname_length, &destination_dbname, &destination_dbname_length) == FAILURE) {
return;
}

destination_obj = Z_SQLITE3_DB_P(destination_zval);

SQLITE3_CHECK_INITIALIZED(destination_obj, destination_obj->initialised, SQLite3)

dbBackup = sqlite3_backup_init(destination_obj->db, destination_dbname, source_obj->db, source_dbname);

if (dbBackup) {
do {
rc = sqlite3_backup_step(dbBackup, -1);
} while (rc == SQLITE_OK);

/* Release resources allocated by backup_init(). */
rc = sqlite3_backup_finish(dbBackup);
}
else {
rc = sqlite3_errcode(source_obj->db);
}

if (rc != SQLITE_OK) {
if (rc == SQLITE_BUSY) {
php_sqlite3_error(source_obj, "Backup failed: source database is busy");
}
else if (rc == SQLITE_LOCKED) {
php_sqlite3_error(source_obj, "Backup failed: source database is locked");
}
else {
php_sqlite3_error(source_obj, "Backup failed: %d, %s", rc, sqlite3_errmsg(source_obj->db));
}
RETURN_FALSE;
}

RETURN_TRUE;
}
/* }}} */
#endif

/* {{{ proto int SQLite3Stmt::paramCount()
Returns the number of parameters within the prepared statement. */
PHP_METHOD(sqlite3stmt, paramCount)
Expand Down Expand Up @@ -2058,6 +2115,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_enableexceptions, 0, 0, 0)
ZEND_ARG_INFO(0, enableExceptions)
ZEND_END_ARG_INFO()

#if SQLITE_VERSION_NUMBER >= 3006011
ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_backup, 0, 0, 1)
ZEND_ARG_INFO(0, destination_db)
ZEND_ARG_INFO(0, source_dbname)
ZEND_ARG_INFO(0, destination_dbname)
ZEND_END_ARG_INFO()
#endif

ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3stmt_bindparam, 0, 0, 2)
ZEND_ARG_INFO(0, param_number)
ZEND_ARG_INFO(1, param)
Expand Down Expand Up @@ -2117,6 +2182,9 @@ static const zend_function_entry php_sqlite3_class_methods[] = {
PHP_ME(sqlite3, createCollation, arginfo_sqlite3_createcollation, ZEND_ACC_PUBLIC)
PHP_ME(sqlite3, openBlob, arginfo_sqlite3_openblob, ZEND_ACC_PUBLIC)
PHP_ME(sqlite3, enableExceptions, arginfo_sqlite3_enableexceptions, ZEND_ACC_PUBLIC)
#if SQLITE_VERSION_NUMBER >= 3006011
PHP_ME(sqlite3, backup, arginfo_sqlite3_backup, ZEND_ACC_PUBLIC)
#endif
/* Aliases */
PHP_MALIAS(sqlite3, __construct, open, arginfo_sqlite3_open, ZEND_ACC_PUBLIC)
PHP_FE_END
Expand Down
58 changes: 58 additions & 0 deletions ext/sqlite3/tests/sqlite3_38_backup.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
--TEST--
SQLite3::backup test
--SKIPIF--
<?php require_once(__DIR__ . '/skipif.inc'); ?>
--FILE--
<?php

require_once(__DIR__ . '/new_db.inc');

echo "Creating table\n";
$db->exec('CREATE TABLE test (a, b);');
$db->exec('INSERT INTO test VALUES (42, \'php\');');

echo "Checking if table has been created\n";
var_dump($db->querySingle('SELECT COUNT(*) FROM sqlite_master;'));

$db2 = new SQLite3(':memory:');

echo "Backup to DB2\n";
var_dump($db->backup($db2));

echo "Checking if table has been copied\n";
var_dump($db2->querySingle('SELECT COUNT(*) FROM sqlite_master;'));

echo "Checking backup contents\n";
var_dump($db2->querySingle('SELECT a FROM test;'));
var_dump($db2->querySingle('SELECT b FROM test;'));

echo "Resetting DB2\n";

$db2->close();
$db2 = new SQLite3(':memory:');

echo "Locking DB1\n";
var_dump($db->exec('BEGIN EXCLUSIVE;'));

echo "Backup to DB2 (should fail)\n";
var_dump($db->backup($db2));

?>
--EXPECTF--
Creating table
Checking if table has been created
int(1)
Backup to DB2
bool(true)
Checking if table has been copied
int(1)
Checking backup contents
int(42)
string(3) "php"
Resetting DB2
Locking DB1
bool(true)
Backup to DB2 (should fail)

Warning: SQLite3::backup(): Backup failed: source database is busy in %s on line %d
bool(false)

0 comments on commit ce22ccc

Please sign in to comment.