morrySnow commented on code in PR #39627:
URL: https://github.com/apache/doris/pull/39627#discussion_r1738049286
##########
fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/TableQueryPlanAction.java:
##########
@@ -163,65 +170,78 @@ public Object query_plan(
*/
private void handleQuery(ConnectContext context, String requestDb, String
requestTable, String sql,
Map<String, Object> result) throws DorisHttpException {
- // use SE to resolve sql
- StmtExecutor stmtExecutor = new StmtExecutor(context, new
OriginStatement(sql, 0), false);
+ List<StatementBase> stmts = null;
try {
- TQueryOptions tQueryOptions =
context.getSessionVariable().toThrift();
- // Conduct Planner create SingleNodePlan#createPlanFragments
- tQueryOptions.num_nodes = 1;
- // analyze sql
- stmtExecutor.analyze(tQueryOptions);
+ stmts = new NereidsParser().parseSQL(sql,
context.getSessionVariable());
} catch (Exception e) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
e.getMessage());
}
// the parsed logical statement
- StatementBase query = stmtExecutor.getParsedStmt();
- // only process select semantic
- if (!(query instanceof SelectStmt)) {
+ StatementBase query = stmts.get(0);
+ if (!(query instanceof LogicalPlanAdapter)) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
"Select statement needed, but found [" + sql + " ]");
}
- SelectStmt stmt = (SelectStmt) query;
- // just only process sql like `select * from table where <predicate>`,
only support executing scan semantic
- if (stmt.hasAggInfo() || stmt.hasAnalyticInfo()
- || stmt.hasOrderByClause() || stmt.hasOffset() ||
stmt.hasLimit() || stmt.isExplain()) {
+ LogicalPlan parsedPlan = ((LogicalPlanAdapter) query).getLogicalPlan();
+ // only process select semantic
+ if (parsedPlan instanceof Command) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
- "only support single table filter-prune-scan, but found [
" + sql + "]");
+ "Select statement needed, but found [" + sql + " ]");
}
- // process only one table by one http query
- List<TableRef> fromTables = stmt.getTableRefs();
- if (fromTables.size() != 1) {
+
+ if
(!parsedPlan.collectToList(LogicalSubQueryAlias.class::isInstance).isEmpty()) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
- "Select statement must have only one table");
+ "Select statement must not embed another statement");
}
- TableRef fromTable = fromTables.get(0);
- if (fromTable instanceof InlineViewRef) {
+ List<UnboundRelation> unboundRelations =
parsedPlan.collectToList(UnboundRelation.class::isInstance);
+ if (unboundRelations.size() != 1) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
- "Select statement must not embed another statement");
+ "Select statement must have only one table");
}
+
// check consistent http requested resource with sql referenced
// if consistent in this way, can avoid check privilege
- TableName tableAndDb = fromTables.get(0).getName();
- int lower = GlobalVariable.lowerCaseTableNames;
- //Determine whether table names are case-sensitive
- if (lower == 0) {
- if (!(tableAndDb.getDb().equals(requestDb) &&
tableAndDb.getTbl().equals(requestTable))) {
+ List<String> tableQualifier = RelationUtil.getQualifierName(context,
+ unboundRelations.get(0).getNameParts());
+ if (tableQualifier.size() != 3) {
+ throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
+ "can't find table " + String.join(",", tableQualifier));
+ }
+ String dbName = tableQualifier.get(1);
+ String tableName = tableQualifier.get(2);
+
+ if (GlobalVariable.lowerCaseTableNames == 0) {
+ if (!(dbName.equals(requestDb) && tableName.equals(requestTable)))
{
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
"requested database and table must consistent with
sql: request [ "
- + requestDb + "." + requestTable + "]" + "and
sql [" + tableAndDb.toString() + "]");
+ + requestDb + "." + requestTable + "]" + "and
sql [" + dbName + "." + tableName + "]");
}
} else {
- if (!(tableAndDb.getDb().equalsIgnoreCase(requestDb)
- && tableAndDb.getTbl().equalsIgnoreCase(requestTable))) {
+ if (!(dbName.equalsIgnoreCase(requestDb)
+ && tableName.equalsIgnoreCase(requestTable))) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
"requested database and table must consistent with
sql: request [ "
- + requestDb + "." + requestTable + "]" + "and
sql [" + tableAndDb.toString() + "]");
+ + requestDb + "." + requestTable + "]" + "and
sql [" + dbName + "." + tableName + "]");
}
}
+ NereidsPlanner nereidsPlanner = new
NereidsPlanner(context.getStatementContext());
+ LogicalPlan rewrittenPlan = (LogicalPlan)
nereidsPlanner.planWithLock(parsedPlan,
+ PhysicalProperties.GATHER,
ExplainCommand.ExplainLevel.REWRITTEN_PLAN);
+ if (rewrittenPlan.anyMatch(planTreeNode -> planTreeNode instanceof
LogicalAggregate
+ || planTreeNode instanceof LogicalWindow || planTreeNode
instanceof LogicalSort
+ || planTreeNode instanceof LogicalTopN || planTreeNode
instanceof LogicalLimit)) {
+ throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
+ "only support single table filter-prune-scan, but found [
" + sql + "]");
+ }
Review Comment:
black list is not a good idea, because we may add new node type.
use white list, check only have
- unbound relation
- filter
- project
- result sink
##########
fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/TableQueryPlanAction.java:
##########
@@ -163,65 +170,78 @@ public Object query_plan(
*/
private void handleQuery(ConnectContext context, String requestDb, String
requestTable, String sql,
Map<String, Object> result) throws DorisHttpException {
- // use SE to resolve sql
- StmtExecutor stmtExecutor = new StmtExecutor(context, new
OriginStatement(sql, 0), false);
+ List<StatementBase> stmts = null;
try {
- TQueryOptions tQueryOptions =
context.getSessionVariable().toThrift();
- // Conduct Planner create SingleNodePlan#createPlanFragments
- tQueryOptions.num_nodes = 1;
- // analyze sql
- stmtExecutor.analyze(tQueryOptions);
+ stmts = new NereidsParser().parseSQL(sql,
context.getSessionVariable());
} catch (Exception e) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
e.getMessage());
}
// the parsed logical statement
- StatementBase query = stmtExecutor.getParsedStmt();
- // only process select semantic
- if (!(query instanceof SelectStmt)) {
+ StatementBase query = stmts.get(0);
+ if (!(query instanceof LogicalPlanAdapter)) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
"Select statement needed, but found [" + sql + " ]");
}
- SelectStmt stmt = (SelectStmt) query;
- // just only process sql like `select * from table where <predicate>`,
only support executing scan semantic
- if (stmt.hasAggInfo() || stmt.hasAnalyticInfo()
- || stmt.hasOrderByClause() || stmt.hasOffset() ||
stmt.hasLimit() || stmt.isExplain()) {
+ LogicalPlan parsedPlan = ((LogicalPlanAdapter) query).getLogicalPlan();
+ // only process select semantic
+ if (parsedPlan instanceof Command) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
- "only support single table filter-prune-scan, but found [
" + sql + "]");
+ "Select statement needed, but found [" + sql + " ]");
}
- // process only one table by one http query
- List<TableRef> fromTables = stmt.getTableRefs();
- if (fromTables.size() != 1) {
+
+ if
(!parsedPlan.collectToList(LogicalSubQueryAlias.class::isInstance).isEmpty()) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
- "Select statement must have only one table");
+ "Select statement must not embed another statement");
}
- TableRef fromTable = fromTables.get(0);
- if (fromTable instanceof InlineViewRef) {
+ List<UnboundRelation> unboundRelations =
parsedPlan.collectToList(UnboundRelation.class::isInstance);
+ if (unboundRelations.size() != 1) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
- "Select statement must not embed another statement");
+ "Select statement must have only one table");
}
+
// check consistent http requested resource with sql referenced
// if consistent in this way, can avoid check privilege
- TableName tableAndDb = fromTables.get(0).getName();
- int lower = GlobalVariable.lowerCaseTableNames;
- //Determine whether table names are case-sensitive
- if (lower == 0) {
- if (!(tableAndDb.getDb().equals(requestDb) &&
tableAndDb.getTbl().equals(requestTable))) {
+ List<String> tableQualifier = RelationUtil.getQualifierName(context,
+ unboundRelations.get(0).getNameParts());
+ if (tableQualifier.size() != 3) {
+ throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
+ "can't find table " + String.join(",", tableQualifier));
+ }
+ String dbName = tableQualifier.get(1);
+ String tableName = tableQualifier.get(2);
+
+ if (GlobalVariable.lowerCaseTableNames == 0) {
+ if (!(dbName.equals(requestDb) && tableName.equals(requestTable)))
{
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
"requested database and table must consistent with
sql: request [ "
- + requestDb + "." + requestTable + "]" + "and
sql [" + tableAndDb.toString() + "]");
+ + requestDb + "." + requestTable + "]" + "and
sql [" + dbName + "." + tableName + "]");
}
} else {
- if (!(tableAndDb.getDb().equalsIgnoreCase(requestDb)
- && tableAndDb.getTbl().equalsIgnoreCase(requestTable))) {
+ if (!(dbName.equalsIgnoreCase(requestDb)
+ && tableName.equalsIgnoreCase(requestTable))) {
throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
"requested database and table must consistent with
sql: request [ "
- + requestDb + "." + requestTable + "]" + "and
sql [" + tableAndDb.toString() + "]");
+ + requestDb + "." + requestTable + "]" + "and
sql [" + dbName + "." + tableName + "]");
}
}
+ NereidsPlanner nereidsPlanner = new
NereidsPlanner(context.getStatementContext());
+ LogicalPlan rewrittenPlan = (LogicalPlan)
nereidsPlanner.planWithLock(parsedPlan,
+ PhysicalProperties.GATHER,
ExplainCommand.ExplainLevel.REWRITTEN_PLAN);
+ if (rewrittenPlan.anyMatch(planTreeNode -> planTreeNode instanceof
LogicalAggregate
+ || planTreeNode instanceof LogicalWindow || planTreeNode
instanceof LogicalSort
+ || planTreeNode instanceof LogicalTopN || planTreeNode
instanceof LogicalLimit)) {
+ throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
+ "only support single table filter-prune-scan, but found [
" + sql + "]");
+ }
+ NereidsPlanner planner = new
NereidsPlanner(context.getStatementContext());
+ try {
+ planner.plan(query, context.getSessionVariable().toThrift());
+ } catch (Exception ex) {
+ throw new DorisHttpException(HttpResponseStatus.BAD_REQUEST,
+ "only support single table filter-prune-scan, but found [
" + sql + "]");
+ }
- // acquired Planner to get PlanNode and fragment templates
- Planner planner = stmtExecutor.planner();
Review Comment:
we need to forbid some rules to avoid generate empty relation
- PRUNE_EMPTY_PARTITION
- ELIMINATE_FILTER
##########
fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/TableQueryPlanAction.java:
##########
@@ -163,65 +170,78 @@ public Object query_plan(
*/
private void handleQuery(ConnectContext context, String requestDb, String
requestTable, String sql,
Map<String, Object> result) throws DorisHttpException {
- // use SE to resolve sql
- StmtExecutor stmtExecutor = new StmtExecutor(context, new
OriginStatement(sql, 0), false);
+ List<StatementBase> stmts = null;
try {
- TQueryOptions tQueryOptions =
context.getSessionVariable().toThrift();
- // Conduct Planner create SingleNodePlan#createPlanFragments
- tQueryOptions.num_nodes = 1;
Review Comment:
this is used to ensure generate one fragment plan. and will be check
fragment num later. so in nereids, we should set enable_parallel_result_sink =
true
--
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]