Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: add mutable and immutable ft implementation #51916

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pkg/parser/types/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ go_library(
"etc.go",
"eval_type.go",
"field_type.go",
"immtable_field_types.go",
"mutable_field_types.go",
],
importpath = "github.com/pingcap/tidb/pkg/parser/types",
visibility = ["//visibility:public"],
Expand All @@ -24,10 +26,11 @@ go_test(
srcs = [
"etc_test.go",
"field_type_test.go",
"immutable_field_types_test.go",
],
embed = [":types"],
flaky = True,
shard_count = 6,
shard_count = 8,
deps = [
"//pkg/parser",
"//pkg/parser/ast",
Expand Down
27 changes: 25 additions & 2 deletions pkg/parser/types/field_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ var (
TiDBStrictIntegerDisplayWidth bool
)

// FieldType records field type information.
// FieldType is the internal structure records field type information, with all elements unexported.
// todo: move all the GET and SET from core structure of *FieldType to outer *MutableFieldType and *ImmutableFieldType
type FieldType struct {
// tp is type of the field
tp byte
Expand All @@ -60,7 +61,8 @@ type FieldType struct {
// Please keep in mind that jsonFieldType should be updated if you add a new field here.
}

// NewFieldType returns a FieldType,
// NewFieldType returns a FieldType.
// todo: change NewFieldType as unexported as newFieldType, and it can only be used inside this pkg.
// with a type and other information about field type.
func NewFieldType(tp byte) *FieldType {
return &FieldType{
Expand All @@ -70,6 +72,27 @@ func NewFieldType(tp byte) *FieldType {
}
}

// ImmutableRef implements the mutable interface by returning an immutable reference to current obj.
func (ft *FieldType) ImmutableRef() ImmutableFieldType {
// no-loss upcast
return ft
}

// MutableRef implements the immutable interface by returning the mutable reference to current obj.
func (ft *FieldType) MutableRef() MutableFieldType {
// no-loss downcast
// MutableRef return the mutable reference to the current MutableFieldType.
return ft
}

// MutableCopy implements the immutable interface by returning a new copy the basic ft with COW.
func (ft *FieldType) MutableCopy() MutableFieldType {
// when change an immutable fieldType to a mutable one, its kind of COW logic here.
// clone the basic element of original ImmutableFieldType it has, downcast it out.
newCP := *ft
return &newCP
}

// IsDecimalValid checks whether the decimal is valid.
func (ft *FieldType) IsDecimalValid() bool {
if ft.GetType() == mysql.TypeNewDecimal && (ft.decimal < 0 || ft.decimal > mysql.MaxDecimalScale || ft.flen <= 0 || ft.flen > mysql.MaxDecimalWidth || ft.flen < ft.decimal) {
Expand Down
72 changes: 72 additions & 0 deletions pkg/parser/types/immtable_field_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package types

import (
"io"

"github.com/pingcap/tidb/pkg/parser/format"
)

var _ ImmutableFieldType = &FieldType{}

// ImmutableFieldType refs to an immutable field type, with all GET type interface integrated.
// SET interface.
// can't be resolved anywhere, lack of definition.
//
// GET interface.
// when calling the ImmutableFieldType.GET(xxx), it resolve the func name successfully itself. The read
// happens to the deepest internal implemented core structure --- *FieldType.
//
// ImmutableFieldType.GET(xxx) (resolve func success itself) ------------> *FieldType (where the read happens)
type ImmutableFieldType interface {
// MutableCopy copy and return a new basic ft with COW.
// MutableCopy is different from MutableRef, the inside immutable ref is not point
// to original one. In otherwise, it's a brand-new one copied elements from the old.
MutableCopy() MutableFieldType
// MutableRef downcast the current interface pointer as son.
MutableRef() MutableFieldType

IsDecimalValid() bool
IsVarLengthType() bool
GetType() byte
GetFlag() uint
GetFlen() int
GetDecimal() int
GetCharset() string
GetCollate() string
GetElems() []string
IsArray() bool
GetElem(idx int) string
GetElemIsBinaryLit(idx int) bool
Equal(other *FieldType) bool
PartialEqual(other *FieldType, unsafe bool) bool
EvalType() EvalType
Hybrid() bool
CompactStr() string
InfoSchemaStr() string
String() string
Restore(ctx *format.RestoreCtx) error
RestoreAsCastType(ctx *format.RestoreCtx, explicitCharset bool)
FormatAsCastType(w io.Writer, explicitCharset bool)
StorageLength() int
MarshalJSON() ([]byte, error)
MemoryUsage() (sum int64)
}

// NewImmutableFieldType create an immutable field type.
func NewImmutableFieldType(tp byte) ImmutableFieldType {
return NewFieldType(tp)
}
121 changes: 121 additions & 0 deletions pkg/parser/types/immutable_field_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2024 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package types

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

type mutable interface {
immutable
mutable()
makeImmutable() immutable
}

type immutable interface {
immutable()
makeMutable() mutable
}

// ONE is a test structure for interface embedding test.
type ONE struct {
}

func (o *ONE) immutable() {
fmt.Printf("immutable,addr %p\n", o)
}

func (o *ONE) makeMutable() mutable {
// no-loss downcast.
// since we all know that ONE is implemented both immutable and mutable, we can direct return o here.
return o
}

func (o *ONE) mutable() {
fmt.Printf("mutable,addr %p\n", o)
}

func (o *ONE) makeImmutable() immutable {
// no-loss upcast.
// since we all know that ONE is implemented both immutable and mutable, we can direct return o here.
return o
}

func TestInterfaceWrapper(t *testing.T) {
var one mutable = &ONE{}
one.mutable()
one.immutable()
var sec immutable = one // upcast (no loss)
sec.immutable()
var third = sec.(mutable) // downcast (no loss)
third.mutable()
third.immutable()

// use normalized-defined interface access point.
var forth = third.makeImmutable() // upcast
forth.immutable()
var sixth = forth.makeMutable() // downcast
sixth.mutable()
sixth.immutable()
ft, ok := sixth.(*ONE)
require.Equal(t, ok, true)
require.NotNil(t, t, ft)
}

// currently the Mutable and Immutable and basic ft is organized as multi level interface wrapping.
// why use interface embedding is for no-loss downcast and upcast between mutable pointer and the
// immutable one.
//
// type ImmutableFieldType interface { // grandparent -----+ <------+
// // GET methods & makeImmutable | |
// } | (downcast) | (upcast)
// | |
// type MutableFieldType struct { // parent <----+ -------+
// ImmutableFieldType | |
// // SET methods & makeMutable | |
// } | |
// | (downcast) | (upcast)
// type FieldType struct { // son | |
// ... | |
// } <----+ -------+
func TestMutableAndImmutableFT(t *testing.T) {
var immutableFT ImmutableFieldType = NewFieldType(1)
// immutable methods
require.Equal(t, immutableFT.GetType(), uint8(1))
var mutableFT = immutableFT.MutableRef()
mutableFT.SetType(2)
// you can call immutable methods directly on mutableFT.
require.Equal(t, mutableFT.GetType(), uint8(2))
// or you can just up-cast mutableFT as immutableFT
immutableFT = mutableFT.ImmutableRef()
require.Equal(t, immutableFT.GetType(), uint8(2))

// make a mutable copy out from immutable pointer.
newCP := immutableFT.MutableCopy()
newCP.SetType(3)
require.Equal(t, newCP.GetType(), uint8(3))
// while the original immutableFT stay the same.
require.Equal(t, immutableFT.GetType(), uint8(2))

// make a mutable copy out from mutable pointer.
newCP2 := mutableFT.MutableCopy()
newCP2.SetType(4)
require.Equal(t, newCP2.GetType(), uint8(4))
require.Equal(t, immutableFT.GetType(), uint8(2))
require.Equal(t, mutableFT.GetType(), uint8(2))
}
66 changes: 66 additions & 0 deletions pkg/parser/types/mutable_field_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2024 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package types

var _ MutableFieldType = &FieldType{}

// MutableFieldType refs to a mutable field type, with all SET interface integrated.
// SET interface.
// when calling MutableFieldType.SET(xxx), it resolve the func name successfully and change happens
// on deepest implemented core *FieldType structure
//
// MutableFieldType.SET(xxx) -------------------------> *FieldType (where the change happens)
//
// GET interface.
// when calling MutableFieldType.GET(xxx), itself can't resolve the function call of GET cause
// lack of definition, so it will resort to inner wrapped interface ImmutableFieldType to explain
// this and it works. Finally, the read happens to the deepest implemented core structure --- *FieldType.
//
// MutableFieldType.GET(xxx) (resolve func fail)
//
// +-------------> ImmutableFieldType.GET(xxx) (resolve func success)
// +---------------------------> *FieldType (where the read happens)
type MutableFieldType interface {
ImmutableFieldType
ImmutableRef() ImmutableFieldType

SetType(tp byte)
SetFlag(flag uint)
AddFlag(flag uint)
AndFlag(flag uint)
ToggleFlag(flag uint)
DelFlag(flag uint)
SetFlen(flen int)
SetFlenUnderLimit(flen int)
SetDecimal(decimal int)
SetDecimalUnderLimit(decimal int)
UpdateFlenAndDecimalUnderLimit(old *FieldType, deltaDecimal int, deltaFlen int)
SetCharset(charset string)
SetCollate(collate string)
SetElems(elems []string)
SetElem(idx int, element string)
SetArray(array bool)
ArrayType() *FieldType
SetElemWithIsBinaryLit(idx int, element string, isBinaryLit bool)
CleanElemIsBinaryLit()
Clone() *FieldType
Init(tp byte)
UnmarshalJSON(data []byte) error
}

// NewMutableFieldType create a mutable field type.
func NewMutableFieldType(tp byte) MutableFieldType {
return NewFieldType(tp)
}