diff --git a/src/Connection.php b/src/Connection.php index a42310b8..3234576f 100755 --- a/src/Connection.php +++ b/src/Connection.php @@ -1222,11 +1222,11 @@ public function getCacheSize(): int * An internal wrapper to dbsafeString, used to process a complete array of parameters * as used by prepared statements. * - * @param array $params + * @param array $params * @param list|false $escapes An array of boolean for each param, used to block the escaping of html-special chars. * If not passed, all params will be cleaned. * - * @return list + * @return list * * @see Db::dbsafeString($string, $htmlSpecialChars = true) */ @@ -1257,6 +1257,7 @@ private function dbsafeParams(array $params, array | false $escapes = []): array $replace[$key] = $param; } + /** @var list */ return array_values($replace); } diff --git a/src/Driver/MysqliDriver.php b/src/Driver/MysqliDriver.php index 2801720a..a4e3f640 100755 --- a/src/Driver/MysqliDriver.php +++ b/src/Driver/MysqliDriver.php @@ -196,17 +196,31 @@ public function getPArray(string $query, array $params): Generator throw new QueryException('Could not execute statement: ' . $this->getError(), $query, $params); } - $result = $statement->get_result(); - - if ($result === false) { - return; + $meta = $statement->result_metadata(); + if ($meta === false) { + throw new QueryException('Could not get result meta', $query, $params); } - while ($row = $result->fetch_assoc()) { - yield $row; - } + $columnNames = array_column($meta->fetch_fields(), 'name'); + + $meta->free(); + + $statement->store_result(); + + $boundValues = array_fill(0, count($columnNames), null); - $result->free_result(); + $refs = &$boundValues; + + $statement->bind_result(...$refs); + + while ($statement->fetch()) { + $values = []; + foreach ($boundValues as $value) { + $values[] = $value; + } + + yield array_combine($columnNames, $values); + } } /** diff --git a/tests/ConnectionTestCase.php b/tests/ConnectionTestCase.php index fb8e59c6..97fed1be 100644 --- a/tests/ConnectionTestCase.php +++ b/tests/ConnectionTestCase.php @@ -19,6 +19,7 @@ use Artemeon\Database\Exception\ConnectionException; use Artemeon\Database\Exception\QueryException; use Artemeon\Database\Schema\DataType; +use Override; use PHPUnit\Framework\TestCase; abstract class ConnectionTestCase extends TestCase @@ -27,7 +28,7 @@ abstract class ConnectionTestCase extends TestCase protected const string TEST_TABLE_NAME = 'agp_test_table'; - #[\Override] + #[Override] protected function setUp(): void { parent::setUp(); @@ -36,7 +37,7 @@ protected function setUp(): void $this->setupFixture(); } - #[\Override] + #[Override] protected function tearDown(): void { parent::tearDown(); diff --git a/tests/Driver/PostgresDriverTest.php b/tests/Driver/PostgresDriverTest.php index d6817e31..8c3cf976 100644 --- a/tests/Driver/PostgresDriverTest.php +++ b/tests/Driver/PostgresDriverTest.php @@ -7,8 +7,10 @@ use Artemeon\Database\ConnectionParameters; use Artemeon\Database\Driver\PostgresDriver; use Mockery; +use Override; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; /** @@ -16,6 +18,15 @@ */ final class PostgresDriverTest extends TestCase { + #[Override] + protected function setUp(): void + { + $dumpBin = new ExecutableFinder()->find('pg_dump'); + if ($dumpBin === null) { + self::markTestSkipped('pg_dump not available'); + } + } + public function testBuildsDatabaseSpecificSubstringExpression(): void { $postgresDriver = new PostgresDriver();