Skip to content

Commit

Permalink
sqlmodel(*): optimize O(N*N) generated column check to O(N) (#10314)
Browse files Browse the repository at this point in the history
close #10313
  • Loading branch information
zhangjinpeng87 authored Dec 18, 2023
1 parent 111a0a0 commit 18c21b2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 8 deletions.
13 changes: 10 additions & 3 deletions pkg/sqlmodel/multirow.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,16 @@ func GenUpdateSQL(changes ...*RowChange) (string, []any) {
whenCaseStmts[i] = whereBuf.String()
}

// Build gegerated columns lower name set to accelerate the following check
targetGeneratedColSet := generatedColumnsNameSet(first.targetTableInfo.Columns)

// Generate `ColumnName`=CASE WHEN .. THEN .. END
// Use this value in order to identify which is the first CaseWhenThen line,
// because generated column can happen any where and it will be skipped.
isFirstCaseWhenThenLine := true
for _, column := range first.targetTableInfo.Columns {
if isGenerated(first.targetTableInfo.Columns, column.Name) {
// skip generated columns
if _, ok := targetGeneratedColSet[column.Name.L]; ok {
continue
}
if !isFirstCaseWhenThenLine {
Expand Down Expand Up @@ -166,7 +170,7 @@ func GenUpdateSQL(changes ...*RowChange) (string, []any) {
var assignValueColumnCount int
var skipColIdx []int
for i, col := range first.sourceTableInfo.Columns {
if isGenerated(first.targetTableInfo.Columns, col.Name) {
if _, ok := targetGeneratedColSet[col.Name.L]; ok {
skipColIdx = append(skipColIdx, i)
continue
}
Expand Down Expand Up @@ -235,8 +239,11 @@ func GenInsertSQL(tp DMLType, changes ...*RowChange) (string, []interface{}) {
buf.WriteString(" (")
columnNum := 0
var skipColIdx []int

// build gegerated columns lower name set to accelerate the following check
generatedColumns := generatedColumnsNameSet(first.targetTableInfo.Columns)
for i, col := range first.sourceTableInfo.Columns {
if isGenerated(first.targetTableInfo.Columns, col.Name) {
if _, ok := generatedColumns[col.Name.L]; ok {
skipColIdx = append(skipColIdx, i)
continue
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/sqlmodel/row_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,12 @@ func (r *RowChange) genUpdateSQL() (string, []interface{}) {
buf.WriteString(r.targetTable.QuoteString())
buf.WriteString(" SET ")

// Build target generated columns lower names set to accelerate following check
generatedColumns := generatedColumnsNameSet(r.targetTableInfo.Columns)
args := make([]interface{}, 0, len(r.preValues)+len(r.postValues))
writtenFirstCol := false
for i, col := range r.sourceTableInfo.Columns {
if isGenerated(r.targetTableInfo.Columns, col.Name) {
if _, ok := generatedColumns[col.Name.L]; ok {
continue
}

Expand Down
10 changes: 6 additions & 4 deletions pkg/sqlmodel/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ func valuesHolder(n int) string {
return builder.String()
}

func isGenerated(columns []*timodel.ColumnInfo, name timodel.CIStr) bool {
// generatedColumnsNameSet returns a set of generated columns' name.
func generatedColumnsNameSet(columns []*timodel.ColumnInfo) map[string]struct{} {
m := make(map[string]struct{})
for _, col := range columns {
if col.Name.L == name.L {
return col.IsGenerated()
if col.IsGenerated() {
m[col.Name.L] = struct{}{}
}
}
return false
return m
}

// ColValAsStr convert column value as string
Expand Down
30 changes: 30 additions & 0 deletions pkg/sqlmodel/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package sqlmodel
import (
"testing"

timodel "github.com/pingcap/tidb/pkg/parser/model"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/require"
)
Expand All @@ -40,3 +41,32 @@ func TestValidatorGenColData(t *testing.T) {
res = ColValAsStr(decimal.NewFromInt(222123123))
require.Equal(t, "222123123", res)
}

func TestGeneratedColumnsNameSet(t *testing.T) {
t.Parallel()

cols := []*timodel.ColumnInfo{
{
Name: timodel.CIStr{O: "A", L: "a"},
GeneratedExprString: "generated_expr",
},
{
Name: timodel.CIStr{O: "B", L: "b"},
},
{
Name: timodel.CIStr{O: "C", L: "c"},
GeneratedExprString: "generated_expr",
},
{
Name: timodel.CIStr{O: "D", L: "d"},
GeneratedExprString: "generated_expr",
},
}

m := generatedColumnsNameSet(cols)
require.Equal(t, map[string]struct{}{
"a": {},
"c": {},
"d": {},
}, m)
}

0 comments on commit 18c21b2

Please sign in to comment.