You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.6 KiB
185 lines
5.6 KiB
// Copyright 2019 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
|
|
|
|
import (
|
|
"math"
|
|
|
|
"github.com/cznic/mathutil"
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/parser/ast"
|
|
"github.com/pingcap/parser/model"
|
|
"github.com/pingcap/tidb/ddl/util"
|
|
"github.com/pingcap/tidb/infoschema"
|
|
"github.com/pingcap/tidb/meta"
|
|
math2 "github.com/pingcap/tidb/util/math"
|
|
)
|
|
|
|
func onCreateSequence(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) {
|
|
schemaID := job.SchemaID
|
|
tbInfo := &model.TableInfo{}
|
|
if err := job.DecodeArgs(tbInfo); err != nil {
|
|
// Invalid arguments, cancel this job.
|
|
job.State = model.JobStateCancelled
|
|
return ver, errors.Trace(err)
|
|
}
|
|
|
|
tbInfo.State = model.StateNone
|
|
err := checkTableNotExists(d, t, schemaID, tbInfo.Name.L)
|
|
if err != nil {
|
|
if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) {
|
|
job.State = model.JobStateCancelled
|
|
}
|
|
return ver, errors.Trace(err)
|
|
}
|
|
|
|
ver, err = updateSchemaVersion(t, job)
|
|
if err != nil {
|
|
return ver, errors.Trace(err)
|
|
}
|
|
|
|
switch tbInfo.State {
|
|
case model.StateNone:
|
|
// none -> public
|
|
tbInfo.State = model.StatePublic
|
|
tbInfo.UpdateTS = t.StartTS
|
|
err = createSequenceWithCheck(t, job, schemaID, tbInfo)
|
|
if err != nil {
|
|
return ver, errors.Trace(err)
|
|
}
|
|
// Finish this job.
|
|
job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo)
|
|
asyncNotifyEvent(d, &util.Event{Tp: model.ActionCreateSequence, TableInfo: tbInfo})
|
|
return ver, nil
|
|
default:
|
|
return ver, ErrInvalidDDLState.GenWithStackByArgs("sequence", tbInfo.State)
|
|
}
|
|
}
|
|
|
|
func createSequenceWithCheck(t *meta.Meta, job *model.Job, schemaID int64, tbInfo *model.TableInfo) error {
|
|
err := checkTableInfoValid(tbInfo)
|
|
if err != nil {
|
|
job.State = model.JobStateCancelled
|
|
return errors.Trace(err)
|
|
}
|
|
var sequenceBase int64
|
|
if tbInfo.Sequence.Increment >= 0 {
|
|
sequenceBase = tbInfo.Sequence.Start - 1
|
|
} else {
|
|
sequenceBase = tbInfo.Sequence.Start + 1
|
|
}
|
|
return t.CreateSequenceAndSetSeqValue(schemaID, tbInfo, sequenceBase)
|
|
}
|
|
|
|
func handleSequenceOptions(SeqOptions []*ast.SequenceOption, sequenceInfo *model.SequenceInfo) {
|
|
var (
|
|
minSetFlag bool
|
|
maxSetFlag bool
|
|
startSetFlag bool
|
|
)
|
|
for _, op := range SeqOptions {
|
|
switch op.Tp {
|
|
case ast.SequenceOptionIncrementBy:
|
|
sequenceInfo.Increment = op.IntValue
|
|
case ast.SequenceStartWith:
|
|
sequenceInfo.Start = op.IntValue
|
|
startSetFlag = true
|
|
case ast.SequenceMinValue:
|
|
sequenceInfo.MinValue = op.IntValue
|
|
minSetFlag = true
|
|
case ast.SequenceMaxValue:
|
|
sequenceInfo.MaxValue = op.IntValue
|
|
maxSetFlag = true
|
|
case ast.SequenceCache:
|
|
sequenceInfo.CacheValue = op.IntValue
|
|
case ast.SequenceNoCache:
|
|
sequenceInfo.Cache = false
|
|
case ast.SequenceCycle:
|
|
sequenceInfo.Cycle = true
|
|
case ast.SequenceNoCycle:
|
|
sequenceInfo.Cycle = false
|
|
}
|
|
}
|
|
// Fill the default value, min/max/start should be adjusted with the sign of sequenceInfo.Increment.
|
|
if !(minSetFlag && maxSetFlag && startSetFlag) {
|
|
if sequenceInfo.Increment >= 0 {
|
|
if !minSetFlag {
|
|
sequenceInfo.MinValue = model.DefaultPositiveSequenceMinValue
|
|
}
|
|
if !startSetFlag {
|
|
sequenceInfo.Start = mathutil.MaxInt64(sequenceInfo.MinValue, model.DefaultPositiveSequenceStartValue)
|
|
}
|
|
if !maxSetFlag {
|
|
sequenceInfo.MaxValue = model.DefaultPositiveSequenceMaxValue
|
|
}
|
|
} else {
|
|
if !maxSetFlag {
|
|
sequenceInfo.MaxValue = model.DefaultNegativeSequenceMaxValue
|
|
}
|
|
if !startSetFlag {
|
|
sequenceInfo.Start = mathutil.MinInt64(sequenceInfo.MaxValue, model.DefaultNegativeSequenceStartValue)
|
|
}
|
|
if !minSetFlag {
|
|
sequenceInfo.MinValue = model.DefaultNegativeSequenceMinValue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func validateSequenceOptions(seqInfo *model.SequenceInfo) bool {
|
|
// To ensure that cache * increment will never overflows.
|
|
var maxIncrement int64
|
|
if seqInfo.Increment == 0 {
|
|
// Increment shouldn't be set as 0.
|
|
return false
|
|
}
|
|
if seqInfo.CacheValue <= 0 {
|
|
// Cache value should be bigger than 0.
|
|
return false
|
|
}
|
|
maxIncrement = math2.Abs(seqInfo.Increment)
|
|
|
|
return seqInfo.MaxValue >= seqInfo.Start &&
|
|
seqInfo.MaxValue > seqInfo.MinValue &&
|
|
seqInfo.Start >= seqInfo.MinValue &&
|
|
seqInfo.MaxValue != math.MaxInt64 &&
|
|
seqInfo.MinValue != math.MinInt64 &&
|
|
seqInfo.CacheValue < (math.MaxInt64-maxIncrement)/maxIncrement
|
|
}
|
|
|
|
func buildSequenceInfo(stmt *ast.CreateSequenceStmt, ident ast.Ident) (*model.SequenceInfo, error) {
|
|
sequenceInfo := &model.SequenceInfo{
|
|
Cache: model.DefaultSequenceCacheBool,
|
|
Cycle: model.DefaultSequenceCycleBool,
|
|
CacheValue: model.DefaultSequenceCacheValue,
|
|
Increment: model.DefaultSequenceIncrementValue,
|
|
}
|
|
|
|
// Handle table comment options.
|
|
for _, op := range stmt.TblOptions {
|
|
switch op.Tp {
|
|
case ast.TableOptionComment:
|
|
sequenceInfo.Comment = op.StrValue
|
|
case ast.TableOptionEngine:
|
|
// TableOptionEngine will always be 'InnoDB', thus we do nothing in this branch to avoid error happening.
|
|
default:
|
|
return nil, ErrSequenceUnsupportedTableOption.GenWithStackByArgs(op.StrValue)
|
|
}
|
|
}
|
|
handleSequenceOptions(stmt.SeqOptions, sequenceInfo)
|
|
if !validateSequenceOptions(sequenceInfo) {
|
|
return nil, ErrSequenceInvalidData.GenWithStackByArgs(ident.Schema.L, ident.Name.L)
|
|
}
|
|
return sequenceInfo, nil
|
|
}
|
|
|