Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,7 @@ primaryExpression
| CASE value=expression whenClause+ (ELSE elseExpression=expression)? END #simpleCase
| name=CAST LEFT_PAREN expression AS castDataType RIGHT_PAREN #cast
| name=TRY_CAST LEFT_PAREN expression AS castDataType RIGHT_PAREN #tryCast
| DEFAULT LEFT_PAREN qualifiedName RIGHT_PAREN #defaultValue
| constant #constantDefault
| interval #intervalLiteral
| ASTERISK (exceptOrReplace)* #star
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@
import org.apache.doris.nereids.trees.expressions.BitXor;
import org.apache.doris.nereids.trees.expressions.CaseWhen;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.Default;
import org.apache.doris.nereids.trees.expressions.DefaultValueSlot;
import org.apache.doris.nereids.trees.expressions.DereferenceExpression;
import org.apache.doris.nereids.trees.expressions.Divide;
Expand Down Expand Up @@ -3124,6 +3125,17 @@ public Expression visitTryCast(DorisParser.TryCastContext ctx) {
return ParserUtils.withOrigin(ctx, () -> processTryCast(getExpression(ctx.expression()), ctx.castDataType()));
}

@Override
public Expression visitDefaultValue(DorisParser.DefaultValueContext ctx) {
return ParserUtils.withOrigin(ctx, () -> {
List<String> nameParts = ctx.qualifiedName().identifier()
.stream()
.map(RuleContext::getText)
.collect(ImmutableList.toImmutableList());
return new Default(new UnboundSlot(nameParts));
});
}

@Override
public UnboundFunction visitExtract(DorisParser.ExtractContext ctx) {
return ParserUtils.withOrigin(ctx, () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.doris.nereids.rules.expression.rules.MergeDateTrunc;
import org.apache.doris.nereids.rules.expression.rules.NormalizeBinaryPredicatesRule;
import org.apache.doris.nereids.rules.expression.rules.NormalizeStructElement;
import org.apache.doris.nereids.rules.expression.rules.RewriteDefaultExpression;
import org.apache.doris.nereids.rules.expression.rules.SimplifyArithmeticComparisonRule;
import org.apache.doris.nereids.rules.expression.rules.SimplifyArithmeticRule;
import org.apache.doris.nereids.rules.expression.rules.SimplifyCastRule;
Expand All @@ -54,6 +55,7 @@ public class ExpressionNormalization extends ExpressionRewrite {
public static final List<ExpressionRewriteRule<ExpressionRewriteContext>> NORMALIZE_REWRITE_RULES
= ImmutableList.of(
bottomUp(
RewriteDefaultExpression.INSTANCE,
SupportJavaDateFormatter.INSTANCE,
NormalizeBinaryPredicatesRule.INSTANCE,
InPredicateDedup.INSTANCE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public enum ExpressionRuleType {
NORMALIZE_STRUCT_ELEMENT,
TIMESTAMP_TO_ADD_TIME,
TOPN_TO_MAX,
REWRITE_DEFAULT_EXPRESSION,
ADD_SESSION_VAR_GUARD;

public int type() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.apache.doris.nereids.rules.expression.rules;

import org.apache.doris.catalog.Column;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.analyzer.UnboundFunction;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer;
import org.apache.doris.nereids.rules.expression.ExpressionMatchingContext;
import org.apache.doris.nereids.rules.expression.ExpressionPatternMatcher;
import org.apache.doris.nereids.rules.expression.ExpressionPatternRuleFactory;
import org.apache.doris.nereids.rules.expression.ExpressionRuleType;
import org.apache.doris.nereids.trees.expressions.Default;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.util.TypeCoercionUtils;

import com.google.common.collect.ImmutableList;

import java.util.List;
import java.util.Optional;

/**
* Rewrite DEFAULT(column) expression to the actual default value of the column.
* This rule transforms:
* 1. DEFAULT(column) with default value -> the actual default value expression
* 2. DEFAULT(column) with no default value but nullable -> NULL
* 3. DEFAULT(column) with no default value and not nullable -> error
*/
public class RewriteDefaultExpression implements ExpressionPatternRuleFactory {

public static final RewriteDefaultExpression INSTANCE = new RewriteDefaultExpression();

@Override
public List<ExpressionPatternMatcher<? extends Expression>> buildRules() {
return ImmutableList.of(
matchesType(Default.class)
.thenApply(RewriteDefaultExpression::rewrite)
.toRule(ExpressionRuleType.REWRITE_DEFAULT_EXPRESSION)
);
}

private static Expression rewrite(ExpressionMatchingContext<Default> context) {
Default defaultExpr = context.expr;
Expression child = defaultExpr.child();

if (!(child instanceof SlotReference)) {
throw new AnalysisException("DEFAULT requires a column reference, but got: " + child.toSql());
}

SlotReference slotRef = (SlotReference) child;
Optional<Column> columnOpt = slotRef.getOriginalColumn();
if (!columnOpt.isPresent()) {
throw new AnalysisException("Cannot find column information for DEFAULT("
+ slotRef.getName() + ")");
}

Column column = columnOpt.get();
DataType targetType = DataType.fromCatalogType(column.getType());
if (column.isGeneratedColumn()) {
throw new AnalysisException("DEFAULT cannot be used on generated column '"
+ column.getName() + "'");
}

String defaultValueSql = column.getDefaultValueSql();
if (defaultValueSql == null) {
if (column.isAllowNull()) {
return new NullLiteral(targetType);
} else {
throw new AnalysisException("Column '" + column.getName()
+ "' has no default value and does not allow NULL or column is auto-increment");
}
}

Expression defaultValueExpr = new NereidsParser().parseExpression(defaultValueSql);
if (defaultValueExpr instanceof UnboundFunction) {
CascadesContext cascadesContext = context.cascadesContext;
LogicalPlan plan = (LogicalPlan) context.rewriteContext.plan.orElse(null);
defaultValueExpr = ExpressionAnalyzer.analyzeFunction(plan, cascadesContext, defaultValueExpr);
}

return TypeCoercionUtils.castIfNotSameType(defaultValueExpr, targetType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.apache.doris.nereids.trees.expressions;

import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

import java.util.List;

/**
* Default value expression.
*/
public class Default extends Expression
implements UnaryExpression, AlwaysNullable {

public Default(Expression child) {
super(ImmutableList.of(child));
}

/**
* constructor with 1 argument.
*/
public Default(Expression arg, DataType targetType) {
super(ImmutableList.of(arg));
}

@Override
public Default withChildren(List<Expression> children) {
Preconditions.checkArgument(children.size() == 1);
return new Default(children.get(0));
}

@Override
public DataType getDataType() {
return child().getDataType();
}

@Override
public void checkLegalityBeforeTypeCoercion() {
Expression arg = child();
if (!(arg instanceof SlotReference)) {
throw new AnalysisException("DEFAULT requires a column reference");
}
}

@Override
public void checkLegalityAfterRewrite() {
checkLegalityBeforeTypeCoercion();
}

@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitDefault(this, context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
import org.apache.doris.nereids.trees.expressions.Default;
import org.apache.doris.nereids.trees.expressions.DefaultValueSlot;
import org.apache.doris.nereids.trees.expressions.DereferenceExpression;
import org.apache.doris.nereids.trees.expressions.Divide;
Expand Down Expand Up @@ -239,6 +240,10 @@ public R visitSlotReference(SlotReference slotReference, C context) {
return visitSlot(slotReference, context);
}

public R visitDefault(Default defaultExpr, C context) {
return visit(defaultExpr, context);
}

public R visitDefaultValue(DefaultValueSlot defaultValueSlot, C context) {
return visitSlot(defaultValueSlot, context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- This file is automatically generated. You should know what you did if you want to edit this
-- !scalar_defaults --
true 7 32000 2147483647 9223372036854775807 170141183460469231731687303715884105727 3.125 2.718281828 123456789.123456789 99999.1234 charDemo plain string 2025-10-25 11:22:33.666777+08:00 2025-10-25T11:22:33 2025-10-31 \N 192.168.1.1 2001:db8::1 [] \N \N \N \N

-- !null_column_default --
\N

-- !boundary_defaults --
-128 127 -32768 32767 -2147483648 2147483647 -9223372036854775808 9223372036854775807 0.0 0 0.00 \N

-- !complex_defaults --
[] \N \N \N \N \N

-- !datetime_format_defaults --
2025-01-01 2025-01-01T12:30:45

-- !ip_defaults --
127.0.0.1 10.0.0.1 ::1 2001:db8:85a3::8a2e:370:7334

-- !string_defaults --
fixed variable length string very long string that can be much longer than varchar

-- !pi_e_defaults --
3.141592653589793 2.718281828459045

-- !empty_table_defaults --

-- !same_col_name --
hello 2026-01-13 3.141592653589793 2023-12-31T23:59:59
hello 2026-01-13 3.141592653589793 2023-12-31T23:59:59
hello 2026-01-13 3.141592653589793 2023-12-31T23:59:59

Loading
Loading