Merge pull request #4722 from dolthub/james/prefix-index

implementing prefix indexes for string types
This commit is contained in:
James Cor
2022-11-11 13:32:30 -08:00
committed by GitHub
31 changed files with 718 additions and 130 deletions

View File

@@ -649,7 +649,33 @@ func (rcv *Index) MutateSystemDefined(n bool) bool {
return rcv._tab.MutateBoolSlot(18, n)
}
const IndexNumFields = 8
func (rcv *Index) PrefixLengths(j int) uint16 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(20))
if o != 0 {
a := rcv._tab.Vector(o)
return rcv._tab.GetUint16(a + flatbuffers.UOffsetT(j*2))
}
return 0
}
func (rcv *Index) PrefixLengthsLength() int {
o := flatbuffers.UOffsetT(rcv._tab.Offset(20))
if o != 0 {
return rcv._tab.VectorLen(o)
}
return 0
}
func (rcv *Index) MutatePrefixLengths(j int, n uint16) bool {
o := flatbuffers.UOffsetT(rcv._tab.Offset(20))
if o != 0 {
a := rcv._tab.Vector(o)
return rcv._tab.MutateUint16(a+flatbuffers.UOffsetT(j*2), n)
}
return false
}
const IndexNumFields = 9
func IndexStart(builder *flatbuffers.Builder) {
builder.StartObject(IndexNumFields)
@@ -687,6 +713,12 @@ func IndexAddUniqueKey(builder *flatbuffers.Builder, uniqueKey bool) {
func IndexAddSystemDefined(builder *flatbuffers.Builder, systemDefined bool) {
builder.PrependBoolSlot(7, systemDefined, false)
}
func IndexAddPrefixLengths(builder *flatbuffers.Builder, prefixLengths flatbuffers.UOffsetT) {
builder.PrependUOffsetTSlot(8, flatbuffers.UOffsetT(prefixLengths), 0)
}
func IndexStartPrefixLengthsVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT {
return builder.StartVector(2, numElems, 2)
}
func IndexEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
return builder.EndObject()
}

View File

@@ -58,7 +58,7 @@ require (
github.com/cenkalti/backoff/v4 v4.1.3
github.com/cespare/xxhash v1.1.0
github.com/creasty/defaults v1.6.0
github.com/dolthub/go-mysql-server v0.14.1-0.20221110233726-0dafee9b8f80
github.com/dolthub/go-mysql-server v0.14.1-0.20221111192934-cf0c1818d579
github.com/google/flatbuffers v2.0.6+incompatible
github.com/kch42/buzhash v0.0.0-20160816060738-9bdec3dec7c6
github.com/mitchellh/go-ps v1.0.0

View File

@@ -180,8 +180,8 @@ github.com/dolthub/flatbuffers v1.13.0-dh.1 h1:OWJdaPep22N52O/0xsUevxJ6Qfw1M2txC
github.com/dolthub/flatbuffers v1.13.0-dh.1/go.mod h1:CorYGaDmXjHz1Z7i50PYXG1Ricn31GcA2wNOTFIQAKE=
github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U=
github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0=
github.com/dolthub/go-mysql-server v0.14.1-0.20221110233726-0dafee9b8f80 h1:38XwYasYADK4SO+rxwmTD2hE8zngczzKSNo2XmYFe0Q=
github.com/dolthub/go-mysql-server v0.14.1-0.20221110233726-0dafee9b8f80/go.mod h1:KtpU4Sf7J+SIat/nxoA733QTn3tdL34NtoGxEBFcTsA=
github.com/dolthub/go-mysql-server v0.14.1-0.20221111192934-cf0c1818d579 h1:rOV6whqkxka2wGMGD/DOgUgX0jWw/gaJwTMqJ1ye2wA=
github.com/dolthub/go-mysql-server v0.14.1-0.20221111192934-cf0c1818d579/go.mod h1:KtpU4Sf7J+SIat/nxoA733QTn3tdL34NtoGxEBFcTsA=
github.com/dolthub/ishell v0.0.0-20220112232610-14e753f0f371 h1:oyPHJlzumKta1vnOQqUnfdz+pk3EmnHS3Nd0cCT0I2g=
github.com/dolthub/ishell v0.0.0-20220112232610-14e753f0f371/go.mod h1:dhGBqcCEfK5kuFmeO5+WOx3hqc1k3M29c1oS/R7N4ms=
github.com/dolthub/jsonpath v0.0.0-20210609232853-d49537a30474 h1:xTrR+l5l+1Lfq0NvhiEsctylXinUMFhhsqaEcl414p8=

View File

@@ -64,9 +64,9 @@ func createTestSchema(t *testing.T) schema.Schema {
)
sch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexName, []uint64{firstTag, lastTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexName, []uint64{firstTag, lastTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexAge, []uint64{ageTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexAge, []uint64{ageTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
return sch
}

View File

@@ -55,7 +55,7 @@ func Schema() (schema.Schema, error) {
)
sch := schema.MustSchemaFromCols(typedColColl)
_, err := sch.Indexes().AddIndexByColTags(IndexName, []uint64{NameTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
_, err := sch.Indexes().AddIndexByColTags(IndexName, []uint64{NameTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
if err != nil {
return nil, err
}

View File

@@ -62,8 +62,8 @@ var indexSchema schema.Index
var compositeIndexSchema schema.Index
func init() {
indexSchema, _ = sch.Indexes().AddIndexByColTags("idx_col1", []uint64{col1Tag}, schema.IndexProperties{IsUnique: false, Comment: ""})
compositeIndexSchema, _ = sch.Indexes().AddIndexByColTags("idx_col1_col2", []uint64{col1Tag, col2Tag}, schema.IndexProperties{IsUnique: false, Comment: ""})
indexSchema, _ = sch.Indexes().AddIndexByColTags("idx_col1", []uint64{col1Tag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
compositeIndexSchema, _ = sch.Indexes().AddIndexByColTags("idx_col1_col2", []uint64{col1Tag, col2Tag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
}
type rowV struct {

View File

@@ -154,7 +154,7 @@ func SetupHookRefKeys(ctx context.Context, dEnv *env.DoltEnv) (*env.DoltEnv, err
if err != nil {
return nil, err
}
_, err = sch.Indexes().AddIndexByColNames("blob_idx", []string{"c1"}, schema.IndexProperties{IsUserDefined: true})
_, err = sch.Indexes().AddIndexByColNames("blob_idx", []string{"c1"}, nil, schema.IndexProperties{IsUserDefined: true})
if err != nil {
return nil, err
}

View File

@@ -66,7 +66,7 @@ var sch, _ = schema.SchemaFromPKAndNonPKCols(testKeyColColl, testNonKeyColColl)
var index schema.Index
func init() {
index, _ = sch.Indexes().AddIndexByColTags(indexName, []uint64{ageColTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
index, _ = sch.Indexes().AddIndexByColTags(indexName, []uint64{ageColTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
}
func newTestRow() (Row, error) {

View File

@@ -152,6 +152,7 @@ type encodedIndex struct {
Comment string `noms:"comment" json:"comment"`
Unique bool `noms:"unique" json:"unique"`
IsSystemDefined bool `noms:"hidden,omitempty" json:"hidden,omitempty"` // Was previously named Hidden, do not change noms name
PrefixLengths []uint16 `noms:"prefixLengths,omitempty" json:"prefixLengths,omitempty"`
}
type encodedCheck struct {
@@ -186,6 +187,12 @@ func (sd *schemaData) Copy() *schemaData {
for j, tag := range idx.Tags {
idxCol[i].Tags[j] = tag
}
if len(idx.PrefixLengths) > 0 {
idxCol[i].PrefixLengths = make([]uint16, len(idx.PrefixLengths))
for j, prefixLength := range idx.PrefixLengths {
idxCol[i].PrefixLengths[j] = prefixLength
}
}
}
}
@@ -238,6 +245,7 @@ func toSchemaData(sch schema.Schema) (schemaData, error) {
Comment: index.Comment(),
Unique: index.IsUnique(),
IsSystemDefined: !index.IsUserDefined(),
PrefixLengths: index.PrefixLengths(),
}
}
@@ -296,6 +304,7 @@ func (sd schemaData) addChecksIndexesAndPkOrderingToSchema(sch schema.Schema) er
_, err := sch.Indexes().UnsafeAddIndexByColTags(
encodedIndex.Name,
encodedIndex.Tags,
encodedIndex.PrefixLengths,
schema.IndexProperties{
IsUnique: encodedIndex.Unique,
IsUserDefined: !encodedIndex.IsSystemDefined,

View File

@@ -46,7 +46,7 @@ func createTestSchema() schema.Schema {
colColl := schema.NewColCollection(columns...)
sch := schema.MustSchemaFromCols(colColl)
_, _ = sch.Indexes().AddIndexByColTags("idx_age", []uint64{3}, schema.IndexProperties{IsUnique: false, Comment: ""})
_, _ = sch.Indexes().AddIndexByColTags("idx_age", []uint64{3}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
return sch
}
@@ -255,7 +255,7 @@ func (tsd testSchemaData) decodeSchema() (schema.Schema, error) {
sch.SetCollation(tsd.Collation)
for _, encodedIndex := range tsd.IndexCollection {
_, err = sch.Indexes().AddIndexByColTags(encodedIndex.Name, encodedIndex.Tags, schema.IndexProperties{IsUnique: encodedIndex.Unique, Comment: encodedIndex.Comment})
_, err = sch.Indexes().AddIndexByColTags(encodedIndex.Name, encodedIndex.Tags, nil, schema.IndexProperties{IsUnique: encodedIndex.Unique, Comment: encodedIndex.Comment})
if err != nil {
return nil, err
}

View File

@@ -333,6 +333,14 @@ func serializeSecondaryIndexes(b *fb.Builder, sch schema.Schema, indexes []schem
}
ko := b.EndVector(len(tags))
// serialize prefix lengths
prefixLengths := idx.PrefixLengths()
serial.IndexStartPrefixLengthsVector(b, len(prefixLengths))
for j := len(prefixLengths) - 1; j >= 0; j-- {
b.PrependUint16(prefixLengths[j])
}
po := b.EndVector(len(prefixLengths))
serial.IndexStart(b)
serial.IndexAddName(b, no)
serial.IndexAddComment(b, co)
@@ -341,6 +349,7 @@ func serializeSecondaryIndexes(b *fb.Builder, sch schema.Schema, indexes []schem
serial.IndexAddPrimaryKey(b, false)
serial.IndexAddUniqueKey(b, idx.IsUnique())
serial.IndexAddSystemDefined(b, !idx.IsUserDefined())
serial.IndexAddPrefixLengths(b, po)
offs[i] = serial.IndexEnd(b)
}
@@ -372,7 +381,16 @@ func deserializeSecondaryIndexes(sch schema.Schema, s *serial.TableSchema) error
tags[j] = col.Tag()
}
_, err := sch.Indexes().AddIndexByColTags(name, tags, props)
var prefixLengths []uint16
prefixLengthsLength := idx.PrefixLengthsLength()
if prefixLengthsLength > 0 {
prefixLengths = make([]uint16, prefixLengthsLength)
for j := range prefixLengths {
prefixLengths[j] = idx.PrefixLengths(j)
}
}
_, err := sch.Indexes().AddIndexByColTags(name, tags, prefixLengths, props)
if err != nil {
return err
}

View File

@@ -54,6 +54,8 @@ type Index interface {
// ToTableTuple returns a tuple that may be used to retrieve the original row from the indexed table when given
// a full index key (and not a partial index key).
ToTableTuple(ctx context.Context, fullKey types.Tuple, format *types.NomsBinFormat) (types.Tuple, error)
// PrefixLengths returns the prefix lengths for the index
PrefixLengths() []uint16
}
var _ Index = (*indexImpl)(nil)
@@ -66,6 +68,7 @@ type indexImpl struct {
isUnique bool
isUserDefined bool
comment string
prefixLengths []uint16
}
func NewIndex(name string, tags, allTags []uint64, indexColl IndexCollection, props IndexProperties) Index {
@@ -241,6 +244,11 @@ func (ix *indexImpl) ToTableTuple(ctx context.Context, fullKey types.Tuple, form
return types.NewTuple(format, resVals...)
}
// GetPrefixLengths implements Index.
func (ix *indexImpl) PrefixLengths() []uint16 {
return ix.prefixLengths
}
// copy returns an exact copy of the calling index.
func (ix *indexImpl) copy() *indexImpl {
newIx := *ix
@@ -248,5 +256,9 @@ func (ix *indexImpl) copy() *indexImpl {
_ = copy(newIx.tags, ix.tags)
newIx.allTags = make([]uint64, len(ix.allTags))
_ = copy(newIx.allTags, ix.allTags)
if len(ix.prefixLengths) > 0 {
newIx.prefixLengths = make([]uint16, len(ix.prefixLengths))
_ = copy(newIx.prefixLengths, ix.prefixLengths)
}
return &newIx
}

View File

@@ -25,11 +25,11 @@ type IndexCollection interface {
// It does not perform any kind of checking, and is intended for schema modifications.
AddIndex(indexes ...Index)
// AddIndexByColNames adds an index with the given name and columns (in index order).
AddIndexByColNames(indexName string, cols []string, props IndexProperties) (Index, error)
AddIndexByColNames(indexName string, cols []string, prefixLengths []uint16, props IndexProperties) (Index, error)
// AddIndexByColTags adds an index with the given name and column tags (in index order).
AddIndexByColTags(indexName string, tags []uint64, props IndexProperties) (Index, error)
AddIndexByColTags(indexName string, tags []uint64, prefixLengths []uint16, props IndexProperties) (Index, error)
// todo: this method is trash, clean up this interface
UnsafeAddIndexByColTags(indexName string, tags []uint64, props IndexProperties) (Index, error)
UnsafeAddIndexByColTags(indexName string, tags []uint64, prefixLengths []uint16, props IndexProperties) (Index, error)
// AllIndexes returns a slice containing all of the indexes in this collection.
AllIndexes() []Index
// Contains returns whether the given index name already exists for this table.
@@ -125,15 +125,15 @@ func (ixc *indexCollectionImpl) AddIndex(indexes ...Index) {
}
}
func (ixc *indexCollectionImpl) AddIndexByColNames(indexName string, cols []string, props IndexProperties) (Index, error) {
func (ixc *indexCollectionImpl) AddIndexByColNames(indexName string, cols []string, prefixLengths []uint16, props IndexProperties) (Index, error) {
tags, ok := ixc.columnNamesToTags(cols)
if !ok {
return nil, fmt.Errorf("the table does not contain at least one of the following columns: `%v`", cols)
}
return ixc.AddIndexByColTags(indexName, tags, props)
return ixc.AddIndexByColTags(indexName, tags, prefixLengths, props)
}
func (ixc *indexCollectionImpl) AddIndexByColTags(indexName string, tags []uint64, props IndexProperties) (Index, error) {
func (ixc *indexCollectionImpl) AddIndexByColTags(indexName string, tags []uint64, prefixLengths []uint16, props IndexProperties) (Index, error) {
if strings.HasPrefix(indexName, "dolt_") {
return nil, fmt.Errorf("indexes cannot be prefixed with `dolt_`")
}
@@ -161,6 +161,7 @@ func (ixc *indexCollectionImpl) AddIndexByColTags(indexName string, tags []uint6
isUnique: props.IsUnique,
isUserDefined: props.IsUserDefined,
comment: props.Comment,
prefixLengths: prefixLengths,
}
ixc.indexes[indexName] = index
for _, tag := range tags {
@@ -177,7 +178,7 @@ func validateColumnIndexable(c Column) error {
return nil
}
func (ixc *indexCollectionImpl) UnsafeAddIndexByColTags(indexName string, tags []uint64, props IndexProperties) (Index, error) {
func (ixc *indexCollectionImpl) UnsafeAddIndexByColTags(indexName string, tags []uint64, prefixLengths []uint16, props IndexProperties) (Index, error) {
index := &indexImpl{
indexColl: ixc,
name: indexName,
@@ -186,6 +187,7 @@ func (ixc *indexCollectionImpl) UnsafeAddIndexByColTags(indexName string, tags [
isUnique: props.IsUnique,
isUserDefined: props.IsUserDefined,
comment: props.Comment,
prefixLengths: prefixLengths,
}
ixc.indexes[indexName] = index
for _, tag := range tags {
@@ -325,6 +327,7 @@ func (ixc *indexCollectionImpl) Merge(indexes ...Index) {
isUnique: index.IsUnique(),
isUserDefined: index.IsUserDefined(),
comment: index.Comment(),
prefixLengths: index.PrefixLengths(),
}
ixc.AddIndex(newIndex)
}

View File

@@ -196,7 +196,7 @@ func TestIndexCollectionAddIndexByColNames(t *testing.T) {
assert.False(t, indexColl.hasIndexOnTags(testIndex.index.IndexedColumnTags()...))
assert.Nil(t, indexColl.GetByName(testIndex.index.Name()))
resIndex, err := indexColl.AddIndexByColNames(testIndex.index.Name(), testIndex.cols, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
resIndex, err := indexColl.AddIndexByColNames(testIndex.index.Name(), testIndex.cols, nil, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
assert.NoError(t, err)
assert.Equal(t, testIndex.index, resIndex)
assert.Equal(t, testIndex.index, indexColl.GetByName(resIndex.Name()))
@@ -216,20 +216,20 @@ func TestIndexCollectionAddIndexByColNames(t *testing.T) {
t.Run("Pre-existing", func(t *testing.T) {
for _, testIndex := range testIndexes {
_, err := indexColl.AddIndexByColNames(testIndex.index.Name(), testIndex.cols, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
_, err := indexColl.AddIndexByColNames(testIndex.index.Name(), testIndex.cols, nil, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
assert.NoError(t, err)
_, err = indexColl.AddIndexByColNames(testIndex.index.Name()+"copy", testIndex.cols, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
_, err = indexColl.AddIndexByColNames(testIndex.index.Name()+"copy", testIndex.cols, nil, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
assert.NoError(t, err)
_, err = indexColl.AddIndexByColNames(testIndex.index.Name(), []string{"v2"}, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
_, err = indexColl.AddIndexByColNames(testIndex.index.Name(), []string{"v2"}, nil, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()})
assert.Error(t, err)
}
indexColl.clear(t)
})
t.Run("Non-existing Columns", func(t *testing.T) {
_, err := indexColl.AddIndexByColNames("nonsense", []string{"v4"}, IndexProperties{IsUnique: false, Comment: ""})
_, err := indexColl.AddIndexByColNames("nonsense", []string{"v4"}, nil, IndexProperties{IsUnique: false, Comment: ""})
assert.Error(t, err)
_, err = indexColl.AddIndexByColNames("nonsense", []string{"v1", "v2", "pk3"}, IndexProperties{IsUnique: false, Comment: ""})
_, err = indexColl.AddIndexByColNames("nonsense", []string{"v1", "v2", "pk3"}, nil, IndexProperties{IsUnique: false, Comment: ""})
assert.Error(t, err)
})
}
@@ -279,7 +279,7 @@ func TestIndexCollectionAddIndexByColTags(t *testing.T) {
assert.False(t, indexColl.hasIndexOnTags(testIndex.IndexedColumnTags()...))
assert.Nil(t, indexColl.GetByName(testIndex.Name()))
resIndex, err := indexColl.AddIndexByColTags(testIndex.Name(), testIndex.tags, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
resIndex, err := indexColl.AddIndexByColTags(testIndex.Name(), testIndex.tags, nil, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
assert.NoError(t, err)
assert.Equal(t, testIndex, resIndex)
assert.Equal(t, testIndex, indexColl.GetByName(resIndex.Name()))
@@ -299,20 +299,20 @@ func TestIndexCollectionAddIndexByColTags(t *testing.T) {
t.Run("Pre-existing", func(t *testing.T) {
for _, testIndex := range testIndexes {
_, err := indexColl.AddIndexByColTags(testIndex.Name(), testIndex.tags, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
_, err := indexColl.AddIndexByColTags(testIndex.Name(), testIndex.tags, nil, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
assert.NoError(t, err)
_, err = indexColl.AddIndexByColTags(testIndex.Name()+"copy", testIndex.tags, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
_, err = indexColl.AddIndexByColTags(testIndex.Name()+"copy", testIndex.tags, nil, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
assert.NoError(t, err)
_, err = indexColl.AddIndexByColTags(testIndex.Name(), []uint64{4}, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
_, err = indexColl.AddIndexByColTags(testIndex.Name(), []uint64{4}, nil, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()})
assert.Error(t, err)
}
indexColl.clear(t)
})
t.Run("Non-existing Tags", func(t *testing.T) {
_, err := indexColl.AddIndexByColTags("nonsense", []uint64{6}, IndexProperties{IsUnique: false, Comment: ""})
_, err := indexColl.AddIndexByColTags("nonsense", []uint64{6}, nil, IndexProperties{IsUnique: false, Comment: ""})
assert.Error(t, err)
_, err = indexColl.AddIndexByColTags("nonsense", []uint64{3, 4, 10}, IndexProperties{IsUnique: false, Comment: ""})
_, err = indexColl.AddIndexByColTags("nonsense", []uint64{3, 4, 10}, nil, IndexProperties{IsUnique: false, Comment: ""})
assert.Error(t, err)
})
}
@@ -331,9 +331,9 @@ func TestIndexCollectionAllIndexes(t *testing.T) {
name: "idx_z",
tags: []uint64{3},
})
_, err := indexColl.AddIndexByColNames("idx_a", []string{"v2"}, IndexProperties{IsUnique: false, Comment: ""})
_, err := indexColl.AddIndexByColNames("idx_a", []string{"v2"}, nil, IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
_, err = indexColl.AddIndexByColTags("idx_n", []uint64{5}, IndexProperties{IsUnique: false, Comment: "hello there"})
_, err = indexColl.AddIndexByColTags("idx_n", []uint64{5}, nil, IndexProperties{IsUnique: false, Comment: "hello there"})
require.NoError(t, err)
assert.Equal(t, []Index{
@@ -501,7 +501,7 @@ func TestIndexCollectionDuplicateIndexes(t *testing.T) {
assert.Nil(t, indexColl.GetByName(origIndex.index.Name()))
// Insert origIndex and see that no errors occur
resOrigIndex, err := indexColl.AddIndexByColNames(origIndex.index.Name(), origIndex.cols, IndexProperties{IsUnique: origIndex.index.IsUnique(), Comment: origIndex.index.Comment()})
resOrigIndex, err := indexColl.AddIndexByColNames(origIndex.index.Name(), origIndex.cols, nil, IndexProperties{IsUnique: origIndex.index.IsUnique(), Comment: origIndex.index.Comment()})
assert.NoError(t, err)
assert.Equal(t, origIndex.index, resOrigIndex)
assert.Equal(t, origIndex.index, indexColl.GetByName(resOrigIndex.Name()))
@@ -525,7 +525,7 @@ func TestIndexCollectionDuplicateIndexes(t *testing.T) {
assert.Nil(t, indexColl.GetByName(copyIndex.index.Name()))
// Insert copyIndex and see that no errors occur
resCopyIndex, err := indexColl.AddIndexByColNames(copyIndex.index.Name(), copyIndex.cols, IndexProperties{IsUnique: copyIndex.index.IsUnique(), Comment: copyIndex.index.Comment()})
resCopyIndex, err := indexColl.AddIndexByColNames(copyIndex.index.Name(), copyIndex.cols, nil, IndexProperties{IsUnique: copyIndex.index.IsUnique(), Comment: copyIndex.index.Comment()})
assert.NoError(t, err)
assert.Equal(t, copyIndex.index, resCopyIndex)
assert.Equal(t, copyIndex.index, indexColl.GetByName(resCopyIndex.Name()))

View File

@@ -265,11 +265,15 @@ func replaceColumnInSchema(sch schema.Schema, oldCol schema.Column, newCol schem
tags[i] = newCol.Tag
}
}
_, err = newSch.Indexes().AddIndexByColTags(index.Name(), tags, schema.IndexProperties{
IsUnique: index.IsUnique(),
IsUserDefined: index.IsUserDefined(),
Comment: index.Comment(),
})
_, err = newSch.Indexes().AddIndexByColTags(
index.Name(),
tags,
index.PrefixLengths(),
schema.IndexProperties{
IsUnique: index.IsUnique(),
IsUserDefined: index.IsUserDefined(),
Comment: index.Comment(),
})
if err != nil {
return nil, err
}

View File

@@ -506,6 +506,7 @@ func TestBlobs(t *testing.T) {
}
func TestIndexPrefix(t *testing.T) {
skipOldFormat(t)
harness := newDoltHarness(t)
enginetest.TestIndexPrefix(t, harness)
for _, script := range DoltIndexPrefixScripts {

View File

@@ -8126,7 +8126,7 @@ var DoltCommitTests = []queries.ScriptTest{
var DoltIndexPrefixScripts = []queries.ScriptTest{
{
Name: "varchar prefix",
Name: "varchar primary key prefix",
SetUpScript: []string{
"create table t (v varchar(100))",
},
@@ -8135,22 +8135,84 @@ var DoltIndexPrefixScripts = []queries.ScriptTest{
Query: "alter table t add primary key (v(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "alter table t add index (v(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table v_tbl (v varchar(100), primary key (v(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table v_tbl (i int primary key, v varchar(100), index (v(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
},
},
{
Name: "char prefix",
Name: "varchar keyed secondary index prefix",
SetUpScript: []string{
"create table t (i int primary key, v varchar(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (v(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `i` int NOT NULL,\n `v` varchar(10),\n PRIMARY KEY (`i`),\n UNIQUE KEY `v` (`v`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values (0, 'aa'), (1, 'ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "insert into t values (0, 'aa'), (1, 'bb'), (2, 'cc')",
Expected: []sql.Row{{sql.NewOkResult(3)}},
},
{
Query: "select * from t where v = 'a'",
Expected: []sql.Row{},
},
{
Query: "select * from t where v = 'aa'",
Expected: []sql.Row{
{0, "aa"},
},
},
{
Query: "create table v_tbl (i int primary key, v varchar(100), index (v(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table v_tbl",
Expected: []sql.Row{{"v_tbl", "CREATE TABLE `v_tbl` (\n `i` int NOT NULL,\n `v` varchar(100),\n PRIMARY KEY (`i`),\n KEY `v` (`v`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "varchar keyless secondary index prefix",
SetUpScript: []string{
"create table t (v varchar(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (v(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `v` varchar(10),\n UNIQUE KEY `v` (`v`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values ('aa'), ('ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table v_tbl (v varchar(100), index (v(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table v_tbl",
Expected: []sql.Row{{"v_tbl", "CREATE TABLE `v_tbl` (\n `v` varchar(100),\n KEY `v` (`v`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "char primary key prefix",
SetUpScript: []string{
"create table t (c char(100))",
},
@@ -8159,18 +8221,354 @@ var DoltIndexPrefixScripts = []queries.ScriptTest{
Query: "alter table t add primary key (c(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "alter table t add index (c(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table c_tbl (c char(100), primary key (c(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table c_tbl (i int primary key, c char(100), index (c(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
},
},
{
Name: "char keyed secondary index prefix",
SetUpScript: []string{
"create table t (i int primary key, c char(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (c(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `i` int NOT NULL,\n `c` char(10),\n PRIMARY KEY (`i`),\n UNIQUE KEY `c` (`c`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values (0, 'aa'), (1, 'ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table c_tbl (i int primary key, c varchar(100), index (c(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table c_tbl",
Expected: []sql.Row{{"c_tbl", "CREATE TABLE `c_tbl` (\n `i` int NOT NULL,\n `c` varchar(100),\n PRIMARY KEY (`i`),\n KEY `c` (`c`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "char keyless secondary index prefix",
SetUpScript: []string{
"create table t (c char(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (c(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `c` char(10),\n UNIQUE KEY `c` (`c`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values ('aa'), ('ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table c_tbl (c char(100), index (c(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table c_tbl",
Expected: []sql.Row{{"c_tbl", "CREATE TABLE `c_tbl` (\n `c` char(100),\n KEY `c` (`c`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "varbinary primary key prefix",
SetUpScript: []string{
"create table t (v varbinary(100))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add primary key (v(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table v_tbl (v varbinary(100), primary key (v(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
},
},
{
Name: "varbinary keyed secondary index prefix",
SetUpScript: []string{
"create table t (i int primary key, v varbinary(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (v(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `i` int NOT NULL,\n `v` varbinary(10),\n PRIMARY KEY (`i`),\n UNIQUE KEY `v` (`v`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values (0, 'aa'), (1, 'ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table v_tbl (i int primary key, v varbinary(100), index (v(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table v_tbl",
Expected: []sql.Row{{"v_tbl", "CREATE TABLE `v_tbl` (\n `i` int NOT NULL,\n `v` varbinary(100),\n PRIMARY KEY (`i`),\n KEY `v` (`v`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "varbinary keyless secondary index prefix",
SetUpScript: []string{
"create table t (v varbinary(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (v(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `v` varbinary(10),\n UNIQUE KEY `v` (`v`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values ('aa'), ('ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table v_tbl (v varbinary(100), index (v(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table v_tbl",
Expected: []sql.Row{{"v_tbl", "CREATE TABLE `v_tbl` (\n `v` varbinary(100),\n KEY `v` (`v`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "binary primary key prefix",
SetUpScript: []string{
"create table t (b binary(100))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add primary key (b(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table b_tbl (b binary(100), primary key (b(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
},
},
{
Name: "binary keyed secondary index prefix",
SetUpScript: []string{
"create table t (i int primary key, b binary(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (b(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `i` int NOT NULL,\n `b` binary(10),\n PRIMARY KEY (`i`),\n UNIQUE KEY `b` (`b`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values (0, 'aa'), (1, 'ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table b_tbl (i int primary key, b binary(100), index (b(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table b_tbl",
Expected: []sql.Row{{"b_tbl", "CREATE TABLE `b_tbl` (\n `i` int NOT NULL,\n `b` binary(100),\n PRIMARY KEY (`i`),\n KEY `b` (`b`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "binary keyless secondary index prefix",
SetUpScript: []string{
"create table t (b binary(10))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (b(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `b` binary(10),\n UNIQUE KEY `b` (`b`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values ('aa'), ('ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table b_tbl (b binary(100), index (b(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table b_tbl",
Expected: []sql.Row{{"b_tbl", "CREATE TABLE `b_tbl` (\n `b` binary(100),\n KEY `b` (`b`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "blob primary key prefix",
SetUpScript: []string{
"create table t (b blob)",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add primary key (b(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table b_tbl (b blob, primary key (b(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
},
},
{
Name: "blob keyed secondary index prefix",
SetUpScript: []string{
"create table t (i int primary key, b blob)",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (b(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `i` int NOT NULL,\n `b` blob,\n PRIMARY KEY (`i`),\n UNIQUE KEY `b` (`b`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values (0, 'aa'), (1, 'ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table b_tbl (i int primary key, b blob, index (b(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table b_tbl",
Expected: []sql.Row{{"b_tbl", "CREATE TABLE `b_tbl` (\n `i` int NOT NULL,\n `b` blob,\n PRIMARY KEY (`i`),\n KEY `b` (`b`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "blob keyless secondary index prefix",
SetUpScript: []string{
"create table t (b blob)",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (b(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `b` blob,\n UNIQUE KEY `b` (`b`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values ('aa'), ('ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table b_tbl (b blob, index (b(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table b_tbl",
Expected: []sql.Row{{"b_tbl", "CREATE TABLE `b_tbl` (\n `b` blob,\n KEY `b` (`b`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "text primary key prefix",
SetUpScript: []string{
"create table t (t text)",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add primary key (t(10))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
{
Query: "create table b_tbl (t text, primary key (t(10)))",
ExpectedErr: sql.ErrUnsupportedIndexPrefix,
},
},
},
{
Name: "text keyed secondary index prefix",
SetUpScript: []string{
"create table t (i int primary key, t text)",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (t(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `i` int NOT NULL,\n `t` text,\n PRIMARY KEY (`i`),\n UNIQUE KEY `t` (`t`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values (0, 'aa'), (1, 'ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table t_tbl (i int primary key, t text, index (t(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t_tbl",
Expected: []sql.Row{{"t_tbl", "CREATE TABLE `t_tbl` (\n `i` int NOT NULL,\n `t` text,\n PRIMARY KEY (`i`),\n KEY `t` (`t`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "text keyless secondary index prefix",
SetUpScript: []string{
"create table t (t text)",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "alter table t add unique index (t(1))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n `t` text,\n UNIQUE KEY `t` (`t`(1))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t values ('aa'), ('ab')",
ExpectedErr: sql.ErrUniqueKeyViolation,
},
{
Query: "create table t_tbl (t text, index (t(10)))",
Expected: []sql.Row{{sql.NewOkResult(0)}},
},
{
Query: "show create table t_tbl",
Expected: []sql.Row{{"t_tbl", "CREATE TABLE `t_tbl` (\n `t` text,\n KEY `t` (`t`(10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
}

View File

@@ -141,6 +141,11 @@ func validateIndexConsistency(
def schema.Index,
primary, secondary prolly.Map,
) error {
// TODO: fix this later
if len(def.PrefixLengths()) > 0 {
return nil
}
if schema.IsKeyless(sch) {
return validateKeylessIndex(ctx, sch, def, primary, secondary)
}

View File

@@ -255,6 +255,7 @@ func getSecondaryIndex(ctx context.Context, db, tbl string, t *doltdb.Table, sch
order: sql.IndexOrderAsc,
constrainedToLookupExpression: true,
doltBinFormat: types.IsFormat_DOLT(vrw.Format()),
prefixLengths: idx.PrefixLengths(),
}, nil
}
@@ -386,12 +387,17 @@ type doltIndex struct {
cache cachedDurableIndexes
doltBinFormat bool
prefixLengths []uint16
}
var _ DoltIndex = (*doltIndex)(nil)
// CanSupport implements sql.Index
func (di *doltIndex) CanSupport(...sql.Range) bool {
if len(di.prefixLengths) > 0 {
return false
}
return true
}
@@ -591,6 +597,10 @@ func (di *doltIndex) coversColumns(s *durableIndexState, cols []uint64) bool {
return s.coversAllColumns(di)
}
if len(di.prefixLengths) > 0 {
return false
}
var idxCols *schema.ColCollection
if types.IsFormat_DOLT(di.Format()) {
// prolly indexes can cover an index lookup using
@@ -672,6 +682,11 @@ func (di *doltIndex) Comment() string {
return di.comment
}
// PrefixLengths implements sql.Index
func (di *doltIndex) PrefixLengths() []uint16 {
return di.prefixLengths
}
// IndexType implements sql.Index
func (di *doltIndex) IndexType() string {
return "BTREE"

View File

@@ -294,6 +294,11 @@ func (idx fmtIndex) Comment() string {
return idx.comment
}
// PrefixLengths implements sql.Index
func (idx fmtIndex) PrefixLengths() []uint16 {
return nil
}
// IndexType implements sql.Index
func (idx fmtIndex) IndexType() string {
return "BTREE"

View File

@@ -1342,11 +1342,15 @@ func (t *AlterableDoltTable) RewriteInserter(
colNames = append(colNames, colName)
}
}
newSch.Indexes().AddIndexByColNames(index.Name(), colNames, schema.IndexProperties{
IsUnique: index.IsUnique(),
IsUserDefined: index.IsUserDefined(),
Comment: index.Comment(),
})
newSch.Indexes().AddIndexByColNames(
index.Name(),
colNames,
index.PrefixLengths(),
schema.IndexProperties{
IsUnique: index.IsUnique(),
IsUserDefined: index.IsUserDefined(),
Comment: index.Comment(),
})
}
} else {
newSch = schema.CopyIndexes(oldSch, newSch)
@@ -1501,7 +1505,7 @@ func validateSchemaChange(
) error {
for _, idxCol := range idxCols {
col := newSchema.Schema[newSchema.Schema.IndexOfColName(idxCol.Name)]
if idxCol.Length > 0 && sql.IsText(col.Type) {
if col.PrimaryKey && idxCol.Length > 0 && sql.IsText(col.Type) {
return sql.ErrUnsupportedIndexPrefix.New(col.Name)
}
}
@@ -1757,6 +1761,29 @@ func (t *AlterableDoltTable) getFirstAutoIncrementValue(
return seq, nil
}
// hasNonZeroPrefixLength will return true if at least one of the sql.IndexColumns has a Length > 0
func hasNonZeroPrefixLength(idxCols []sql.IndexColumn) bool {
for _, idxCol := range idxCols {
if idxCol.Length > 0 {
return true
}
}
return false
}
// allocatePrefixLengths will return a []uint16 populated with the Length field from sql.IndexColumn
// if all the lengths have a value of 0, it will return nil
func allocatePrefixLengths(idxCols []sql.IndexColumn) []uint16 {
if !hasNonZeroPrefixLength(idxCols) {
return nil
}
prefixLengths := make([]uint16, len(idxCols))
for i, idxCol := range idxCols {
prefixLengths[i] = uint16(idxCol.Length)
}
return prefixLengths
}
// CreateIndex implements sql.IndexAlterableTable
func (t *AlterableDoltTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) error {
if err := branch_control.CheckAccess(ctx, branch_control.Permissions_Write); err != nil {
@@ -1765,6 +1792,7 @@ func (t *AlterableDoltTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) err
if idx.Constraint != sql.IndexConstraint_None && idx.Constraint != sql.IndexConstraint_Unique {
return fmt.Errorf("only the following types of index constraints are supported: none, unique")
}
columns := make([]string, len(idx.Columns))
for i, indexCol := range idx.Columns {
columns[i] = indexCol.Name
@@ -1775,18 +1803,12 @@ func (t *AlterableDoltTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) err
return err
}
for _, idxCol := range idx.Columns {
col := t.DoltTable.sqlSch.Schema[t.DoltTable.sqlSch.IndexOfColName(idxCol.Name)]
if idxCol.Length > 0 && sql.IsText(col.Type) {
return sql.ErrUnsupportedIndexPrefix.New(col.Name)
}
}
ret, err := creation.CreateIndex(
ctx,
table,
idx.Name,
columns,
allocatePrefixLengths(idx.Columns),
idx.Constraint == sql.IndexConstraint_Unique,
true,
idx.Comment,
@@ -2161,11 +2183,20 @@ func (t *AlterableDoltTable) UpdateForeignKey(ctx *sql.Context, fkName string, s
// schema.Index interface (which is used internally to represent indexes across the codebase). In the
// meantime, we must generate a duplicate key over the primary key.
//TODO: use the primary key as-is
idxReturn, err := creation.CreateIndex(ctx, tbl, "", sqlFk.Columns, false, false, "", editor.Options{
ForeignKeyChecksDisabled: true,
Deaf: t.opts.Deaf,
Tempdir: t.opts.Tempdir,
})
idxReturn, err := creation.CreateIndex(
ctx,
tbl,
"",
sqlFk.Columns,
nil,
false,
false,
"",
editor.Options{
ForeignKeyChecksDisabled: true,
Deaf: t.opts.Deaf,
Tempdir: t.opts.Tempdir,
})
if err != nil {
return err
}
@@ -2199,11 +2230,20 @@ func (t *AlterableDoltTable) UpdateForeignKey(ctx *sql.Context, fkName string, s
// Our duplicate index is only unique if it's the entire primary key (which is by definition unique)
unique := len(refPkTags) == len(refColTags)
idxReturn, err := creation.CreateIndex(ctx, refTbl, "", colNames, unique, false, "", editor.Options{
ForeignKeyChecksDisabled: true,
Deaf: t.opts.Deaf,
Tempdir: t.opts.Tempdir,
})
idxReturn, err := creation.CreateIndex(
ctx,
refTbl,
"",
colNames,
nil,
unique,
false,
"",
editor.Options{
ForeignKeyChecksDisabled: true,
Deaf: t.opts.Deaf,
Tempdir: t.opts.Tempdir,
})
if err != nil {
return err
}
@@ -2256,6 +2296,7 @@ func (t *AlterableDoltTable) CreateIndexForForeignKey(ctx *sql.Context, idx sql.
table,
idx.Name,
columns,
allocatePrefixLengths(idx.Columns),
idx.Constraint == sql.IndexConstraint_Unique,
false,
"",

View File

@@ -272,6 +272,7 @@ func (t *TempTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) error {
t.table,
idx.Name,
cols,
allocatePrefixLengths(idx.Columns),
idx.Constraint == sql.IndexConstraint_Unique,
true,
idx.Comment,

View File

@@ -244,9 +244,10 @@ func (m prollyIndexWriter) uniqueKeyError(ctx context.Context, keyStr string, ke
}
type prollySecondaryIndexWriter struct {
name string
mut *prolly.MutableMap
unique bool
name string
mut *prolly.MutableMap
unique bool
prefixLengths []uint16
// number of indexed cols
idxCols int
@@ -283,10 +284,28 @@ func (m prollySecondaryIndexWriter) ValidateKeyViolations(ctx context.Context, s
return nil
}
// trimKeyPart will trim entry into the sql.Row depending on the prefixLengths
func (m prollySecondaryIndexWriter) trimKeyPart(to int, keyPart interface{}) interface{} {
var prefixLength uint16
if len(m.prefixLengths) > to {
prefixLength = m.prefixLengths[to]
}
if prefixLength != 0 {
switch kp := keyPart.(type) {
case string:
keyPart = kp[:prefixLength]
case []uint8:
keyPart = kp[:prefixLength]
}
}
return keyPart
}
func (m prollySecondaryIndexWriter) keyFromRow(ctx context.Context, sqlRow sql.Row) (val.Tuple, error) {
for to := range m.keyMap {
from := m.keyMap.MapOrdinal(to)
if err := index.PutField(ctx, m.mut.NodeStore(), m.keyBld, to, sqlRow[from]); err != nil {
keyPart := m.trimKeyPart(to, sqlRow[from])
if err := index.PutField(ctx, m.mut.NodeStore(), m.keyBld, to, keyPart); err != nil {
return nil, err
}
}
@@ -311,7 +330,8 @@ func (m prollySecondaryIndexWriter) checkForUniqueKeyErr(ctx context.Context, sq
m.keyBld.Recycle()
return nil
}
if err := index.PutField(ctx, ns, m.keyBld, to, sqlRow[from]); err != nil {
keyPart := m.trimKeyPart(to, sqlRow[from])
if err := index.PutField(ctx, ns, m.keyBld, to, keyPart); err != nil {
return err
}
}

View File

@@ -180,10 +180,11 @@ func (e secondaryUniqueKeyError) Error() string {
}
type prollyKeylessSecondaryWriter struct {
name string
mut *prolly.MutableMap
primary prollyKeylessWriter
unique bool
name string
mut *prolly.MutableMap
primary prollyKeylessWriter
unique bool
prefixLengths []uint16
keyBld *val.TupleBuilder
prefixBld *val.TupleBuilder
@@ -208,15 +209,33 @@ func (writer prollyKeylessSecondaryWriter) ValidateKeyViolations(ctx context.Con
return nil
}
// trimKeyPart will trim entry into the sql.Row depending on the prefixLengths
func (writer prollyKeylessSecondaryWriter) trimKeyPart(to int, keyPart interface{}) interface{} {
var prefixLength uint16
if len(writer.prefixLengths) > to {
prefixLength = writer.prefixLengths[to]
}
if prefixLength != 0 {
switch kp := keyPart.(type) {
case string:
keyPart = kp[:prefixLength]
case []uint8:
keyPart = kp[:prefixLength]
}
}
return keyPart
}
// Insert implements the interface indexWriter.
func (writer prollyKeylessSecondaryWriter) Insert(ctx context.Context, sqlRow sql.Row) error {
for to := range writer.keyMap {
from := writer.keyMap.MapOrdinal(to)
if err := index.PutField(ctx, writer.mut.NodeStore(), writer.keyBld, to, sqlRow[from]); err != nil {
keyPart := writer.trimKeyPart(to, sqlRow[from])
if err := index.PutField(ctx, writer.mut.NodeStore(), writer.keyBld, to, keyPart); err != nil {
return err
}
if to < writer.prefixBld.Desc.Count() {
if err := index.PutField(ctx, writer.mut.NodeStore(), writer.prefixBld, to, sqlRow[from]); err != nil {
if err := index.PutField(ctx, writer.mut.NodeStore(), writer.prefixBld, to, keyPart); err != nil {
return err
}
}

View File

@@ -77,16 +77,16 @@ func getSecondaryProllyIndexWriters(ctx context.Context, t *doltdb.Table, sqlSch
// mapping from secondary index key to primary key
pkMap := makeIndexToIndexMapping(def.Schema().GetPKCols(), sch.GetPKCols())
writers[defName] = prollySecondaryIndexWriter{
name: defName,
mut: idxMap.Mutate(),
unique: def.IsUnique(),
idxCols: def.Count(),
keyMap: keyMap,
keyBld: val.NewTupleBuilder(keyDesc),
pkMap: pkMap,
pkBld: val.NewTupleBuilder(pkDesc),
name: defName,
mut: idxMap.Mutate(),
unique: def.IsUnique(),
prefixLengths: def.PrefixLengths(),
idxCols: def.Count(),
keyMap: keyMap,
keyBld: val.NewTupleBuilder(keyDesc),
pkMap: pkMap,
pkBld: val.NewTupleBuilder(pkDesc),
}
}
@@ -115,14 +115,15 @@ func getSecondaryKeylessProllyWriters(ctx context.Context, t *doltdb.Table, sqlS
keyDesc, _ := m.Descriptors()
writers[defName] = prollyKeylessSecondaryWriter{
name: defName,
mut: m.Mutate(),
primary: primary,
unique: def.IsUnique(),
keyBld: val.NewTupleBuilder(keyDesc),
prefixBld: val.NewTupleBuilder(keyDesc.PrefixDesc(def.Count())),
hashBld: val.NewTupleBuilder(val.NewTupleDescriptor(val.Type{Enc: val.Hash128Enc})),
keyMap: keyMap,
name: defName,
mut: m.Mutate(),
primary: primary,
unique: def.IsUnique(),
prefixLengths: def.PrefixLengths(),
keyBld: val.NewTupleBuilder(keyDesc),
prefixBld: val.NewTupleBuilder(keyDesc.PrefixDesc(def.Count())),
hashBld: val.NewTupleBuilder(val.NewTupleDescriptor(val.Type{Enc: val.Hash128Enc})),
keyMap: keyMap,
}
}

View File

@@ -47,6 +47,7 @@ func CreateIndex(
table *doltdb.Table,
indexName string,
columns []string,
prefixLengths []uint16,
isUnique bool,
isUserDefined bool,
comment string,
@@ -99,6 +100,7 @@ func CreateIndex(
index, err := sch.Indexes().AddIndexByColNames(
indexName,
realColNames,
prefixLengths,
schema.IndexProperties{
IsUnique: isUnique,
IsUserDefined: isUserDefined,

View File

@@ -62,7 +62,7 @@ func TestIndexEditorConcurrency(t *testing.T) {
schema.NewColumn("v2", 2, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
index, err := tableSch.Indexes().AddIndexByColNames("idx_concurrency", []string{"v1"}, schema.IndexProperties{IsUnique: false, Comment: ""})
index, err := tableSch.Indexes().AddIndexByColNames("idx_concurrency", []string{"v1"}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
indexSch := index.Schema()
emptyMap, err := types.NewMap(context.Background(), vrw)
@@ -158,7 +158,7 @@ func TestIndexEditorConcurrencyPostInsert(t *testing.T) {
schema.NewColumn("v2", 2, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
index, err := tableSch.Indexes().AddIndexByColNames("idx_concurrency", []string{"v1"}, schema.IndexProperties{IsUnique: false, Comment: ""})
index, err := tableSch.Indexes().AddIndexByColNames("idx_concurrency", []string{"v1"}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
indexSch := index.Schema()
emptyMap, err := types.NewMap(context.Background(), vrw)
@@ -250,7 +250,7 @@ func TestIndexEditorUniqueMultipleNil(t *testing.T) {
schema.NewColumn("v1", 1, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
index, err := tableSch.Indexes().AddIndexByColNames("idx_unique", []string{"v1"}, schema.IndexProperties{IsUnique: true, Comment: ""})
index, err := tableSch.Indexes().AddIndexByColNames("idx_unique", []string{"v1"}, nil, schema.IndexProperties{IsUnique: true, Comment: ""})
require.NoError(t, err)
indexSch := index.Schema()
emptyMap, err := types.NewMap(context.Background(), vrw)
@@ -297,7 +297,7 @@ func TestIndexEditorWriteAfterFlush(t *testing.T) {
schema.NewColumn("v2", 2, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
index, err := tableSch.Indexes().AddIndexByColNames("idx_concurrency", []string{"v1"}, schema.IndexProperties{IsUnique: false, Comment: ""})
index, err := tableSch.Indexes().AddIndexByColNames("idx_concurrency", []string{"v1"}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
indexSch := index.Schema()
emptyMap, err := types.NewMap(context.Background(), vrw)
@@ -365,7 +365,7 @@ func TestIndexEditorUniqueErrorDoesntPersist(t *testing.T) {
schema.NewColumn("v1", 1, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
index, err := tableSch.Indexes().AddIndexByColNames("idx_unq", []string{"v1"}, schema.IndexProperties{IsUnique: true, Comment: ""})
index, err := tableSch.Indexes().AddIndexByColNames("idx_unq", []string{"v1"}, nil, schema.IndexProperties{IsUnique: true, Comment: ""})
require.NoError(t, err)
indexSch := index.Schema()
emptyMap, err := types.NewMap(context.Background(), vrw)
@@ -618,7 +618,7 @@ func TestIndexRebuildingUniqueSuccessOneCol(t *testing.T) {
originalTable, err := createTableWithoutIndexRebuilding(context.Background(), vrw, ns, sch, rowData)
require.NoError(t, err)
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2}, schema.IndexProperties{IsUnique: true, Comment: ""})
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2}, nil, schema.IndexProperties{IsUnique: true, Comment: ""})
require.NoError(t, err)
updatedTable, err := originalTable.UpdateSchema(context.Background(), sch)
require.NoError(t, err)
@@ -649,7 +649,7 @@ func TestIndexRebuildingUniqueSuccessTwoCol(t *testing.T) {
originalTable, err := createTableWithoutIndexRebuilding(context.Background(), vrw, ns, sch, rowData)
require.NoError(t, err)
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2, 3}, schema.IndexProperties{IsUnique: true, Comment: ""})
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2, 3}, nil, schema.IndexProperties{IsUnique: true, Comment: ""})
require.NoError(t, err)
updatedTable, err := originalTable.UpdateSchema(context.Background(), sch)
require.NoError(t, err)
@@ -680,7 +680,7 @@ func TestIndexRebuildingUniqueFailOneCol(t *testing.T) {
originalTable, err := createTableWithoutIndexRebuilding(context.Background(), vrw, ns, sch, rowData)
require.NoError(t, err)
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2}, schema.IndexProperties{IsUnique: true, Comment: ""})
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2}, nil, schema.IndexProperties{IsUnique: true, Comment: ""})
require.NoError(t, err)
updatedTable, err := originalTable.UpdateSchema(context.Background(), sch)
require.NoError(t, err)
@@ -712,7 +712,7 @@ func TestIndexRebuildingUniqueFailTwoCol(t *testing.T) {
originalTable, err := createTableWithoutIndexRebuilding(context.Background(), vrw, ns, sch, rowData)
require.NoError(t, err)
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2, 3}, schema.IndexProperties{IsUnique: true, Comment: ""})
index, err := sch.Indexes().AddIndexByColTags("idx_v1", []uint64{2, 3}, nil, schema.IndexProperties{IsUnique: true, Comment: ""})
require.NoError(t, err)
updatedTable, err := originalTable.UpdateSchema(context.Background(), sch)
require.NoError(t, err)
@@ -738,7 +738,7 @@ func TestIndexEditorCapacityExceeded(t *testing.T) {
schema.NewColumn("v1", 1, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
index, err := tableSch.Indexes().AddIndexByColNames("idx_cap", []string{"v1"}, schema.IndexProperties{IsUnique: false, Comment: ""})
index, err := tableSch.Indexes().AddIndexByColNames("idx_cap", []string{"v1"}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
indexSch := index.Schema()
emptyMap, err := types.NewMap(ctx, vrw)
@@ -837,9 +837,9 @@ func createTestSchema(t *testing.T) schema.Schema {
)
sch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexName, []uint64{firstTag, lastTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexName, []uint64{firstTag, lastTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexAge, []uint64{ageTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
_, err = sch.Indexes().AddIndexByColTags(testSchemaIndexAge, []uint64{ageTag}, nil, schema.IndexProperties{IsUnique: false, Comment: ""})
require.NoError(t, err)
return sch
}

View File

@@ -426,11 +426,11 @@ func TestKeylessTableEditorMultipleIndexErrorHandling(t *testing.T) {
schema.NewColumn("v2", 2, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
idxv1, err := tableSch.Indexes().AddIndexByColNames("idx_v1", []string{"v1"}, schema.IndexProperties{
idxv1, err := tableSch.Indexes().AddIndexByColNames("idx_v1", []string{"v1"}, nil, schema.IndexProperties{
IsUnique: false,
})
require.NoError(t, err)
idxv2, err := tableSch.Indexes().AddIndexByColNames("idx_v2", []string{"v2"}, schema.IndexProperties{
idxv2, err := tableSch.Indexes().AddIndexByColNames("idx_v2", []string{"v2"}, nil, schema.IndexProperties{
IsUnique: false,
})
require.NoError(t, err)
@@ -583,7 +583,7 @@ func TestKeylessTableEditorIndexCardinality(t *testing.T) {
schema.NewColumn("v2", 2, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
idxv1, err := tableSch.Indexes().AddIndexByColNames("idx_v1", []string{"v1"}, schema.IndexProperties{
idxv1, err := tableSch.Indexes().AddIndexByColNames("idx_v1", []string{"v1"}, nil, schema.IndexProperties{
IsUnique: false,
})
require.NoError(t, err)

View File

@@ -392,11 +392,11 @@ func TestTableEditorMultipleIndexErrorHandling(t *testing.T) {
schema.NewColumn("v2", 2, types.IntKind, false))
tableSch, err := schema.SchemaFromCols(colColl)
require.NoError(t, err)
idxv1, err := tableSch.Indexes().AddIndexByColNames("idx_v1", []string{"v1"}, schema.IndexProperties{
idxv1, err := tableSch.Indexes().AddIndexByColNames("idx_v1", []string{"v1"}, nil, schema.IndexProperties{
IsUnique: true,
})
require.NoError(t, err)
idxv2, err := tableSch.Indexes().AddIndexByColNames("idx_v2", []string{"v2"}, schema.IndexProperties{
idxv2, err := tableSch.Indexes().AddIndexByColNames("idx_v2", []string{"v2"}, nil, schema.IndexProperties{
IsUnique: true,
})
require.NoError(t, err)

View File

@@ -90,6 +90,8 @@ table Index {
primary_key:bool;
unique_key:bool;
system_defined:bool;
prefix_lengths:[uint16];
}
table CheckConstraint {

View File

@@ -29,7 +29,7 @@ teardown() {
dolt tag -v
run dolt tag -v
[ "$status" -eq 0 ]
[[ "$output" =~ "tdkt7s7805k1ml4hu37pm688g5i0b8ie" ]] || false
[[ "$output" =~ "7jrvg1ajcdq6t9sevcejv4e9o0fgrmle" ]] || false
[[ ! "$output" =~ "r9jv07tf9un3fm1fg72v7ad9er89oeo7" ]] || false
# validate TEXT migration
@@ -55,7 +55,7 @@ teardown() {
dolt tag -v
run dolt tag -v
[ "$status" -eq 0 ]
[[ "$output" =~ "tdkt7s7805k1ml4hu37pm688g5i0b8ie" ]] || false
[[ "$output" =~ "7jrvg1ajcdq6t9sevcejv4e9o0fgrmle" ]] || false
[[ ! "$output" =~ "r9jv07tf9un3fm1fg72v7ad9er89oeo7" ]] || false
# validate TEXT migration