Skip to content

Commit b7a10e4

Browse files
authored
Merge pull request #25 from clue-labs/prepare-errors
Fix error reporting when preparing query with placeholder fails
2 parents a8c8d26 + 665ff73 commit b7a10e4

File tree

3 files changed

+91
-29
lines changed

3 files changed

+91
-29
lines changed

res/sqlite-worker.php

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -132,34 +132,38 @@
132132
if ($data->params[1] === []) {
133133
$result = @$db->query($data->params[0]);
134134
} else {
135-
$statement = $db->prepare($data->params[0]);
136-
foreach ($data->params[1] as $index => $value) {
137-
if ($value === null) {
138-
$type = \SQLITE3_NULL;
139-
} elseif ($value === true || $value === false) {
140-
// explicitly cast bool to int because SQLite does not have a native boolean
141-
$type = \SQLITE3_INTEGER;
142-
$value = (int)$value;
143-
} elseif (\is_int($value)) {
144-
$type = \SQLITE3_INTEGER;
145-
} elseif (isset($value->float)) {
146-
$type = \SQLITE3_FLOAT;
147-
$value = (float)$value->float;
148-
} elseif (isset($value->base64)) {
149-
// base64-decode string parameters as BLOB
150-
$type = \SQLITE3_BLOB;
151-
$value = \base64_decode($value->base64);
152-
} else {
153-
$type = \SQLITE3_TEXT;
154-
}
135+
$statement = @$db->prepare($data->params[0]);
136+
if ($statement === false) {
137+
$result = false;
138+
} else {
139+
foreach ($data->params[1] as $index => $value) {
140+
if ($value === null) {
141+
$type = \SQLITE3_NULL;
142+
} elseif ($value === true || $value === false) {
143+
// explicitly cast bool to int because SQLite does not have a native boolean
144+
$type = \SQLITE3_INTEGER;
145+
$value = (int)$value;
146+
} elseif (\is_int($value)) {
147+
$type = \SQLITE3_INTEGER;
148+
} elseif (isset($value->float)) {
149+
$type = \SQLITE3_FLOAT;
150+
$value = (float)$value->float;
151+
} elseif (isset($value->base64)) {
152+
// base64-decode string parameters as BLOB
153+
$type = \SQLITE3_BLOB;
154+
$value = \base64_decode($value->base64);
155+
} else {
156+
$type = \SQLITE3_TEXT;
157+
}
155158

156-
$statement->bindValue(
157-
\is_int($index) ? $index + 1 : $index,
158-
$value,
159-
$type
160-
);
159+
$statement->bindValue(
160+
\is_int($index) ? $index + 1 : $index,
161+
$value,
162+
$type
163+
);
164+
}
165+
$result = @$statement->execute();
161166
}
162-
$result = @$statement->execute();
163167
}
164168

165169
if ($result === false) {

src/Factory.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ private function openProcessIo($filename, $flags = null)
211211
}
212212
// @codeCoverageIgnoreEnd
213213

214-
// launch process with default STDIO pipes
214+
// launch process with default STDIO pipes, but inherit STDERR
215215
$pipes = array(
216216
array('pipe', 'r'),
217217
array('pipe', 'w'),
218-
array('pipe', 'w')
218+
\defined('STDERR') ? \STDERR : \fopen('php://stderr', 'w')
219219
);
220220

221221
// do not inherit open FDs by explicitly overwriting existing FDs with dummy files
@@ -253,7 +253,7 @@ private function openSocketIo($filename, $flags = null)
253253
{
254254
$command = \escapeshellarg($this->bin) . ' sqlite-worker.php';
255255

256-
// launch process without default STDIO pipes
256+
// launch process without default STDIO pipes, but inherit STDERR
257257
$null = \DIRECTORY_SEPARATOR === '\\' ? 'nul' : '/dev/null';
258258
$pipes = array(
259259
array('file', $null, 'r'),

tests/FunctionalDatabaseTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,64 @@ public function testQueryStringResolvesWithResultWithTypeStringAndRunsUntilQuit(
292292
$this->assertSame(array(array('value' => 'hellö')), $data);
293293
}
294294

295+
/**
296+
* @dataProvider provideSocketFlags
297+
* @param bool $flag
298+
*/
299+
public function testQueryInvalidTableRejectsWithExceptionAndRunsUntilQuit($flag)
300+
{
301+
$loop = React\EventLoop\Factory::create();
302+
$factory = new Factory($loop);
303+
304+
$ref = new ReflectionProperty($factory, 'useSocket');
305+
$ref->setAccessible(true);
306+
$ref->setValue($factory, $flag);
307+
308+
$promise = $factory->open(':memory:');
309+
310+
$data = null;
311+
$promise->then(function (DatabaseInterface $db) use (&$data){
312+
$db->query('SELECT 1 FROM foo')->then('var_dump', function (Exception $e) use (&$data) {
313+
$data = $e->getMessage();
314+
});
315+
316+
$db->quit();
317+
});
318+
319+
$loop->run();
320+
321+
$this->assertSame('no such table: foo', $data);
322+
}
323+
324+
/**
325+
* @dataProvider provideSocketFlags
326+
* @param bool $flag
327+
*/
328+
public function testQueryInvalidTableWithPlaceholderRejectsWithExceptionAndRunsUntilQuit($flag)
329+
{
330+
$loop = React\EventLoop\Factory::create();
331+
$factory = new Factory($loop);
332+
333+
$ref = new ReflectionProperty($factory, 'useSocket');
334+
$ref->setAccessible(true);
335+
$ref->setValue($factory, $flag);
336+
337+
$promise = $factory->open(':memory:');
338+
339+
$data = null;
340+
$promise->then(function (DatabaseInterface $db) use (&$data){
341+
$db->query('SELECT ? FROM foo', [1])->then('var_dump', function (Exception $e) use (&$data) {
342+
$data = $e->getMessage();
343+
});
344+
345+
$db->quit();
346+
});
347+
348+
$loop->run();
349+
350+
$this->assertSame('no such table: foo', $data);
351+
}
352+
295353
public function provideSqlDataWillBeReturnedWithType()
296354
{
297355
return array_merge(

0 commit comments

Comments
 (0)