diff --git a/result_set.go b/result_set.go index 6588bcd5..3196df2b 100644 --- a/result_set.go +++ b/result_set.go @@ -1252,6 +1252,65 @@ func (res ResultSet) MakeDotGraphByStruct() string { return builder.String() } +// generate profiling data for both Row and TCK formats. +func MakeProfilingData(planNodeDesc *graph.PlanNodeDescription, isTckFmt bool) string { + var profileArr []string + for i, profile := range planNodeDesc.GetProfiles() { + var statArr []string + statArr = append(statArr, fmt.Sprintf("\"version\":%d", i)) + statArr = append(statArr, fmt.Sprintf("\"rows\":%d", profile.GetRows())) + if !isTckFmt { + // tck format doesn't need these fields + statArr = append(statArr, fmt.Sprintf("\"execTime\":\"%d(us)\"", profile.GetExecDurationInUs())) + statArr = append(statArr, fmt.Sprintf("\"totalTime\":\"%d(us)\"", profile.GetTotalDurationInUs())) + } + for k, v := range profile.GetOtherStats() { + s := string(v) + if matched, err := regexp.Match(`^[^{(\[]\w+`, v); err == nil && matched { + if !strings.HasPrefix(s, "\"") { + s = fmt.Sprintf("\"%s", s) + } + if !strings.HasSuffix(s, "\"") { + s = fmt.Sprintf("%s\"", s) + } + } + statArr = append(statArr, fmt.Sprintf("\"%s\": %s", k, s)) + } + sort.Strings(statArr) + statStr := fmt.Sprintf("{%s}", strings.Join(statArr, ",\n")) + profileArr = append(profileArr, statStr) + } + allProfiles := strings.Join(profileArr, ",\n") + if len(profileArr) > 1 { + allProfiles = fmt.Sprintf("[%s]", allProfiles) + } + var buffer bytes.Buffer + json.Indent(&buffer, []byte(allProfiles), "", " ") + return string(buffer.Bytes()) +} + +// generate operator info for Row format. +func MakeOperatorInfo(planNodeDesc *graph.PlanNodeDescription) string { + var columnInfo []string + if planNodeDesc.IsSetBranchInfo() { + branchInfo := planNodeDesc.GetBranchInfo() + columnInfo = append(columnInfo, fmt.Sprintf("branch: %t, nodeId: %d\n", + branchInfo.GetIsDoBranch(), branchInfo.GetConditionNodeID())) + } + + outputVar := fmt.Sprintf("outputVar: %s", prettyFormatJsonString(planNodeDesc.GetOutputVar())) + columnInfo = append(columnInfo, outputVar) + + if planNodeDesc.IsSetDescription() { + desc := planNodeDesc.GetDescription() + for _, pair := range desc { + value := prettyFormatJsonString(pair.GetValue()) + columnInfo = append(columnInfo, fmt.Sprintf("%s: %s", string(pair.GetKey()), value)) + } + } + return strings.Join(columnInfo, "\n") +} + // explain/profile format="row" func (res ResultSet) MakePlanByRow() [][]interface{} { p := res.GetPlanDesc() @@ -1272,58 +1331,48 @@ func (res ResultSet) MakePlanByRow() [][]interface{} { } if planNodeDesc.IsSetProfiles() { - var profileArr []string - for i, profile := range planNodeDesc.GetProfiles() { - var statArr []string - statArr = append(statArr, fmt.Sprintf("\"version\":%d", i)) - statArr = append(statArr, fmt.Sprintf("\"rows\":%d", profile.GetRows())) - statArr = append(statArr, fmt.Sprintf("\"execTime\":\"%d(us)\"", profile.GetExecDurationInUs())) - statArr = append(statArr, fmt.Sprintf("\"totalTime\":\"%d(us)\"", profile.GetTotalDurationInUs())) - for k, v := range profile.GetOtherStats() { - s := string(v) - if matched, err := regexp.Match(`^[^{(\[]\w+`, v); err == nil && matched { - if !strings.HasPrefix(s, "\"") { - s = fmt.Sprintf("\"%s", s) - } - if !strings.HasSuffix(s, "\"") { - s = fmt.Sprintf("%s\"", s) - } - } - statArr = append(statArr, fmt.Sprintf("\"%s\": %s", k, s)) - } - sort.Strings(statArr) - statStr := fmt.Sprintf("{%s}", strings.Join(statArr, ",\n")) - profileArr = append(profileArr, statStr) - } - allProfiles := strings.Join(profileArr, ",\n") - if len(profileArr) > 1 { - allProfiles = fmt.Sprintf("[%s]", allProfiles) - } - var buffer bytes.Buffer - json.Indent(&buffer, []byte(allProfiles), "", " ") - row = append(row, string(buffer.Bytes())) + row = append(row, MakeProfilingData(planNodeDesc, false)) } else { row = append(row, "") } + aaa := MakeOperatorInfo(planNodeDesc) + fmt.Println(aaa) + row = append(row, aaa) + rows = append(rows, row) + } + return rows +} - var columnInfo []string - if planNodeDesc.IsSetBranchInfo() { - branchInfo := planNodeDesc.GetBranchInfo() - columnInfo = append(columnInfo, fmt.Sprintf("branch: %t, nodeId: %d\n", - branchInfo.GetIsDoBranch(), branchInfo.GetConditionNodeID())) - } - - outputVar := fmt.Sprintf("outputVar: %s", prettyFormatJsonString(planNodeDesc.GetOutputVar())) - columnInfo = append(columnInfo, outputVar) +// explain/profile format="tck" +func (res ResultSet) MakePlanByTck() [][]interface{} { + p := res.GetPlanDesc() + planNodeDescs := p.GetPlanNodeDescs() + var rows [][]interface{} + for _, planNodeDesc := range planNodeDescs { + var row []interface{} + row = append(row, planNodeDesc.GetId(), string(planNodeDesc.GetName())) - if planNodeDesc.IsSetDescription() { - desc := planNodeDesc.GetDescription() - for _, pair := range desc { - value := prettyFormatJsonString(pair.GetValue()) - columnInfo = append(columnInfo, fmt.Sprintf("%s: %s", string(pair.GetKey()), value)) + if planNodeDesc.IsSetDependencies() { + var deps []string + for _, dep := range planNodeDesc.GetDependencies() { + deps = append(deps, fmt.Sprintf("%d", dep)) } + row = append(row, strings.Join(deps, ",")) + } else { + row = append(row, "") } - row = append(row, strings.Join(columnInfo, "\n")) + + if planNodeDesc.IsSetProfiles() { + var compactProfilingData bytes.Buffer + // compress JSON data and remove whitespace characters + json.Compact(&compactProfilingData, []byte(MakeProfilingData(planNodeDesc, true))) + row = append(row, compactProfilingData.String()) + } else { + row = append(row, "") + } + // append operator info + row = append(row, "") + rows = append(rows, row) } return rows