Skip to content

Commit

Permalink
JSON validation should accept string 'null' to match MySQL behavior (#97
Browse files Browse the repository at this point in the history
)

* make json validation accept the string 'null' and convert it to proper null in the database, which matches behavior in MySQL

* make the linter happy

---------

Co-authored-by: Kevin Tabb <[email protected]>
  • Loading branch information
K128kevin and Kevin Tabb authored Feb 20, 2023
1 parent 708178e commit b706db5
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 4 deletions.
16 changes: 12 additions & 4 deletions src/DataIntegrity.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,18 @@ public static function ensureFieldsPresent(dict<string, mixed> $row, table_schem
// validate json string
$json_obj = \json_decode((string)$row[$field_name]);
if ($json_obj is null) {
// invalid json
throw new SQLFakeRuntimeException(
"Invalid value '{$field_value}' for column '{$field_name}' on '{$schema['name']}', expected json",
);
// MySQL will accept the string 'null' in a json column and it converts it to a proper NULL
// the string 'null', however, returns NULL when decoded via \json_decode() which is the same
// as what we get from decoding invalid json
if ((string)$row[$field_name] === 'null') {
$row[$field_name] = null;
$field_value = null;
} else {
// invalid json
throw new SQLFakeRuntimeException(
"Invalid value '{$field_value}' for column '{$field_name}' on '{$schema['name']}', expected json",
);
}
}
} else {
// empty strings are not valid for json columns
Expand Down
18 changes: 18 additions & 0 deletions tests/InsertQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,24 @@ final class InsertQueryTest extends HackTest {
);
}

public async function testNullStringCapsInsertIntoJsonColumn(): Awaitable<void> {
$conn = static::$conn as nonnull;
QueryContext::$strictSQLMode = true;
expect(() ==> $conn->query("INSERT INTO table_with_json (id, data) VALUES (1, 'NULL')"))
->toThrow(
SQLFakeRuntimeException::class,
"Invalid value 'NULL' for column 'data' on 'table_with_json', expected json",
);
}

public async function testNullStringLowercaseInsertIntoJsonColumn(): Awaitable<void> {
$conn = static::$conn as nonnull;
QueryContext::$strictSQLMode = true;
await $conn->query("INSERT INTO table_with_json (id, data) VALUES (1, 'null')");
$result = await $conn->query('SELECT * FROM table_with_json');
expect($result->rows())->toBeSame(vec[dict['id' => 1, 'data' => null]]);
}

public async function testNullInsertIntoJsonColumn(): Awaitable<void> {
$conn = static::$conn as nonnull;
QueryContext::$strictSQLMode = true;
Expand Down

0 comments on commit b706db5

Please sign in to comment.