Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fa12954
feat:support expaln statement for SCDB(#478)
zhuyanhuazhuyanhua Aug 9, 2025
84ea160
Merge branch 'secretflow:main' into main
zhuyanhuazhuyanhua Aug 9, 2025
14410ef
feat:handle explain in runSQL
zhuyanhuazhuyanhua Aug 11, 2025
71b7164
Feat:imply with runSQL
zhuyanhuazhuyanhua Aug 11, 2025
6e00a83
Merge branch 'main' into main
Candicepan Aug 22, 2025
478cab1
Feat:fix and imply in submitAndGet
zhuyanhuazhuyanhua Aug 26, 2025
6183c85
Merge branch 'main' of https://github.com/zhuyanhuazhuyanhua/scql
zhuyanhuazhuyanhua Aug 26, 2025
b92dbc6
Merge branch 'main' into main
Candicepan Aug 28, 2025
eb23b93
Feat:add isexplain test and fix submitandget
zhuyanhuazhuyanhua Aug 29, 2025
a08f5f6
Merge branch 'main' of https://github.com/zhuyanhuazhuyanhua/scql
zhuyanhuazhuyanhua Aug 29, 2025
19c9397
Feat:support select and union state in buildexplain
zhuyanhuazhuyanhua Sep 1, 2025
2a9615b
Feat:imply explain in 3 steps
zhuyanhuazhuyanhua Sep 6, 2025
8f5ee99
bug:fix bug in scdb_api.swagger.yaml
zhuyanhuazhuyanhua Sep 7, 2025
b742b93
Feat:store expalin in OutColumns instead of Status
zhuyanhuazhuyanhua Sep 9, 2025
034c377
Feat:fix SubmitAndGet in api\scdb_api.proto
zhuyanhuazhuyanhua Sep 9, 2025
c7030f3
Feat:imply explain in runSQL
zhuyanhuazhuyanhua Sep 15, 2025
1b24bbf
Feat:imply in runSQL
zhuyanhuazhuyanhua Sep 19, 2025
258d1f3
undo:imply explain in runDQL
zhuyanhuazhuyanhua Sep 25, 2025
8c6f668
Feat:imply in runSQL
zhuyanhuazhuyanhua Sep 28, 2025
9fde404
feat: add test
zhuyanhuazhuyanhua Sep 28, 2025
757e034
feat:add explain test func
zhuyanhuazhuyanhua Sep 29, 2025
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
14 changes: 14 additions & 0 deletions pkg/scdb/executor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func (b *executorBuilder) build(p core.Plan) Executor {
return b.buildShow(v)
case *core.Set:
return b.buildSet(v)
case *core.Explain:
return b.buildExplain(v)
default:
b.err = ErrUnknownPlan.GenWithStack("unsupported Plan %T", p)
return nil
Expand Down Expand Up @@ -150,3 +152,15 @@ func (b *executorBuilder) buildSet(v *core.Set) Executor {
}
return e
}

func (b *executorBuilder) buildExplain(v *core.Explain) Executor {
base := newBaseExecutor(b.ctx, v.Schema(), v.ExplainID())
base.initCap = chunk.ZeroCapacity
e := &ExplainExec{
baseExecutor: base,
TargetPlan: v.TargetPlan,
Format: v.Format,
Analyze: v.Analyze,
}
return e
}
66 changes: 66 additions & 0 deletions pkg/scdb/executor/explain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2023 Ant Group Co., Ltd.

// Copyright 2015 PingCAP, Inc.
//
// Licensed 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,
// See the License for the specific language governing permissions and
// limitations under the License.

// Modified by Ant Group in 2023
package executor

import (
"context"
"fmt"

"github.com/secretflow/scql/pkg/interpreter"
"github.com/secretflow/scql/pkg/planner/core"
"github.com/secretflow/scql/pkg/util/chunk"
)

type ExplainExec struct {
baseExecutor
TargetPlan core.Plan
Format string
Analyze bool
done bool
}

func (e *ExplainExec) Next(ctx context.Context, req *chunk.Chunk) (err error) {
if e.done {
return nil
}
e.done = true
interpreterInstance := interpreter.NewInterpreter()

// 调用解释器编译,这里需要根据 Compile 方法的实际参数进行调整
// 假设 Compile 方法需要的参数可以从上下文或者其他地方获取,以下为示例
// 实际使用时需要根据真实情况补充完整参数
compiledPlan, err := interpreterInstance.Compile(ctx, nil) // 这里的参数需要根据实际情况修改
if err != nil {
return err
}

if compiledPlan.Explain == nil || compiledPlan.Explain.ExeGraphDot == "" {
return nil
}
dotInfo := compiledPlan.Explain.ExeGraphDot

req.Reset()

if req.NumCols() == 0 {
return fmt.Errorf("chunk has no columns")
}

col := req.Column(0)
col.AppendString(dotInfo)

return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The Next method for ExplainExec is currently implemented in a way that will cause a panic. The call interpreterInstance.Compile(ctx, nil) passes a nil request, which will lead to a nil pointer dereference inside the Compile function. The comment // 这里的参数需要根据实际情况修改 also indicates this is incomplete.

Furthermore, due to the special handling of EXPLAIN queries in pkg/scdb/server/submit_and_get_handler.go, this ExplainExec executor appears to be dead code. The query is intercepted and handled by handleExplainQuery before it would ever reach the executor building stage for this type of plan.

This broken and unused code should be removed to avoid confusion and prevent potential bugs if the query handling logic changes in the future. The related changes in pkg/scdb/executor/builder.go that add the build logic for Explain should also be removed along with this file.

38 changes: 38 additions & 0 deletions pkg/scdb/server/submit_and_get_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ func (app *App) submitAndGet(ctx context.Context, req *scql.SCDBQueryRequest) *s
return newErrorSCDBQueryResultResponse(scql.Code_UNAUTHENTICATED, err.Error())
}

if strings.HasPrefix(strings.ToUpper(req.Query), "EXPLAIN") {
return app.handleExplainQuery(ctx, session, req)
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: It is better to handle the EXPLAIN statement within the runSQL function, that will provide a more consistent approach.

isDQL, err := isDQL(req.Query)
if err != nil {
return newErrorSCDBQueryResultResponse(scql.Code_SQL_PARSE_ERROR, err.Error())
Expand All @@ -109,6 +113,40 @@ func (app *App) submitAndGet(ctx context.Context, req *scql.SCDBQueryRequest) *s
return session.result
}

// handleExplainQuery
func (app *App) handleExplainQuery(ctx context.Context, s *session, req *scql.SCDBQueryRequest) *scql.SCDBQueryResultResponse {
compileReq, err := app.buildCompileRequest(ctx, s)
if err != nil {
return newErrorSCDBQueryResultResponse(scql.Code_INTERNAL, err.Error())
}

intrpr := interpreter.NewInterpreter()
compiledPlan, err := intrpr.Compile(ctx, compileReq)
if err != nil {
return newErrorSCDBQueryResultResponse(scql.Code_INTERNAL, err.Error())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for building the compile request and compiling the plan in this function is very similar to the beginning of the runDQL function. To avoid code duplication and improve maintainability, consider extracting this common logic into a separate helper method. This new method could take ctx and session as arguments and return the compiledPlan and an error. Both handleExplainQuery and runDQL could then call this helper method.

For example:

func (app *App) compileQuery(ctx context.Context, s *session) (*pb.CompiledPlan, error) {
    compileReq, err := app.buildCompileRequest(ctx, s)
    if err != nil {
        return nil, err
    }
    intrpr := interpreter.NewInterpreter()
    return intrpr.Compile(ctx, compileReq)
}

Then handleExplainQuery would become:

func (app *App) handleExplainQuery(ctx context.Context, s *session, req *scql.SCDBQueryRequest) *scql.SCDBQueryResultResponse {
	compiledPlan, err := app.compileQuery(ctx, s)
	if err != nil {
		return newErrorSCDBQueryResultResponse(scql.Code_INTERNAL, err.Error())
	}
    // ...
}


if compiledPlan.Explain == nil || compiledPlan.Explain.ExeGraphDot == "" {
return newErrorSCDBQueryResultResponse(scql.Code_INTERNAL, "no explain information available")
}

return &scql.SCDBQueryResultResponse{
Status: &scql.Status{
Code: int32(scql.Code_OK),
Message: "success",
},
OutColumns: []*scql.Tensor{
{
Name: "EXPLAIN",
ElemType: scql.PrimitiveDataType_STRING,
StringData: []string{compiledPlan.Explain.ExeGraphDot},
},
},
ScdbSessionId: s.id,
AffectedRows: 0,
}
}

func (app *App) buildCompileRequest(ctx context.Context, s *session) (*scql.CompileQueryRequest, error) {
issuer := s.GetSessionVars().User
if issuer.Username == storage.DefaultRootName && issuer.Hostname == storage.DefaultHostName {
Expand Down
Loading