Skip to content

Commit

Permalink
Normalized Null handling in DataSync
Browse files Browse the repository at this point in the history
This fixes a bug where queries like 'col = NULL' is generated instead
of the correct 'col IS NULL'
  • Loading branch information
mpscholten committed Nov 7, 2022
1 parent 27aa2ea commit 29fe460
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 12 deletions.
1 change: 0 additions & 1 deletion IHP/DataSync/DynamicQuery.hs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ data OrderByClause
-- | Represents a WHERE conditions of a 'DynamicSQLQuery'
data ConditionExpression
= ColumnExpression { field :: !Text }
| NullExpression
| InfixOperatorExpression
{ left :: !ConditionExpression
, op :: !ConditionOperator
Expand Down
7 changes: 3 additions & 4 deletions IHP/DataSync/DynamicQueryCompiler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,8 @@ compileSelectedColumns (SelectSpecific fields) = PG.Many args

compileCondition :: ConditionExpression -> (PG.Query, [PG.Action])
compileCondition (ColumnExpression column) = ("?", [PG.toField $ PG.Identifier (fieldNameToColumnName column)])
compileCondition NullExpression = ("NULL", [])
compileCondition (InfixOperatorExpression a OpEqual NullExpression) = compileCondition (InfixOperatorExpression a OpIs NullExpression) -- Turn 'a = NULL' into 'a IS NULL'
compileCondition (InfixOperatorExpression a OpNotEqual NullExpression) = compileCondition (InfixOperatorExpression a OpIsNot NullExpression) -- Turn 'a <> NULL' into 'a IS NOT NULL'
compileCondition (InfixOperatorExpression a OpEqual (LiteralExpression Null)) = compileCondition (InfixOperatorExpression a OpIs (LiteralExpression Null)) -- Turn 'a = NULL' into 'a IS NULL'
compileCondition (InfixOperatorExpression a OpNotEqual (LiteralExpression Null)) = compileCondition (InfixOperatorExpression a OpIsNot (LiteralExpression Null)) -- Turn 'a <> NULL' into 'a IS NOT NULL'
compileCondition (InfixOperatorExpression a operator b) = ("(" <> queryA <> ") " <> compileOperator operator <> " " <> rightOperand, paramsA <> paramsB)
where
(queryA, paramsA) = compileCondition a
Expand All @@ -98,7 +97,7 @@ compileCondition (InfixOperatorExpression a operator b) = ("(" <> queryA <> ") "
rightParentheses :: Bool
rightParentheses =
case b of
NullExpression -> False
LiteralExpression Null -> False
ListExpression {} -> False -- The () are inserted already via @PG.In@
_ -> True
compileCondition (LiteralExpression literal) = ("?", [PG.toField literal])
Expand Down
12 changes: 6 additions & 6 deletions Test/DataSync/DynamicQueryCompiler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -148,32 +148,32 @@ tests = do
let query = DynamicSQLQuery
{ table = "posts"
, selectedColumns = SelectAll
, whereCondition = Just $ InfixOperatorExpression (ColumnExpression "userId") OpEqual NullExpression
, whereCondition = Just $ InfixOperatorExpression (ColumnExpression "userId") OpEqual (LiteralExpression Null)
, orderByClause = []
, distinctOnColumn = Nothing
, limit = Nothing
, offset = Nothing
}

compileQuery query `shouldBe`
( "SELECT ? FROM ? WHERE (?) IS NULL"
, [PG.Plain "*", PG.EscapeIdentifier "posts", PG.EscapeIdentifier "user_id"]
( "SELECT ? FROM ? WHERE (?) IS ?"
, [PG.Plain "*", PG.EscapeIdentifier "posts", PG.EscapeIdentifier "user_id", PG.Plain "null"]
)

it "compile 'field <> NULL' conditions to 'field IS NOT NULL'" do
let query = DynamicSQLQuery
{ table = "posts"
, selectedColumns = SelectAll
, whereCondition = Just $ InfixOperatorExpression (ColumnExpression "userId") OpNotEqual NullExpression
, whereCondition = Just $ InfixOperatorExpression (ColumnExpression "userId") OpNotEqual (LiteralExpression Null)
, orderByClause = []
, distinctOnColumn = Nothing
, limit = Nothing
, offset = Nothing
}

compileQuery query `shouldBe`
( "SELECT ? FROM ? WHERE (?) IS NOT NULL"
, [PG.Plain "*", PG.EscapeIdentifier "posts", PG.EscapeIdentifier "user_id"]
( "SELECT ? FROM ? WHERE (?) IS NOT ?"
, [PG.Plain "*", PG.EscapeIdentifier "posts", PG.EscapeIdentifier "user_id", PG.Plain "null"]
)

it "compile queries with TS expressions" do
Expand Down
1 change: 0 additions & 1 deletion lib/IHP/DataSync/ihp-querybuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,6 @@ export function recordMatchesQuery(query, record) {
default: throw new Error('Unsupported operator ' + expression.op);
}
}
case 'NullExpression': return null;
case 'LiteralExpression': return (expression.value.tag === 'Null' ? null : expression.value.contents);
default: throw new Error('Unsupported expression in evaluate: ' + expression.tag);
}
Expand Down

0 comments on commit 29fe460

Please sign in to comment.