github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/sqlmodel/where_handle_test.go (about) 1 // Copyright 2022 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package sqlmodel 15 16 import ( 17 "testing" 18 19 "github.com/pingcap/tidb/pkg/ddl" 20 "github.com/pingcap/tidb/pkg/parser" 21 "github.com/pingcap/tidb/pkg/parser/ast" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func TestGenWhereHandle(t *testing.T) { 26 t.Parallel() 27 28 // 1. target is same as source 29 30 createSQL := ` 31 CREATE TABLE t ( 32 c INT, c2 INT NOT NULL, c3 VARCHAR(20) NOT NULL, 33 UNIQUE INDEX idx3 (c2, c3) 34 )` 35 p := parser.New() 36 node, err := p.ParseOneStmt(createSQL, "", "") 37 require.NoError(t, err) 38 ti, err := ddl.BuildTableInfoFromAST(node.(*ast.CreateTableStmt)) 39 require.NoError(t, err) 40 require.Len(t, ti.Indices, 1) 41 idx := ti.Indices[0] 42 rewritten := rewriteColsOffset(idx, ti) 43 require.Equal(t, idx, rewritten) 44 45 // check GetWhereHandle when target is same as source 46 handle := GetWhereHandle(ti, ti) 47 require.Len(t, handle.UniqueNotNullIdx.Columns, 2) 48 require.Equal(t, handle.UniqueNotNullIdx.Columns[0].Offset, 1) 49 require.Equal(t, handle.UniqueNotNullIdx.Columns[1].Offset, 2) 50 require.Len(t, handle.UniqueIdxs, 1) 51 52 // 2. target has more columns, some index doesn't use it 53 54 targetCreateSQL := ` 55 CREATE TABLE t ( 56 pk INT PRIMARY KEY, c INT, c2 INT NOT NULL, c3 VARCHAR(20) NOT NULL, extra INT, 57 UNIQUE INDEX idx2 (c2, c3), 58 UNIQUE INDEX idx3 (extra) 59 )` 60 node, err = p.ParseOneStmt(targetCreateSQL, "", "") 61 require.NoError(t, err) 62 targetTI, err := ddl.BuildTableInfoFromAST(node.(*ast.CreateTableStmt)) 63 require.NoError(t, err) 64 require.Len(t, targetTI.Indices, 2) 65 targetIdx := targetTI.Indices[0] 66 require.Len(t, targetIdx.Columns, 2) 67 require.Equal(t, targetIdx.Columns[0].Offset, 2) 68 require.Equal(t, targetIdx.Columns[1].Offset, 3) 69 70 rewritten = rewriteColsOffset(targetIdx, ti) 71 require.Len(t, rewritten.Columns, 2) 72 require.Equal(t, rewritten.Columns[0].Offset, 1) 73 require.Equal(t, rewritten.Columns[1].Offset, 2) 74 75 // target has more columns, some index uses it 76 targetIdx = targetTI.Indices[1] 77 require.Len(t, targetIdx.Columns, 1) 78 require.Equal(t, targetIdx.Columns[0].Offset, 4) 79 80 rewritten = rewriteColsOffset(targetIdx, ti) 81 require.Nil(t, rewritten) 82 83 // check GetWhereHandle when target has more columns 84 handle = GetWhereHandle(ti, targetTI) 85 require.Len(t, handle.UniqueNotNullIdx.Columns, 2) 86 require.Equal(t, handle.UniqueNotNullIdx.Columns[0].Offset, 1) 87 require.Equal(t, handle.UniqueNotNullIdx.Columns[1].Offset, 2) 88 // PRIMARY and idx3 is not usable 89 require.Len(t, handle.UniqueIdxs, 1) 90 91 // 3. PKIsHandle case 92 93 targetCreateSQL = ` 94 CREATE TABLE t ( 95 extra INT, c INT PRIMARY KEY 96 )` 97 node, err = p.ParseOneStmt(targetCreateSQL, "", "") 98 require.NoError(t, err) 99 targetTI, err = ddl.BuildTableInfoFromAST(node.(*ast.CreateTableStmt)) 100 require.NoError(t, err) 101 // PKIsHandle has no entry in Indices 102 require.Len(t, targetTI.Indices, 0) 103 104 handle = GetWhereHandle(ti, targetTI) 105 require.Len(t, handle.UniqueNotNullIdx.Columns, 1) 106 require.Equal(t, handle.UniqueNotNullIdx.Columns[0].Offset, 0) 107 require.Len(t, handle.UniqueIdxs, 1) 108 109 // 4. target has no available index 110 111 targetCreateSQL = ` 112 CREATE TABLE t ( 113 extra INT PRIMARY KEY 114 )` 115 node, err = p.ParseOneStmt(targetCreateSQL, "", "") 116 require.NoError(t, err) 117 targetTI, err = ddl.BuildTableInfoFromAST(node.(*ast.CreateTableStmt)) 118 require.NoError(t, err) 119 // PKIsHandle has no entry in Indices 120 require.Len(t, targetTI.Indices, 0) 121 122 handle = GetWhereHandle(ti, targetTI) 123 require.Nil(t, handle.UniqueNotNullIdx) 124 require.Len(t, handle.UniqueIdxs, 0) 125 126 // 5. composite PK, and PK has higher priority 127 128 targetCreateSQL = ` 129 CREATE TABLE t ( 130 extra INT, c INT NOT NULL, c2 INT NOT NULL, c3 VARCHAR(20) NOT NULL, 131 UNIQUE INDEX idx (c, c3), 132 PRIMARY KEY (c, c2), 133 UNIQUE INDEX idx3 (c2, c3) 134 )` 135 node, err = p.ParseOneStmt(targetCreateSQL, "", "") 136 require.NoError(t, err) 137 targetTI, err = ddl.BuildTableInfoFromAST(node.(*ast.CreateTableStmt)) 138 require.NoError(t, err) 139 140 handle = GetWhereHandle(ti, targetTI) 141 require.Len(t, handle.UniqueNotNullIdx.Columns, 2) 142 require.Equal(t, handle.UniqueNotNullIdx.Columns[0].Offset, 0) 143 require.Equal(t, handle.UniqueNotNullIdx.Columns[1].Offset, 1) 144 require.Len(t, handle.UniqueIdxs, 3) 145 } 146 147 func TestAllColsNotNull(t *testing.T) { 148 t.Parallel() 149 150 createSQL := ` 151 CREATE TABLE t ( 152 pk VARCHAR(20) PRIMARY KEY, 153 c1 INT, 154 c2 INT, 155 c3 INT NOT NULL, 156 c4 INT NOT NULL, 157 INDEX idx1 (c1, c2), 158 INDEX idx2 (c2, c3), 159 INDEX idx3 (c3, c4) 160 )` 161 p := parser.New() 162 node, err := p.ParseOneStmt(createSQL, "", "") 163 require.NoError(t, err) 164 ti, err := ddl.BuildTableInfoFromAST(node.(*ast.CreateTableStmt)) 165 require.NoError(t, err) 166 require.Len(t, ti.Indices, 4) 167 168 pk := ti.Indices[3] 169 require.Equal(t, "PRIMARY", pk.Name.O) 170 require.True(t, allColsNotNull(pk, ti.Columns)) 171 172 idx1 := ti.Indices[0] 173 require.Equal(t, "idx1", idx1.Name.O) 174 require.False(t, allColsNotNull(idx1, ti.Columns)) 175 176 idx2 := ti.Indices[1] 177 require.Equal(t, "idx2", idx2.Name.O) 178 require.False(t, allColsNotNull(idx2, ti.Columns)) 179 180 idx3 := ti.Indices[2] 181 require.Equal(t, "idx3", idx3.Name.O) 182 require.True(t, allColsNotNull(idx3, ti.Columns)) 183 } 184 185 func TestGetWhereIdxByData(t *testing.T) { 186 t.Parallel() 187 188 createSQL := ` 189 CREATE TABLE t ( 190 c1 INT, 191 c2 INT, 192 c3 INT, 193 c4 INT, 194 UNIQUE INDEX idx1 (c1, c2), 195 UNIQUE INDEX idx2 (c3, c4) 196 )` 197 p := parser.New() 198 node, err := p.ParseOneStmt(createSQL, "", "") 199 require.NoError(t, err) 200 ti, err := ddl.BuildTableInfoFromAST(node.(*ast.CreateTableStmt)) 201 require.NoError(t, err) 202 203 handle := GetWhereHandle(ti, ti) 204 idx := handle.getWhereIdxByData([]interface{}{nil, 2, 3, 4}) 205 require.Equal(t, "idx2", idx.Name.L) 206 require.Equal(t, idx, handle.UniqueIdxs[0]) 207 208 // last used index is moved to front 209 idx = handle.getWhereIdxByData([]interface{}{1, 2, 3, nil}) 210 require.Equal(t, "idx1", idx.Name.L) 211 require.Equal(t, idx, handle.UniqueIdxs[0]) 212 213 // no index available 214 idx = handle.getWhereIdxByData([]interface{}{1, nil, 3, nil}) 215 require.Nil(t, idx) 216 }