From 3d45bbcabc43f9801e4aaccd705b8582ba0d9d99 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 4 Jun 2024 20:59:24 -0400 Subject: [PATCH] Change param prefix from @ to : --- src/Parameters.php | 6 +- src/Patch.php | 4 +- src/Query.php | 6 +- src/Query/Patch.php | 4 +- src/RemoveFields.php | 4 +- tests/unit/ParametersTest.php | 28 +++--- tests/unit/Query/CountTest.php | 4 +- tests/unit/Query/DeleteTest.php | 6 +- tests/unit/Query/ExistsTest.php | 6 +- tests/unit/Query/FindTest.php | 6 +- tests/unit/Query/PatchTest.php | 12 +-- tests/unit/Query/RemoveFieldsTest.php | 121 ++++++++++++++++++++++++++ tests/unit/QueryTest.php | 22 ++--- 13 files changed, 175 insertions(+), 54 deletions(-) create mode 100644 tests/unit/Query/RemoveFieldsTest.php diff --git a/src/Parameters.php b/src/Parameters.php index 592b135..0d32fab 100644 --- a/src/Parameters.php +++ b/src/Parameters.php @@ -8,14 +8,14 @@ namespace BitBadger\PDODocument; class Parameters { /** - * Create an ID parameter (name "@id", key will be treated as a string) + * Create an ID parameter (name ":id", key will be treated as a string) * * @param mixed $key The key representing the ID of the document * @return array|string[] An associative array with an "@id" parameter/value pair */ public static function id(mixed $key): array { - return ['@id' => is_string($key) ? $key : "$key"]; + return [':id' => is_string($key) ? $key : "$key"]; } /** @@ -39,7 +39,7 @@ class Parameters public static function nameFields(array $fields): array { for ($idx = 0; $idx < sizeof($fields); $idx++) { - if ($fields[$idx]->paramName == '') $fields[$idx]->paramName = "@field$idx"; + if ($fields[$idx]->paramName == '') $fields[$idx]->paramName = ":field$idx"; } return $fields; } diff --git a/src/Patch.php b/src/Patch.php index 40afa7e..9bbbaa9 100644 --- a/src/Patch.php +++ b/src/Patch.php @@ -21,7 +21,7 @@ class Patch public static function byId(string $tableName, mixed $docId, array|object $patch, ?PDO $pdo = null): void { Custom::nonQuery(Query\Patch::byId($tableName), - array_merge(Parameters::id($docId), Parameters::json('@data', $patch)), $pdo); + array_merge(Parameters::id($docId), Parameters::json(':data', $patch)), $pdo); } /** @@ -39,6 +39,6 @@ class Patch { $namedFields = Parameters::nameFields($fields); Custom::nonQuery(Query\Patch::byFields($tableName, $namedFields, $conjunction), - Parameters::addFields($namedFields, Parameters::json('@data', $patch)), $pdo); + Parameters::addFields($namedFields, Parameters::json(':data', $patch)), $pdo); } } diff --git a/src/Query.php b/src/Query.php index a1facbc..495315a 100644 --- a/src/Query.php +++ b/src/Query.php @@ -36,7 +36,7 @@ class Query * @param string $paramName The parameter name where the value of the ID will be provided (optional; default @id) * @return string The WHERE clause fragment to match by ID */ - public static function whereById(string $paramName = '@id'): string + public static function whereById(string $paramName = ':id'): string { return self::whereByFields([Field::EQ(Configuration::$idField, 0, $paramName)]); } @@ -49,7 +49,7 @@ class Query */ public static function insert(string $tableName): string { - return "INSERT INTO $tableName VALUES (@data)"; + return "INSERT INTO $tableName VALUES (:data)"; } /** @@ -72,6 +72,6 @@ class Query */ public static function update(string $tableName): string { - return "UPDATE $tableName SET data = @data WHERE " . self::whereById(); + return "UPDATE $tableName SET data = :data WHERE " . self::whereById(); } } diff --git a/src/Query/Patch.php b/src/Query/Patch.php index 5811647..7f63253 100644 --- a/src/Query/Patch.php +++ b/src/Query/Patch.php @@ -24,8 +24,8 @@ class Patch public static function update(string $tableName, string $whereClause): string { $setValue = match (Configuration::$mode) { - Mode::PgSQL => 'data || @data', - Mode::SQLite => 'json_patch(data, json(@data))', + Mode::PgSQL => 'data || :data', + Mode::SQLite => 'json_patch(data, json(:data))', default => throw new DocumentException('Database mode not set; cannot make patch statement') }; return "UPDATE $tableName SET data = $setValue WHERE $whereClause"; diff --git a/src/RemoveFields.php b/src/RemoveFields.php index 236ea99..4432c0f 100644 --- a/src/RemoveFields.php +++ b/src/RemoveFields.php @@ -20,7 +20,7 @@ class RemoveFields */ public static function byId(string $tableName, mixed $docId, array $fieldNames, ?PDO $pdo = null): void { - $nameParams = Parameters::fieldNames('@name', $fieldNames); + $nameParams = Parameters::fieldNames(':name', $fieldNames); Custom::nonQuery(Query\RemoveFields::byId($tableName, $nameParams), array_merge(Parameters::id($docId), $nameParams), $pdo); } @@ -38,7 +38,7 @@ class RemoveFields public static function byFields(string $tableName, array $fields, array $fieldNames, ?PDO $pdo = null, string $conjunction = 'AND'): void { - $nameParams = Parameters::fieldNames('@name', $fieldNames); + $nameParams = Parameters::fieldNames(':name', $fieldNames); $namedFields = Parameters::nameFields($fields); Custom::nonQuery(Query\RemoveFields::byFields($tableName, $namedFields, $nameParams, $conjunction), Parameters::addFields($namedFields, $nameParams), $pdo); diff --git a/tests/unit/ParametersTest.php b/tests/unit/ParametersTest.php index 1a0627c..1bf0ec0 100644 --- a/tests/unit/ParametersTest.php +++ b/tests/unit/ParametersTest.php @@ -18,35 +18,35 @@ class ParametersTest extends TestCase #[TestDox('ID succeeds with string')] public function testIdSucceedsWithString(): void { - $this->assertEquals(['@id' => 'key'], Parameters::id('key'), 'ID parameter not constructed correctly'); + $this->assertEquals([':id' => 'key'], Parameters::id('key'), 'ID parameter not constructed correctly'); } #[TestDox('ID succeeds with non string')] public function testIdSucceedsWithNonString(): void { - $this->assertEquals(['@id' => '7'], Parameters::id(7), 'ID parameter not constructed correctly'); + $this->assertEquals([':id' => '7'], Parameters::id(7), 'ID parameter not constructed correctly'); } public function testJsonSucceeds(): void { - $this->assertEquals(['@it' => '{"id":18,"url":"https://www.unittest.com"}'], - Parameters::json('@it', ['id' => 18, 'url' => 'https://www.unittest.com']), + $this->assertEquals([':it' => '{"id":18,"url":"https://www.unittest.com"}'], + Parameters::json(':it', ['id' => 18, 'url' => 'https://www.unittest.com']), 'JSON parameter not constructed correctly'); } public function testNameFieldsSucceeds(): void { - $named = Parameters::nameFields([Field::EQ('it', 17), Field::EQ('also', 22, '@also'), Field::EQ('other', 24)]); + $named = Parameters::nameFields([Field::EQ('it', 17), Field::EQ('also', 22, ':also'), Field::EQ('other', 24)]); $this->assertCount(3, $named, 'There should be 3 parameters in the array'); - $this->assertEquals('@field0', $named[0]->paramName, 'Parameter 1 not named correctly'); - $this->assertEquals('@also', $named[1]->paramName, 'Parameter 2 not named correctly'); - $this->assertEquals('@field2', $named[2]->paramName, 'Parameter 3 not named correctly'); + $this->assertEquals(':field0', $named[0]->paramName, 'Parameter 1 not named correctly'); + $this->assertEquals(':also', $named[1]->paramName, 'Parameter 2 not named correctly'); + $this->assertEquals(':field2', $named[2]->paramName, 'Parameter 3 not named correctly'); } public function testAddFieldsSucceeds(): void { - $this->assertEquals(['@a' => 1, '@b' => 'two', '@z' => 18], - Parameters::addFields([Field::EQ('b', 'two', '@b'), Field::EQ('z', 18, '@z')], ['@a' => 1]), + $this->assertEquals([':a' => 1, ':b' => 'two', ':z' => 18], + Parameters::addFields([Field::EQ('b', 'two', ':b'), Field::EQ('z', 18, ':z')], [':a' => 1]), 'Field parameters not added correctly'); } @@ -55,8 +55,8 @@ class ParametersTest extends TestCase { try { Configuration::$mode = Mode::PgSQL; - $this->assertEquals(['@names' => "ARRAY['one','two','seven']"], - Parameters::fieldNames('@names', ['one', 'two', 'seven']), 'Field name parameters not correct'); + $this->assertEquals([':names' => "ARRAY['one','two','seven']"], + Parameters::fieldNames(':names', ['one', 'two', 'seven']), 'Field name parameters not correct'); } finally { Configuration::$mode = null; } @@ -67,8 +67,8 @@ class ParametersTest extends TestCase { try { Configuration::$mode = Mode::SQLite; - $this->assertEquals(['@it0' => 'test', '@it1' => 'unit', '@it2' => 'wow'], - Parameters::fieldNames('@it', ['test', 'unit', 'wow']), 'Field name parameters not correct'); + $this->assertEquals([':it0' => 'test', ':it1' => 'unit', ':it2' => 'wow'], + Parameters::fieldNames(':it', ['test', 'unit', 'wow']), 'Field name parameters not correct'); } finally { Configuration::$mode = null; } diff --git a/tests/unit/Query/CountTest.php b/tests/unit/Query/CountTest.php index d0b6223..0a70527 100644 --- a/tests/unit/Query/CountTest.php +++ b/tests/unit/Query/CountTest.php @@ -20,7 +20,7 @@ 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')])); + $this->assertEquals("SELECT COUNT(*) FROM somewhere WHERE data->>'errors' > :errors", + Count::byFields('somewhere', [Field::GT('errors', 10, ':errors')])); } } diff --git a/tests/unit/Query/DeleteTest.php b/tests/unit/Query/DeleteTest.php index 8636713..edf21bc 100644 --- a/tests/unit/Query/DeleteTest.php +++ b/tests/unit/Query/DeleteTest.php @@ -15,14 +15,14 @@ class DeleteTest extends TestCase #[TestDox('By ID succeeds')] public function testByIdSucceeds(): void { - $this->assertEquals("DELETE FROM over_there WHERE data->>'id' = @id", Delete::byId('over_there'), + $this->assertEquals("DELETE FROM over_there WHERE data->>'id' = :id", Delete::byId('over_there'), 'DELETE statement not constructed correctly'); } public function testByFieldsSucceeds(): void { - $this->assertEquals("DELETE FROM my_table WHERE data->>'value' < @max AND data->>'value' >= @min", - Delete::byFields('my_table', [Field::LT('value', 99, '@max'), Field::GE('value', 18, '@min')]), + $this->assertEquals("DELETE FROM my_table WHERE data->>'value' < :max AND data->>'value' >= :min", + Delete::byFields('my_table', [Field::LT('value', 99, ':max'), Field::GE('value', 18, ':min')]), 'DELETE statement not constructed correctly'); } } diff --git a/tests/unit/Query/ExistsTest.php b/tests/unit/Query/ExistsTest.php index 60d1093..32fbae2 100644 --- a/tests/unit/Query/ExistsTest.php +++ b/tests/unit/Query/ExistsTest.php @@ -21,14 +21,14 @@ class ExistsTest extends TestCase #[TestDox('By ID succeeds')] public function testByIdSucceeds(): void { - $this->assertEquals("SELECT EXISTS (SELECT 1 FROM dox WHERE data->>'id' = @id)", Exists::byId('dox'), + $this->assertEquals("SELECT EXISTS (SELECT 1 FROM dox WHERE data->>'id' = :id)", Exists::byId('dox'), 'Existence query not generated correctly'); } public function testByFieldsSucceeds(): void { - $this->assertEquals("SELECT EXISTS (SELECT 1 FROM box WHERE data->>'status' <> @status)", - Exists::byFields('box', [Field::NE('status', 'occupied', '@status')]), + $this->assertEquals("SELECT EXISTS (SELECT 1 FROM box WHERE data->>'status' <> :status)", + Exists::byFields('box', [Field::NE('status', 'occupied', ':status')]), 'Existence query not generated correctly'); } } diff --git a/tests/unit/Query/FindTest.php b/tests/unit/Query/FindTest.php index 5a92241..65bc571 100644 --- a/tests/unit/Query/FindTest.php +++ b/tests/unit/Query/FindTest.php @@ -15,14 +15,14 @@ class FindTest extends TestCase #[TestDox('By ID succeeds')] public function testByIdSucceeds(): void { - $this->assertEquals("SELECT data FROM here WHERE data->>'id' = @id", Find::byId('here'), + $this->assertEquals("SELECT data FROM here WHERE data->>'id' = :id", Find::byId('here'), 'SELECT query not generated correctly'); } public function testByFieldsSucceeds(): void { - $this->assertEquals("SELECT data FROM there WHERE data->>'active' = @act OR data->>'locked' = @lock", - Find::byFields('there', [Field::EQ('active', true, '@act'), Field::EQ('locked', true, '@lock')], 'OR'), + $this->assertEquals("SELECT data FROM there WHERE data->>'active' = :act OR data->>'locked' = :lock", + Find::byFields('there', [Field::EQ('active', true, ':act'), Field::EQ('locked', true, ':lock')], 'OR'), 'SELECT query not generated correctly'); } } diff --git a/tests/unit/Query/PatchTest.php b/tests/unit/Query/PatchTest.php index 81c5fd8..1e338a1 100644 --- a/tests/unit/Query/PatchTest.php +++ b/tests/unit/Query/PatchTest.php @@ -20,7 +20,7 @@ class PatchTest extends TestCase { try { Configuration::$mode = Mode::PgSQL; - $this->assertEquals("UPDATE doc_table SET data = data || @data WHERE data->>'id' = @id", + $this->assertEquals("UPDATE doc_table SET data = data || :data WHERE data->>'id' = :id", Patch::byId('doc_table'), 'Patch UPDATE statement is not correct'); } finally { Configuration::$mode = null; @@ -32,7 +32,7 @@ class PatchTest extends TestCase { try { Configuration::$mode = Mode::SQLite; - $this->assertEquals("UPDATE my_table SET data = json_patch(data, json(@data)) WHERE data->>'id' = @id", + $this->assertEquals("UPDATE my_table SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id", Patch::byId('my_table'), 'Patch UPDATE statement is not correct'); } finally { Configuration::$mode = null; @@ -52,8 +52,8 @@ class PatchTest extends TestCase { try { Configuration::$mode = Mode::PgSQL; - $this->assertEquals("UPDATE that SET data = data || @data WHERE data->>'something' < @some", - Patch::byFields('that', [Field::LT('something', 17, '@some')]), + $this->assertEquals("UPDATE that SET data = data || :data WHERE data->>'something' < :some", + Patch::byFields('that', [Field::LT('something', 17, ':some')]), 'Patch UPDATE statement is not correct'); } finally { Configuration::$mode = null; @@ -66,8 +66,8 @@ class PatchTest extends TestCase try { Configuration::$mode = Mode::SQLite; $this->assertEquals( - "UPDATE a_table SET data = json_patch(data, json(@data)) WHERE data->>'something' > @it", - Patch::byFields('a_table', [Field::GT('something', 17, '@it')]), + "UPDATE a_table SET data = json_patch(data, json(:data)) WHERE data->>'something' > :it", + Patch::byFields('a_table', [Field::GT('something', 17, ':it')]), 'Patch UPDATE statement is not correct'); } finally { Configuration::$mode = null; diff --git a/tests/unit/Query/RemoveFieldsTest.php b/tests/unit/Query/RemoveFieldsTest.php new file mode 100644 index 0000000..bd79858 --- /dev/null +++ b/tests/unit/Query/RemoveFieldsTest.php @@ -0,0 +1,121 @@ +assertEquals('UPDATE taco SET data = data - :names WHERE it = true', + RemoveFields::update('taco', [':names' => "ARRAY['one','two']"], 'it = true'), + 'UPDATE statement not correct'); + } finally { + Configuration::$mode = null; + } + } + + #[TestDox('Update succeeds for SQLite')] + public function testUpdateSucceedsForSQLite(): void + { + try { + Configuration::$mode = Mode::SQLite; + $this->assertEquals('UPDATE burrito SET data = json_remove(data, :name0, :name1, :name2) WHERE a = b', + RemoveFields::update('burrito', Parameters::fieldNames(':name', ['one', 'two', 'ten']), 'a = b'), + 'UPDATE statement not correct'); + } finally { + Configuration::$mode = null; + } + } + + public function testUpdateFailsWhenModeNotSet(): void + { + $this->expectException(DocumentException::class); + Configuration::$mode = null; + RemoveFields::update('wow', [], ''); + } + + #[TestDox('By ID succeeds for PostgreSQL')] + public function testByIdSucceedsForPostgreSQL() + { + try { + Configuration::$mode = Mode::PgSQL; + $this->assertEquals("UPDATE churro SET data = data - :bite WHERE data->>'id' = :id", + RemoveFields::byId('churro', Parameters::fieldNames(':bite', ['byte'])), + 'UPDATE statement not correct'); + } finally { + Configuration::$mode = null; + } + } + + #[TestDox('By ID succeeds for SQLite')] + public function testByIdSucceedsForSQLite() + { + try { + Configuration::$mode = Mode::SQLite; + $this->assertEquals("UPDATE quesadilla SET data = json_remove(data, :bite0) WHERE data->>'id' = :id", + RemoveFields::byId('quesadilla', Parameters::fieldNames(':bite', ['byte'])), + 'UPDATE statement not correct'); + } finally { + Configuration::$mode = null; + } + } + + #[TestDox('By ID fails when mode not set')] + public function testByIdFailsWhenModeNotSet(): void + { + $this->expectException(DocumentException::class); + Configuration::$mode = null; + RemoveFields::byId('oof', []); + } + + #[TestDox('By fields succeeds for PostgreSQL')] + public function testByFieldsSucceedsForPostgreSQL() + { + try { + Configuration::$mode = Mode::PgSQL; + $this->assertEquals("UPDATE enchilada SET data = data - :sauce WHERE data->>'cheese' = :queso", + RemoveFields::byFields('enchilada', [Field::EQ('cheese', 'jack', ':queso')], + Parameters::fieldNames(':sauce', ['white'])), + 'UPDATE statement not correct'); + } finally { + Configuration::$mode = null; + } + } + + #[TestDox('By fields succeeds for SQLite')] + public function testByFieldsSucceedsForSQLite() + { + try { + Configuration::$mode = Mode::SQLite; + $this->assertEquals( + "UPDATE chimichanga SET data = json_remove(data, :filling0) WHERE data->>'side' = :rice", + RemoveFields::byFields('chimichanga', [Field::EQ('side', 'beans', ':rice')], + Parameters::fieldNames(':filling', ['beef'])), + 'UPDATE statement not correct'); + } finally { + Configuration::$mode = null; + } + } + + public function testByFieldsFailsWhenModeNotSet(): void + { + $this->expectException(DocumentException::class); + Configuration::$mode = null; + RemoveFields::byFields('boing', [], []); + } +} diff --git a/tests/unit/QueryTest.php b/tests/unit/QueryTest.php index 3ed215e..894a700 100644 --- a/tests/unit/QueryTest.php +++ b/tests/unit/QueryTest.php @@ -20,52 +20,52 @@ class QueryTest extends TestCase public function testWhereByFieldsSucceedsForSingleField(): void { - $this->assertEquals("data->>'test_field' <= @it", - Query::whereByFields([Field::LE('test_field', '', '@it')]), 'WHERE fragment not constructed correctly'); + $this->assertEquals("data->>'test_field' <= :it", + Query::whereByFields([Field::LE('test_field', '', ':it')]), 'WHERE fragment not constructed correctly'); } public function testWhereByFieldsSucceedsForMultipleFields(): void { - $this->assertEquals("data->>'test_field' <= @it AND data->>'other_field' = @other", - Query::whereByFields([Field::LE('test_field', '', '@it'), Field::EQ('other_field', '', '@other')]), + $this->assertEquals("data->>'test_field' <= :it AND data->>'other_field' = :other", + Query::whereByFields([Field::LE('test_field', '', ':it'), Field::EQ('other_field', '', ':other')]), 'WHERE fragment not constructed correctly'); } public function testWhereByFieldsSucceedsForMultipleFieldsWithOr(): void { - $this->assertEquals("data->>'test_field' <= @it OR data->>'other_field' = @other", - Query::whereByFields([Field::LE('test_field', '', '@it'), Field::EQ('other_field', '', '@other')], 'OR'), + $this->assertEquals("data->>'test_field' <= :it OR data->>'other_field' = :other", + Query::whereByFields([Field::LE('test_field', '', ':it'), Field::EQ('other_field', '', ':other')], 'OR'), 'WHERE fragment not constructed correctly'); } #[TestDox('Where by ID succeeds with default parameter')] public function testWhereByIdSucceedsWithDefaultParameter(): void { - $this->assertEquals("data->>'id' = @id", Query::whereById(), 'WHERE fragment not constructed correctly'); + $this->assertEquals("data->>'id' = :id", Query::whereById(), 'WHERE fragment not constructed correctly'); } #[TestDox('Where by ID succeeds with specific parameter')] public function testWhereByIdSucceedsWithSpecificParameter(): void { - $this->assertEquals("data->>'id' = @di", Query::whereById('@di'), 'WHERE fragment not constructed correctly'); + $this->assertEquals("data->>'id' = :di", Query::whereById(':di'), 'WHERE fragment not constructed correctly'); } public function testInsertSucceeds(): void { - $this->assertEquals('INSERT INTO my_table VALUES (@data)', Query::insert('my_table'), + $this->assertEquals('INSERT INTO my_table VALUES (:data)', Query::insert('my_table'), 'INSERT statement not constructed correctly'); } public function testSaveSucceeds(): void { $this->assertEquals( - "INSERT INTO test_tbl VALUES (@data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data", + "INSERT INTO test_tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data", Query::save('test_tbl'), 'INSERT ON CONFLICT statement not constructed correctly'); } public function testUpdateSucceeds() { - $this->assertEquals("UPDATE testing SET data = @data WHERE data->>'id' = @id", Query::update('testing'), + $this->assertEquals("UPDATE testing SET data = :data WHERE data->>'id' = :id", Query::update('testing'), 'UPDATE statement not constructed correctly'); } }