When subfield pushdown is enabled, makeGettersOverSkyline returns nullptr if a projection references an intermediate subfield path that has no matching prefix in the column's skyline. This is null value is stored directly in renames_ and returned during translate... calls which can lead to crashes without explicit null check handling.
Repro
pushdown subfields enabled on:
SELECT v, nested[1]
FROM t
LEFT JOIN (SELECT k, m[100] as nested FROM u) u ON t.k = u.k
(See SubfieldTest.leftJoinWithUnmaterializedSubfield)
Step by step analysis:
intersectWithSkyline iterates all prefixes of the requested subfield path against skyline.pathToExpr. If no prefix matches, it returns nullptr
makeGettersOverSkyline forwards the nullptr
translateSubfield wraps it in an engaged std::optional<ExprCP>(nullptr)
translateExpr checks if (auto path = translateSubfield(expr)) — the optional is truthy, so path.value() returns nullptr
addProjection stores renames_[name] = expr unconditionally, including when expr is nullptr.
- Any caller of
translateColumn that dereferences the result without a null check crashes.
Known crash sites:
addJoinColumns
addUnionColumns
Proposed fix: We should probably prevent the nullptr upstream. Either makeGettersOverSkyline should materialize the intermediate container when a skyline exists but doesn't cover the requested prefix, or translateSubfield should distinguish "not a subfield" (std::nullopt) from "subfield but not materialized" (currently optional(nullptr)) and handle the latter explicitly rather than propagating nullptr into renames_.
When subfield pushdown is enabled, makeGettersOverSkyline returns nullptr if a projection references an intermediate subfield path that has no matching prefix in the column's skyline. This is null value is stored directly in
renames_and returned duringtranslate...calls which can lead to crashes without explicit null check handling.Repro
pushdown subfields enabled on:
(See
SubfieldTest.leftJoinWithUnmaterializedSubfield)Step by step analysis:
intersectWithSkylineiterates all prefixes of the requested subfield path againstskyline.pathToExpr. If no prefix matches, it returnsnullptrmakeGettersOverSkylineforwards thenullptrtranslateSubfieldwraps it in an engagedstd::optional<ExprCP>(nullptr)translateExprchecksif (auto path = translateSubfield(expr))— the optional is truthy, sopath.value()returnsnullptraddProjectionstoresrenames_[name] = exprunconditionally, including when expr isnullptr.translateColumnthat dereferences the result without a null check crashes.Known crash sites:
addJoinColumnsaddUnionColumnsProposed fix: We should probably prevent the nullptr upstream. Either makeGettersOverSkyline should materialize the intermediate container when a skyline exists but doesn't cover the requested prefix, or translateSubfield should distinguish "not a subfield" (std::nullopt) from "subfield but not materialized" (currently optional(nullptr)) and handle the latter explicitly rather than propagating nullptr into renames_.