Skip to content

Commit

Permalink
Fix result set rows for DDL queries to be null, instead of empty array
Browse files Browse the repository at this point in the history
The result set for DDL queries (INSERT, UPDATE, DELETE etc.) is empty,
i.e. it does not contain any columns or rows. Columns and rows should
thus be represented by a null value. In contrast, an empty result set
from a SELECT query will always contain a non-empty array of columns and
may contain any number of rows (possibly an empty array of rows).
  • Loading branch information
clue committed May 17, 2019
1 parent d8f0d34 commit 9793e66
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 18 deletions.
13 changes: 7 additions & 6 deletions res/sqlite-worker.php
Expand Up @@ -172,16 +172,15 @@
'error' => array('message' => $db->lastErrorMsg())
));
} else {
$columns = array();
for ($i = 0, $n = $result->numColumns(); $i < $n; ++$i) {
$columns[] = $result->columnName($i);
}

$rows = array();
if ($result->numColumns() !== 0) {
// Fetch all rows only if this result set has any columns.
// INSERT/UPDATE/DELETE etc. do not return any columns, trying
// to fetch the results here will issue the same query again.
$rows = $columns = [];
for ($i = 0, $n = $result->numColumns(); $i < $n; ++$i) {
$columns[] = $result->columnName($i);
}

while (($row = $result->fetchArray(\SQLITE3_ASSOC)) !== false) {
// base64-encode any string that is not valid UTF-8 without control characters (BLOB)
foreach ($row as &$value) {
Expand All @@ -193,6 +192,8 @@
}
$rows[] = $row;
}
} else {
$rows = $columns = null;
}
$result->finalize();

Expand Down
17 changes: 9 additions & 8 deletions src/Io/ProcessIoDatabase.php
Expand Up @@ -90,18 +90,19 @@ public function query($sql, array $params = array())
$result->changed = $data['changed'];
$result->insertId = $data['insertId'];
$result->columns = $data['columns'];
$result->rows = $data['rows'];

// base64-decode string result values for BLOBS
$result->rows = [];
foreach ($data['rows'] as $row) {
foreach ($row as &$value) {
if (isset($value['base64'])) {
$value = \base64_decode($value['base64']);
} elseif (isset($value['float'])) {
$value = (float)$value['float'];
if ($result->rows !== null) {
foreach ($result->rows as &$row) {
foreach ($row as &$value) {
if (isset($value['base64'])) {
$value = \base64_decode($value['base64']);
} elseif (isset($value['float'])) {
$value = (float)$value['float'];
}
}
}
$result->rows[] = $row;
}

return $result;
Expand Down
43 changes: 39 additions & 4 deletions tests/FunctionalDatabaseTest.php
Expand Up @@ -658,7 +658,7 @@ public function testExecRejectsWhenAlreadyClosed($flag)
* @dataProvider provideSocketFlags
* @param bool $flag
*/
public function testQueryInsertResolvesWithResultWithLastInsertIdAndRunsUntilQuit($flag)
public function testQueryInsertResolvesWithEmptyResultSetWithLastInsertIdAndRunsUntilQuit($flag)
{
$loop = React\EventLoop\Factory::create();
$factory = new Factory($loop);
Expand All @@ -673,15 +673,50 @@ public function testQueryInsertResolvesWithResultWithLastInsertIdAndRunsUntilQui
$promise->then(function (DatabaseInterface $db) use (&$data){
$db->exec('CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, bar STRING)');
$db->query('INSERT INTO foo (bar) VALUES (?)', ['test'])->then(function (Result $result) use (&$data) {
$data = $result->insertId;
});
$data = $result;
}, 'printf');

$db->quit();
});

$loop->run();

$this->assertInstanceOf('Clue\React\SQLite\Result', $data);
$this->assertSame(1, $data->insertId);
$this->assertNull($data->columns);
$this->assertNull($data->rows);
}

/**
* @dataProvider provideSocketFlags
* @param bool $flag
*/
public function testQuerySelectEmptyResolvesWithEmptyResultSetWithColumnsAndNoRowsAndRunsUntilQuit($flag)
{
$loop = React\EventLoop\Factory::create();
$factory = new Factory($loop);

$ref = new ReflectionProperty($factory, 'useSocket');
$ref->setAccessible(true);
$ref->setValue($factory, $flag);

$promise = $factory->open(':memory:');

$data = null;
$promise->then(function (DatabaseInterface $db) use (&$data){
$db->exec('CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, bar STRING)');
$db->query('SELECT * FROM foo LIMIT 0')->then(function (Result $result) use (&$data) {
$data = $result;
}, 'printf');

$db->quit();
});

$loop->run();

$this->assertSame(1, $data);
$this->assertInstanceOf('Clue\React\SQLite\Result', $data);
$this->assertSame(['id', 'bar'], $data->columns);
$this->assertSame([], $data->rows);
}

protected function expectCallableNever()
Expand Down

0 comments on commit 9793e66

Please sign in to comment.