[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:
mch_ucchi
2023-09-15 21:46:37 +08:00
committed by GitHub
parent 5b43969e35
commit ba4c738ac7
10 changed files with 253 additions and 12 deletions

View File

@ -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

View File

@ -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);

View File

@ -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());
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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.");

View File

@ -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 {