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

View File

@ -0,0 +1,51 @@
-- This file is automatically generated. You should know what you did if you want to edit this
-- !sql_cross_join --
1 10 1 1 1.0 2000-01-01 1 10 10 10.0 2000-01-10 1
1 10 1 1 1.0 2000-01-01 1 10 10 10.0 2000-01-10 4
1 10 1 1 1.0 2000-01-01 1 10 10 10.0 2000-01-10 5
1 10 1 1 1.0 2000-01-01 2 20 20 20.0 2000-01-20 1
1 10 1 1 1.0 2000-01-01 2 20 20 20.0 2000-01-20 4
1 10 1 1 1.0 2000-01-01 2 20 20 20.0 2000-01-20 5
1 10 1 1 1.0 2000-01-01 3 30 30 30.0 2000-01-30 1
1 10 1 1 1.0 2000-01-01 3 30 30 30.0 2000-01-30 4
1 10 1 1 1.0 2000-01-01 3 30 30 30.0 2000-01-30 5
1 10 1 1 1.0 2000-01-01 4 4 4 4.0 2000-01-04 1
1 10 1 1 1.0 2000-01-01 4 4 4 4.0 2000-01-04 4
1 10 1 1 1.0 2000-01-01 4 4 4 4.0 2000-01-04 5
1 10 1 1 1.0 2000-01-01 5 5 5 5.0 2000-01-05 1
1 10 1 1 1.0 2000-01-01 5 5 5 5.0 2000-01-05 4
1 10 1 1 1.0 2000-01-01 5 5 5 5.0 2000-01-05 5
2 20 2 2 2.0 2000-01-02 1 10 10 10.0 2000-01-10 1
2 20 2 2 2.0 2000-01-02 1 10 10 10.0 2000-01-10 4
2 20 2 2 2.0 2000-01-02 1 10 10 10.0 2000-01-10 5
2 20 2 2 2.0 2000-01-02 2 20 20 20.0 2000-01-20 1
2 20 2 2 2.0 2000-01-02 2 20 20 20.0 2000-01-20 4
2 20 2 2 2.0 2000-01-02 2 20 20 20.0 2000-01-20 5
2 20 2 2 2.0 2000-01-02 3 30 30 30.0 2000-01-30 1
2 20 2 2 2.0 2000-01-02 3 30 30 30.0 2000-01-30 4
2 20 2 2 2.0 2000-01-02 3 30 30 30.0 2000-01-30 5
2 20 2 2 2.0 2000-01-02 4 4 4 4.0 2000-01-04 1
2 20 2 2 2.0 2000-01-02 4 4 4 4.0 2000-01-04 4
2 20 2 2 2.0 2000-01-02 4 4 4 4.0 2000-01-04 5
2 20 2 2 2.0 2000-01-02 5 5 5 5.0 2000-01-05 1
2 20 2 2 2.0 2000-01-02 5 5 5 5.0 2000-01-05 4
2 20 2 2 2.0 2000-01-02 5 5 5 5.0 2000-01-05 5
3 30 3 3 3.0 2000-01-03 1 10 10 10.0 2000-01-10 1
3 30 3 3 3.0 2000-01-03 1 10 10 10.0 2000-01-10 4
3 30 3 3 3.0 2000-01-03 1 10 10 10.0 2000-01-10 5
3 30 3 3 3.0 2000-01-03 2 20 20 20.0 2000-01-20 1
3 30 3 3 3.0 2000-01-03 2 20 20 20.0 2000-01-20 4
3 30 3 3 3.0 2000-01-03 2 20 20 20.0 2000-01-20 5
3 30 3 3 3.0 2000-01-03 3 30 30 30.0 2000-01-30 1
3 30 3 3 3.0 2000-01-03 3 30 30 30.0 2000-01-30 4
3 30 3 3 3.0 2000-01-03 3 30 30 30.0 2000-01-30 5
3 30 3 3 3.0 2000-01-03 4 4 4 4.0 2000-01-04 1
3 30 3 3 3.0 2000-01-03 4 4 4 4.0 2000-01-04 4
3 30 3 3 3.0 2000-01-03 4 4 4 4.0 2000-01-04 5
3 30 3 3 3.0 2000-01-03 5 5 5 5.0 2000-01-05 1
3 30 3 3 3.0 2000-01-03 5 5 5 5.0 2000-01-05 4
3 30 3 3 3.0 2000-01-03 5 5 5 5.0 2000-01-05 5
-- !mv --
-4 -4 -4 d

View File

@ -208,5 +208,4 @@ suite('complex_insert') {
sql 'insert into agg_have_dup_base select -4, -4, -4, \'d\''
sql 'sync'
qt_mv 'select * from agg_have_dup_base'
}

View File

@ -0,0 +1,118 @@
// 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.
suite('nereids_insert_into_values') {
sql 'set enable_nereids_planner=true'
sql 'set enable_fallback_to_original_planner=false'
sql 'set enable_nereids_dml=true'
sql 'set enable_strict_consistency_dml=true'
sql 'use nereids_insert_into_table_test'
def t1 = 'value_t1'
def t2 = 'value_t2'
def t3 = 'value_t3'
sql "drop table if exists ${t1}"
sql "drop table if exists ${t2}"
sql "drop table if exists ${t3}"
sql """
create table ${t1} (
id int,
id1 int,
c1 bigint,
c2 string,
c3 double,
c4 date
) unique key (id, id1)
distributed by hash(id, id1) buckets 13
properties(
'replication_num'='1',
"function_column.sequence_col" = "c4"
);
"""
sql """
create table ${t2} (
id int,
c1 bigint,
c2 string,
c3 double,
c4 date
) unique key (id)
distributed by hash(id) buckets 13
properties(
'replication_num'='1'
);
"""
sql """
create table ${t3} (
id int
) distributed by hash(id) buckets 13
properties(
'replication_num'='1'
);
"""
sql """
INSERT INTO ${t1} VALUES
(1, (1 + 9) * (10 - 9), 1, '1', 1.0, '2000-01-01'),
(2, 20, 2, '2', 2.0, days_add('2000-01-01', 1)),
(3, 30, 3, '3', 3.0, makedate(2000, 3));
"""
sql """
INSERT INTO ${t2} VALUES
(1, 10, '10', 10.0, '2000-01-10'),
(2, 20, '20', 20.0, '2000-01-20'),
(3, 30, '30', 30.0, '2000-01-30'),
(4, 4, '4', 4.0, '2000-01-04'),
(5, 5, '5', 5.0, '2000-01-05');
"""
sql """
INSERT INTO ${t3} VALUES
(1),
(4),
(5);
"""
sql "sync"
qt_sql_cross_join "select * from ${t1}, ${t2}, ${t3} order by ${t1}.id, ${t1}.id1, ${t2}.id, ${t3}.id"
sql "drop table if exists agg_have_dup_base_value"
sql """
create table agg_have_dup_base_value (
k1 int null,
k2 int not null,
k3 bigint null,
k4 varchar(100) null
)
duplicate key (k1, k2, k3)
distributed by hash(k1) buckets 3
properties("replication_num" = "1");
"""
createMV("create materialized view k12s3m as select k1, sum(k2), max(k2) from agg_have_dup_base_value group by k1;")
sql "insert into agg_have_dup_base_value values (-4, -4, -4, 'd')"
sql "sync"
qt_mv "select * from agg_have_dup_base_value"
}