Skip to content

Commit 6dba7af

Browse files
committed
New analyzer rule to apply implicit prefix lengths for TEXT columns in secondary indexes
1 parent 54bd6d6 commit 6dba7af

File tree

4 files changed

+124
-2
lines changed

4 files changed

+124
-2
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright 2024 Dolthub, 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+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package analyzer
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/dolthub/go-mysql-server/sql"
21+
"github.com/dolthub/go-mysql-server/sql/plan"
22+
"github.com/dolthub/go-mysql-server/sql/transform"
23+
"github.com/dolthub/go-mysql-server/sql/types"
24+
)
25+
26+
// defaultIndexPrefixLength is the index prefix length that this analyzer rule applies automatically to TEXT columns
27+
// in secondary indexes.
28+
const defaultIndexPrefixLength = 255
29+
30+
// AddImplicitPrefixLengths searches the |node| tree for any nodes creating an index, and plugs in a default index
31+
// prefix length for any TEXT columns in those new indexes. This rule is intended to be used for Postgres compatibility,
32+
// since Postgres does not require specifying prefix lengths for TEXT columns.
33+
func AddImplicitPrefixLengths(_ *sql.Context, _ *Analyzer, node sql.Node, _ *plan.Scope, _ RuleSelector, _ *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
34+
var initialSchema, targetSchema sql.Schema
35+
transform.Inspect(node, func(node sql.Node) bool {
36+
if st, ok := node.(sql.SchemaTarget); ok {
37+
targetSchema = st.TargetSchema()
38+
initialSchema = targetSchema.Copy()
39+
return false
40+
}
41+
return true
42+
})
43+
44+
// Recurse through the node tree to fill in prefix lengths. Note that some statements come in as Block nodes
45+
// that contain multiple nodes, so we need to recurse through and handle all of them.
46+
return transform.Node(node, func(node sql.Node) (sql.Node, transform.TreeIdentity, error) {
47+
switch node := node.(type) {
48+
case *plan.AddColumn:
49+
// For any AddColumn nodes, we need to update the target schema with the column being added, otherwise
50+
// we won't be able to find those columns if they are also being added to a secondary index.
51+
var err error
52+
targetSchema, err = validateAddColumn(initialSchema, targetSchema, node)
53+
if err != nil {
54+
return nil, transform.SameTree, err
55+
}
56+
57+
case *plan.CreateTable:
58+
newIndexes := make([]*sql.IndexDef, len(node.Indexes()))
59+
for i := range node.Indexes() {
60+
copy := *node.Indexes()[i]
61+
newIndexes[i] = &copy
62+
}
63+
indexModified := false
64+
for _, index := range newIndexes {
65+
targetSchema := node.TargetSchema()
66+
colMap := schToColMap(targetSchema)
67+
68+
for i, _ := range index.Columns {
69+
col, ok := colMap[index.Columns[i].Name]
70+
if !ok {
71+
return nil, false, fmt.Errorf("indexed column %s not found in schema", index.Columns[i].Name)
72+
}
73+
if types.IsText(col.Type) && index.Columns[i].Length == 0 {
74+
index.Columns[i].Length = defaultIndexPrefixLength
75+
indexModified = true
76+
}
77+
}
78+
}
79+
if indexModified {
80+
newNode, err := node.WithIndexDefs(newIndexes)
81+
return newNode, transform.NewTree, err
82+
}
83+
84+
case *plan.AlterIndex:
85+
if node.Action == plan.IndexAction_Create {
86+
colMap := schToColMap(targetSchema)
87+
newColumns := make([]sql.IndexColumn, len(node.Columns))
88+
for i := range node.Columns {
89+
copy := node.Columns[i]
90+
newColumns[i] = copy
91+
}
92+
indexModified := false
93+
for i, _ := range newColumns {
94+
col, ok := colMap[newColumns[i].Name]
95+
if !ok {
96+
return nil, false, fmt.Errorf("indexed column %s not found in schema", newColumns[i].Name)
97+
}
98+
if types.IsText(col.Type) && newColumns[i].Length == 0 {
99+
newColumns[i].Length = defaultIndexPrefixLength
100+
indexModified = true
101+
}
102+
}
103+
if indexModified {
104+
newNode, err := node.WithColumns(newColumns)
105+
return newNode, transform.NewTree, err
106+
}
107+
}
108+
}
109+
return node, transform.SameTree, nil
110+
})
111+
}

sql/analyzer/validate_create_table.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,8 +828,6 @@ func validateAutoIncrementAdd(schema sql.Schema, keyColumns map[string]bool) err
828828
return nil
829829
}
830830

831-
const textIndexPrefix = 1000
832-
833831
func schToColMap(sch sql.Schema) map[string]*sql.Column {
834832
colMap := make(map[string]*sql.Column, len(sch))
835833
for _, col := range sch {

sql/plan/alter_index.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ func (p AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) {
138138
}
139139
}
140140

141+
func (p AlterIndex) WithColumns(columns []sql.IndexColumn) (sql.Node, error) {
142+
p.Columns = columns
143+
return &p, nil
144+
}
145+
141146
func (p AlterIndex) WithTargetSchema(schema sql.Schema) (sql.Node, error) {
142147
p.targetSchema = schema
143148
return &p, nil

sql/plan/ddl.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ func (c *CreateTable) WithDatabase(db sql.Database) (sql.Node, error) {
133133
return &nc, nil
134134
}
135135

136+
// WithIndexDefs returns a copy of this CreateTable instance, with the index definitions
137+
// set to |idxDefs|.
138+
func (c *CreateTable) WithIndexDefs(idxDefs sql.IndexDefs) (*CreateTable, error) {
139+
nc := *c
140+
nc.idxDefs = idxDefs
141+
return &nc, nil
142+
}
143+
136144
// Name implements the Nameable interface.
137145
func (c *CreateTable) Name() string {
138146
return c.name

0 commit comments

Comments
 (0)