Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/storage/predicate/column_predicate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,43 @@ static bool isColumnRef(ExpressionType type) {
return type == ExpressionType::PROPERTY || type == ExpressionType::VARIABLE;
}

static bool isIntegerUpTo64Bits(LogicalTypeID type) {
return LogicalTypeUtils::isIntegral(type) && type != LogicalTypeID::INT128 &&
type != LogicalTypeID::UINT128;
}

// The pushed-down predicate compares the column's stats, read as the cast's target type, with
// the literal. That is only right when the cast keeps every value and its order in the same
// stats slot: the same type, integer widening up to 64 bits (an unsigned value reads the same
// from the signed slot of a wider type) and FLOAT to DOUBLE. 128-bit targets are excluded because
// a negative 64-bit value is stored without sign extension into the high half.
static bool castKeepsStats(const LogicalType& from, const LogicalType& to) {
if (from == to) {
return true;
}
if (from.getLogicalTypeID() == LogicalTypeID::FLOAT) {
return to.getLogicalTypeID() == LogicalTypeID::DOUBLE;
}
if (!isIntegerUpTo64Bits(from.getLogicalTypeID()) ||
!isIntegerUpTo64Bits(to.getLogicalTypeID())) {
return false;
}
auto fromSize = PhysicalTypeUtils::getFixedTypeSize(from.getPhysicalType());
auto toSize = PhysicalTypeUtils::getFixedTypeSize(to.getPhysicalType());
if (LogicalTypeUtils::isUnsigned(to)) {
return LogicalTypeUtils::isUnsigned(from) && fromSize <= toSize;
}
return LogicalTypeUtils::isUnsigned(from) ? fromSize < toSize : fromSize <= toSize;
}

static bool isCastedColumnRef(const Expression& expr) {
if (expr.expressionType == ExpressionType::FUNCTION) {
const auto& funcExpr = expr.constCast<ScalarFunctionExpression>();
if (funcExpr.getFunction().name.starts_with("CAST")) {
DASSERT(funcExpr.getNumChildren() > 0);
return isColumnRef(funcExpr.getChild(0)->expressionType);
const auto& child = *funcExpr.getChild(0);
return isColumnRef(child.expressionType) &&
castKeepsStats(child.getDataType(), expr.getDataType());
}
}
return false;
Expand Down
118 changes: 118 additions & 0 deletions test/test_files/filter/cast.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,121 @@
-STATEMENT match (c:Comment) where c.id = 687194777181 return c.browserUsed, c.content;
---- 1
Chrome|right

-CASE ZoneMapCastedColumnNumericLiteral
-STATEMENT CREATE NODE TABLE T(id INT64 PRIMARY KEY, i INT64, i32 INT32, u32 UINT32, f DOUBLE, fl FLOAT, d DECIMAL(18,2), dt DATE, tms TIMESTAMP_MS);
---- ok
-STATEMENT CREATE (:T {id: 1, i: 30, i32: 30, u32: 30, f: 30.7, fl: 30.5, d: 5.00, dt: date('2020-01-01'), tms: CAST('2020-01-01 00:00:00' AS TIMESTAMP_MS)});
---- ok
-STATEMENT CREATE (:T {id: 2, i: 40, i32: 40, u32: 4000000000, f: 40.2, fl: 40.5, d: 7.00, dt: date('2021-01-01'), tms: CAST('2021-01-01 00:00:00' AS TIMESTAMP_MS)});
---- ok
-STATEMENT CREATE NODE TABLE N(id INT64 PRIMARY KEY, i INT64, i32 INT32);
---- ok
-STATEMENT CREATE (:N {id: 1, i: -5, i32: -20});
---- ok
-STATEMENT CREATE (:N {id: 2, i: 40, i32: 40});
---- ok
-LOG InMemory
-STATEMENT MATCH (n:T) WHERE n.i > 30.5 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.i = 40.0 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.i32 > 35.5 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE CAST(n.f AS INT64) = 31 RETURN n.id;
---- 1
1
-STATEMENT MATCH (n:T) WHERE CAST(n.d AS INT64) = 5 RETURN n.id;
---- 1
1
-STATEMENT MATCH (n:T) WHERE n.dt > timestamp('2020-06-01 00:00:00') RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.tms > timestamp('2020-06-01 00:00:00') RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE CAST(n.i32 AS INT64) = 40 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.u32 > -1 RETURN n.id ORDER BY n.id;
-CHECK_ORDER
---- 2
1
2
-STATEMENT MATCH (n:T) WHERE n.u32 < 5000000000 RETURN n.id ORDER BY n.id;
-CHECK_ORDER
---- 2
1
2
-STATEMENT MATCH (n:T) WHERE n.u32 > 5000000000 RETURN n.id;
---- 0
-STATEMENT MATCH (n:T) WHERE n.fl > 40.25 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:N) WHERE n.i < 9223372036854775808 RETURN n.id ORDER BY n.id;
-CHECK_ORDER
---- 2
1
2
-STATEMENT MATCH (n:N) WHERE CAST(n.i32 AS INT64) = -20 RETURN n.id;
---- 1
1
-STATEMENT MATCH (n:N) WHERE CAST(n.i32 AS INT64) <> 40 RETURN n.id;
---- 1
1
-STATEMENT CHECKPOINT;
---- ok
-LOG Checkpointed
-STATEMENT MATCH (n:T) WHERE n.i > 30.5 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.i = 40.0 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.i32 > 35.5 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE CAST(n.f AS INT64) = 31 RETURN n.id;
---- 1
1
-STATEMENT MATCH (n:T) WHERE CAST(n.d AS INT64) = 5 RETURN n.id;
---- 1
1
-STATEMENT MATCH (n:T) WHERE n.dt > timestamp('2020-06-01 00:00:00') RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.tms > timestamp('2020-06-01 00:00:00') RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE CAST(n.i32 AS INT64) = 40 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:T) WHERE n.u32 > -1 RETURN n.id ORDER BY n.id;
-CHECK_ORDER
---- 2
1
2
-STATEMENT MATCH (n:T) WHERE n.u32 < 5000000000 RETURN n.id ORDER BY n.id;
-CHECK_ORDER
---- 2
1
2
-STATEMENT MATCH (n:T) WHERE n.u32 > 5000000000 RETURN n.id;
---- 0
-STATEMENT MATCH (n:T) WHERE n.fl > 40.25 RETURN n.id;
---- 1
2
-STATEMENT MATCH (n:N) WHERE n.i < 9223372036854775808 RETURN n.id ORDER BY n.id;
-CHECK_ORDER
---- 2
1
2
-STATEMENT MATCH (n:N) WHERE CAST(n.i32 AS INT64) = -20 RETURN n.id;
---- 1
1
-STATEMENT MATCH (n:N) WHERE CAST(n.i32 AS INT64) <> 40 RETURN n.id;
---- 1
1
Loading