Skip to content

Commit 13a2c5e

Browse files
committed
Support type mapping for float and boolean values
1 parent 6a971a6 commit 13a2c5e

File tree

4 files changed

+117
-30
lines changed

4 files changed

+117
-30
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ query like this:
202202
$db->query('SELECT * FROM user WHERE id > :id', ['id' => $id]);
203203
```
204204

205+
All placeholder values will automatically be mapped to the native SQLite
206+
datatypes and all result values will automatically be mapped to the
207+
native PHP datatypes. This conversion supports `int`, `float`, `string`
208+
(text) and `null`. SQLite does not have a native boolean type, so `true`
209+
and `false` will be mapped to integer values `1` and `0` respectively.
210+
205211
#### quit()
206212

207213
The `quit(): PromiseInterface<void, Exception>` method can be used to

res/sqlite-worker.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
$statement->bindValue(
138138
$index + 1,
139139
$value,
140-
$value === null ? \SQLITE3_NULL : \is_int($value) ? \SQLITE3_INTEGER : \SQLITE3_TEXT
140+
$value === null ? \SQLITE3_NULL : \is_int($value) || \is_bool($value) ? \SQLITE3_INTEGER : \is_float($value) ? \SQLITE3_FLOAT : \SQLITE3_TEXT
141141
);
142142
}
143143
$result = @$statement->execute();
@@ -176,7 +176,7 @@
176176
$statement->bindValue(
177177
$index,
178178
$value,
179-
$value === null ? \SQLITE3_NULL : \is_int($value) ? \SQLITE3_INTEGER : \SQLITE3_TEXT
179+
$value === null ? \SQLITE3_NULL : \is_int($value) || \is_bool($value) ? \SQLITE3_INTEGER : \is_float($value) ? \SQLITE3_FLOAT : \SQLITE3_TEXT
180180
);
181181
}
182182
$result = @$statement->execute();

src/DatabaseInterface.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ public function exec($sql);
136136
* $db->query('SELECT * FROM user WHERE id > :id', ['id' => $id]);
137137
* ```
138138
*
139+
* All placeholder values will automatically be mapped to the native SQLite
140+
* datatypes and all result values will automatically be mapped to the
141+
* native PHP datatypes. This conversion supports `int`, `float`, `string`
142+
* (text) and `null`. SQLite does not have a native boolean type, so `true`
143+
* and `false` will be mapped to integer values `1` and `0` respectively.
144+
*
139145
* @param string $sql SQL statement
140146
* @param array $params Parameters which should be bound to query
141147
* @return PromiseInterface<Result> Resolves with Result instance or rejects with Exception

tests/FunctionalDatabaseTest.php

Lines changed: 103 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -279,24 +279,34 @@ public function testQueryStringResolvesWithResultWithTypeStringAndRunsUntilQuit(
279279
$this->assertSame(array(array('value' => 'hellö')), $data);
280280
}
281281

282+
public function provideSqlDataWillBeReturnedWithType()
283+
{
284+
return [
285+
['42', 42],
286+
['1.5', 1.5],
287+
['null', null],
288+
['"hello"', 'hello'],
289+
['"hellö"', 'hellö'],
290+
['true', 1],
291+
['false', 0],
292+
];
293+
}
294+
282295
/**
283-
* @dataProvider provideSocketFlags
284-
* @param bool $flag
296+
* @dataProvider provideSqlDataWillBeReturnedWithType
297+
* @param mixed $value
298+
* @param mixed $expected
285299
*/
286-
public function testQueryIntegerPlaceholderPositionalResolvesWithResultWithTypeIntegerAndRunsUntilQuit($flag)
300+
public function testQueryValueInStatementResolvesWithResultWithTypeAndRunsUntilQuit($value, $expected)
287301
{
288302
$loop = React\EventLoop\Factory::create();
289303
$factory = new Factory($loop);
290304

291-
$ref = new ReflectionProperty($factory, 'useSocket');
292-
$ref->setAccessible(true);
293-
$ref->setValue($factory, $flag);
294-
295305
$promise = $factory->open(':memory:');
296306

297307
$data = null;
298-
$promise->then(function (DatabaseInterface $db) use (&$data){
299-
$db->query('SELECT ? AS value', array(1))->then(function (Result $result) use (&$data) {
308+
$promise->then(function (DatabaseInterface $db) use (&$data, $value){
309+
$db->query('SELECT ' . $value . ' AS value')->then(function (Result $result) use (&$data) {
300310
$data = $result->rows;
301311
});
302312

@@ -305,27 +315,60 @@ public function testQueryIntegerPlaceholderPositionalResolvesWithResultWithTypeI
305315

306316
$loop->run();
307317

308-
$this->assertSame(array(array('value' => 1)), $data);
318+
$this->assertSame(array(array('value' => $expected)), $data);
319+
}
320+
321+
public function provideDataWillBeReturnedWithType()
322+
{
323+
return [
324+
[0],
325+
[1],
326+
[1.5],
327+
[null],
328+
['hello'],
329+
['hellö']
330+
];
309331
}
310332

311333
/**
312-
* @dataProvider provideSocketFlags
313-
* @param bool $flag
334+
* @dataProvider provideDataWillBeReturnedWithType
335+
* @param mixed $value
314336
*/
315-
public function testQueryIntegerPlaceholderNamedResolvesWithResultWithTypeIntegerAndRunsUntilQuit($flag)
337+
public function testQueryValuePlaceholderPositionalResolvesWithResultWithExactTypeAndRunsUntilQuit($value)
316338
{
317339
$loop = React\EventLoop\Factory::create();
318340
$factory = new Factory($loop);
319341

320-
$ref = new ReflectionProperty($factory, 'useSocket');
321-
$ref->setAccessible(true);
322-
$ref->setValue($factory, $flag);
342+
$promise = $factory->open(':memory:');
343+
344+
$data = null;
345+
$promise->then(function (DatabaseInterface $db) use (&$data, $value){
346+
$db->query('SELECT ? AS value', array($value))->then(function (Result $result) use (&$data) {
347+
$data = $result->rows;
348+
});
349+
350+
$db->quit();
351+
});
352+
353+
$loop->run();
354+
355+
$this->assertSame(array(array('value' => $value)), $data);
356+
}
357+
358+
/**
359+
* @dataProvider provideDataWillBeReturnedWithType
360+
* @param mixed $value
361+
*/
362+
public function testQueryValuePlaceholderNamedResolvesWithResultWithExactTypeAndRunsUntilQuit($value)
363+
{
364+
$loop = React\EventLoop\Factory::create();
365+
$factory = new Factory($loop);
323366

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

326369
$data = null;
327-
$promise->then(function (DatabaseInterface $db) use (&$data){
328-
$db->query('SELECT :value AS value', array('value' => 1))->then(function (Result $result) use (&$data) {
370+
$promise->then(function (DatabaseInterface $db) use (&$data, $value){
371+
$db->query('SELECT :value AS value', array('value' => $value))->then(function (Result $result) use (&$data) {
329372
$data = $result->rows;
330373
});
331374

@@ -334,27 +377,59 @@ public function testQueryIntegerPlaceholderNamedResolvesWithResultWithTypeIntege
334377

335378
$loop->run();
336379

337-
$this->assertSame(array(array('value' => 1)), $data);
380+
$this->assertSame(array(array('value' => $value)), $data);
381+
}
382+
383+
public function provideDataWillBeReturnedWithOtherType()
384+
{
385+
return [
386+
'true' => [true, 1],
387+
'false' => [false, 0],
388+
'float without fraction is int' => [1.0, 1]
389+
];
338390
}
339391

340392
/**
341-
* @dataProvider provideSocketFlags
342-
* @param bool $flag
393+
* @dataProvider provideDataWillBeReturnedWithOtherType
394+
* @param mixed $value
395+
* @param mixed $expected
343396
*/
344-
public function testQueryNullPlaceholderPositionalResolvesWithResultWithTypeNullAndRunsUntilQuit($flag)
397+
public function testQueryValuePlaceholderPositionalResolvesWithResultWithOtherTypeAndRunsUntilQuit($value, $expected)
345398
{
346399
$loop = React\EventLoop\Factory::create();
347400
$factory = new Factory($loop);
348401

349-
$ref = new ReflectionProperty($factory, 'useSocket');
350-
$ref->setAccessible(true);
351-
$ref->setValue($factory, $flag);
402+
$promise = $factory->open(':memory:');
403+
404+
$data = null;
405+
$promise->then(function (DatabaseInterface $db) use (&$data, $value){
406+
$db->query('SELECT ? AS value', array($value))->then(function (Result $result) use (&$data) {
407+
$data = $result->rows;
408+
});
409+
410+
$db->quit();
411+
});
412+
413+
$loop->run();
414+
415+
$this->assertSame(array(array('value' => $expected)), $data);
416+
}
417+
418+
/**
419+
* @dataProvider provideDataWillBeReturnedWithOtherType
420+
* @param mixed $value
421+
* @param mixed $expected
422+
*/
423+
public function testQueryValuePlaceholderNamedResolvesWithResultWithOtherTypeAndRunsUntilQuit($value, $expected)
424+
{
425+
$loop = React\EventLoop\Factory::create();
426+
$factory = new Factory($loop);
352427

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

355430
$data = null;
356-
$promise->then(function (DatabaseInterface $db) use (&$data){
357-
$db->query('SELECT ? AS value', array(null))->then(function (Result $result) use (&$data) {
431+
$promise->then(function (DatabaseInterface $db) use (&$data, $value){
432+
$db->query('SELECT :value AS value', array('value' => $value))->then(function (Result $result) use (&$data) {
358433
$data = $result->rows;
359434
});
360435

@@ -363,7 +438,7 @@ public function testQueryNullPlaceholderPositionalResolvesWithResultWithTypeNull
363438

364439
$loop->run();
365440

366-
$this->assertSame(array(array('value' => null)), $data);
441+
$this->assertSame(array(array('value' => $expected)), $data);
367442
}
368443

369444
/**

0 commit comments

Comments
 (0)