Browse Source

Various changes:

- Fix handling of binary data and long strings
- Simplify handling of socket connections
- Fix coverage
microsub
J. King 5 years ago
parent
commit
e92bda5373
  1. 2
      lib/Conf.php
  2. 14
      lib/Db/MySQL/Driver.php
  3. 6
      lib/Db/MySQL/ExceptionBuilder.php
  4. 17
      lib/Db/MySQL/PDODriver.php
  5. 24
      lib/Db/MySQL/Statement.php
  6. 1
      lib/Db/PostgreSQL/Dispatch.php
  7. 1
      lib/Db/PostgreSQL/PDOStatement.php
  8. 2
      lib/Db/SQLState.php
  9. 2
      tests/cases/Db/BaseStatement.php
  10. 4
      tests/cases/Db/MySQL/TestDriver.php
  11. 3
      tests/cases/Db/MySQL/TestResult.php
  12. 16
      tests/cases/Db/MySQL/TestStatement.php
  13. 4
      tests/cases/Db/MySQL/TestUpdate.php
  14. 4
      tests/cases/Db/MySQLPDO/TestDriver.php
  15. 4
      tests/cases/Db/MySQLPDO/TestResult.php
  16. 4
      tests/cases/Db/MySQLPDO/TestStatement.php
  17. 5
      tests/cases/Db/MySQLPDO/TestUpdate.php
  18. 3
      tests/cases/Db/PostgreSQL/TestDriver.php
  19. 3
      tests/cases/Db/PostgreSQL/TestStatement.php
  20. 3
      tests/cases/Db/PostgreSQL/TestUpdate.php
  21. 3
      tests/cases/Db/PostgreSQLPDO/TestDriver.php
  22. 3
      tests/cases/Db/PostgreSQLPDO/TestStatement.php
  23. 4
      tests/cases/Db/PostgreSQLPDO/TestUpdate.php
  24. 3
      tests/cases/Db/SQLite3PDO/TestCreation.php
  25. 3
      tests/cases/Db/SQLite3PDO/TestDriver.php
  26. 3
      tests/cases/Db/SQLite3PDO/TestStatement.php
  27. 4
      tests/cases/Db/SQLite3PDO/TestUpdate.php

2
lib/Conf.php

@ -53,6 +53,8 @@ class Conf {
public $dbMySQLPort = 3306;
/** @var string Database name on MySQL/MariaDB database server (if using MySQL/MariaDB) */
public $dbMySQLDb = "arsse";
/** @var string Unix domain socket or named pipe to use for MySQL when not connecting over TCP */
public $dbMySQLSocket = "";
/** @var string Class of the user management driver in use (Internal by default) */
public $userDriver = User\Internal\Driver::class;

14
lib/Db/MySQL/Driver.php

@ -28,22 +28,14 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
if (!static::requirementsMet()) {
throw new Exception("extMissing", static::driverName()); // @codeCoverageIgnore
}
$host = Arsse::$conf->dbMySQLHost;
if ($host[0] === "/") {
// host is a Unix socket
$socket = $host;
$host = "";
} elseif(substr($host, 0, 9) === "\\\\.\\pipe\\") {
// host is a Windows named piple
$socket = substr($host, 10);
$host = "";
}
$host = strtolower(!strlen((string) Arsse::$conf->dbMySQLHost) ? "localhost" : Arsse::$conf->dbMySQLHost);
$socket = strlen((string) Arsse::$conf->dbMySQLSocket) ? Arsse::$conf->dbMySQLSocket : ini_get("mysqli.default_socket");
$user = Arsse::$conf->dbMySQLUser ?? "";
$pass = Arsse::$conf->dbMySQLPass ?? "";
$port = Arsse::$conf->dbMySQLPost ?? 3306;
$db = Arsse::$conf->dbMySQLDb ?? "arsse";
// make the connection
$this->makeConnection($user, $pass, $db, $host, $port, $socket ?? "");
$this->makeConnection($user, $pass, $db, $host, $port, $socket);
// set session variables
foreach (static::makeSetupQueries() as $q) {
$this->exec($q);

6
lib/Db/MySQL/ExceptionBuilder.php

@ -11,16 +11,12 @@ use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\ExceptionTimeout;
trait ExceptionBuilder {
protected function buildException(): array {
return self::buildEngineException($this->db->errno, $this->db->error);
}
public static function buildEngineException($code, string $msg): array {
switch ($code) {
case 1205:
return [ExceptionTimeout::class, 'general', $msg];
case 1364:
return [ExceptionInput::class, "constraintViolation", $msg];
return [ExceptionInput::class, "engineConstraintViolation", $msg];
case 1366:
return [ExceptionInput::class, 'engineTypeViolation', $msg];
default:

17
lib/Db/MySQL/PDODriver.php

@ -21,16 +21,13 @@ class PDODriver extends Driver {
}
protected function makeConnection(string $db, string $user, string $password, string $host, int $port, string $socket) {
$dsn = [];
$dsn[] = "charset=utf8mb4";
$dsn[] = "dbname=$db";
if (strlen($host)) {
$dsn[] = "host=$host";
$dsn[] = "port=$port";
} elseif (strlen($socket)) {
$dsn[] = "socket=$socket";
}
$dsn = "mysql:".implode(";", $dsn);
$dsn = "mysql:".implode(";", [
"charset=utf8mb4",
"dbname=$db",
"host=$host",
"socket=$socket",
"port=$port",
]);
$this->db = new \PDO($dsn, $user, $password, [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
]);

24
lib/Db/MySQL/Statement.php

@ -28,6 +28,7 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
protected $packetSize;
protected $values;
protected $longs;
protected $binds = "";
public function __construct(\mysqli $db, string $query, array $bindings = [], int $packetSize = 4194304) {
@ -39,9 +40,9 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
protected function prepare(string $query): bool {
$this->st = $this->db->prepare($query);
if (!$this->st) {
list($excClass, $excMsg, $excData) = $this->buildEngineException($this->db->errno, $this->db->error);
throw new $excClass($excMsg, $excData);
if (!$this->st) { // @codeCoverageIgnore
list($excClass, $excMsg, $excData) = $this->buildEngineException($this->db->errno, $this->db->error); // @codeCoverageIgnore
throw new $excClass($excMsg, $excData); // @codeCoverageIgnore
}
return true;
}
@ -56,16 +57,26 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
public function runArray(array $values = []): \JKingWeb\Arsse\Db\Result {
$this->st->reset();
// clear normalized values
$this->binds = "";
$this->values = [];
$this->longs = [];
// prepare values and them all at once
$this->bindValues($values);
if ($this->values) {
$this->st->bind_param($this->binds, ...$this->values);
}
// packetize any large values
foreach ($this->longs as $pos => $data) {
$this->st->send_long_data($pos, $data);
unset($data);
}
// execute the statement
$this->st->execute();
// clear normalized values
$this->binds = "";
$this->values = [];
$this->longs = [];
// check for errors
if ($this->st->sqlstate !== "00000") {
if ($this->st->sqlstate === "HY000") {
@ -85,14 +96,15 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
protected function bindValue($value, string $type, int $position): bool {
// this is a bit of a hack: we collect values (and MySQL bind types) here so that we can take
// advantage of the work done by bindValues() even though MySQL requires everything to be bound
// all at once; we also packetize large values here if necessary
// all at once; we also segregate large values for later packetization
if (($type === "binary" && !is_null($value)) || (is_string($value) && strlen($value) > $this->packetSize)) {
$this->values[] = null;
$this->st->send_long_data($position - 1, $value);
$this->longs[$position - 1] = $value;
$this->binds .= "b";
} else {
$this->values[] = $value;
$this->binds .= self::BINDINGS[$type];
}
$this->binds .= self::BINDINGS[$type];
return true;
}
public static function mungeQuery(string $query, array $types, ...$extraData): string {

1
lib/Db/PostgreSQL/Dispatch.php

@ -17,6 +17,7 @@ trait Dispatch {
}
}
/** @codeCoverageIgnore */
public static function buildEngineException($code, string $msg): array {
// PostgreSQL uses SQLSTATE exclusively, so this is not used
return [];

1
lib/Db/PostgreSQL/PDOStatement.php

@ -11,6 +11,7 @@ class PDOStatement extends \JKingWeb\Arsse\Db\PDOStatement {
return Statement::mungeQuery($query, $types, false);
}
/** @codeCoverageIgnore */
public static function buildEngineException($code, string $msg): array {
// PostgreSQL uses SQLSTATE exclusively, so this is not used
return [];

2
lib/Db/SQLState.php

@ -20,7 +20,7 @@ trait SQLState {
case "23000":
case "23502":
case "23505":
return [ExceptionInput::class, "constraintViolation", $msg];
return [ExceptionInput::class, "engineConstraintViolation", $msg];
case "55P03":
case "57014":
return [ExceptionTimeout::class, 'general', $msg];

2
tests/cases/Db/BaseStatement.php

@ -67,7 +67,7 @@ abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideBinaryBindings */
public function testHandleBinaryData($value, string $type, string $exp) {
if (in_array(static::$implementation, ["MySQL", "PostgreSQL", "PDO PostgreSQL"])) {
if (in_array(static::$implementation, ["PostgreSQL", "PDO PostgreSQL"])) {
$this->markTestSkipped("Correct handling of binary data with PostgreSQL and native MySQL is currently unknown");
}
if ($exp === "null") {

4
tests/cases/Db/MySQL/TestDriver.php

@ -8,7 +8,9 @@ namespace JKingWeb\Arsse\TestCase\Db\MySQL;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\MySQL\Driver<extended> */
* @covers \JKingWeb\Arsse\Db\MySQL\Driver<extended>
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQL;

3
tests/cases/Db/MySQL/TestResult.php

@ -11,7 +11,8 @@ use JKingWeb\Arsse\Test\DatabaseInformation;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\MySQL\Result<extended>
*/
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQL;

16
tests/cases/Db/MySQL/TestStatement.php

@ -8,7 +8,9 @@ namespace JKingWeb\Arsse\TestCase\Db\MySQL;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\MySQL\Statement<extended> */
* @covers \JKingWeb\Arsse\Db\MySQL\Statement<extended>
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQL;
@ -31,4 +33,16 @@ class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
return $value;
}
}
public function testBindLongString() {
// this test requires some set-up to be effective
static::$interface->query("CREATE TABLE arsse_test(`value` longtext not null) character set utf8mb4");
// we'll use an unrealistic packet size of 1 byte to trigger special handling for strings which are too long for the maximum packet size
$str = "long string";
$s = new \JKingWeb\Arsse\Db\MySQL\Statement(static::$interface, "INSERT INTO arsse_test values(?)", ["str"], 1);
$s->runArray([$str]);
$s = new \JKingWeb\Arsse\Db\MySQL\Statement(static::$interface, "SELECT * from arsse_test", []);
$val = $s->run()->getValue();
$this->assertSame($str, $val);
}
}

4
tests/cases/Db/MySQL/TestUpdate.php

@ -8,7 +8,9 @@ namespace JKingWeb\Arsse\TestCase\Db\MySQL;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\MySQL\Driver<extended> */
* @covers \JKingWeb\Arsse\Db\MySQL\Driver<extended>
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQL;

4
tests/cases/Db/MySQLPDO/TestDriver.php

@ -9,8 +9,10 @@ namespace JKingWeb\Arsse\TestCase\Db\MySQLPDO;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\MySQL\PDODriver<extended>
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\PDODriver
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQLPDO;

4
tests/cases/Db/MySQLPDO/TestResult.php

@ -11,7 +11,9 @@ use JKingWeb\Arsse\Test\DatabaseInformation;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\PDOResult<extended>
*/
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQLPDO;

4
tests/cases/Db/MySQLPDO/TestStatement.php

@ -9,7 +9,9 @@ namespace JKingWeb\Arsse\TestCase\Db\MySQLPDO;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\MySQL\PDOStatement<extended>
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQLPDO;

5
tests/cases/Db/MySQLPDO/TestUpdate.php

@ -9,7 +9,10 @@ namespace JKingWeb\Arsse\TestCase\Db\MySQLPDO;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\MySQL\PDODriver<extended>
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\MySQL\ExceptionBuilder
* @covers \JKingWeb\Arsse\Db\PDODriver
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\MySQLPDO;

3
tests/cases/Db/PostgreSQL/TestDriver.php

@ -9,7 +9,8 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQL;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Driver<extended>
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Dispatch<extended> */
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Dispatch<extended>
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\PostgreSQL;

3
tests/cases/Db/PostgreSQL/TestStatement.php

@ -9,7 +9,8 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQL;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Statement<extended>
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Dispatch<extended> */
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Dispatch<extended>
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\PostgreSQL;

3
tests/cases/Db/PostgreSQL/TestUpdate.php

@ -8,7 +8,8 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQL;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Driver<extended> */
* @covers \JKingWeb\Arsse\Db\PostgreSQL\Driver<extended>
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\PostgreSQL;

3
tests/cases/Db/PostgreSQLPDO/TestDriver.php

@ -10,7 +10,8 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQLPDO;
* @group slow
* @covers \JKingWeb\Arsse\Db\PostgreSQL\PDODriver<extended>
* @covers \JKingWeb\Arsse\Db\PDODriver
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\PostgreSQLPDO;

3
tests/cases/Db/PostgreSQLPDO/TestStatement.php

@ -9,7 +9,8 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQLPDO;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\PostgreSQL\PDOStatement<extended>
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\PostgreSQLPDO;

4
tests/cases/Db/PostgreSQLPDO/TestUpdate.php

@ -9,7 +9,9 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQLPDO;
/**
* @group slow
* @covers \JKingWeb\Arsse\Db\PostgreSQL\PDODriver<extended>
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDODriver
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\PostgreSQLPDO;

3
tests/cases/Db/SQLite3PDO/TestCreation.php

@ -15,7 +15,8 @@ use Phake;
/**
* @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver<extended>
* @covers \JKingWeb\Arsse\Db\PDODriver
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest {
protected $data;
protected $drv;

3
tests/cases/Db/SQLite3PDO/TestDriver.php

@ -9,7 +9,8 @@ namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO;
/**
* @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver<extended>
* @covers \JKingWeb\Arsse\Db\PDODriver
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\SQLite3PDO;

3
tests/cases/Db/SQLite3PDO/TestStatement.php

@ -8,7 +8,8 @@ namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO;
/**
* @covers \JKingWeb\Arsse\Db\PDOStatement<extended>
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\SQLite3PDO;

4
tests/cases/Db/SQLite3PDO/TestUpdate.php

@ -8,7 +8,9 @@ namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO;
/**
* @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver<extended>
* @covers \JKingWeb\Arsse\Db\PDOError */
* @covers \JKingWeb\Arsse\Db\PDODriver
* @covers \JKingWeb\Arsse\Db\PDOError
* @covers \JKingWeb\Arsse\Db\SQLState */
class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate {
use \JKingWeb\Arsse\TestCase\DatabaseDrivers\SQLite3PDO;

Loading…
Cancel
Save