cloud-fan commented on code in PR #58117:
URL: https://github.com/apache/spark/pull/58117#discussion_r3995048897
##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/bitmapExpressions.scala:
##########
@@ -375,6 +383,83 @@ case class BitmapXor(left: Expression, right: Expression)
extends BitmapBinaryEx
copy(left = newLeft, right = newRight)
}
+@ExpressionDescription(
+ usage = """
+ _FUNC_(bitmap, bit_position) - Returns true if the bit at the given
bucket-local position is
+ set in the bitmap, false otherwise.
+ """,
+ arguments = """
+ Arguments:
+ * bitmap - The bitmap to test.
+ An expression that evaluates to a binary. A NULL bitmap produces a
NULL result.
+ * bit_position - The bucket-local bit position to test.
+ An expression that evaluates to a numeric value and is cast to a long.
A NULL position
+ produces a NULL result. A negative position, a position at or above
32768, or a position
+ beyond the actual bitmap length returns false. To query an original
value, use
+ bitmap_bit_position(value) and match bitmap_bucket_number(value) to
the bitmap bucket.
+ """,
+ examples = """
+ Examples:
+ > SELECT _FUNC_(X '01', 0L);
+ true
+ > SELECT _FUNC_(X '01', 1L);
+ false
+ > SELECT _FUNC_(X '10', 4L);
+ true
+ """,
+ since = "4.4.0",
+ group = "misc_funcs")
+case class BitmapContains(left: Expression, right: Expression)
+ extends BinaryExpression with RuntimeReplaceable {
+
+ override def checkInputDataTypes(): TypeCheckResult = {
+ if (left.dataType != BinaryType && left.dataType != NullType) {
+ DataTypeMismatch(
+ errorSubClass = "UNEXPECTED_INPUT_TYPE",
+ messageParameters = Map(
+ "paramIndex" -> ordinalNumber(0),
+ "requiredType" -> toSQLType(BinaryType),
+ "inputSql" -> toSQLExpr(left),
+ "inputType" -> toSQLType(left.dataType)
+ )
+ )
+ } else if (!right.dataType.isInstanceOf[NumericType] && right.dataType !=
NullType) {
+ DataTypeMismatch(
+ errorSubClass = "UNEXPECTED_INPUT_TYPE",
+ messageParameters = Map(
+ "paramIndex" -> ordinalNumber(1),
+ "requiredType" -> toSQLType(LongType),
+ "inputSql" -> toSQLExpr(right),
+ "inputType" -> toSQLType(right.dataType)
+ )
+ )
+ } else {
+ TypeCheckSuccess
+ }
+ }
+
+ override def dataType: DataType = BooleanType
+
+ override def prettyName: String = "bitmap_contains"
+
+ override lazy val replacement: Expression = {
+ // StaticInvoke needs an exact BinaryType child, including for a bare NULL
literal.
+ val bitmap = if (left.dataType == NullType) Cast(left, BinaryType) else
left
+ StaticInvoke(
+ classOf[BitmapExpressionUtils],
+ BooleanType,
+ "bitmapContains",
+ Seq(bitmap, Cast(right, LongType)),
Review Comment:
**Non-blocking (P2):** Because `StaticInvoke` short-circuits its generated
arguments but interpreted `InvokeLike` evaluates all arguments before checking
nulls, this argument order makes behavior depend on
`spark.sql.codegen.factoryMode`. With ANSI mode enabled and a dynamic row whose
bitmap is NULL and whose position overflows long, generated evaluation skips
the cast and returns NULL, while `NO_CODEGEN` evaluates the cast and throws
`CAST_OVERFLOW`. The new whole-stage-codegen toggle does not force the
interpreted projection and therefore misses this case. Please align the two
`InvokeLike` paths (or otherwise make this replacement mode-consistent) and add
a `CODEGEN_ONLY`/`NO_CODEGEN` regression for the null-plus-overflow input.
**Recommended change:** Make interpreted InvokeLike argument preparation
stop after an argument evaluates to null when needNullCheckForIndex marks that
argument as result-determining, mirroring prepareArguments. Add a focused
InvokeLike/StaticInvoke regression and update BitmapExpressionsQuerySuite to
select CODEGEN_ONLY and NO_CODEGEN for a dynamic null bitmap paired with an
ANSI-overflowing numeric position.
**Why this works:** Guard the interpreted evaluation loop with the
accumulated result-null state (or break immediately when the current null is
result-determining) so later expressions are not evaluated when the invocation
will return null. Keep storing and passing every argument on the non-null path.
Exercise both projection factory modes so generated and interpreted evaluation
return the same NULL and ensure the non-null bitmap still propagates
CAST_OVERFLOW.
**Scope:**
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects,
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions,
sql/core/src/test/scala/org/apache/spark/sql
**Compatibility:** When no earlier argument determines a null result,
arguments remain left-to-right, all required casts execute, the target method
receives the same values, and existing method or cast failures propagate
unchanged.
**Risks:** Interpreted execution will no longer observe side effects or
exceptions from later arguments after a result-determining null; this is an
intentional parity change but may expose callers that incorrectly depended on
the previous interpreted-only eagerness. A loop change must preserve evaluation
order and evaluatedArgs population whenever no result-determining null is
encountered.
**Constraints:** Do not change evaluation of later arguments when
propagateNull is false and their nullability does not require a primitive null
guard. Preserve reflection exception unwrapping, return boxing, foldability,
and StaticInvoke nullability declarations. Keep the bitmap function's existing
numeric-to-long coercion and out-of-range false semantics unchanged.
**Success:** A null result-determining argument prevents every later
InvokeLike argument from being evaluated in both generated and interpreted
modes. bitmap_contains returns NULL under both CODEGEN_ONLY and NO_CODEGEN for
a dynamic null bitmap paired with an ANSI-overflowing numeric position.
bitmap_contains still raises CAST_OVERFLOW under ANSI mode when the bitmap is
non-null and the numeric position overflows long. Non-null StaticInvoke,
Invoke, and NewInstance argument evaluation and invocation behavior remain
unchanged. Focused Catalyst object-expression and SQL bitmap-expression suites
pass.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]