Initial SQLite development #1

Merged
danieljsummers merged 25 commits from develop into main 2024-06-08 23:58:45 +00:00
21 changed files with 691 additions and 42 deletions
Showing only changes of commit 2d8f8b6e87 - Show all commits

View File

@ -47,7 +47,9 @@ class Custom
if ($stmt->execute()) return $stmt;
} catch (PDOException $ex) {
$keyword = explode(' ', $query, 2)[0];
throw new DocumentException("Error executing $keyword statement: " . $stmt->errorCode(), previous: $ex);
throw new DocumentException(
sprintf("Error executing %s statement: [%s] %s", $keyword, $stmt->errorCode(), $stmt->errorInfo()[2]),
previous: $ex);
}
$keyword = explode(' ', $query, 2)[0];
throw new DocumentException("Error executing $keyword statement: " . $stmt->errorCode());

View File

@ -15,13 +15,23 @@ use PDOStatement;
*/
class DocumentList
{
/** @var TDoc|null $_first The first item from the results */
private mixed $_first = null;
/**
* Constructor
*
* @param PDOStatement|null $result The result of the query
* @param Mapper<TDoc> $mapper The mapper to deserialize JSON
*/
private function __construct(private ?PDOStatement &$result, private readonly Mapper $mapper) { }
private function __construct(private ?PDOStatement &$result, private readonly Mapper $mapper)
{
if ($row = $this->result->fetch(PDO::FETCH_ASSOC)) {
$this->_first = $this->mapper->map($row);
} else {
$this->result = null;
}
}
/**
* Construct a new document list
@ -45,9 +55,29 @@ class DocumentList
*/
public function items(): Generator
{
if ($this->result) {
while ($row = $this->result->fetch(PDO::FETCH_ASSOC)) yield $this->mapper->map($row);
if (!$this->result) return;
yield $this->_first;
while ($row = $this->result->fetch(PDO::FETCH_ASSOC)) {
yield $this->mapper->map($row);
}
$this->result = null;
}
/**
* Does this list have items remaining?
*
* @return bool True if there are items still to be retrieved from the list, false if not
*/
public function hasItems(): bool
{
return !is_null($this->result);
}
/**
* Ensure the statement is destroyed if the generator is not exhausted
*/
public function __destruct()
{
if (!is_null($this->result)) $this->result = null;
}
}

View File

@ -48,6 +48,7 @@ class Field
* Get the WHERE clause fragment for this parameter
*
* @return string The WHERE clause fragment for this parameter
* @throws DocumentException If the database mode has not been set
*/
public function toWhere(): string
{
@ -57,7 +58,18 @@ class Field
default => " $this->paramName"
};
$prefix = $this->qualifier == '' ? '' : "$this->qualifier.";
return "{$prefix}data->>'$this->fieldName' " . $this->op->toString() . $criteria;
$fieldPath = match (Configuration::$mode) {
Mode::SQLite => "{$prefix}data->>'"
. (str_contains($this->fieldName, '.')
? implode("'->>'", explode('.', $this->fieldName))
: $this->fieldName)
. "'",
Mode::PgSQL => $this->op == Op::BT && is_numeric($this->value[0])
? "({$prefix}data->>'$this->fieldName')::numeric"
: "{$prefix}data->>'$this->fieldName'",
default => throw new DocumentException('Database mode not set; cannot make field WHERE clause')
};
return $fieldPath . ' ' . $this->op->toString() . $criteria;
}
/**

View File

@ -72,7 +72,7 @@ class Parameters
case Mode::SQLite:
$it = [];
$idx = 0;
foreach ($fieldNames as $field) $it[$paramName . $idx++] = $field;
foreach ($fieldNames as $field) $it[$paramName . $idx++] = "$.$field";
return $it;
default:
throw new DocumentException('Database mode not set; cannot generate field name parameters');

View File

@ -3,8 +3,13 @@
namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Count, Field};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
/**
* SQLite integration tests for the Count class
*/
#[TestDox('Count (SQLite integration)')]
class CountTest extends TestCase
{
/** @var string Database name for throwaway database */

View File

@ -4,12 +4,14 @@ namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Count, Custom, DocumentException, Query};
use BitBadger\PDODocument\Mapper\{CountMapper, DocumentMapper};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use Test\Integration\TestDocument;
/**
* SQLite Integration tests for the Custom class
*/
#[TestDox('Custom (SQLite integration)')]
class CustomTest extends TestCase
{
/** @var string Database name for throwaway database */
@ -61,9 +63,7 @@ class CustomTest extends TestCase
$list = Custom::list(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'num_value' > :value",
[':value' => 100], new DocumentMapper(TestDocument::class));
$this->assertNotNull($list, 'The document list should not be null');
$count = 0;
foreach ($list->items() as $ignored) $count++;
$this->assertEquals(0, $count, 'There should have been no documents in the list');
$this->assertFalse($list->hasItems(), 'There should have been no documents in the list');
}
public function testArraySucceedsWhenDataIsFound()

View File

@ -4,11 +4,13 @@ namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Custom, Definition, DocumentException};
use BitBadger\PDODocument\Mapper\ExistsMapper;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
/**
* SQLite integration tests for the Definition class
*/
#[TestDox('Definition (SQLite integration)')]
class DefinitionTest extends TestCase
{
/** @var string Database name for throwaway database */

View File

@ -0,0 +1,59 @@
<?php declare(strict_types=1);
namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Count, Delete, Field};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
/**
* SQLite integration tests for the Delete class
*/
#[TestDox('Delete (SQLite integration)')]
class DeleteTest extends TestCase
{
/** @var string Database name for throwaway database */
private string $dbName;
protected function setUp(): void
{
parent::setUp();
$this->dbName = ThrowawayDb::create();
}
protected function tearDown(): void
{
ThrowawayDb::destroy($this->dbName);
parent::tearDown();
}
#[TestDox('By ID succeeds when a document is deleted')]
public function testByIdSucceedsWhenADocumentIsDeleted(): void
{
$this->assertEquals(5, Count::all(ThrowawayDb::TABLE), 'There should have been 5 documents to start');
Delete::byId(ThrowawayDb::TABLE, 'four');
$this->assertEquals(4, Count::all(ThrowawayDb::TABLE), 'There should have been 4 documents remaining');
}
#[TestDox('By ID succeeds when a document is not deleted')]
public function testByIdSucceedsWhenADocumentIsNotDeleted(): void
{
$this->assertEquals(5, Count::all(ThrowawayDb::TABLE), 'There should have been 5 documents to start');
Delete::byId(ThrowawayDb::TABLE, 'negative four');
$this->assertEquals(5, Count::all(ThrowawayDb::TABLE), 'There should have been 5 documents remaining');
}
public function testByFieldsSucceedsWhenDocumentsAreDeleted(): void
{
$this->assertEquals(5, Count::all(ThrowawayDb::TABLE), 'There should have been 5 documents to start');
Delete::byFields(ThrowawayDb::TABLE, [Field::NE('value', 'purple')]);
$this->assertEquals(2, Count::all(ThrowawayDb::TABLE), 'There should have been 2 documents remaining');
}
public function testByFieldsSucceedsWhenDocumentsAreNotDeleted(): void
{
$this->assertEquals(5, Count::all(ThrowawayDb::TABLE), 'There should have been 5 documents to start');
Delete::byFields(ThrowawayDb::TABLE, [Field::EQ('value', 'crimson')]);
$this->assertEquals(5, Count::all(ThrowawayDb::TABLE), 'There should have been 5 documents remaining');
}
}

View File

@ -0,0 +1,73 @@
<?php declare(strict_types=1);
namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{DocumentList, Query};
use BitBadger\PDODocument\Mapper\DocumentMapper;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use Test\Integration\TestDocument;
/**
* SQLite integration tests for the DocumentList class
*/
#[TestDox('DocumentList (SQLite integration)')]
class DocumentListTest extends TestCase
{
/** @var string Database name for throwaway database */
private string $dbName;
protected function setUp(): void
{
parent::setUp();
$this->dbName = ThrowawayDb::create();
}
protected function tearDown(): void
{
ThrowawayDb::destroy($this->dbName);
parent::tearDown();
}
public function testCreateSucceeds(): void
{
$list = DocumentList::create(Query::selectFromTable(ThrowawayDb::TABLE), [],
new DocumentMapper(TestDocument::class));
$this->assertNotNull($list, 'There should have been a document list created');
$list = null;
}
public function testItems(): void
{
$list = DocumentList::create(Query::selectFromTable(ThrowawayDb::TABLE), [],
new DocumentMapper(TestDocument::class));
$this->assertNotNull($list, 'There should have been a document list created');
$count = 0;
foreach ($list->items() as $item) {
$this->assertContains($item->id, ['one', 'two', 'three', 'four', 'five'],
'An unexpected document ID was returned');
$count++;
}
$this->assertEquals(5, $count, 'There should have been 5 documents returned');
}
public function testHasItemsSucceedsWithEmptyResults(): void
{
$list = DocumentList::create(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'num_value' < 0", [],
new DocumentMapper(TestDocument::class));
$this->assertNotNull($list, 'There should have been a document list created');
$this->assertFalse($list->hasItems(), 'There should be no items in the list');
}
public function testHasItemsSucceedsWithNonEmptyResults(): void
{
$list = DocumentList::create(Query::selectFromTable(ThrowawayDb::TABLE), [],
new DocumentMapper(TestDocument::class));
$this->assertNotNull($list, 'There should have been a document list created');
$this->assertTrue($list->hasItems(), 'There should be items in the list');
foreach ($list->items() as $ignored) {
$this->assertTrue($list->hasItems(), 'There should be items remaining in the list');
}
$this->assertFalse($list->hasItems(), 'There should be no remaining items in the list');
}
}

View File

@ -3,12 +3,14 @@
namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Document, DocumentException, Find};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use Test\Integration\{SubDocument, TestDocument};
/**
* SQLite integration tests for the Document class
*/
#[TestDox('Document (SQLite integration)')]
class DocumentTest extends TestCase
{
/** @var string Database name for throwaway database */

View File

@ -0,0 +1,54 @@
<?php declare(strict_types=1);
namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Exists, Field};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
/**
* SQLite integration tests for the Exists class
*/
#[TestDox('Exists (SQLite integration)')]
class ExistsTest extends TestCase
{
/** @var string Database name for throwaway database */
private string $dbName;
protected function setUp(): void
{
parent::setUp();
$this->dbName = ThrowawayDb::create();
}
protected function tearDown(): void
{
ThrowawayDb::destroy($this->dbName);
parent::tearDown();
}
#[TestDox('By ID succeeds when a document exists')]
public function testByIdSucceedsWhenADocumentExists(): void
{
$this->assertTrue(Exists::byId(ThrowawayDb::TABLE, 'three'), 'There should have been an existing document');
}
#[TestDox('By ID succeeds when a document does not exist')]
public function testByIdSucceedsWhenADocumentDoesNotExist(): void
{
$this->assertFalse(Exists::byId(ThrowawayDb::TABLE, 'seven'),
'There should not have been an existing document');
}
public function testByFieldsSucceedsWhenDocumentsExist(): void
{
$this->assertTrue(Exists::byFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 10)]),
'There should have been existing documents');
}
public function testByFieldsSucceedsWhenNoMatchingDocumentsExist(): void
{
$this->assertFalse(Exists::byFields(ThrowawayDb::TABLE, [Field::LT('nothing', 'none')]),
'There should not have been any existing documents');
}
}

View File

@ -0,0 +1,100 @@
<?php declare(strict_types=1);
namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Custom, Field, Find};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use Test\Integration\TestDocument;
/**
* SQLite integration tests for the Find class
*/
#[TestDox('Find (SQLite integration)')]
class FindTest extends TestCase
{
/** @var string Database name for throwaway database */
private string $dbName;
protected function setUp(): void
{
parent::setUp();
$this->dbName = ThrowawayDb::create();
}
protected function tearDown(): void
{
ThrowawayDb::destroy($this->dbName);
parent::tearDown();
}
public function testAllSucceedsWhenThereIsData(): void
{
$docs = Find::all(ThrowawayDb::TABLE, TestDocument::class);
$this->assertNotNull($docs, 'There should have been a document list returned');
$count = 0;
foreach ($docs->items() as $ignored) $count++;
$this->assertEquals(5, $count, 'There should have been 5 documents in the list');
}
public function testAllSucceedsWhenThereIsNoData(): void
{
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
$docs = Find::all(ThrowawayDb::TABLE, TestDocument::class);
$this->assertNotNull($docs, 'There should have been a document list returned');
$this->assertFalse($docs->hasItems(), 'There should have been no documents in the list');
}
#[TestDox('By ID succeeds when a document is found')]
public function testByIdSucceedsWhenADocumentIsFound(): void
{
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned');
$this->assertEquals('two', $doc->id, 'An incorrect document was returned');
}
#[TestDox('By ID succeeds when a document is not found')]
public function testByIdSucceedsWhenADocumentIsNotFound(): void
{
$doc = Find::byId(ThrowawayDb::TABLE, 'seventy-five', TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned');
}
public function testByFieldsSucceedsWhenDocumentsAreFound(): void
{
$docs = Find::byFields(ThrowawayDb::TABLE, [Field::GT('num_value', 15)], TestDocument::class);
$this->assertNotNull($docs, 'There should have been a document list returned');
$count = 0;
foreach ($docs->items() as $ignored) $count++;
$this->assertEquals(2, $count, 'There should have been 2 documents in the list');
}
public function testByFieldsSucceedsWhenNoDocumentsAreFound(): void
{
$docs = Find::byFields(ThrowawayDb::TABLE, [Field::GT('num_value', 100)], TestDocument::class);
$this->assertNotNull($docs, 'There should have been a document list returned');
$count = 0;
foreach ($docs->items() as $ignored) $count++;
$this->assertFalse($docs->hasItems(), 'There should have been no documents in the list');
}
public function testFirstByFieldsSucceedsWhenADocumentIsFound(): void
{
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'another')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned');
$this->assertEquals('two', $doc->id, 'The incorrect document was returned');
}
public function testFirstByFieldsSucceedsWhenMultipleDocumentsAreFound(): void
{
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('sub.foo', 'green')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned');
$this->assertContains($doc->id, ['two', 'four'], 'An incorrect document was returned');
}
public function testFirstByFieldsSucceedsWhenADocumentIsNotFound(): void
{
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'absent')], TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned');
}
}

View File

@ -10,6 +10,7 @@ use Test\Integration\TestDocument;
/**
* SQLite integration tests for the Patch class
*/
#[TestDox('Patch (SQLite integration)')]
class PatchTest extends TestCase
{
/** @var string Database name for throwaway database */

View File

@ -0,0 +1,74 @@
<?php declare(strict_types=1);
namespace Test\Integration\SQLite;
use BitBadger\PDODocument\{Field, Find, RemoveFields};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use Test\Integration\TestDocument;
/**
* SQLite integration tests for the RemoveFields class
*/
#[TestDox('Remove Fields (SQLite integration)')]
class RemoveFieldsTest extends TestCase
{
/** @var string Database name for throwaway database */
private string $dbName;
protected function setUp(): void
{
parent::setUp();
$this->dbName = ThrowawayDb::create();
}
protected function tearDown(): void
{
ThrowawayDb::destroy($this->dbName);
parent::tearDown();
}
#[TestDox('By ID succeeds when fields are removed')]
public function testByIdSucceedsWhenFieldsAreRemoved(): void
{
RemoveFields::byId(ThrowawayDb::TABLE, 'two', ['sub', 'value']);
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned');
$this->assertEquals('', $doc->value, 'Value should have been blank (its default value)');
$this->assertNull($doc->sub, 'Sub-document should have been null');
}
#[TestDox('By ID succeeds when a field is not removed')]
public function testByIdSucceedsWhenAFieldIsNotRemoved(): void
{
RemoveFields::byId(ThrowawayDb::TABLE, 'one', ['a_field_that_does_not_exist']);
$this->assertTrue(true, 'The above not throwing an exception is the test');
}
#[TestDox('By ID succeeds when no document is matched')]
public function testByIdSucceedsWhenNoDocumentIsMatched(): void
{
RemoveFields::byId(ThrowawayDb::TABLE, 'fifty', ['sub']);
$this->assertTrue(true, 'The above not throwing an exception is the test');
}
public function testByFieldsSucceedsWhenAFieldIsRemoved(): void
{
RemoveFields::byFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], ['sub']);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned');
$this->assertNull($doc->sub, 'Sub-document should have been null');
}
public function testByFieldsSucceedsWhenAFieldIsNotRemoved(): void
{
RemoveFields::byFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], ['nada']);
$this->assertTrue(true, 'The above not throwing an exception is the test');
}
public function testByFieldsSucceedsWhenNoDocumentIsMatched(): void
{
RemoveFields::byFields(ThrowawayDb::TABLE, [Field::NE('missing', 'nope')], ['value']);
$this->assertTrue(true, 'The above not throwing an exception is the test');
}
}

View File

@ -2,7 +2,7 @@
namespace Test\Unit;
use BitBadger\PDODocument\{Field, Op};
use BitBadger\PDODocument\{Configuration, Field, Mode, Op};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
@ -36,46 +36,236 @@ class FieldTest extends TestCase
'Field parameter not returned correctly');
}
#[TestDox('To where succeeds for EX without qualifier')]
public function testToWhereSucceedsForEXWithoutQualifier(): void
#[TestDox('To where succeeds for EX without qualifier for PostgreSQL')]
public function testToWhereSucceedsForEXWithoutQualifierForPostgreSQL(): void
{
$this->assertEquals("data->>'that_field' IS NOT NULL", Field::EX('that_field')->toWhere(),
'WHERE fragment not generated correctly');
Configuration::$mode = Mode::PgSQL;
try {
$this->assertEquals("data->>'that_field' IS NOT NULL", Field::EX('that_field')->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for NEX without qualifier')]
public function testToWhereSucceedsForNEXWithoutQualifier(): void
#[TestDox('To where succeeds for EX without qualifier for SQLite')]
public function testToWhereSucceedsForEXWithoutQualifierForSQLite(): void
{
$this->assertEquals("data->>'a_field' IS NULL", Field::NEX('a_field')->toWhere(),
'WHERE fragment not generated correctly');
Configuration::$mode = Mode::SQLite;
try {
$this->assertEquals("data->>'that_field' IS NOT NULL", Field::EX('that_field')->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for BT without qualifier')]
public function testToWhereSucceedsForBTWithoutQualifier(): void
#[TestDox('To where succeeds for NEX without qualifier for PostgreSQL')]
public function testToWhereSucceedsForNEXWithoutQualifierForPostgreSQL(): void
{
$this->assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field::BT('age', 13, 17, '@age')->toWhere(),
'WHERE fragment not generated correctly');
Configuration::$mode = Mode::PgSQL;
try {
$this->assertEquals("data->>'a_field' IS NULL", Field::NEX('a_field')->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
public function testToWhereSucceedsForOthersWithoutQualifier(): void
#[TestDox('To where succeeds for NEX without qualifier for SQLite')]
public function testToWhereSucceedsForNEXWithoutQualifierForSQLite(): void
{
$this->assertEquals("data->>'some_field' = @value", Field::EQ('some_field', '', '@value')->toWhere(),
'WHERE fragment not generated correctly');
Configuration::$mode = Mode::SQLite;
try {
$this->assertEquals("data->>'a_field' IS NULL", Field::NEX('a_field')->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
public function testToWhereSucceedsWithQualifierNoParameter(): void
#[TestDox('To where succeeds for BT without qualifier for SQLite')]
public function testToWhereSucceedsForBTWithoutQualifierForSQLite(): void
{
$field = Field::EX('no_field');
$field->qualifier = 'test';
$this->assertEquals("test.data->>'no_field' IS NOT NULL", $field->toWhere(),
'WHERE fragment not generated correctly');
Configuration::$mode = Mode::SQLite;
try {
$this->assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field::BT('age', 13, 17, '@age')->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
public function testToWhereSucceedsWithQualifierAndParameter(): void
#[TestDox('To where succeeds for BT without qualifier for PostgreSQL with numeric range')]
public function testToWhereSucceedsForBTWithoutQualifierForPostgreSQLWithNumericRange(): void
{
$field = Field::LE('le_field', 18, '@it');
$field->qualifier = 'q';
$this->assertEquals("q.data->>'le_field' <= @it", $field->toWhere(), 'WHERE fragment not generated correctly');
Configuration::$mode = Mode::PgSQL;
try {
$this->assertEquals("(data->>'age')::numeric BETWEEN @agemin AND @agemax",
Field::BT('age', 13, 17, '@age')->toWhere(), 'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for BT without qualifier for PostgreSQL with non-numeric range')]
public function testToWhereSucceedsForBTWithoutQualifierForPostgreSQLWithNonNumericRange(): void
{
Configuration::$mode = Mode::PgSQL;
try {
$this->assertEquals("data->>'city' BETWEEN :citymin AND :citymax",
Field::BT('city', 'Atlanta', 'Chicago', ':city')->toWhere(), 'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for BT with qualifier for SQLite')]
public function testToWhereSucceedsForBTWithQualifierForSQLite(): void
{
Configuration::$mode = Mode::SQLite;
try {
$field = Field::BT('age', 13, 17, '@age');
$field->qualifier = 'me';
$this->assertEquals("me.data->>'age' BETWEEN @agemin AND @agemax", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for BT with qualifier for PostgreSQL with numeric range')]
public function testToWhereSucceedsForBTWithQualifierForPostgreSQLWithNumericRange(): void
{
Configuration::$mode = Mode::PgSQL;
try {
$field = Field::BT('age', 13, 17, '@age');
$field->qualifier = 'me';
$this->assertEquals("(me.data->>'age')::numeric BETWEEN @agemin AND @agemax", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for BT with qualifier for PostgreSQL with non-numeric range')]
public function testToWhereSucceedsForBTWithQualifierForPostgreSQLWithNonNumericRange(): void
{
Configuration::$mode = Mode::PgSQL;
try {
$field = Field::BT('city', 'Atlanta', 'Chicago', ':city');
$field->qualifier = 'me';
$this->assertEquals("me.data->>'city' BETWEEN :citymin AND :citymax", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for others without qualifier for PostgreSQL')]
public function testToWhereSucceedsForOthersWithoutQualifierForPostgreSQL(): void
{
Configuration::$mode = Mode::PgSQL;
try {
$this->assertEquals("data->>'some_field' = @value", Field::EQ('some_field', '', '@value')->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds for others without qualifier for SQLite')]
public function testToWhereSucceedsForOthersWithoutQualifierForSQLite(): void
{
Configuration::$mode = Mode::SQLite;
try {
$this->assertEquals("data->>'some_field' = @value", Field::EQ('some_field', '', '@value')->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds with qualifier no parameter for PostgreSQL')]
public function testToWhereSucceedsWithQualifierNoParameterForPostgreSQL(): void
{
Configuration::$mode = Mode::PgSQL;
try {
$field = Field::EX('no_field');
$field->qualifier = 'test';
$this->assertEquals("test.data->>'no_field' IS NOT NULL", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds with qualifier no parameter for SQLite')]
public function testToWhereSucceedsWithQualifierNoParameterForSQLite(): void
{
Configuration::$mode = Mode::SQLite;
try {
$field = Field::EX('no_field');
$field->qualifier = 'test';
$this->assertEquals("test.data->>'no_field' IS NOT NULL", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds with qualifier and parameter for PostgreSQL')]
public function testToWhereSucceedsWithQualifierAndParameterForPostgreSQL(): void
{
Configuration::$mode = Mode::PgSQL;
try {
$field = Field::LE('le_field', 18, '@it');
$field->qualifier = 'q';
$this->assertEquals("q.data->>'le_field' <= @it", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds with qualifier and parameter for SQLite')]
public function testToWhereSucceedsWithQualifierAndParameterForSQLite(): void
{
Configuration::$mode = Mode::SQLite;
try {
$field = Field::LE('le_field', 18, '@it');
$field->qualifier = 'q';
$this->assertEquals("q.data->>'le_field' <= @it", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds with sub-document for PostgreSQL')]
public function testToWhereSucceedsWithSubDocumentForPostgreSQL(): void
{
Configuration::$mode = Mode::PgSQL;
try {
$field = Field::EQ('sub.foo.bar', 22, '@it');
$this->assertEquals("data->>'sub.foo.bar' = @it", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('To where succeeds with sub-document for SQLite')]
public function testToWhereSucceedsWithSubDocumentForSQLite(): void
{
Configuration::$mode = Mode::SQLite;
try {
$field = Field::EQ('sub.foo.bar', 22, '@it');
$this->assertEquals("data->>'sub'->>'foo'->>'bar' = @it", $field->toWhere(),
'WHERE fragment not generated correctly');
} finally {
Configuration::$mode = null;
}
}
#[TestDox('EQ succeeds without parameter')]

View File

@ -63,7 +63,7 @@ class ParametersTest extends TestCase
{
try {
Configuration::$mode = Mode::SQLite;
$this->assertEquals([':it0' => 'test', ':it1' => 'unit', ':it2' => 'wow'],
$this->assertEquals([':it0' => '$.test', ':it1' => '$.unit', ':it2' => '$.wow'],
Parameters::fieldNames(':it', ['test', 'unit', 'wow']), 'Field name parameters not correct');
} finally {
Configuration::$mode = null;

View File

@ -2,7 +2,7 @@
namespace Test\Unit\Query;
use BitBadger\PDODocument\Field;
use BitBadger\PDODocument\{Configuration, Field, Mode};
use BitBadger\PDODocument\Query\Count;
use PHPUnit\Framework\TestCase;
@ -20,7 +20,12 @@ class CountTest extends TestCase
public function testByFieldsSucceeds()
{
$this->assertEquals("SELECT COUNT(*) FROM somewhere WHERE data->>'errors' > :errors",
Count::byFields('somewhere', [Field::GT('errors', 10, ':errors')]));
Configuration::$mode = Mode::SQLite;
try {
$this->assertEquals("SELECT COUNT(*) FROM somewhere WHERE data->>'errors' > :errors",
Count::byFields('somewhere', [Field::GT('errors', 10, ':errors')]));
} finally {
Configuration::$mode = null;
}
}
}

View File

@ -2,7 +2,7 @@
namespace Test\Unit\Query;
use BitBadger\PDODocument\Field;
use BitBadger\PDODocument\{Configuration, Field, Mode};
use BitBadger\PDODocument\Query\Delete;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
@ -12,6 +12,16 @@ use PHPUnit\Framework\TestCase;
*/
class DeleteTest extends TestCase
{
protected function setUp(): void
{
Configuration::$mode = Mode::SQLite;
}
protected function tearDown(): void
{
Configuration::$mode = null;
}
#[TestDox('By ID succeeds')]
public function testByIdSucceeds(): void
{

View File

@ -2,7 +2,7 @@
namespace Test\Unit\Query;
use BitBadger\PDODocument\Field;
use BitBadger\PDODocument\{Configuration, Field, Mode};
use BitBadger\PDODocument\Query\Exists;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
@ -12,6 +12,16 @@ use PHPUnit\Framework\TestCase;
*/
class ExistsTest extends TestCase
{
protected function setUp(): void
{
Configuration::$mode = Mode::SQLite;
}
protected function tearDown(): void
{
Configuration::$mode = null;
}
public function testQuerySucceeds(): void
{
$this->assertEquals('SELECT EXISTS (SELECT 1 FROM abc WHERE def)', Exists::query('abc', 'def'),

View File

@ -2,7 +2,7 @@
namespace Test\Unit\Query;
use BitBadger\PDODocument\Field;
use BitBadger\PDODocument\{Configuration, Field, Mode};
use BitBadger\PDODocument\Query\Find;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
@ -12,6 +12,16 @@ use PHPUnit\Framework\TestCase;
*/
class FindTest extends TestCase
{
protected function setUp(): void
{
Configuration::$mode = Mode::SQLite;
}
protected function tearDown(): void
{
Configuration::$mode = null;
}
#[TestDox('By ID succeeds')]
public function testByIdSucceeds(): void
{

View File

@ -2,7 +2,7 @@
namespace Test\Unit;
use BitBadger\PDODocument\{Field, Query};
use BitBadger\PDODocument\{Configuration, Field, Mode, Query};
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
@ -11,6 +11,16 @@ use PHPUnit\Framework\TestCase;
*/
class QueryTest extends TestCase
{
protected function setUp(): void
{
Configuration::$mode = Mode::SQLite;
}
protected function tearDown(): void
{
Configuration::$mode = null;
}
public function testSelectFromTableSucceeds(): void
{
$this->assertEquals('SELECT data FROM testing', Query::selectFromTable('testing'),