1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace N98\Magento\Command\Database; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use N98\Magento\Command\Database\Compressor\AbstractCompressor; |
7
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
8
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
9
|
|
|
use Symfony\Component\Console\Input\InputOption; |
10
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
11
|
|
|
|
12
|
|
|
class ImportCommand extends AbstractDatabaseCommand |
13
|
|
|
{ |
14
|
|
|
protected function configure() |
15
|
|
|
{ |
16
|
|
|
$this |
17
|
|
|
->setName('db:import') |
18
|
|
|
->addArgument('filename', InputArgument::OPTIONAL, 'Dump filename') |
19
|
|
|
->addOption('compression', 'c', InputOption::VALUE_REQUIRED, 'The compression of the specified file') |
20
|
|
|
->addOption('only-command', null, InputOption::VALUE_NONE, 'Print only mysql command. Do not execute') |
21
|
|
|
->addOption('only-if-empty', null, InputOption::VALUE_NONE, 'Imports only if database is empty') |
22
|
|
|
->addOption( |
23
|
|
|
'optimize', |
24
|
|
|
null, |
25
|
|
|
InputOption::VALUE_NONE, |
26
|
|
|
'Convert verbose INSERTs to short ones before import (not working with compression)' |
27
|
|
|
) |
28
|
|
|
->addOption('drop', null, InputOption::VALUE_NONE, 'Drop and recreate database before import') |
29
|
|
|
->addOption('drop-tables', null, InputOption::VALUE_NONE, 'Drop tables before import') |
30
|
|
|
->setDescription('Imports database with mysql cli client according to database defined in local.xml'); |
31
|
|
|
|
32
|
|
|
$help = <<<HELP |
33
|
|
|
Imports an SQL file with mysql cli client into current configured database. |
34
|
|
|
|
35
|
|
|
You need to have MySQL client tools installed on your system. |
36
|
|
|
HELP; |
37
|
|
|
$this->setHelp($help); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @return bool |
42
|
|
|
*/ |
43
|
|
|
public function isEnabled() |
44
|
|
|
{ |
45
|
|
|
return function_exists('exec'); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Optimize a dump by converting single INSERTs per line to INSERTs with multiple lines |
50
|
|
|
* |
51
|
|
|
* @param $fileName |
52
|
|
|
* @return string temporary filename |
53
|
|
|
*/ |
54
|
|
|
protected function optimize($fileName) |
55
|
|
|
{ |
56
|
|
|
$in = fopen($fileName, 'r'); |
57
|
|
|
$result = tempnam(sys_get_temp_dir(), 'dump') . '.sql'; |
58
|
|
|
$out = fopen($result, 'w'); |
59
|
|
|
|
60
|
|
|
$currentTable = ''; |
61
|
|
|
$maxlen = 8 * 1024 * 1024; // 8 MB |
62
|
|
|
$len = 0; |
63
|
|
|
while ($line = fgets($in)) { |
64
|
|
|
if (strtolower(substr($line, 0, 11)) == 'insert into') { |
65
|
|
|
preg_match('/^insert into `(.*)` \(.*\) values (.*);/i', $line, $m); |
66
|
|
|
|
67
|
|
View Code Duplication |
if (count($m) < 3) { // fallback for very long lines or other cases where the preg_match fails |
|
|
|
|
68
|
|
|
if ($currentTable != '') { |
69
|
|
|
fwrite($out, ";\n"); |
70
|
|
|
} |
71
|
|
|
fwrite($out, $line); |
72
|
|
|
$currentTable = ''; |
73
|
|
|
continue; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
$table = $m[1]; |
77
|
|
|
$values = $m[2]; |
78
|
|
|
|
79
|
|
|
if ($table != $currentTable or ($len > $maxlen - 1000)) { |
|
|
|
|
80
|
|
|
if ($currentTable != '') { |
81
|
|
|
fwrite($out, ";\n\n"); |
82
|
|
|
} |
83
|
|
|
$currentTable = $table; |
84
|
|
|
$insert = 'INSERT INTO `' . $table . '` VALUES ' . $values; |
85
|
|
|
fwrite($out, $insert); |
86
|
|
|
$len = strlen($insert); |
87
|
|
|
} else { |
88
|
|
|
fwrite($out, ',' . $values); |
89
|
|
|
$len += strlen($values) + 1; |
90
|
|
|
} |
91
|
|
View Code Duplication |
} else { |
|
|
|
|
92
|
|
|
if ($currentTable != '') { |
93
|
|
|
fwrite($out, ";\n"); |
94
|
|
|
$currentTable = ''; |
95
|
|
|
} |
96
|
|
|
fwrite($out, $line); |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
fclose($in); |
100
|
|
|
fclose($out); |
101
|
|
|
|
102
|
|
|
return $result; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @param InputInterface $input |
107
|
|
|
* @param OutputInterface $output |
108
|
|
|
* @return int|null|void |
109
|
|
|
* @throws Exception |
110
|
|
|
*/ |
111
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
112
|
|
|
{ |
113
|
|
|
$this->detectDbSettings($output); |
114
|
|
|
$this->writeSection($output, 'Import MySQL Database'); |
115
|
|
|
$dbHelper = $this->getHelper('database'); |
116
|
|
|
|
117
|
|
|
$fileName = $this->checkFilename($input); |
118
|
|
|
|
119
|
|
|
$compressor = AbstractCompressor::create($input->getOption('compression')); |
120
|
|
|
|
121
|
|
|
// create import command |
122
|
|
|
$exec = $compressor->getDecompressingCommand( |
123
|
|
|
'mysql ' . $dbHelper->getMysqlClientToolConnectionString(), |
124
|
|
|
$fileName |
125
|
|
|
); |
126
|
|
|
if ($input->getOption('only-command')) { |
127
|
|
|
$output->writeln($exec); |
128
|
|
|
|
129
|
|
|
return; |
130
|
|
|
} else { |
131
|
|
|
if ($input->getOption('only-if-empty') |
132
|
|
|
&& count($dbHelper->getTables()) > 0 |
133
|
|
|
) { |
134
|
|
|
$output->writeln('<comment>Skip import. Database is not empty</comment>'); |
135
|
|
|
|
136
|
|
|
return; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
if ($input->getOption('optimize')) { |
141
|
|
|
if ($input->getOption('compression')) { |
142
|
|
|
throw new Exception('Options --compression and --optimize are not compatible'); |
143
|
|
|
} |
144
|
|
|
$output->writeln('<comment>Optimizing <info>' . $fileName . '</info> to temporary file'); |
145
|
|
|
$fileName = $this->optimize($fileName); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
if ($input->getOption('drop')) { |
149
|
|
|
$dbHelper->dropDatabase($output); |
150
|
|
|
$dbHelper->createDatabase($output); |
151
|
|
|
} |
152
|
|
|
if ($input->getOption('drop-tables')) { |
153
|
|
|
$dbHelper->dropTables($output); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$this->doImport($output, $fileName, $exec); |
157
|
|
|
|
158
|
|
|
if ($input->getOption('optimize')) { |
159
|
|
|
unlink($fileName); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
public function asText() |
164
|
|
|
{ |
165
|
|
|
return parent::asText() . "\n" . |
|
|
|
|
166
|
|
|
$this->getCompressionHelp(); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @param InputInterface $input |
171
|
|
|
* |
172
|
|
|
* @return mixed |
173
|
|
|
* @throws \InvalidArgumentException |
174
|
|
|
*/ |
175
|
|
|
protected function checkFilename(InputInterface $input) |
176
|
|
|
{ |
177
|
|
|
$fileName = $input->getArgument('filename'); |
178
|
|
|
if (!file_exists($fileName)) { |
179
|
|
|
throw new \InvalidArgumentException('File does not exist'); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $fileName; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @param OutputInterface $output |
187
|
|
|
* @param string $fileName |
188
|
|
|
* @param string $exec |
189
|
|
|
* |
190
|
|
|
* @return void |
191
|
|
|
*/ |
192
|
|
|
protected function doImport(OutputInterface $output, $fileName, $exec) |
193
|
|
|
{ |
194
|
|
|
$returnValue = null; |
195
|
|
|
$commandOutput = null; |
196
|
|
|
$output->writeln( |
197
|
|
|
'<comment>Importing SQL dump <info>' . $fileName . '</info> to database <info>' |
198
|
|
|
. $this->dbSettings['dbname'] . '</info>' |
199
|
|
|
); |
200
|
|
|
exec($exec, $commandOutput, $returnValue); |
201
|
|
|
if ($returnValue <> 0) { |
|
|
|
|
202
|
|
|
$output->writeln('<error>' . implode(PHP_EOL, $commandOutput) . '</error>'); |
203
|
|
|
} |
204
|
|
|
$output->writeln('<info>Finished</info>'); |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.