github-actions[bot] commented on code in PR #69761:
URL: https://github.com/apache/doris/pull/69761#discussion_r4410943754
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java:
##########
@@ -1019,12 +1025,75 @@ public Expression visitMatch(Match match,
ExpressionRewriteContext context) {
right.toSql(), match.toSql(), right.getDataType()));
}
- if (left.getDataType().isVariantType()) {
- left = new Cast(left, right.getDataType());
+ // Only direct roots use leaf-token semantics. Ordinary object
subpaths keep their
+ // existing JSON-to-STRING scalar semantics, including when pruning is
disabled.
+ if (left.getDataType().isVariantType()
+ && (!(left instanceof SlotReference) || ((SlotReference)
left).hasSubColPath()
+ || (currentPlan != null && isScalarVariantMatchInput(left,
currentPlan, new HashMap<>()))
+ || (match.op() != Operator.MATCH_ANY && match.op() !=
Operator.MATCH_ALL))) {
+ left = TypeCoercionUtils.castIfNotSameType(left,
StringType.INSTANCE);
}
+
return match.withChildren(left, right);
}
+ // Alias and set-operation outputs can have neither a subpath nor a source
column.
+ // Prove that every producer is scalar before using STRING semantics. An
unknown
+ // producer or any actual root keeps VARIANT semantics and must bind an
analyzer.
+ private boolean isScalarVariantMatchInput(Expression input, Plan plan,
Map<Slot, Boolean> scalarCteOutputs) {
+ // Set-operation coercion can cast both root columns to a common
VARIANT
+ // layout. That changes storage properties, not root/subpath lineage.
+ if (input instanceof Cast && input.getDataType().isVariantType()
+ && input.child(0).getDataType().isVariantType()) {
+ return isScalarVariantMatchInput(input.child(0), plan,
scalarCteOutputs);
+ }
+ if (!(input instanceof SlotReference)) {
+ return true;
+ }
+ SlotReference slot = (SlotReference) input;
+ if (slot.hasSubColPath()) {
+ return true;
+ }
+ if (plan instanceof LogicalCTEConsumer) {
+ LogicalCTEConsumer consumer = (LogicalCTEConsumer) plan;
+ Slot producerSlot = consumer.getProducerSlot(slot);
+ Boolean scalar = scalarCteOutputs.get(producerSlot);
+ if (scalar != null) {
+ return scalar;
+ }
+ LogicalCTEProducer<?> producer =
getCascadesContext().getStatementContext()
+ .getCteProducerByCteId(consumer.getCteId());
+ // Shared CTE chains form a DAG; do not expand the same producer
at every reference.
+ scalar = isScalarVariantMatchInput(producerSlot, producer.child(),
scalarCteOutputs);
+ scalarCteOutputs.put(producerSlot, scalar);
+ return scalar;
+ }
+ if (plan instanceof LogicalSetOperation) {
+ LogicalSetOperation setOperation = (LogicalSetOperation) plan;
+ int index = setOperation.getOutput().indexOf(slot);
+ if (index >= 0) {
+ for (int i = 0; i < setOperation.arity(); i++) {
+ if
(!isScalarVariantMatchInput(setOperation.getRegularChildrenOutputs().get(i).get(index),
+ setOperation.child(i), scalarCteOutputs)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ for (Expression expression : plan.getExpressions()) {
+ if (expression instanceof Alias && ((Alias)
expression).getExprId().equals(slot.getExprId())) {
+ return isScalarVariantMatchInput(expression.child(0), plan,
scalarCteOutputs);
+ }
+ }
+ for (Plan child : plan.children()) {
+ if (child.getOutputSet().contains(slot)) {
+ return isScalarVariantMatchInput(slot, child,
scalarCteOutputs);
+ }
+ }
+ return false;
Review Comment:
[P2] Preserve scalar MATCH for generated VARIANT slots
With `enable_match_without_inverted_index=true` and `t.v` containing
`["hello"]`, this scalar projection now fails:
```sql
SELECT element MATCH_ANY 'hello'
FROM t LATERAL VIEW explode_variant_array(v) e AS element;
```
Its reduced plan is:
```text
Project(element#2 MATCH_ANY 'hello')
Project(g#3 AS element#2)
Generate(explode_variant_array(v#1) AS g#3)
Scan(t.v#1)
```
The helper follows the alias to `g#3`, but
`LogicalGenerate.getExpressions()` exposes generator functions rather than an
Alias defining that slot, and `g#3` is absent from the scan's outputs. This
fallback returns false, so `visitMatch` omits the previous STRING cast. The
generated slot has no base-column metadata and `ExpressionTranslator` then
rejects it with `no unambiguous index binding`. The supported BE
explode/string-cast/scalar-MATCH path still handles this value.
Recursive CTEs carrying `v['obj']` through `UNION ALL` hit the same
classification gap: `LogicalRecursiveUnion` extends `LogicalBinary`, its fresh
output slots cannot reach the added regular-set-operation or Alias handling,
and their MATCH projections are also rejected. These are separate producer
boundaries from the repaired ordinary alias case. Extend the scalar proof to
generator and recursive-union/worktable outputs and add regression cases for
both.
--
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]