Skip to content

Commit 8f7a578

Browse files
jonasfjsigurdm
andauthored
Document equality operators and prepare release (#281)
* Document equality operators and prepare release * Apply suggestions from code review Co-authored-by: Sigurd Meldgaard <[email protected]> --------- Co-authored-by: Sigurd Meldgaard <[email protected]>
1 parent 1a62549 commit 8f7a578

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

typed_sql/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 0.1.2-wip
1+
## 0.1.2
22
* Support for _composite foreign keys_ using the `@ForeignKey` annotation.
33
* Extension methods for joining table using _foreign keys_.
44

typed_sql/doc/03_writing_queries.md

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,12 +644,15 @@ The following is a high-level reference of _some_ of the available
644644
_extension methods_:
645645

646646
* `Expr<T?>`, when `T` is one of `bool`, `int`, `double`, `String`, `DateTime`, has:
647-
* `.equals(Expr<T?> other) -> Expr<bool>`
648-
* `.notEquals(Expr<T?> other) -> Expr<bool>`
647+
* `.equals(Expr<T> other) -> Expr<bool>`
648+
* `.equalsUnlessNull(Expr<T?> other) -> Expr<bool?>`
649+
* `.isNotDistinctFrom(Expr<T?> other) -> Expr<bool>`
649650
* `.isNull() -> Expr<bool>`
650651
* `.isNotNull() -> Expr<bool>`
651652
* `.orElse(Expr<T> other) -> Expr<T>`
652653
* `.asNotNull() -> Expr<T>`
654+
* `Expr<T>`, when `T` is one of `bool`, `int`, `double`, `String`, `DateTime`, has:
655+
* `.equals(Expr<T?> other) -> Expr<bool>`
653656
* `Expr<bool>`, has:
654657
* `.not() -> Expr<bool>` (also available as operator `~`)
655658
* `.and(Expr<bool> other) -> Expr<bool>` (also available as operator `&`)
@@ -694,6 +697,58 @@ The reference above, deliberately omits variations such as
694697
`.<method>Value(...)` and `.not<method>(...)` because they are merely
695698
convinience functions.
696699

700+
### Equality operators
701+
In the previous reference there are 3 equality operators:
702+
703+
| `package:typed_sql` | Return type | SQL equivalent | `NULL` compared to `NULL`? |
704+
|--------------------------|---------------|:--------------------------:|:--------------------------:|
705+
| `a.equals(b)` | `Expr<bool>` | `a = b` | N/A |
706+
| `a.equalsUnlessNull(b)` | `Expr<bool?>` | `a = b` | `NULL` |
707+
| `a.isNotDistinctFrom(b)` | `Expr<bool>` | `a IS NOT DISTINCT FROM b` | `TRUE` |
708+
709+
The difference between these operators is what arguments they take, and how they
710+
behave when comparing to `NULL`. In SQL `NULL = NULL` yields `UNKNOWN`
711+
represented by `NULL`. Meaning that when we compare two expressions in SQL using
712+
the `=` operator, the result cannot be `TRUE` if one of the expressions is `NULL`.
713+
This is very different from Dart. Thus, to avoid any confusion the SQL `=`
714+
operator is exposed using the `.equalsUnlessNull` extension method.
715+
716+
The `.equalsUnlessNull` extension method will return `NULL` if any of the two
717+
operands are `NULL`, thus, the return type for `.equalsUnlessNull` is
718+
`Expr<bool?>`. This isn't very convenient, but if you're comparing two
719+
expressions where one of them is not nullable, you can use the `.equals`
720+
extension method.
721+
722+
The `.equals` extension method requires that at least one of the two operands
723+
are not nullable. This is implemented by having two variants:
724+
* `Expr<T>.equals(Expr<T?> other) -> Expr<bool>`, and,
725+
* `Expr<T?>.equals(Expr<T> other) -> Expr<bool>`.
726+
727+
Thus, when using the `.equals` extension method the return type is `Expr<bool>`,
728+
and the SQL operator used is `=`. The downside is that you cannot compare two
729+
nullable expressions. If you wish to compare two nullable expressions you can use
730+
`.isNotDistinctFrom` which has the same semantics as Dart, meaning that
731+
`NULL IS NOT DISTINCT FROM NULL` evaluates to `TRUE`. Or you can use
732+
`.equalsUnlessNull` if you want SQL semantics, where `NULL = NULL` evaluates to
733+
`NULL`.
734+
735+
If you wish to compare two nullable expressions in manner where `NULL = NULL`
736+
evaluates to `FALSE`, you can use `a.equalsUnlessNull(b).orElseValue(false)`.
737+
Or you can do `a.isNotDistinctFrom(b) & a.isNotNull()`.
738+
739+
> [!TIP]
740+
> While it is tempting to always use `.isNotDistinctFrom`, which has the same
741+
> comparison semantics as equality in Dart, there are many scenarios where
742+
> database engines are optimized for the `=` operator in SQL.
743+
> And if you are joining tables you'll
744+
> often find that you do not want to join two rows when the key in both tables
745+
> is `NULL`.
746+
>
747+
> Thus, whenever you find that the `.equals` extension method doesn't work,
748+
> because you are comparing two nullable expressions, do consider if you want
749+
> the `NULL = NULL` to be `TRUE` or `FALSE`, before resorting to use
750+
> `.isNotDistinctFrom`.
751+
697752

698753
## Query reference
699754
The following is a high-level overview of the most important extension methods

typed_sql/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: typed_sql
2-
version: 0.1.1
2+
version: 0.1.2
33
description: Package for doing SQL with some type safety.
44
homepage: https://github.com/google/dart-neats/tree/master/typed_sql
55
repository: https://github.com/google/dart-neats.git

0 commit comments

Comments
 (0)