Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix evaluation of <foo>=NULL #107

Merged
merged 2 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Expressions/BinaryOperatorExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ public function evaluateImpl(row $row, AsyncMysqlConnection $conn): mixed {
case Operator::OR:
invariant(false, 'impossible to arrive here');
case Operator::EQUALS:
// foo = NULL is always NULL, no matter what the value of foo
// (specifically, if foo is also NULL!). Negation of NULL is always
// NULL, so we ignore that.
if ($l_value is null || $r_value is null) {
return null;
}
// maybe do some stuff with data types here
// comparing strings: gotta think about collation and case sensitivity!
return (bool)(\HH\Lib\Legacy_FIXME\eq($l_value, $r_value) ? 1 : 0 ^ $this->negatedInt);
Expand Down
34 changes: 30 additions & 4 deletions tests/SelectExpressionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ final class SelectExpressionTest extends HackTest {

public async function testSelectExpressions(): Awaitable<void> {
$conn = static::$conn as nonnull;
$results =
await $conn->query('SELECT id, group_id as my_fav_group_id, id*1000 as math FROM table3 WHERE group_id=6');
$results = await $conn->query(
'SELECT id, group_id as my_fav_group_id, id*1000 as math FROM table3 WHERE group_id=6',
);
expect($results->rows())->toBeSame(
vec[
dict['id' => 4, 'my_fav_group_id' => 6, 'math' => 4000],
Expand Down Expand Up @@ -156,6 +157,12 @@ final class SelectExpressionTest extends HackTest {
],
'adding a column reference still parses',
);

$results = await $conn->query('SELECT id FROM table4 WHERE id IN (NULL)');
expect($results->rows())->toBeEmpty();

$results = await $conn->query('SELECT id FROM table4 WHERE id IN (NULL=NULL)');
expect($results->rows())->toBeEmpty();
}

public async function testNotIn(): Awaitable<void> {
Expand Down Expand Up @@ -199,7 +206,9 @@ final class SelectExpressionTest extends HackTest {

// weird this is even valid SQL, and possibly pedantic, but this demonstrates a lot of how
// case statements are implemented such that it doesn't blow up on the second THEN or second CASE
$results = await $conn->query("SELECT CASE WHEN 4 = CASE WHEN 1 = 2 THEN 3 ELSE 4 END THEN 'yes' ELSE 'no' END");
$results = await $conn->query(
"SELECT CASE WHEN 4 = CASE WHEN 1 = 2 THEN 3 ELSE 4 END THEN 'yes' ELSE 'no' END",
);
expect($results->rows())->toBeSame(
vec[
dict["CASE WHEN 4 = CASE WHEN 1 = 2 THEN 3 ELSE 4 END THEN 'yes' ELSE 'no' END" => 'yes'],
Expand Down Expand Up @@ -321,6 +330,21 @@ final class SelectExpressionTest extends HackTest {
]);
}

public async function testEqualsNull(): Awaitable<void> {
$conn = static::$conn as nonnull;
$results = await $conn->query('SELECT id FROM table3 WHERE NULL=NULL');
expect($results->rows())->toBeEmpty();

$results = await $conn->query('SELECT id FROM table3 WHERE NULL!=NULL');
expect($results->rows())->toBeEmpty();

$results = await $conn->query('SELECT id FROM table3 WHERE table_3_id=NULL');
expect($results->rows())->toBeEmpty();

$results = await $conn->query('SELECT id FROM table3 WHERE table_3_id!=NULL');
expect($results->rows())->toBeEmpty();
}

public async function testIsNotNull(): Awaitable<void> {
$conn = static::$conn as nonnull;
$results = await $conn->query(
Expand All @@ -338,7 +362,9 @@ final class SelectExpressionTest extends HackTest {

public async function testNotParens(): Awaitable<void> {
$conn = static::$conn as nonnull;
$results = await $conn->query("SELECT id FROM table3 WHERE group_id=12345 AND NOT (name='name1' OR name='name3')");
$results = await $conn->query(
"SELECT id FROM table3 WHERE group_id=12345 AND NOT (name='name1' OR name='name3')",
);
expect($results->rows())->toBeSame(vec[
dict['id' => 2],
]);
Expand Down
Loading