Skip to content

Commit b706db5

Browse files
K128kevinKevin Tabb
andauthored
JSON validation should accept string 'null' to match MySQL behavior (#97)
* 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]>
1 parent 708178e commit b706db5

File tree

2 files changed

+30
-4
lines changed

2 files changed

+30
-4
lines changed

src/DataIntegrity.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,18 @@ public static function ensureFieldsPresent(dict<string, mixed> $row, table_schem
187187
// validate json string
188188
$json_obj = \json_decode((string)$row[$field_name]);
189189
if ($json_obj is null) {
190-
// invalid json
191-
throw new SQLFakeRuntimeException(
192-
"Invalid value '{$field_value}' for column '{$field_name}' on '{$schema['name']}', expected json",
193-
);
190+
// MySQL will accept the string 'null' in a json column and it converts it to a proper NULL
191+
// the string 'null', however, returns NULL when decoded via \json_decode() which is the same
192+
// as what we get from decoding invalid json
193+
if ((string)$row[$field_name] === 'null') {
194+
$row[$field_name] = null;
195+
$field_value = null;
196+
} else {
197+
// invalid json
198+
throw new SQLFakeRuntimeException(
199+
"Invalid value '{$field_value}' for column '{$field_name}' on '{$schema['name']}', expected json",
200+
);
201+
}
194202
}
195203
} else {
196204
// empty strings are not valid for json columns

tests/InsertQueryTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,24 @@ final class InsertQueryTest extends HackTest {
260260
);
261261
}
262262

263+
public async function testNullStringCapsInsertIntoJsonColumn(): Awaitable<void> {
264+
$conn = static::$conn as nonnull;
265+
QueryContext::$strictSQLMode = true;
266+
expect(() ==> $conn->query("INSERT INTO table_with_json (id, data) VALUES (1, 'NULL')"))
267+
->toThrow(
268+
SQLFakeRuntimeException::class,
269+
"Invalid value 'NULL' for column 'data' on 'table_with_json', expected json",
270+
);
271+
}
272+
273+
public async function testNullStringLowercaseInsertIntoJsonColumn(): Awaitable<void> {
274+
$conn = static::$conn as nonnull;
275+
QueryContext::$strictSQLMode = true;
276+
await $conn->query("INSERT INTO table_with_json (id, data) VALUES (1, 'null')");
277+
$result = await $conn->query('SELECT * FROM table_with_json');
278+
expect($result->rows())->toBeSame(vec[dict['id' => 1, 'data' => null]]);
279+
}
280+
263281
public async function testNullInsertIntoJsonColumn(): Awaitable<void> {
264282
$conn = static::$conn as nonnull;
265283
QueryContext::$strictSQLMode = true;

0 commit comments

Comments
 (0)