diff --git a/res/sqlite-worker.php b/res/sqlite-worker.php index 68c3827..6722133 100644 --- a/res/sqlite-worker.php +++ b/res/sqlite-worker.php @@ -132,34 +132,38 @@ if ($data->params[1] === []) { $result = @$db->query($data->params[0]); } else { - $statement = $db->prepare($data->params[0]); - foreach ($data->params[1] as $index => $value) { - if ($value === null) { - $type = \SQLITE3_NULL; - } elseif ($value === true || $value === false) { - // explicitly cast bool to int because SQLite does not have a native boolean - $type = \SQLITE3_INTEGER; - $value = (int)$value; - } elseif (\is_int($value)) { - $type = \SQLITE3_INTEGER; - } elseif (isset($value->float)) { - $type = \SQLITE3_FLOAT; - $value = (float)$value->float; - } elseif (isset($value->base64)) { - // base64-decode string parameters as BLOB - $type = \SQLITE3_BLOB; - $value = \base64_decode($value->base64); - } else { - $type = \SQLITE3_TEXT; - } + $statement = @$db->prepare($data->params[0]); + if ($statement === false) { + $result = false; + } else { + foreach ($data->params[1] as $index => $value) { + if ($value === null) { + $type = \SQLITE3_NULL; + } elseif ($value === true || $value === false) { + // explicitly cast bool to int because SQLite does not have a native boolean + $type = \SQLITE3_INTEGER; + $value = (int)$value; + } elseif (\is_int($value)) { + $type = \SQLITE3_INTEGER; + } elseif (isset($value->float)) { + $type = \SQLITE3_FLOAT; + $value = (float)$value->float; + } elseif (isset($value->base64)) { + // base64-decode string parameters as BLOB + $type = \SQLITE3_BLOB; + $value = \base64_decode($value->base64); + } else { + $type = \SQLITE3_TEXT; + } - $statement->bindValue( - \is_int($index) ? $index + 1 : $index, - $value, - $type - ); + $statement->bindValue( + \is_int($index) ? $index + 1 : $index, + $value, + $type + ); + } + $result = @$statement->execute(); } - $result = @$statement->execute(); } if ($result === false) { diff --git a/src/Factory.php b/src/Factory.php index c8212d5..d59ac62 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -211,11 +211,11 @@ private function openProcessIo($filename, $flags = null) } // @codeCoverageIgnoreEnd - // launch process with default STDIO pipes + // launch process with default STDIO pipes, but inherit STDERR $pipes = array( array('pipe', 'r'), array('pipe', 'w'), - array('pipe', 'w') + \defined('STDERR') ? \STDERR : \fopen('php://stderr', 'w') ); // do not inherit open FDs by explicitly overwriting existing FDs with dummy files @@ -253,7 +253,7 @@ private function openSocketIo($filename, $flags = null) { $command = \escapeshellarg($this->bin) . ' sqlite-worker.php'; - // launch process without default STDIO pipes + // launch process without default STDIO pipes, but inherit STDERR $null = \DIRECTORY_SEPARATOR === '\\' ? 'nul' : '/dev/null'; $pipes = array( array('file', $null, 'r'), diff --git a/tests/FunctionalDatabaseTest.php b/tests/FunctionalDatabaseTest.php index 95a4ad4..f957916 100644 --- a/tests/FunctionalDatabaseTest.php +++ b/tests/FunctionalDatabaseTest.php @@ -292,6 +292,64 @@ public function testQueryStringResolvesWithResultWithTypeStringAndRunsUntilQuit( $this->assertSame(array(array('value' => 'hellö')), $data); } + /** + * @dataProvider provideSocketFlags + * @param bool $flag + */ + public function testQueryInvalidTableRejectsWithExceptionAndRunsUntilQuit($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->query('SELECT 1 FROM foo')->then('var_dump', function (Exception $e) use (&$data) { + $data = $e->getMessage(); + }); + + $db->quit(); + }); + + $loop->run(); + + $this->assertSame('no such table: foo', $data); + } + + /** + * @dataProvider provideSocketFlags + * @param bool $flag + */ + public function testQueryInvalidTableWithPlaceholderRejectsWithExceptionAndRunsUntilQuit($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->query('SELECT ? FROM foo', [1])->then('var_dump', function (Exception $e) use (&$data) { + $data = $e->getMessage(); + }); + + $db->quit(); + }); + + $loop->run(); + + $this->assertSame('no such table: foo', $data); + } + public function provideSqlDataWillBeReturnedWithType() { return array_merge(