-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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 alter algorithm INPLACE/INSTANT #8811
Changes from 11 commits
470cbdd
dd8fa6d
ee0288c
aa82a65
5de30c5
47c7d15
cf84689
c2c4d55
d3e57b8
cf056bb
ab0a613
f0abc89
25f0b20
d4b9de2
52125ab
cdf3652
b6e2b28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright 2018 PingCAP, Inc. | ||
crazycs520 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package ddl | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/pingcap/parser/ast" | ||
) | ||
|
||
// AlterAlgorithm is used to store supported alter algorithm. | ||
// For now, TiDB only support AlterAlgorithmInplace and AlterAlgorithmInstant. | ||
// The most alter operations are using instant algorithm, and only the add index is using inplace(not really inplace, | ||
// because we never block the DML but costs some time to backfill the index data) | ||
// See https://dev.mysql.com/doc/refman/8.0/en/alter-table.html#alter-table-performance. | ||
type AlterAlgorithm struct { | ||
supported []ast.AlterAlgorithm | ||
// If the alter algorithm is not given, the defAlgorithm will be used. | ||
defAlgorithm ast.AlterAlgorithm | ||
} | ||
|
||
var ( | ||
instantAlgorithm = &AlterAlgorithm{ | ||
supported: []ast.AlterAlgorithm{ast.AlterAlgorithmInstant}, | ||
defAlgorithm: ast.AlterAlgorithmInstant, | ||
} | ||
|
||
inplaceAlgorithm = &AlterAlgorithm{ | ||
supported: []ast.AlterAlgorithm{ast.AlterAlgorithmInplace}, | ||
defAlgorithm: ast.AlterAlgorithmInplace, | ||
} | ||
|
||
defaultAlgorithm = ast.AlterAlgorithmInstant | ||
) | ||
|
||
func getProperAlgorithm(specify ast.AlterAlgorithm, algorithm *AlterAlgorithm) (ast.AlterAlgorithm, error) { | ||
if specify == ast.AlterAlgorithmDefault { | ||
return algorithm.defAlgorithm, nil | ||
} | ||
|
||
for _, a := range algorithm.supported { | ||
if specify == a { | ||
return specify, nil | ||
} | ||
} | ||
|
||
return algorithm.defAlgorithm, ErrAlterOperationNotSupported.GenWithStackByArgs(fmt.Sprintf("ALGORITHM=%s", specify), fmt.Sprintf("Cannot alter table by %s", specify), fmt.Sprintf("ALGORITHM=%s", algorithm.defAlgorithm)) | ||
} | ||
|
||
// ResolveAlterAlgorithm resolves the algorithm of the alterSpec. | ||
// If specify algorithm is not supported by the alter action, errAlterOperationNotSupported will be returned. | ||
// If specify is the ast.AlterAlgorithmDefault, then the default algorithm of the alter action will be returned. | ||
func ResolveAlterAlgorithm(alterSpec *ast.AlterTableSpec, specify ast.AlterAlgorithm) (ast.AlterAlgorithm, error) { | ||
switch alterSpec.Tp { | ||
// For now, TiDB only support inplace algorithm and instant algorithm. | ||
case ast.AlterTableAddConstraint: | ||
return getProperAlgorithm(specify, inplaceAlgorithm) | ||
default: | ||
return getProperAlgorithm(specify, instantAlgorithm) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright 2018 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, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package ddl_test | ||
|
||
import ( | ||
. "github.com/pingcap/check" | ||
"github.com/pingcap/parser/ast" | ||
"github.com/pingcap/tidb/ddl" | ||
) | ||
|
||
var _ = Suite(&testDDLAlgorithmSuite{}) | ||
|
||
var ( | ||
allAlgorithm = []ast.AlterAlgorithm{ast.AlterAlgorithmCopy, | ||
ast.AlterAlgorithmInplace, ast.AlterAlgorithmInstant} | ||
) | ||
|
||
type testDDLAlgorithmSuite struct{} | ||
|
||
type testCase struct { | ||
alterSpec ast.AlterTableSpec | ||
supportedAlgorithm []ast.AlterAlgorithm | ||
defAlgorithm ast.AlterAlgorithm | ||
} | ||
|
||
func (s *testDDLAlgorithmSuite) TestFindAlterAlgorithm(c *C) { | ||
instantAlgorithm := []ast.AlterAlgorithm{ast.AlterAlgorithmInstant} | ||
inplaceAlgorithm := []ast.AlterAlgorithm{ast.AlterAlgorithmInplace} | ||
|
||
testCases := []testCase{ | ||
{ast.AlterTableSpec{Tp: ast.AlterTableAddConstraint}, inplaceAlgorithm, ast.AlterAlgorithmInplace}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableAddColumns}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableDropColumn}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableDropPrimaryKey}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableDropIndex}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableDropForeignKey}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableRenameTable}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableRenameIndex}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
|
||
// Alter table options. | ||
{ast.AlterTableSpec{Tp: ast.AlterTableOption, Options: []*ast.TableOption{{Tp: ast.TableOptionShardRowID}}}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableOption, Options: []*ast.TableOption{{Tp: ast.TableOptionAutoIncrement}}}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableOption, Options: []*ast.TableOption{{Tp: ast.TableOptionComment}}}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableOption, Options: []*ast.TableOption{{Tp: ast.TableOptionCharset}}}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableOption, Options: []*ast.TableOption{{Tp: ast.TableOptionCollate}}}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
|
||
// TODO: after we support migrate the data of partitions, change below cases. | ||
{ast.AlterTableSpec{Tp: ast.AlterTableCoalescePartitions}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableAddPartitions}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableDropPartition}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableTruncatePartition}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
|
||
// TODO: after we support lock a table, change the below case. | ||
{ast.AlterTableSpec{Tp: ast.AlterTableLock}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
// TODO: after we support changing the column type, below cases need to change. | ||
{ast.AlterTableSpec{Tp: ast.AlterTableModifyColumn}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableChangeColumn}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
{ast.AlterTableSpec{Tp: ast.AlterTableAlterColumn}, instantAlgorithm, ast.AlterAlgorithmInstant}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
runAlterAlgorithmTestCases(c, &tc) | ||
} | ||
} | ||
|
||
func runAlterAlgorithmTestCases(c *C, tc *testCase) { | ||
algorithm, err := ddl.ResolveAlterAlgorithm(&tc.alterSpec, ast.AlterAlgorithmDefault) | ||
c.Assert(err, IsNil) | ||
c.Assert(algorithm, Equals, tc.defAlgorithm) | ||
|
||
unsupported := make([]ast.AlterAlgorithm, 0, len(allAlgorithm)) | ||
Loop: | ||
for _, alm := range allAlgorithm { | ||
for _, almSupport := range tc.supportedAlgorithm { | ||
if alm == almSupport { | ||
continue Loop | ||
} | ||
} | ||
|
||
unsupported = append(unsupported, alm) | ||
} | ||
|
||
// Test supported. | ||
for _, alm := range tc.supportedAlgorithm { | ||
algorithm, err = ddl.ResolveAlterAlgorithm(&tc.alterSpec, alm) | ||
c.Assert(err, IsNil) | ||
c.Assert(algorithm, Equals, alm) | ||
} | ||
|
||
// Test unsupported. | ||
for _, alm := range unsupported { | ||
algorithm, err = ddl.ResolveAlterAlgorithm(&tc.alterSpec, alm) | ||
c.Assert(err, NotNil, Commentf("Tp:%v, alm:%s", tc.alterSpec.Tp, alm)) | ||
c.Assert(ddl.ErrAlterOperationNotSupported.Equal(err), IsTrue) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1407,10 +1407,16 @@ func getCharsetAndCollateInTableOption(startIdx int, options []*ast.TableOption) | |
return | ||
} | ||
|
||
func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.AlterTableSpec) (err error) { | ||
// Only handle valid specs. | ||
// resolveAlterTableSpec resolve alter table algorithm and remove ignore table spec in specs. | ||
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. s/resolve/resolves |
||
// returns valied specs, and the occured error. | ||
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. s/returns/Return |
||
func resolveAlterTableSpec(ctx sessionctx.Context, specs []*ast.AlterTableSpec) ([]*ast.AlterTableSpec, error) { | ||
validSpecs := make([]*ast.AlterTableSpec, 0, len(specs)) | ||
algorithm := ast.AlterAlgorithmDefault | ||
for _, spec := range specs { | ||
if spec.Tp == ast.AlterTableAlgorithm { | ||
// find the last AlterTableAlgorithm. | ||
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. s/find/Find |
||
algorithm = spec.Algorithm | ||
} | ||
if isIgnorableSpec(spec.Tp) { | ||
continue | ||
} | ||
|
@@ -1420,7 +1426,29 @@ func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.A | |
if len(validSpecs) != 1 { | ||
// TODO: Hanlde len(validSpecs) == 0. | ||
// Now we only allow one schema changing at the same time. | ||
return errRunMultiSchemaChanges | ||
return nil, errRunMultiSchemaChanges | ||
} | ||
|
||
// Verify whether the algorithm is supported. | ||
for _, spec := range validSpecs { | ||
algorithm, err := ResolveAlterAlgorithm(spec, algorithm) | ||
if err != nil { | ||
// For the compatibility, we return warning instead of error. | ||
ctx.GetSessionVars().StmtCtx.AppendError(err) | ||
err = nil | ||
} | ||
|
||
spec.Algorithm = algorithm | ||
} | ||
|
||
// Only handle valid specs. | ||
return validSpecs, nil | ||
} | ||
|
||
func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.AlterTableSpec) (err error) { | ||
validSpecs, err := resolveAlterTableSpec(ctx, specs) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
|
||
is := d.infoHandle.Get() | ||
|
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.
I think adding the warning message as an argument can be beneficial for calls to other functions.