Skip to content

Commit

Permalink
Add Support to import bz2 compressed database dumps (#61)
Browse files Browse the repository at this point in the history
* Support bz2 dump files

* Change Method Signature

* Update throws comments

---------

Co-authored-by: Stefan Zweifel <[email protected]>
  • Loading branch information
alies-dev and stefanzweifel authored Jan 31, 2024
1 parent 67edf10 commit 0ad5517
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 18 deletions.
22 changes: 16 additions & 6 deletions src/Databases/MySql.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

namespace Wnx\LaravelBackupRestore\Databases;

use Illuminate\Support\Facades\File;
use Spatie\Backup\Exceptions\CannotCreateDbDumper;
use Spatie\Backup\Tasks\Backup\DbDumperFactory;
use Wnx\LaravelBackupRestore\Exceptions\ImportFailed;

class MySql extends DbImporter
{
/**
* @throws CannotCreateDbDumper
* @throws CannotCreateDbDumper|ImportFailed
*/
public function getImportCommand(string $dumpFile, string $connection): string
{
Expand All @@ -28,11 +30,10 @@ public function getImportCommand(string $dumpFile, string $connection): string
'password' => config("database.connections.{$connection}.password"),
];

// Build Shell Command to import a gzipped SQL file to a MySQL database
if (str($dumpFile)->endsWith('gz')) {
$command = $this->getMySqlImportCommandForCompressedDump($dumpFile, $importToDatabase, $credentialsArray);
} else {
if (str($dumpFile)->endsWith('sql')) {
$command = $this->getMySqlImportCommandForUncompressedDump($importToDatabase, $dumpFile, $credentialsArray);
} else {
$command = $this->getMySqlImportCommandForCompressedDump($dumpFile, $importToDatabase, $credentialsArray);
}

return $command;
Expand All @@ -43,13 +44,22 @@ public function getCliName(): string
return 'mysql';
}

/**
* @throws ImportFailed
*/
private function getMySqlImportCommandForCompressedDump(string $storagePathToDatabaseFile, string $importToDatabase, array $credentials): string
{
$quote = $this->determineQuote();
$password = $credentials['password'];

$decompressCommand = match (File::extension($storagePathToDatabaseFile)) {
'gz' => "gunzip < {$storagePathToDatabaseFile}",
'bz2' => "bunzip2 -c {$storagePathToDatabaseFile}",
default => throw ImportFailed::decompressionFailed($storagePathToDatabaseFile, 'Unknown compression format'),
};

return collect([
"gunzip < {$storagePathToDatabaseFile}",
$decompressCommand,
'|',
"{$quote}{$this->dumpBinaryPath}mysql{$quote}",
'-u', $credentials['user'],
Expand Down
20 changes: 14 additions & 6 deletions src/Databases/PostgreSql.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

namespace Wnx\LaravelBackupRestore\Databases;

use Illuminate\Support\Facades\File;
use Spatie\Backup\Exceptions\CannotCreateDbDumper;
use Spatie\Backup\Tasks\Backup\DbDumperFactory;
use Wnx\LaravelBackupRestore\Exceptions\ImportFailed;

class PostgreSql extends DbImporter
{
/**
* @throws CannotCreateDbDumper
* @throws CannotCreateDbDumper|ImportFailed
*/
public function getImportCommand(string $dumpFile, string $connection): string
{
Expand All @@ -22,24 +24,30 @@ public function getImportCommand(string $dumpFile, string $connection): string
$dumper = DbDumperFactory::createFromConnection($connection);
$dumper->getContentsOfCredentialsFile();

// @todo: Improve detection of compressed files
if (str($dumpFile)->endsWith('gz')) {
if (str($dumpFile)->endsWith('sql')) {
return collect([
'gunzip -c '.$dumpFile,
'|',
$this->dumpBinaryPath.'psql',
'-h '.config("database.connections.{$connection}.host"),
'-U '.config("database.connections.{$connection}.username"),
'-d '.config("database.connections.{$connection}.database"),
'< '.$dumpFile,
])->implode(' ');
}

// @todo: Improve detection of compressed files
$decompressCommand = match (File::extension($dumpFile)) {
'gz' => "gunzip -c {$dumpFile}",
'bz2' => "bunzip2 -c {$dumpFile}",
default => throw ImportFailed::decompressionFailed($dumpFile, 'Unknown compression format'),
};

return collect([
$decompressCommand,
'|',
$this->dumpBinaryPath.'psql',
'-h '.config("database.connections.{$connection}.host"),
'-U '.config("database.connections.{$connection}.username"),
'-d '.config("database.connections.{$connection}.database"),
'< '.$dumpFile,
])->implode(' ');
}

Expand Down
21 changes: 16 additions & 5 deletions src/Databases/Sqlite.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,28 @@

namespace Wnx\LaravelBackupRestore\Databases;

use Illuminate\Support\Facades\File;
use Wnx\LaravelBackupRestore\Exceptions\ImportFailed;

class Sqlite extends DbImporter
{
/**
* @throws ImportFailed
*/
public function getImportCommand(string $dumpFile, string $connection): string
{
// @todo: Improve detection of compressed files
if (str($dumpFile)->endsWith('gz')) {
// Shell command to import a gzipped SQL file to a sqlite database
return 'gunzip -c '.$dumpFile.' | sqlite3 '.config("database.connections.{$connection}.database");
if (str($dumpFile)->endsWith('sql')) {
return 'sqlite3 '.config("database.connections.{$connection}.database").' < '.$dumpFile;
}

return 'sqlite3 '.config("database.connections.{$connection}.database").' < '.$dumpFile;
// @todo: Improve detection of compressed files
$decompressCommand = match (File::extension($dumpFile)) {
'gz' => "gunzip -c {$dumpFile}",
'bz2' => "bunzip2 -c {$dumpFile}",
default => throw ImportFailed::decompressionFailed($dumpFile, 'Unknown compression format'),
};

return "$decompressCommand | sqlite3 ".config("database.connections.{$connection}.database");
}

public function getCliName(): string
Expand Down
5 changes: 5 additions & 0 deletions src/Exceptions/ImportFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public static function processDidNotEndSuccessfully(ProcessResult $process): sta
return new static("The import process failed with a none successful exitcode.{$processOutput}");
}

public static function decompressionFailed(string $filename, string $reason): static
{
return new static("Could not decompress $filename dump file: $reason");
}

protected static function formatProcessOutput(ProcessResult $process): string
{
$output = $process->output() ?: '<no output>';
Expand Down
2 changes: 1 addition & 1 deletion src/PendingRestore.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@ public function getAvailableDbDumps(): Collection
->files($this->getPathToLocalDecompressedBackup().DIRECTORY_SEPARATOR.'db-dumps');

return collect($files)
->filter(fn ($file) => Str::endsWith($file, ['.sql', '.sql.gz']));
->filter(fn ($file) => Str::endsWith($file, ['.sql', '.sql.gz', '.sql.bz2']));
}
}
1 change: 1 addition & 0 deletions tests/Databases/MySqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
})->with([
__DIR__.'/../storage/Laravel/2023-01-28-mysql-no-compression-no-encryption.sql',
__DIR__.'/../storage/Laravel/2023-01-28-mysql-compression-no-encryption.sql.gz',
__DIR__.'/../storage/Laravel/2023-01-28-mysql-compression-no-encryption.sql.bz2',
]);

it('uses default binary path to import mysql dump', function () {
Expand Down
1 change: 1 addition & 0 deletions tests/Databases/PostgreSqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
})->with([
__DIR__.'/../storage/Laravel/2023-03-04-pgsql-no-compression-no-encryption.sql',
__DIR__.'/../storage/Laravel/2023-03-04-pgsql-compression-no-encryption.sql.gz',
__DIR__.'/../storage/Laravel/2023-03-04-pgsql-compression-no-encryption.sql.bz2',
])->group('pgsql');

it('uses default binary to import pgsql dump', function () {
Expand Down
1 change: 1 addition & 0 deletions tests/Databases/SqliteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
})->with([
__DIR__.'/../storage/Laravel/2023-02-28-sqlite-no-compression-no-encryption.sql',
__DIR__.'/../storage/Laravel/2023-02-28-sqlite-compression-no-encryption.sql.gz',
__DIR__.'/../storage/Laravel/2023-02-28-sqlite-compression-no-encryption.sql.bz2',
]);

it('throws import failed exception if sqlite dump could not be imported')
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 0ad5517

Please sign in to comment.