Skip to content

Commit

Permalink
add support for multiple databases
Browse files Browse the repository at this point in the history
  • Loading branch information
splitbrain committed Dec 19, 2024
1 parent 32c51a7 commit 3113520
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 12 deletions.
4 changes: 2 additions & 2 deletions _test/GeneralTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ public function testPluginInfo(): void
$this->assertArrayHasKey('url', $info);

$this->assertEquals('dbquery', $info['base']);
$this->assertRegExp('/^https?:\/\//', $info['url']);
$this->assertMatchesRegularExpression('/^https?:\/\//', $info['url']);
$this->assertTrue(mail_isvalid($info['email']));
$this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
$this->assertMatchesRegularExpression('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
$this->assertTrue(false !== strtotime($info['date']));
}

Expand Down
29 changes: 29 additions & 0 deletions _test/HelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,33 @@ public function testPrepareStatement()
$expected = 'SELECT :user, :mail, :id, :page, :ns WHERE \'foo\' in (:group0,:group1,:group2)';
$this->assertEquals($expected, $actual);
}

public function testGetDsnAliases()
{
$conf = "nouser mysql:host=localhost;port=3306;dbname=testdb1\n\n".
"nopass mysql:host=localhost;port=3306;dbname=testdb2 user\n".
"both mysql:host=localhost;port=3306;dbname=testdb3 user pass\n";

$expect = [
'_' => ['dsn' => 'mysql:host=localhost;port=3306;dbname=testdb1', 'user' => 'dfu', 'pass' => 'dfp'],
'nouser' => ['dsn' => 'mysql:host=localhost;port=3306;dbname=testdb1', 'user' => 'dfu', 'pass' => 'dfp'],
'nopass' => ['dsn' => 'mysql:host=localhost;port=3306;dbname=testdb2', 'user' => 'user', 'pass' => 'dfp'],
'both' => ['dsn' => 'mysql:host=localhost;port=3306;dbname=testdb3', 'user' => 'user', 'pass' => 'pass'],
];

$actual = $this->callInaccessibleMethod($this->hlp, 'getDsnAliases', [$conf, 'dfu', 'dfp']);
$this->assertEquals($expect, $actual);
}

public function testGetDsnAliasesLegacy()
{
$conf = 'mysql:host=localhost;port=3306;dbname=testdb1';

$expect = [
'_' => ['dsn' => 'mysql:host=localhost;port=3306;dbname=testdb1', 'user' => 'dfu', 'pass' => 'dfp'],
];

$actual = $this->callInaccessibleMethod($this->hlp, 'getDsnAliases', [$conf, 'dfu', 'dfp']);
$this->assertEquals($expect, $actual);
}
}
2 changes: 1 addition & 1 deletion _test/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ public function testUrlParsing($content, $expect)

$this->callInaccessibleMethod($plugin, 'cellFormat', [$content, $R]);

$this->assertRegExp($expect, $R->doc);
$this->assertMatchesRegularExpression($expect, $R->doc);
}
}
2 changes: 1 addition & 1 deletion conf/metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

$meta['namespace'] = ['string'];
$meta['dsn'] = ['string'];
$meta['dsn'] = [];
$meta['user'] = ['string'];
$meta['pass'] = ['password'];

Expand Down
70 changes: 68 additions & 2 deletions helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ public function getPDO($dsn = null, $user = null, $pass = null)
* @throws \PDOException
* @throws Exception
*/
public function executeQuery($query)
public function executeQuery($query, $dsnalias = null)
{
if (!preg_match('/^select /i', trim($query))) {
throw new \Exception('For security reasons only SELECT statements are allowed in dbquery');
}

$pdo = $this->getPDO();
[$dsn, $user, $pass] = $this->getDSN($dsnalias);
$pdo = $this->getPDO($dsn, $user, $pass);
$params = $this->gatherVariables();
$sth = $this->prepareStatement($pdo, $query, $params);
$sth->execute();
Expand Down Expand Up @@ -138,4 +139,69 @@ public function gatherVariables()
':ns' => ':' . getNS($INFO['id']),
];
}

/**
* Get the DSN, user and pass for a given alias
*
* @param string|null $alias null for default
* @return [string, string, string] DSN, user, pass
* @throws Exception
*/
public function getDSN($alias = null)
{
static $aliases = null;
if ($aliases === null) {
$aliases = $this->getDsnAliases(
$this->getConf('dsn'),
$this->getConf('user'),
$this->getConf('pass')
);
}

if ($aliases === []) throw new \Exception('No DSN aliases defined');

if ($alias === null || !isset($aliases[$alias])) {
$alias = '_';
}

return [$aliases[$alias]['dsn'], $aliases[$alias]['user'], $aliases[$alias]['pass']];
}

/**
* Load and parse the DSN configuration
*
* @param string $config
* @param string $defaultuser
* @param string $defaultpass
* @return array
*/
protected function getDsnAliases($config, $defaultuser, $defaultpass)
{
$aliases = [];
$lines = explode("\n", $config);
foreach ($lines as $line) {
$line = trim($line);
if (!$line) continue;
$parts = preg_split('/\s+/', $line, 4);

if (count($parts) > 1) {
$aliases[$parts[0]] = [
'dsn' => $parts[1],
'user' => $parts[2] ?? $defaultuser,
'pass' => $parts[3] ?? $defaultpass
];
} else {
$parts = ['', $parts[0]];
}

if (!isset($aliases['_'])) {
$aliases['_'] = [
'dsn' => $parts[1],
'user' => $parts[2] ?? $defaultuser,
'pass' => $parts[3] ?? $defaultpass
];
}
}
return $aliases;
}
}
8 changes: 4 additions & 4 deletions lang/en/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @author Andreas Gohr <[email protected]>
*/

$meta['namespace'] = 'The namespace in which your queries are created. Be sure to use the proper ACL restrictions to prevent unauthorized user to do arbitrary SQL queries.';
$meta['dsn'] = 'The connection <a href="https://www.php.net/manual/en/pdo.construct.php">DSN string</a> for your database.';
$meta['user'] = 'The user name to connect to your database.';
$meta['pass'] = 'The password to connect to your database';
$lang['namespace'] = 'The namespace in which your queries are created. Be sure to use the proper ACL restrictions to prevent unauthorized user to do arbitrary SQL queries.';
$lang['dsn'] = 'The connection <a href="https://www.php.net/manual/en/pdo.construct.php">DSN strings</a> for your databases.';
$lang['user'] = 'The user name to connect to your database (if not supplied above).';
$lang['pass'] = 'The password to connect to your database (if not supplied above).';
3 changes: 3 additions & 0 deletions style.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
textarea[name="config[plugin____dbquery____dsn]"] {
white-space: nowrap;
}
7 changes: 6 additions & 1 deletion syntax/macro.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ public function handle($match, $state, $pos, Doku_Handler $handler)
/** @inheritDoc */
public function render($mode, Doku_Renderer $renderer, $data)
{
$renderer->info['dbquery'][$data[0]] = true;
[$name, $value] = sexplode('=', $data[0], 2);
$name = trim($name);
$value = trim($value);
if(!$value) $value = true;

$renderer->info['dbquery'][$name] = $value;
return true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion syntax/query.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function render($mode, Doku_Renderer $renderer, $data)
$hlp = plugin_load('helper', 'dbquery');
try {
$qdata = $hlp->loadDataFromPage($data['name']);
$result = $hlp->executeQuery($qdata['codeblocks']['_']);
$result = $hlp->executeQuery($qdata['codeblocks']['_'], $qdata['macros']['dsn'] ?? null);
} catch (\Exception $e) {
msg(hsc($e->getMessage()), -1);
return true;
Expand Down

0 comments on commit 3113520

Please sign in to comment.