[Feature](Nereids) support values table (#23121)
support insert into table values(...) for Nereids.
sql like:
insert into t values(1, 2, 3)
insert into t values(1 + 1, dayofweek(now()), 4), (4, 5, 6)
insert into t values('1', '6.5', cast(1.5 as int))
This commit is contained in:
@ -145,6 +145,7 @@ setQuantifier
|
||||
queryPrimary
|
||||
: querySpecification #queryPrimaryDefault
|
||||
| LEFT_PAREN query RIGHT_PAREN #subquery
|
||||
| inlineTable #valuesTable
|
||||
;
|
||||
|
||||
querySpecification
|
||||
@ -396,6 +397,11 @@ aggTypeDef
|
||||
tabletList
|
||||
: TABLET LEFT_PAREN tabletIdList+=INTEGER_VALUE (COMMA tabletIdList+=INTEGER_VALUE)* RIGHT_PAREN
|
||||
;
|
||||
|
||||
|
||||
inlineTable
|
||||
: VALUES rowConstructor (COMMA rowConstructor)*
|
||||
;
|
||||
|
||||
// -----------------Expression-----------------
|
||||
namedExpression
|
||||
@ -430,7 +436,9 @@ booleanExpression
|
||||
| left=booleanExpression operator=DOUBLEPIPES right=booleanExpression #doublePipes
|
||||
;
|
||||
|
||||
|
||||
rowConstructor
|
||||
: LEFT_PAREN namedExpression (COMMA namedExpression)+ RIGHT_PAREN
|
||||
;
|
||||
|
||||
predicate
|
||||
: NOT? kind=BETWEEN lower=valueExpression AND upper=valueExpression
|
||||
|
||||
@ -79,6 +79,7 @@ import org.apache.doris.nereids.DorisParser.IdentifierSeqContext;
|
||||
import org.apache.doris.nereids.DorisParser.InPartitionDefContext;
|
||||
import org.apache.doris.nereids.DorisParser.IndexDefContext;
|
||||
import org.apache.doris.nereids.DorisParser.IndexDefsContext;
|
||||
import org.apache.doris.nereids.DorisParser.InlineTableContext;
|
||||
import org.apache.doris.nereids.DorisParser.InsertIntoQueryContext;
|
||||
import org.apache.doris.nereids.DorisParser.IntegerLiteralContext;
|
||||
import org.apache.doris.nereids.DorisParser.IntervalContext;
|
||||
@ -119,6 +120,7 @@ import org.apache.doris.nereids.DorisParser.RegularQuerySpecificationContext;
|
||||
import org.apache.doris.nereids.DorisParser.RelationContext;
|
||||
import org.apache.doris.nereids.DorisParser.RollupDefContext;
|
||||
import org.apache.doris.nereids.DorisParser.RollupDefsContext;
|
||||
import org.apache.doris.nereids.DorisParser.RowConstructorContext;
|
||||
import org.apache.doris.nereids.DorisParser.SelectClauseContext;
|
||||
import org.apache.doris.nereids.DorisParser.SelectColumnClauseContext;
|
||||
import org.apache.doris.nereids.DorisParser.SelectHintContext;
|
||||
@ -170,6 +172,7 @@ import org.apache.doris.nereids.properties.SelectHint;
|
||||
import org.apache.doris.nereids.properties.SelectHintLeading;
|
||||
import org.apache.doris.nereids.properties.SelectHintSetVar;
|
||||
import org.apache.doris.nereids.trees.expressions.Add;
|
||||
import org.apache.doris.nereids.trees.expressions.Alias;
|
||||
import org.apache.doris.nereids.trees.expressions.And;
|
||||
import org.apache.doris.nereids.trees.expressions.BitAnd;
|
||||
import org.apache.doris.nereids.trees.expressions.BitNot;
|
||||
@ -731,6 +734,15 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogicalPlan visitInlineTable(InlineTableContext ctx) {
|
||||
List<LogicalPlan> exprsList = ctx.rowConstructor().stream()
|
||||
.map(this::visitRowConstructor)
|
||||
.map(LogicalPlan.class::cast)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
return reduceToLogicalPlanTree(0, exprsList.size() - 1, exprsList, Qualifier.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an aliased table reference. This is typically used in FROM clauses.
|
||||
*/
|
||||
@ -1674,6 +1686,18 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
return getExpression(ctx.expression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnboundOneRowRelation visitRowConstructor(RowConstructorContext ctx) {
|
||||
return new UnboundOneRowRelation(
|
||||
StatementScopeIdGenerator.newRelationId(),
|
||||
ctx.namedExpression().stream()
|
||||
.map(this::visitNamedExpression)
|
||||
.map(e -> (e instanceof NamedExpression)
|
||||
? ((NamedExpression) e)
|
||||
: new Alias(e, e.toSql()))
|
||||
.collect(ImmutableList.toImmutableList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Expression> visitNamedExpressionSeq(NamedExpressionSeqContext namedCtx) {
|
||||
return visit(namedCtx.namedExpression(), Expression.class);
|
||||
|
||||
@ -32,6 +32,8 @@ import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.parser.NereidsParser;
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
|
||||
import org.apache.doris.nereids.rules.expression.rules.FunctionBinder;
|
||||
import org.apache.doris.nereids.trees.expressions.Alias;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
@ -106,8 +108,8 @@ public class BindSink implements AnalysisRuleFactory {
|
||||
if (ConnectContext.get() != null) {
|
||||
ConnectContext.get().getState().setIsQuery(true);
|
||||
}
|
||||
// generate slots not mentioned in sql, mv slots and shaded slots.
|
||||
try {
|
||||
// generate slots not mentioned in sql, mv slots and shaded slots.
|
||||
for (Column column : boundSink.getTargetTable().getFullSchema()) {
|
||||
if (column.isMaterializedViewColumn()) {
|
||||
List<SlotRef> refs = column.getRefColumns();
|
||||
@ -116,8 +118,13 @@ public class BindSink implements AnalysisRuleFactory {
|
||||
"mv column's ref column cannot be null");
|
||||
Expression parsedExpression = expressionParser.parseExpression(
|
||||
column.getDefineExpr().toSql());
|
||||
Expression boundExpression = SlotReplacer.INSTANCE
|
||||
Expression boundSlotExpression = SlotReplacer.INSTANCE
|
||||
.replace(parsedExpression, columnToOutput);
|
||||
// the boundSlotExpression is an expression whose slots are bound but function
|
||||
// may not be bound, we have to bind it again.
|
||||
// for example: to_bitmap.
|
||||
Expression boundExpression = FunctionBinder.INSTANCE.rewrite(
|
||||
boundSlotExpression, new ExpressionRewriteContext(ctx.cascadesContext));
|
||||
|
||||
NamedExpression slot = boundExpression instanceof NamedExpression
|
||||
? ((NamedExpression) boundExpression)
|
||||
@ -140,10 +147,31 @@ public class BindSink implements AnalysisRuleFactory {
|
||||
column.getName()
|
||||
));
|
||||
} else {
|
||||
columnToOutput.put(column.getName(),
|
||||
new Alias(Literal.of(column.getDefaultValue())
|
||||
.checkedCastTo(DataType.fromCatalogType(column.getType())),
|
||||
column.getName()));
|
||||
try {
|
||||
// it comes from the original planner, if default value expression is
|
||||
// null, we use the literal string of the default value, or it may be
|
||||
// default value function, like CURRENT_TIMESTAMP.
|
||||
if (column.getDefaultValueExpr() == null) {
|
||||
columnToOutput.put(column.getName(),
|
||||
new Alias(Literal.of(column.getDefaultValue())
|
||||
.checkedCastTo(
|
||||
DataType.fromCatalogType(column.getType())),
|
||||
column.getName()));
|
||||
} else {
|
||||
Expression defualtValueExpression = FunctionBinder.INSTANCE.rewrite(
|
||||
new NereidsParser().parseExpression(
|
||||
column.getDefaultValueExpr().toSql()),
|
||||
new ExpressionRewriteContext(ctx.cascadesContext));
|
||||
NamedExpression slot =
|
||||
defualtValueExpression instanceof NamedExpression
|
||||
? ((NamedExpression) defualtValueExpression)
|
||||
: new Alias(defualtValueExpression);
|
||||
|
||||
columnToOutput.put(column.getName(), slot);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new AnalysisException(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,6 +333,6 @@ public class InsertIntoTableCommand extends Command implements ForwardWithSync,
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitInsertIntoCommand(this, context);
|
||||
return visitor.visitInsertIntoTableCommand(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ public interface CommandVisitor<R, C> {
|
||||
return visitCommand(createPolicy, context);
|
||||
}
|
||||
|
||||
default R visitInsertIntoCommand(InsertIntoTableCommand insertIntoSelectCommand,
|
||||
default R visitInsertIntoTableCommand(InsertIntoTableCommand insertIntoSelectCommand,
|
||||
C context) {
|
||||
return visitCommand(insertIntoSelectCommand, context);
|
||||
}
|
||||
|
||||
@ -664,12 +664,15 @@ public class StmtExecutor {
|
||||
profile.getSummaryProfile().setQueryBeginTime();
|
||||
context.setStmtId(STMT_ID_GENERATOR.incrementAndGet());
|
||||
context.setQueryId(queryId);
|
||||
|
||||
// set isQuery first otherwise this state will be lost if some error occurs
|
||||
if (parsedStmt instanceof QueryStmt) {
|
||||
context.getState().setIsQuery(true);
|
||||
}
|
||||
|
||||
try {
|
||||
// parsedStmt maybe null here, we parse it. Or the predicate will not work.
|
||||
parseByLegacy();
|
||||
if (context.isTxnModel() && !(parsedStmt instanceof InsertStmt)
|
||||
&& !(parsedStmt instanceof TransactionStmt)) {
|
||||
throw new TException("This is in a transaction, only insert, commit, rollback is acceptable.");
|
||||
|
||||
@ -109,6 +109,16 @@ public class ExplainInsertCommandTest extends TestWithFeService {
|
||||
Assertions.assertEquals(4, getOutputFragment(sql).getOutputExprs().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertIntoValues() throws Exception {
|
||||
String sql = "explain insert into t1 values(1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3)";
|
||||
Assertions.assertEquals(4, getOutputFragment(sql).getOutputExprs().size());
|
||||
sql = "explain insert into t2 values(1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3)";
|
||||
Assertions.assertEquals(6, getOutputFragment(sql).getOutputExprs().size());
|
||||
sql = "explain insert into agg_have_dup_base values(-4, -4, -4, 'd')";
|
||||
Assertions.assertEquals(8, getOutputFragment(sql).getOutputExprs().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnalysisException() {
|
||||
String sql = "explain insert into t1(v1, v2) select k2 * 2, v1 + 1, v2 + 4 from src";
|
||||
@ -119,8 +129,8 @@ public class ExplainInsertCommandTest extends TestWithFeService {
|
||||
public void testWithMV() throws Exception {
|
||||
String sql = "explain insert into agg_have_dup_base select -4, -4, -4, 'd'";
|
||||
Assertions.assertEquals(8, getOutputFragment(sql).getOutputExprs().size());
|
||||
String sql1 = "explain insert into agg_have_dup_base select -4, k2, -4, 'd' from agg_have_dup_base";
|
||||
Assertions.assertEquals(8, getOutputFragment(sql1).getOutputExprs().size());
|
||||
sql = "explain insert into agg_have_dup_base select -4, k2, -4, 'd' from agg_have_dup_base";
|
||||
Assertions.assertEquals(8, getOutputFragment(sql).getOutputExprs().size());
|
||||
}
|
||||
|
||||
private PlanFragment getOutputFragment(String sql) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user