Skip to content

Commit 39ca1c9

Browse files
committed
Fix selecting empty result sets and avoid implicitly executing twice
Trying to fetch a result set row for a query that returns an empty result set results in the query being executed again. We avoid this by first checking if there are any columns and avoid fetch result rows otherwise.
1 parent 942c9cd commit 39ca1c9

File tree

2 files changed

+43
-8
lines changed

2 files changed

+43
-8
lines changed

res/sqlite-worker.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,21 @@
174174
}
175175

176176
$rows = array();
177-
while (($row = $result->fetchArray(\SQLITE3_ASSOC)) !== false) {
178-
// base64-encode any string that is not valid UTF-8 without control characters (BLOB)
179-
foreach ($row as &$value) {
180-
if (\is_string($value) && \preg_match('/[\x00-\x08\x11\x12\x14-\x1f\x7f]/u', $value) !== 0) {
181-
$value = ['base64' => \base64_encode($value)];
182-
} elseif (\is_float($value)) {
183-
$value = ['float' => $value];
177+
if ($result->numColumns() !== 0) {
178+
// Fetch all rows only if this result set has any columns.
179+
// INSERT/UPDATE/DELETE etc. do not return any columns, trying
180+
// to fetch the results here will issue the same query again.
181+
while (($row = $result->fetchArray(\SQLITE3_ASSOC)) !== false) {
182+
// base64-encode any string that is not valid UTF-8 without control characters (BLOB)
183+
foreach ($row as &$value) {
184+
if (\is_string($value) && \preg_match('/[\x00-\x08\x11\x12\x14-\x1f\x7f]/u', $value) !== 0) {
185+
$value = ['base64' => \base64_encode($value)];
186+
} elseif (\is_float($value)) {
187+
$value = ['float' => $value];
188+
}
184189
}
190+
$rows[] = $row;
185191
}
186-
$rows[] = $row;
187192
}
188193
$result->finalize();
189194

tests/FunctionalDatabaseTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,36 @@ public function testExecRejectsWhenAlreadyClosed($flag)
596596
$loop->run();
597597
}
598598

599+
/**
600+
* @dataProvider provideSocketFlags
601+
* @param bool $flag
602+
*/
603+
public function testQueryInsertResolvesWithResultWithLastInsertIdAndRunsUntilQuit($flag)
604+
{
605+
$loop = React\EventLoop\Factory::create();
606+
$factory = new Factory($loop);
607+
608+
$ref = new ReflectionProperty($factory, 'useSocket');
609+
$ref->setAccessible(true);
610+
$ref->setValue($factory, $flag);
611+
612+
$promise = $factory->open(':memory:');
613+
614+
$data = null;
615+
$promise->then(function (DatabaseInterface $db) use (&$data){
616+
$db->exec('CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, bar STRING)');
617+
$db->query('INSERT INTO foo (bar) VALUES (?)', ['test'])->then(function (Result $result) use (&$data) {
618+
$data = $result->insertId;
619+
});
620+
621+
$db->quit();
622+
});
623+
624+
$loop->run();
625+
626+
$this->assertSame(1, $data);
627+
}
628+
599629
protected function expectCallableNever()
600630
{
601631
$mock = $this->createCallableMock();

0 commit comments

Comments
 (0)