-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
ddl: Support exchange partition #17149
Changes from 76 commits
70f6354
57af241
af97469
adef48e
1a7f599
11809d0
c8bc050
5a32fe4
35941aa
7468924
64fd709
cb88c18
71cf840
a902cdc
a228468
9ff0790
dda9d75
32c6285
899033f
f4f5b38
a38b5e0
3cec6f2
53f4f73
101c673
a1ca360
40db78a
c559f12
f9fdd0c
c979e69
c096c8c
4130a9a
0ffd1ab
488cb5f
b5cfdd3
fac04c1
be92bb3
3f38e2b
d9bc6fa
ea173bf
57aeb95
5a29bdd
aa8da8e
ebce341
65f4c64
5a46b18
ec51f5e
a93fe24
95b2554
134c55d
d724823
894b7e5
40729a1
6ea633d
d9b98c0
b194f11
7807380
4769d3b
325d596
33e0c36
6eeb3fd
b46a6bb
85d71c0
a4d0161
6c1d63a
b5b6824
999b0d9
2cf5d03
52676f8
6bc1a4a
9cd7caa
771e4dd
f2ab275
dc8abd3
44f6018
3ec7b7a
f3fc2e7
8396b42
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2212,8 +2212,6 @@ func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.A | |
err = errors.Trace(errUnsupportedOptimizePartition) | ||
case ast.AlterTableRemovePartitioning: | ||
err = errors.Trace(errUnsupportedRemovePartition) | ||
case ast.AlterTableExchangePartition: | ||
err = errors.Trace(errUnsupportedExchangePartition) | ||
case ast.AlterTableDropColumn: | ||
err = d.DropColumn(ctx, ident, spec) | ||
case ast.AlterTableDropIndex: | ||
|
@@ -2226,6 +2224,8 @@ func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.A | |
err = d.DropTablePartition(ctx, ident, spec) | ||
case ast.AlterTableTruncatePartition: | ||
err = d.TruncateTablePartition(ctx, ident, spec) | ||
case ast.AlterTableExchangePartition: | ||
err = d.ExchangeTablePartition(ctx, ident, spec) | ||
case ast.AlterTableAddConstraint: | ||
constr := spec.Constraint | ||
switch spec.Constraint.Tp { | ||
|
@@ -2454,7 +2454,7 @@ func checkAndCreateNewColumn(ctx sessionctx.Context, ti ast.Ident, schema *model | |
} | ||
|
||
if option.Stored { | ||
return nil, errUnsupportedOnGeneratedColumn.GenWithStackByArgs("Adding generated stored column through ALTER TABLE") | ||
return nil, ErrUnsupportedOnGeneratedColumn.GenWithStackByArgs("Adding generated stored column through ALTER TABLE") | ||
} | ||
|
||
_, dependColNames := findDependedColumnNames(specNewColumn) | ||
|
@@ -2816,6 +2816,174 @@ func (d *ddl) DropTablePartition(ctx sessionctx.Context, ident ast.Ident, spec * | |
return errors.Trace(err) | ||
} | ||
|
||
func checkFieldTypeCompatible(ft *types.FieldType, other *types.FieldType) bool { | ||
// int(1) could match the type with int(8) | ||
partialEqual := ft.Tp == other.Tp && | ||
ft.Decimal == other.Decimal && | ||
ft.Charset == other.Charset && | ||
ft.Collate == other.Collate && | ||
(ft.Flen == other.Flen || ft.StorageLength() != types.VarStorageLen) && | ||
mysql.HasUnsignedFlag(ft.Flag) == mysql.HasUnsignedFlag(other.Flag) && | ||
mysql.HasAutoIncrementFlag(ft.Flag) == mysql.HasAutoIncrementFlag(other.Flag) && | ||
mysql.HasNotNullFlag(ft.Flag) == mysql.HasNotNullFlag(other.Flag) && | ||
mysql.HasZerofillFlag(ft.Flag) == mysql.HasZerofillFlag(other.Flag) && | ||
mysql.HasBinaryFlag(ft.Flag) == mysql.HasBinaryFlag(other.Flag) && | ||
mysql.HasPriKeyFlag(ft.Flag) == mysql.HasPriKeyFlag(other.Flag) | ||
if !partialEqual || len(ft.Elems) != len(other.Elems) { | ||
return false | ||
} | ||
for i := range ft.Elems { | ||
if ft.Elems[i] != other.Elems[i] { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
func checkTableDefCompatible(source *model.TableInfo, target *model.TableInfo) error { | ||
// check auto_random | ||
if source.AutoRandomBits != target.AutoRandomBits || | ||
source.Charset != target.Charset || | ||
source.Collate != target.Collate || | ||
source.ShardRowIDBits != target.ShardRowIDBits || | ||
source.MaxShardRowIDBits != target.MaxShardRowIDBits || | ||
source.TiFlashReplica != target.TiFlashReplica { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
if len(source.Cols()) != len(target.Cols()) { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
// Col compatible check | ||
for i, sourceCol := range source.Cols() { | ||
targetCol := target.Cols()[i] | ||
if isVirtualGeneratedColumn(sourceCol) != isVirtualGeneratedColumn(targetCol) { | ||
return ErrUnsupportedOnGeneratedColumn.GenWithStackByArgs("Exchanging partitions for non-generated columns") | ||
} | ||
// It should strictyle compare expressions for generated columns | ||
if sourceCol.Name.L != targetCol.Name.L || | ||
zhaox1n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sourceCol.Hidden != targetCol.Hidden || | ||
!checkFieldTypeCompatible(&sourceCol.FieldType, &targetCol.FieldType) || | ||
sourceCol.GeneratedExprString != targetCol.GeneratedExprString { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
crazycs520 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if sourceCol.State != model.StatePublic || | ||
targetCol.State != model.StatePublic { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
if sourceCol.ID != targetCol.ID { | ||
return ErrPartitionExchangeDifferentOption.GenWithStackByArgs(fmt.Sprintf("column: %s", sourceCol.Name)) | ||
} | ||
} | ||
if len(source.Indices) != len(target.Indices) { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
for _, sourceIdx := range source.Indices { | ||
var compatIdx *model.IndexInfo | ||
for _, targetIdx := range target.Indices { | ||
if strings.EqualFold(sourceIdx.Name.L, targetIdx.Name.L) { | ||
compatIdx = targetIdx | ||
crazycs520 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
// No match index | ||
if compatIdx == nil { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
// Index type is not compatible | ||
if sourceIdx.Tp != compatIdx.Tp || | ||
tangenta marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sourceIdx.Unique != compatIdx.Unique || | ||
sourceIdx.Primary != compatIdx.Primary { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @crazycs520 It has compared the Primary key here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, they are different. For table There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done @crazycs520 PTAL |
||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
// The index column | ||
if len(sourceIdx.Columns) != len(compatIdx.Columns) { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
for i, sourceIdxCol := range sourceIdx.Columns { | ||
compatIdxCol := compatIdx.Columns[i] | ||
if sourceIdxCol.Length != compatIdxCol.Length || | ||
sourceIdxCol.Name.L != compatIdxCol.Name.L { | ||
return errors.Trace(ErrTablesDifferentMetadata) | ||
} | ||
} | ||
if sourceIdx.ID != compatIdx.ID { | ||
return ErrPartitionExchangeDifferentOption.GenWithStackByArgs(fmt.Sprintf("index: %s", sourceIdx.Name)) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func checkExchangePartition(pt *model.TableInfo, nt *model.TableInfo) error { | ||
if nt.IsView() || nt.IsSequence() { | ||
return errors.Trace(ErrCheckNoSuchTable) | ||
} | ||
if pt.GetPartitionInfo() == nil { | ||
return errors.Trace(ErrPartitionMgmtOnNonpartitioned) | ||
} | ||
if nt.GetPartitionInfo() != nil { | ||
return errors.Trace(ErrPartitionExchangePartTable.GenWithStackByArgs(nt.Name)) | ||
} | ||
|
||
if nt.ForeignKeys != nil { | ||
return errors.Trace(ErrPartitionExchangeForeignKey.GenWithStackByArgs(nt.Name)) | ||
} | ||
|
||
// NOTE: if nt is temporary table, it should be checked | ||
tiancaiamao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil | ||
} | ||
|
||
func (d *ddl) ExchangeTablePartition(ctx sessionctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { | ||
ptSchema, pt, err := d.getSchemaAndTableByIdent(ctx, ident) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
|
||
ptMeta := pt.Meta() | ||
|
||
ntIdent := ast.Ident{Schema: spec.NewTable.Schema, Name: spec.NewTable.Name} | ||
ntSchema, nt, err := d.getSchemaAndTableByIdent(ctx, ntIdent) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
|
||
ntMeta := nt.Meta() | ||
|
||
err = checkExchangePartition(ptMeta, ntMeta) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
|
||
partName := spec.PartitionNames[0].L | ||
|
||
// NOTE: if pt is subPartitioned, it should be checked | ||
|
||
defID, err := tables.FindPartitionByName(ptMeta, partName) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
|
||
err = checkTableDefCompatible(ptMeta, ntMeta) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
|
||
job := &model.Job{ | ||
SchemaID: ntSchema.ID, | ||
TableID: ntMeta.ID, | ||
SchemaName: ntSchema.Name.L, | ||
Type: model.ActionExchangeTablePartition, | ||
BinlogInfo: &model.HistoryInfo{}, | ||
Args: []interface{}{defID, ptSchema.ID, ptMeta.ID, partName, spec.WithValidation}, | ||
} | ||
|
||
err = d.doDDLJob(ctx, job) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
err = d.callHookOnChanged(err) | ||
return errors.Trace(err) | ||
} | ||
|
||
// DropColumn will drop a column from the table, now we don't support drop the column with index covered. | ||
func (d *ddl) DropColumn(ctx sessionctx.Context, ti ast.Ident, spec *ast.AlterTableSpec) error { | ||
schema, t, err := d.getSchemaAndTableByIdent(ctx, ti) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, why not use
ft.Flag == other.Flag
to compare directly.BTW, below SQL will success in This PR but failed in MySQL5.7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that
(a int key)
equals to(a int not null)
intidb
. To compatible with mysql, there are the other attribution that we needn't to compare with such asdefault value
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still need to compare more flags such as
PriKeyFlag