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

// 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
}