Skip to content

Commit 673542a

Browse files
author
Gaetano Giunta
committed
allow to execute sql vommands from a file; refactor
1 parent 571a697 commit 673542a

File tree

9 files changed

+110
-28
lines changed

9 files changed

+110
-28
lines changed

TODO.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
- worker: improve sql execution cmd:
2-
+ allow it to take sql snippet from file
32
+ allow it to pick a set of desired servers
43
+ disallow execution of commands that are part of the db client instead of being sent to the server, such as eg. 'use db'
4+
- ok for mysql? (to be tested), missing for psql
5+
+ examine in detail the differences between running a command vs a file (eg. transaction usage)
56

67
- worker: improve profile of 'user' account (esp: add APP_ENV and APP_DEBUG env vars)
78

WHATSNEW.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ Version 0.3 (unreleased)
66
- Improved the `sql:execute` command:
77
- measure time and memory taken for each db
88
- allow to print output in json/yaml/php format
9-
10-
9+
- allow to execute sql commands stored in a file besides specifying them as cli option
10+
11+
1112
Version 0.2
1213
-----------
1314

app/src/API/Interfaces/ForkedSqlExecutor.php renamed to app/src/API/Interfaces/ForkedCommandExecutor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
use Symfony\Component\Process\Process;
66

7-
interface ForkedSqlExecutor
7+
interface ForkedCommandExecutor
88
{
99
/**
1010
* @param string $sql
1111
* @return Process
1212
*/
13-
public function getProcess($sql);
13+
public function getExecuteCommandProcess($sql);
1414
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Db3v4l\API\Interfaces;
4+
5+
use Symfony\Component\Process\Process;
6+
7+
interface ForkedFileExecutor
8+
{
9+
/**
10+
* @param string $filename
11+
* @return Process
12+
*/
13+
public function getExecuteFileProcess($filename);
14+
}

app/src/Command/SqlExecute.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ protected function configure()
3838
{
3939
$this
4040
->setDescription('Executes an SQL command in parallel on all configured database servers')
41-
->addOption('sql', null, InputOption::VALUE_REQUIRED, 'The command to execute')
41+
->addOption('sql', null, InputOption::VALUE_REQUIRED, 'The sql command(s) string to execute')
42+
->addOption('file', null, InputOption::VALUE_REQUIRED, 'A file with sql commands to execute')
4243
->addOption('output-type', null, InputOption::VALUE_REQUIRED, 'The format for the output: json, php, text or yml', 'text')
4344
->addOption('timeout', null, InputOption::VALUE_REQUIRED, 'The maximum time to wait for execution (secs)', 600)
4445
->addOption('max-parallel', null, InputOption::VALUE_REQUIRED, 'The maximum number of processes to run in parallel', 16)
@@ -65,13 +66,17 @@ protected function execute(InputInterface $input, OutputInterface $output)
6566

6667
$dbList = $this->dbManager->listDatabases();
6768
$sql = $input->getOption('sql');
69+
$file = $input->getOption('file');
6870
$timeout = $input->getOption('timeout');
6971
$maxParallel = $input->getOption('max-parallel');
7072
$dontForceSigchildEnabled = $input->getOption('dont-force-enabled-sigchild');
7173
$format = $input->getOption('output-type');
7274

73-
if ($sql === '') {
74-
throw new \Exception("Please provide an sql command/snippted to be executed");
75+
if ($sql == null && $file == null) {
76+
throw new \Exception("Please provide an sql command/file to be executed");
77+
}
78+
if ($sql != null && $file != null) {
79+
throw new \Exception("Please provide either an sql command or file to be executed, not both");
7580
}
7681

7782
// On Debian, which we use by default, SF has troubles understanding that php was compiled with --enable-sigchild
@@ -82,14 +87,27 @@ protected function execute(InputInterface $input, OutputInterface $output)
8287
Process::forceSigchildEnabled(true);
8388
}
8489

90+
if ($format === 'text') {
91+
$this->writeln('<info>Preparing commands...</info>', OutputInterface::VERBOSITY_VERBOSE);
92+
}
93+
8594
/** @var Process[] $processes */
8695
$processes = [];
8796
$executors = [];
8897
foreach ($dbList as $dbName) {
8998
$dbConnectionSpec = $this->dbManager->getDatabaseConnectionSpecification($dbName);
9099

91100
$executor = $this->executorFactory->createForkedExecutor($dbConnectionSpec);
92-
$process = $executor->getProcess($sql);
101+
102+
if ($sql != null) {
103+
$process = $executor->getExecuteCommandProcess($sql);
104+
} else {
105+
$process = $executor->getExecuteFileProcess($file);
106+
}
107+
108+
if ($format === 'text') {
109+
$this->writeln('Command line: ' . $process->getCommandLine(), OutputInterface::VERBOSITY_VERBOSE);
110+
}
93111

94112
$process->setTimeout($timeout);
95113

app/src/Service/SqlExecutor/Forked/Doctrine.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
namespace Db3v4l\Service\SqlExecutor\Forked;
44

5-
use Db3v4l\API\Interfaces\ForkedSqlExecutor;
5+
use Db3v4l\API\Interfaces\ForkedCommandExecutor;
66
use Db3v4l\Util\Process;
77

8-
class Doctrine extends ForkedExecutor implements ForkedSqlExecutor
8+
class Doctrine extends ForkedExecutor implements ForkedCommandExecutor
99
{
1010
/**
1111
* @param string $sql
1212
* @return Process
1313
*/
14-
public function getProcess($sql)
14+
public function getExecuteCommandProcess($sql)
1515
{
1616
throw new \RuntimeException('TO BE IMPLEMENTED');
1717
}

app/src/Service/SqlExecutor/Forked/NativeClient.php

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,37 @@
22

33
namespace Db3v4l\Service\SqlExecutor\Forked;
44

5-
use Db3v4l\API\Interfaces\ForkedSqlExecutor;
5+
use Db3v4l\API\Interfaces\ForkedCommandExecutor;
6+
use Db3v4l\API\Interfaces\ForkedFileExecutor;
67
use Db3v4l\Util\Process;
78

8-
class NativeClient extends ForkedExecutor implements ForkedSqlExecutor
9+
class NativeClient extends ForkedExecutor implements ForkedCommandExecutor, ForkedFileExecutor
910
{
1011
/**
1112
* @param string $sql
1213
* @return Process
14+
*/
15+
public function getExecuteCommandProcess($sql)
16+
{
17+
return $this->getProcess($sql);
18+
}
19+
20+
/**
21+
* @param string $filename
22+
* @return Process
23+
*/
24+
public function getExecuteFileProcess($filename)
25+
{
26+
return $this->getProcess($filename, true);
27+
}
28+
29+
/**
30+
* @param string $sqlOrFilename
31+
* @param bool $isFile
32+
* @return Process
1333
* @todo allow to inject location of db clients via setter/constructor
1434
*/
15-
public function getProcess($sql)
35+
public function getProcess($sqlOrFilename, $isFile = false)
1636
{
1737
$clientType = $this->getDbClientFromDriver($this->databaseConfiguration['driver']);
1838

@@ -25,9 +45,11 @@ public function getProcess($sql)
2545
'--user=' . $this->databaseConfiguration['user'],
2646
'-p' . $this->databaseConfiguration['password'],
2747
'--binary-mode', // 'It also disables all mysql commands except charset and delimiter in non-interactive mode (for input piped to mysql or loaded using the source command)'
28-
'--execute=' . $sql,
2948
// $dbname
3049
];
50+
if (!$isFile) {
51+
$options[] = '--execute=' . $sqlOrFilename;
52+
}
3153
$env = [
3254
// problematic when wrapping the process in a call to `time`...
3355
//'MYSQL_PWD' => $this->databaseConfiguration['password'],
@@ -41,9 +63,11 @@ public function getProcess($sql)
4163
//'--host=' . $this->databaseConfiguration['host'],
4264
//'--port=' . $this->databaseConfiguration['port'] ?? '5432',
4365
//'--username=' . $this->databaseConfiguration['user'],
44-
'--command=' . $sql,
4566
//'--dbname=' . $dbname
4667
];
68+
if (!$isFile) {
69+
$options[] = '--command=' . $sqlOrFilename;
70+
}
4771
$env = [
4872
// problematic when wrapping the process in a call to `time`...
4973
//'PGPASSWORD' => $this->databaseConfiguration['password'],
@@ -53,7 +77,14 @@ public function getProcess($sql)
5377
throw new \OutOfBoundsException("Unsupported db client '$clientType'");
5478
}
5579

56-
return new Process($this->buildCommandLine($command, $options), null, $env);
80+
$commandLine = $this->buildCommandLine($command, $options);
81+
82+
/// @todo for psql this is probably better done via --file
83+
if ($isFile) {
84+
$commandLine .= ' < ' . escapeshellarg($sqlOrFilename);
85+
}
86+
87+
return new Process($commandLine, null, $env);
5788
}
5889

5990
/**

app/src/Service/SqlExecutor/Forked/TimedExecutor.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,44 @@
22

33
namespace Db3v4l\Service\SqlExecutor\Forked;
44

5-
use Db3v4l\API\Interfaces\ForkedSqlExecutor;
5+
use Db3v4l\API\Interfaces\ForkedCommandExecutor;
6+
use Db3v4l\API\Interfaces\ForkedFileExecutor;
67
use Db3v4l\API\Interfaces\TimedExecutor as TimedExecutorInterface;
78

8-
class TimedExecutor implements ForkedSqlExecutor, TimedExecutorInterface
9+
class TimedExecutor implements ForkedCommandExecutor, ForkedFileExecutor, TimedExecutorInterface
910
{
10-
/** @var ForkedSqlExecutor */
11+
/** @var ForkedCommandExecutor */
1112
protected $wrappedExecutor;
1213
protected $timingFile;
1314

14-
public function __construct(ForkedSqlExecutor $wrappedExecutor)
15+
protected $timeCmd = '/usr/bin/time';
16+
17+
public function __construct(ForkedCommandExecutor $wrappedExecutor)
1518
{
1619
$this->wrappedExecutor = $wrappedExecutor;
1720
}
1821

19-
public function getProcess($sql)
22+
public function getExecuteCommandProcess($sql)
23+
{
24+
$process = $this->wrappedExecutor->getExecuteCommandProcess($sql);
25+
26+
// wrap in a `time` call
27+
$this->timingFile = tempnam(sys_get_temp_dir(), 'db3val_');
28+
$process->setCommandLine(
29+
$this->timeCmd . ' ' . escapeshellarg('--output=' . $this->timingFile) . ' ' . escapeshellarg('--format=%M %e') . ' '
30+
. $process->getCommandLine());
31+
32+
return $process;
33+
}
34+
35+
public function getExecuteFileProcess($sql)
2036
{
21-
$process = $this->wrappedExecutor->getProcess($sql);
37+
$process = $this->wrappedExecutor->getExecuteFileProcess($sql);
2238

2339
// wrap in a `time` call
2440
$this->timingFile = tempnam(sys_get_temp_dir(), 'db3val_');
2541
$process->setCommandLine(
26-
'time ' . escapeshellarg('--output=' . $this->timingFile) . ' ' . escapeshellarg('--format=%M %e') . ' '
42+
$this->timeCmd . ' ' . escapeshellarg('--output=' . $this->timingFile) . ' ' . escapeshellarg('--format=%M %e') . ' '
2743
. $process->getCommandLine());
2844

2945
return $process;

app/src/Service/SqlExecutorFactory.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
namespace Db3v4l\Service;
44

5-
use Db3v4l\API\Interfaces\ForkedSqlExecutor;
5+
use Db3v4l\API\Interfaces\ForkedCommandExecutor;
6+
use Db3v4l\API\Interfaces\ForkedFileExecutor;
67
use Db3v4l\Service\SqlExecutor\Forked\NativeClient;
78
use Db3v4l\Service\SqlExecutor\Forked\Doctrine;
89
use Db3v4l\Service\SqlExecutor\Forked\TimedExecutor;
@@ -12,8 +13,8 @@ class SqlExecutorFactory
1213
/**
1314
* @param array $databaseConnectionConfiguration
1415
* @param string $executionStrategy
15-
* @bool $timed
16-
* @return ForkedSqlExecutor
16+
* @param bool $timed
17+
* @return ForkedCommandExecutor|ForkedFileExecutor
1718
* @throws \OutOfBoundsException
1819
*/
1920
public function createForkedExecutor($databaseConnectionConfiguration, $executionStrategy = 'NativeClient', $timed = true)

0 commit comments

Comments
 (0)