I am asking this question in the context of using a Postgres database, although I would imagine it would apply to most SQL databases.
When working with the results of a database query inside a statically typed application, we need to know whether a particular column could return null
or not. When the underlying data is stored in a table column with a NOT NULL
constraint, we can confidently type the data as non-nullable in our application. Likewise, if there is no constraint, then we type the column as nullable since it could return null
.
This is simple enough to reason about, but things get more complicated when we introduce left and right joins. For example, if we have a join clause like
FROM a
LEFT JOIN b ON a.x = b.y
then all the columns on b
become nullable, since the results could include rows of a
where there was no matching b
row. Likewise, if we do
FROM a
RIGHT JOIN b ON a.x = b.y
now all the columns on a
will be nullable.
In this sense, we could say that certain joins (specifically left and right outer joins) apply a sort of "nullability modifier" to one or more of the set of tables used in the query's projection.
What I am struggling to wrap my head around is how this behavior works when there are multiple joins. For example:
FROM a
LEFT JOIN b ON a.x = b.y
LEFT JOIN c ON b.x = c.y
or
FROM a
LEFT JOIN b ON a.x = b.y
RIGHT JOIN c ON b.x = c.y
or
FROM a
LEFT JOIN b ON a.x = b.y
INNER JOIN c ON b.x = c.y
RIGHT JOIN c ON c.x = d.y
or
FROM a
LEFT JOIN (b INNER JOIN c ON b.x = c.y) ON a.x = b.y
If I iterate over the set of joins, is there a set of heuristics I could apply that would let me accurately set the "nullability modifier" for each referenced table, so that I can derive an accurate application type for the query's result? The snippets above serve to illustrate the complexity of this issue, but the idea is to have a set of heuristics that could be applied to any set of joins, regardless of length, order or composition.