Skip to content

Commit 536211e

Browse files
authored
Merge pull request #1 from orisintel/feature/sqlite
feat(Sqlite): Initial support for Sqlite3 databases.
2 parents fd7ef29 + 7438b0d commit 536211e

File tree

10 files changed

+126
-10
lines changed

10 files changed

+126
-10
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ sudo: required
1919
before_script:
2020
- travis_retry composer self-update
2121
- travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source
22-
- mysql -e 'CREATE DATABASE test_ms;'
23-
- psql -c 'CREATE DATABASE test_ms;' -U postgres
22+
- mysql -e 'CREATE DATABASE forge;'
23+
- psql -c 'CREATE DATABASE forge;' -U postgres
2424
- psql -c 'CREATE ROLE root SUPERUSER LOGIN;' -U postgres
2525

2626
script:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
Simplify and accelerate applying many migrations at once using a flattened dump
88
of the database schema and migrations, similar in spirit to Rails' `schema.rb`.
99

10-
Works with the `mysql` and `pgsql` database drivers.
10+
Works with the `mysql`, `pgsql`, and `sqlite` database drivers.
1111

1212
## Installation
1313

phpunit.xml.travis

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
</logging>
2929

3030
<php>
31-
<env name="DB_DATABASE" value="test_ms"/>
3231
<env name="DB_USERNAME" value="root"/>
3332
</php>
3433
</phpunit>

src/Commands/MigrateDumpCommand.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
final class MigrateDumpCommand extends \Illuminate\Console\Command
66
{
77
public const SCHEMA_SQL_PATH_SUFFIX = '/migrations/sql/schema.sql';
8-
public const SUPPORTED_DB_DRIVERS = ['mysql', 'pgsql'];
8+
public const SUPPORTED_DB_DRIVERS = ['mysql', 'pgsql', 'sqlite'];
99

1010
protected $signature = 'migrate:dump
1111
{--database= : The database connection to use}';
@@ -78,6 +78,9 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i
7878
. ' --user=' . escapeshellarg($db_config['username'])
7979
. ' --password=' . escapeshellarg($db_config['password'])
8080
. ' ' . escapeshellarg($db_config['database']);
81+
// TODO: Suppress warning about insecure password.
82+
// CONSIDER: Intercepting stdout and stderr and converting to colorized
83+
// console output with `$this->info` and `->error`.
8184
passthru(
8285
$command_prefix
8386
. ' --result-file=' . escapeshellarg($schema_sql_path)
@@ -127,6 +130,7 @@ private static function pgsqlDump(array $db_config, string $schema_sql_path) : i
127130
. ' --port=' . escapeshellarg($db_config['port'])
128131
. ' --username=' . escapeshellarg($db_config['username'])
129132
. ' ' . escapeshellarg($db_config['database']);
133+
// TODO: Suppress warning about insecure password.
130134
passthru(
131135
$command_prefix
132136
. ' --file=' . escapeshellarg($schema_sql_path)
@@ -148,4 +152,41 @@ private static function pgsqlDump(array $db_config, string $schema_sql_path) : i
148152

149153
return $exit_code;
150154
}
155+
156+
/**
157+
* @param array $db_config like ['host' => , 'port' => ].
158+
* @param string $schema_sql_path like '.../schema.sql'
159+
*
160+
* @return int containing exit code.
161+
*/
162+
private static function sqliteDump(array $db_config, string $schema_sql_path) : int
163+
{
164+
// CONSIDER: Accepting command name as option or from config.
165+
$command_prefix = 'sqlite3 ' . escapeshellarg($db_config['database']);
166+
167+
// Since Sqlite lacks Information Schema, and dumping everything may be
168+
// too slow or memory intense, just query tables and dump them
169+
// individually.
170+
exec($command_prefix . ' .tables', $output, $exit_code);
171+
if (0 !== $exit_code) {
172+
return $exit_code;
173+
}
174+
$tables = preg_split('/\s+/', implode(' ', $output));
175+
176+
foreach ($tables as $table) {
177+
// Only migrations should dump data with schema.
178+
$sql_command = 'migrations' === $table ? '.dump' : '.schema';
179+
180+
passthru(
181+
$command_prefix . ' ' . escapeshellarg("$sql_command $table")
182+
. ' >> ' . escapeshellarg($schema_sql_path),
183+
$exit_code
184+
);
185+
if (0 !== $exit_code) {
186+
return $exit_code;
187+
}
188+
}
189+
190+
return $exit_code;
191+
}
151192
}

src/Commands/MigrateLoadCommand.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,17 @@ private static function pgsqlLoad(string $path, array $db_config, int $verbosity
145145

146146
return $exit_code;
147147
}
148+
149+
private static function sqliteLoad(string $path, array $db_config, int $verbosity = null) : int
150+
{
151+
// CONSIDER: Directly sending queries via Eloquent (requires parsing SQL
152+
// or intermediate format).
153+
// CONSIDER: Capturing Stderr and outputting with `$this->error()`.
154+
155+
$command = 'sqlite3 ' . escapeshellarg($db_config['database']) . ' ' . escapeshellarg(".read $path");
156+
157+
passthru($command, $exit_code);
158+
159+
return $exit_code;
160+
}
148161
}

src/Handlers/MigrateStartingHandler.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ public function handle(CommandStarting $event)
6666
// Must pass along options or it may use wrong DB or have
6767
// inconsistent output.
6868
$options = self::inputToArtisanOptions($event->input);
69-
$database = $options['--database'] ?? env('DB_CONNECTION');
70-
$db_driver = \DB::connection($database)->getDriverName();
69+
$database = $options['--database'] ?? \DB::getConfig('name');
70+
$db_driver = \DB::getDriverName();
7171
if (! in_array($db_driver, MigrateDumpCommand::SUPPORTED_DB_DRIVERS, true)) {
7272
// CONSIDER: Logging or emitting console warning.
7373
return;
@@ -79,13 +79,13 @@ public function handle(CommandStarting $event)
7979
// Try-catch instead of information_schema since not all have one.
8080
try {
8181
$has_migrated_any = ! is_null(
82-
\DB::connection($database)->table('migrations')->value('id')
82+
\DB::table('migrations')->value('id')
8383
);
8484
} catch (\PDOException $e) {
8585
// No op. when table does not exist.
8686
if (
8787
! in_array($e->getCode(), ['42P01', '42S02'], true)
88-
&& ! preg_match("/\bdoes ?n[o']t exist\b/iu", $e->getMessage())
88+
&& ! preg_match("/\b(does ?n[o']t exist|no such table)\b/iu", $e->getMessage())
8989
) {
9090
throw $e;
9191
}
@@ -112,7 +112,7 @@ public static function inputToArtisanOptions(InputInterface $input) : array
112112
if (false !== $input->getParameterOption($option)) {
113113
$options[$option] = true;
114114
}
115-
} else {
115+
} elseif (false !== $input->getParameterOption($option)) {
116116
$options[$option] = $input->getParameterOption($option);
117117
}
118118
}

tests/Sqlite/MigrateDumpTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
4+
namespace OrisIntel\MigrationSnapshot\Tests\Sqlite;
5+
6+
class MigrateDumpTest extends SqliteTestCase
7+
{
8+
public function test_handle()
9+
{
10+
$this->createTestTablesWithoutMigrate();
11+
$result = \Artisan::call('migrate:dump');
12+
$this->assertEquals(0, $result);
13+
$this->assertDirectoryExists($this->schemaSqlDirectory);
14+
$this->assertFileExists($this->schemaSqlPath);
15+
$result_sql = file_get_contents($this->schemaSqlPath);
16+
$this->assertRegExp('/CREATE TABLE( IF NOT EXISTS)? "test_ms" /', $result_sql);
17+
$this->assertRegExp('/INSERT INTO "?migrations"? /', $result_sql);
18+
}
19+
}

tests/Sqlite/MigrateLoadTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
4+
namespace OrisIntel\MigrationSnapshot\Tests\Sqlite;
5+
6+
7+
class MigrateLoadTest extends SqliteTestCase
8+
{
9+
public function test_handle()
10+
{
11+
// Make the dump file.
12+
$this->createTestTablesWithoutMigrate();
13+
$result = \Artisan::call('migrate:dump');
14+
$this->assertEquals(0, $result);
15+
16+
$result = \Artisan::call('migrate:load');
17+
$this->assertEquals(0, $result);
18+
19+
$this->assertEquals(
20+
'0000_00_00_000000_create_test_tables',
21+
\DB::table('migrations')->value('migration')
22+
);
23+
24+
$this->assertNull(\DB::table('test_ms')->value('name'));
25+
}
26+
}

tests/Sqlite/SqliteTestCase.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
4+
namespace OrisIntel\MigrationSnapshot\Tests\Sqlite;
5+
6+
use OrisIntel\MigrationSnapshot\Tests\TestCase;
7+
8+
abstract class SqliteTestCase extends TestCase
9+
{
10+
protected $dbDefault = 'sqlite';
11+
12+
public static function setUpBeforeClass()
13+
{
14+
// File must exist before connection will initialize, even if empty.
15+
touch(__DIR__ . '/../../vendor/orchestra/testbench-core/laravel/database/database.sqlite');
16+
}
17+
}

tests/TestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ protected function createTestTablesWithoutMigrate() : void
4545
{
4646
// Executing without `loadMigrationsFrom` and without `Artisan::call` to
4747
// avoid unnecessary runs through migration hooks.
48+
4849
require_once(__DIR__ . '/migrations/setup/0000_00_00_000000_create_test_tables.php');
4950
\Schema::dropAllTables();
5051
\Schema::dropAllViews();

0 commit comments

Comments
 (0)