Skip to content

Commit f58b238

Browse files
author
Tom Schlick
committed
update migrate:dump and migrate:load to use a seperate data file
1 parent 2d56f99 commit f58b238

File tree

2 files changed

+227
-65
lines changed

2 files changed

+227
-65
lines changed

src/Commands/MigrateDumpCommand.php

Lines changed: 188 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,30 @@
22

33
namespace OrisIntel\MigrationSnapshot\Commands;
44

5-
final class MigrateDumpCommand extends \Illuminate\Console\Command
5+
use Illuminate\Console\Command;
6+
use Illuminate\Support\Facades\DB;
7+
8+
final class MigrateDumpCommand extends Command
69
{
710
public const SCHEMA_SQL_PATH_SUFFIX = '/migrations/sql/schema.sql';
11+
public const DATA_SQL_PATH_SUFFIX = '/migrations/sql/data.sql';
12+
813
public const SUPPORTED_DB_DRIVERS = ['mysql', 'pgsql', 'sqlite'];
914

1015
protected $signature = 'migrate:dump
11-
{--database= : The database connection to use}';
16+
{--database= : The database connection to use}
17+
{--include-data : Include data present in the tables that was created via migrations. }
18+
';
1219

1320
protected $description = 'Dump current database schema/structure as plain-text SQL file.';
1421

1522
public function handle()
1623
{
1724
$exit_code = null;
1825

19-
$database = $this->option('database') ?: \DB::getDefaultConnection();
20-
\DB::setDefaultConnection($database);
21-
$db_config = \DB::getConfig();
26+
$database = $this->option('database') ?: DB::getDefaultConnection();
27+
DB::setDefaultConnection($database);
28+
$db_config = DB::getConfig();
2229

2330
// CONSIDER: Ending with ".mysql" or "-mysql.sql" unless in
2431
// compatibility mode.
@@ -41,7 +48,7 @@ public function handle()
4148
// CONSIDER: Option to dump to console Stdout instead.
4249
// CONSIDER: Option to dump for each DB connection instead of only one.
4350
// CONSIDER: Separate classes.
44-
$method = $db_config['driver'] . 'Dump';
51+
$method = $db_config['driver'] . 'SchemaDump';
4552
$exit_code = self::{$method}($db_config, $schema_sql_path);
4653

4754
if (0 !== $exit_code) {
@@ -54,6 +61,27 @@ public function handle()
5461
}
5562

5663
$this->info('Dumped schema');
64+
65+
if (! $this->option('include-data')) {
66+
return;
67+
}
68+
69+
$this->info('Starting Data Dump');
70+
71+
$data_sql_path = database_path() . self::DATA_SQL_PATH_SUFFIX;
72+
73+
$method = $db_config['driver'] . 'DataDump';
74+
$exit_code = self::{$method}($db_config, $data_sql_path);
75+
76+
if (0 !== $exit_code) {
77+
if (file_exists($data_sql_path)) {
78+
unlink($data_sql_path);
79+
}
80+
81+
exit($exit_code);
82+
}
83+
84+
$this->info('Finished Data Dump');
5785
}
5886

5987
public static function reorderMigrationRows(array $output) : array
@@ -95,32 +123,18 @@ public static function reorderMigrationRows(array $output) : array
95123
*
96124
* @return int containing exit code.
97125
*/
98-
private static function mysqlDump(array $db_config, string $schema_sql_path) : int
126+
private static function mysqlSchemaDump(array $db_config, string $schema_sql_path) : int
99127
{
100-
// CONSIDER: Supporting unix_socket.
101-
// CONSIDER: Alternative tools like `xtrabackup` or even just querying
102-
// "SHOW CREATE TABLE" via Eloquent.
103-
// CONSIDER: Capturing Stderr and outputting with `$this->error()`.
104-
105-
// Not including connection name in file since typically only one DB.
106-
// Excluding any hash or date suffix since only current is relevant.
107-
$command_prefix = 'mysqldump --routines --skip-add-drop-table'
108-
. ' --skip-add-locks --skip-comments --skip-set-charset --tz-utc'
109-
. ' --host=' . escapeshellarg($db_config['host'])
110-
. ' --port=' . escapeshellarg($db_config['port'])
111-
. ' --user=' . escapeshellarg($db_config['username'])
112-
. ' --password=' . escapeshellarg($db_config['password'])
113-
. ' ' . escapeshellarg($db_config['database']);
114128
// TODO: Suppress warning about insecure password.
115129
// CONSIDER: Intercepting stdout and stderr and converting to colorized
116130
// console output with `$this->info` and `->error`.
117131
passthru(
118-
$command_prefix
119-
. ' --result-file='
120-
. escapeshellarg($schema_sql_path)
121-
. (config('migration-snapshot.data') ? '' : ' --no-data'),
132+
static::mysqlCommandPrefix($db_config)
133+
. ' --result-file=' . escapeshellarg($schema_sql_path)
134+
. ' --no-data',
122135
$exit_code
123136
);
137+
124138
if (0 !== $exit_code) {
125139
return $exit_code;
126140
}
@@ -129,6 +143,7 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i
129143
if (false === $schema_sql) {
130144
return 1;
131145
}
146+
132147
$schema_sql = preg_replace('/\s+AUTO_INCREMENT=[0-9]+/iu', '', $schema_sql);
133148
$schema_sql = self::trimUnderscoresFromForeign($schema_sql);
134149
if (false === file_put_contents($schema_sql_path, $schema_sql)) {
@@ -137,10 +152,11 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i
137152

138153
// Include migration rows to avoid unnecessary reruns conflicting.
139154
exec(
140-
$command_prefix . ' migrations --no-create-info --skip-extended-insert --compact',
155+
static::mysqlCommandPrefix($db_config) . ' migrations --no-create-info --skip-extended-insert --compact',
141156
$output,
142157
$exit_code
143158
);
159+
144160
if (0 !== $exit_code) {
145161
return $exit_code;
146162
}
@@ -158,6 +174,63 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i
158174
return $exit_code;
159175
}
160176

177+
/**
178+
* @param array $db_config like ['host' => , 'port' => ].
179+
* @param string $data_sql_path like '.../data.sql'
180+
*
181+
* @return int containing exit code.
182+
*/
183+
private static function mysqlDataDump(array $db_config, string $data_sql_path) : int
184+
{
185+
passthru(
186+
static::mysqlCommandPrefix($db_config)
187+
. ' --result-file=' . escapeshellarg($data_sql_path)
188+
. ' --no-create-info --skip-triggers'
189+
. ' --ignore-table=' . escapeshellarg($db_config['database']) . '.migrations',
190+
$exit_code
191+
);
192+
193+
if (0 !== $exit_code) {
194+
return $exit_code;
195+
}
196+
197+
$data_sql = file_get_contents($data_sql_path);
198+
if (false === $data_sql) {
199+
return 1;
200+
}
201+
202+
$data_sql = preg_replace('/\s+AUTO_INCREMENT=[0-9]+/iu', '', $data_sql);
203+
if (false === file_put_contents($data_sql_path, $data_sql)) {
204+
return 1;
205+
}
206+
207+
return $exit_code;
208+
}
209+
210+
/**
211+
* @param array $db_config
212+
*
213+
* @return string
214+
*/
215+
private static function mysqlCommandPrefix(array $db_config) : string
216+
{
217+
// CONSIDER: Supporting unix_socket.
218+
// CONSIDER: Alternative tools like `xtrabackup` or even just querying
219+
// "SHOW CREATE TABLE" via Eloquent.
220+
// CONSIDER: Capturing Stderr and outputting with `$this->error()`.
221+
222+
// Not including connection name in file since typically only one DB.
223+
// Excluding any hash or date suffix since only current is relevant.
224+
225+
return 'mysqldump --routines --skip-add-drop-table'
226+
. ' --skip-add-locks --skip-comments --skip-set-charset --tz-utc'
227+
. ' --host=' . escapeshellarg($db_config['host'])
228+
. ' --port=' . escapeshellarg($db_config['port'])
229+
. ' --user=' . escapeshellarg($db_config['username'])
230+
. ' --password=' . escapeshellarg($db_config['password'])
231+
. ' ' . escapeshellarg($db_config['database']);
232+
}
233+
161234
/**
162235
* Trim underscores from FK constraint names to workaround PTOSC quirk.
163236
*
@@ -209,38 +282,30 @@ public static function trimUnderscoresFromForeign(string $sql) : string
209282

210283
/**
211284
* @param array $db_config like ['host' => , 'port' => ].
285+
* @param string $schema_sql_path
212286
*
213287
* @return int containing exit code.
214288
*/
215-
private static function pgsqlDump(array $db_config, string $schema_sql_path) : int
289+
private static function pgsqlSchemaDump(array $db_config, string $schema_sql_path) : int
216290
{
217-
// CONSIDER: Supporting unix_socket.
218-
// CONSIDER: Instead querying pg catalog tables via Eloquent.
219-
// CONSIDER: Capturing Stderr and outputting with `$this->error()`.
220-
221-
// CONSIDER: Instead using DSN-like URL instead of env. var. for pass.
222-
$command_prefix = 'PGPASSWORD=' . escapeshellarg($db_config['password'])
223-
. ' pg_dump'
224-
. ' --host=' . escapeshellarg($db_config['host'])
225-
. ' --port=' . escapeshellarg($db_config['port'])
226-
. ' --username=' . escapeshellarg($db_config['username'])
227-
. ' ' . escapeshellarg($db_config['database']);
228291
passthru(
229-
$command_prefix
292+
static::pgsqlCommandPrefix($db_config)
230293
. ' --file=' . escapeshellarg($schema_sql_path)
231-
. (config('migration-snapshot.data') ? '' : ' --schema-only'),
294+
. ' --schema-only',
232295
$exit_code
233296
);
297+
234298
if (0 !== $exit_code) {
235299
return $exit_code;
236300
}
237301

238302
// Include migration rows to avoid unnecessary reruns conflicting.
239303
exec(
240-
$command_prefix . ' --table=migrations --data-only --inserts',
304+
static::pgsqlCommandPrefix($db_config) . ' --table=migrations --data-only --inserts',
241305
$output,
242306
$exit_code
243307
);
308+
244309
if (0 !== $exit_code) {
245310
return $exit_code;
246311
}
@@ -265,13 +330,44 @@ function ($line) {
265330
return $exit_code;
266331
}
267332

333+
private static function pgsqlDataDump(array $db_config, string $data_sql_path) : int
334+
{
335+
passthru(
336+
static::pgsqlCommandPrefix($db_config)
337+
. ' --file=' . escapeshellarg($data_sql_path)
338+
. ' --data-only',
339+
$exit_code
340+
);
341+
342+
if (0 !== $exit_code) {
343+
return $exit_code;
344+
}
345+
346+
return $exit_code;
347+
}
348+
349+
/**
350+
* @param array $db_config
351+
*
352+
* @return string
353+
*/
354+
private static function pgsqlCommandPrefix(array $db_config) : string
355+
{
356+
return 'PGPASSWORD=' . escapeshellarg($db_config['password'])
357+
. ' pg_dump'
358+
. ' --host=' . escapeshellarg($db_config['host'])
359+
. ' --port=' . escapeshellarg($db_config['port'])
360+
. ' --username=' . escapeshellarg($db_config['username'])
361+
. ' ' . escapeshellarg($db_config['database']);
362+
}
363+
268364
/**
269365
* @param array $db_config like ['host' => , 'port' => ].
270366
* @param string $schema_sql_path like '.../schema.sql'
271367
*
272368
* @return int containing exit code.
273369
*/
274-
private static function sqliteDump(array $db_config, string $schema_sql_path) : int
370+
private static function sqliteSchemaDump(array $db_config, string $schema_sql_path) : int
275371
{
276372
// CONSIDER: Accepting command name as option or from config.
277373
$command_prefix = 'sqlite3 ' . escapeshellarg($db_config['database']);
@@ -284,6 +380,7 @@ private static function sqliteDump(array $db_config, string $schema_sql_path) :
284380
if (0 !== $exit_code) {
285381
return $exit_code;
286382
}
383+
287384
$tables = preg_split('/\s+/', implode(' ', $output));
288385

289386
file_put_contents($schema_sql_path, '');
@@ -298,11 +395,59 @@ private static function sqliteDump(array $db_config, string $schema_sql_path) :
298395
$output,
299396
$exit_code
300397
);
398+
399+
if (0 !== $exit_code) {
400+
return $exit_code;
401+
}
402+
403+
if ('migrations' === $table) {
404+
$insert_rows = array_slice($output, 4, -1);
405+
$sorted = self::reorderMigrationRows($insert_rows);
406+
array_splice($output, 4, -1, $sorted);
407+
}
408+
409+
file_put_contents(
410+
$schema_sql_path,
411+
implode(PHP_EOL, $output) . PHP_EOL,
412+
FILE_APPEND
413+
);
414+
}
415+
416+
return $exit_code;
417+
}
418+
419+
private static function sqliteDataDump(array $db_config, string $schema_sql_path) : int
420+
{
421+
// CONSIDER: Accepting command name as option or from config.
422+
$command_prefix = 'sqlite3 ' . escapeshellarg($db_config['database']);
423+
424+
// Since Sqlite lacks Information Schema, and dumping everything may be
425+
// too slow or memory intense, just query tables and dump them
426+
// individually.
427+
// CONSIDER: Using Laravel's `Schema` code instead.
428+
exec($command_prefix . ' .tables', $output, $exit_code);
429+
if (0 !== $exit_code) {
430+
return $exit_code;
431+
}
432+
433+
$tables = preg_split('/\s+/', implode(' ', $output));
434+
435+
foreach ($tables as $table) {
436+
// Only migrations should dump data with schema.
437+
$sql_command = 'migrations' !== $table ? '.dump' : '.schema';
438+
439+
$output = [];
440+
exec(
441+
$command_prefix . ' ' . escapeshellarg("$sql_command $table"),
442+
$output,
443+
$exit_code
444+
);
445+
301446
if (0 !== $exit_code) {
302447
return $exit_code;
303448
}
304449

305-
if (config('migration-snapshot.data') || 'migrations' === $table) {
450+
if ('migrations' !== $table) {
306451
$insert_rows = array_slice($output, 4, -1);
307452
$sorted = self::reorderMigrationRows($insert_rows);
308453
array_splice($output, 4, -1, $sorted);

0 commit comments

Comments
 (0)