Skip to content

Commit f145f40

Browse files
mbasmanovameta-codesync[bot]
authored andcommitted
feat(parse): Add FieldAccessExpr::tryAsRootColumn helper (#17568)
Summary: Pull Request resolved: #17568 The pattern "is this Expr a top-level FieldAccessExpr referring to an input column" — `dynamic_cast<FieldAccessExpr*>(expr.get())` followed by `field->isRootColumn()` — is hand-rolled across multiple call sites (Axiom's optimizer, parser, planner; Velox's own ExprResolver). Each site reimplements the boilerplate; some need just the name, others also need `alias()`. Add a static helper on `FieldAccessExpr` that returns the `FieldAccessExpr*` when the expression is a root-column FieldAccess and nullptr otherwise. Returning the pointer keeps callers flexible — `name()`, `alias()`, etc. are all reachable from the same handle. Reviewed By: amitkdutta Differential Revision: D105833371 fbshipit-source-id: 2834addf6f407ac7564afa949d77bd33486d1465
1 parent 4b070b7 commit f145f40

3 files changed

Lines changed: 38 additions & 0 deletions

File tree

velox/parse/Expressions.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ VELOX_DEFINE_EMBEDDED_ENUM_NAME(IExpr, Kind, kindNames)
6464
VELOX_DEFINE_EMBEDDED_ENUM_NAME(WindowCallExpr, WindowType, windowTypeNames);
6565
VELOX_DEFINE_EMBEDDED_ENUM_NAME(WindowCallExpr, BoundType, boundTypeNames);
6666

67+
// static
68+
const FieldAccessExpr* FieldAccessExpr::tryAsRootColumn(const ExprPtr& expr) {
69+
const auto* field = dynamic_cast<const FieldAccessExpr*>(expr.get());
70+
if (field == nullptr || !field->isRootColumn()) {
71+
return nullptr;
72+
}
73+
return field;
74+
}
75+
6776
bool FieldAccessExpr::operator==(const IExpr& other) const {
6877
if (!other.is(Kind::kFieldAccess)) {
6978
return false;

velox/parse/Expressions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class FieldAccessExpr : public IExpr {
7272
return input()->is(Kind::kInput);
7373
}
7474

75+
/// Returns 'expr' as a FieldAccessExpr if it refers directly to a
76+
/// top-level input column; otherwise returns nullptr.
77+
static const FieldAccessExpr* tryAsRootColumn(const ExprPtr& expr);
78+
7579
std::string toString() const override;
7680

7781
ExprPtr replaceInputs(std::vector<ExprPtr> newInputs) const override {

velox/parse/tests/ExprTest.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <folly/container/F14Map.h>
1717
#include <gtest/gtest.h>
1818
#include "velox/duckdb/conversion/DuckParser.h"
19+
#include "velox/parse/Expressions.h"
1920
#include "velox/parse/IExpr.h"
2021

2122
namespace facebook::velox::core {
@@ -66,6 +67,30 @@ TEST_F(ExprTest, hashMap) {
6667
}
6768
}
6869

70+
TEST_F(ExprTest, tryAsRootColumn) {
71+
// Top-level field access on an input column.
72+
{
73+
auto expr = parse("a");
74+
const auto* field = FieldAccessExpr::tryAsRootColumn(expr);
75+
ASSERT_NE(field, nullptr);
76+
EXPECT_EQ(field->name(), "a");
77+
}
78+
79+
// Dereference (a.b) is a FieldAccess whose input is not an InputExpr.
80+
{
81+
auto expr = parse("a.b");
82+
EXPECT_EQ(FieldAccessExpr::tryAsRootColumn(expr), nullptr);
83+
}
84+
85+
// Non-FieldAccess expressions are not root columns.
86+
{
87+
EXPECT_EQ(FieldAccessExpr::tryAsRootColumn(parse("a + 1")), nullptr);
88+
EXPECT_EQ(FieldAccessExpr::tryAsRootColumn(parse("42")), nullptr);
89+
EXPECT_EQ(
90+
FieldAccessExpr::tryAsRootColumn(parse("cast(a as bigint)")), nullptr);
91+
}
92+
}
93+
6994
TEST_F(ExprTest, dropAlias) {
7095
EXPECT_EQ(parse("a + b")->dropAlias()->toString(), R"(plus("a","b"))");
7196
EXPECT_EQ(parse("a + b as c")->dropAlias()->toString(), R"(plus("a","b"))");

0 commit comments

Comments
 (0)