270 lines
10 KiB
Go
270 lines
10 KiB
Go
// Copyright 2023 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 (
|
|
"time"
|
|
|
|
contextutil "github.com/pingcap/tidb/pkg/util/context"
|
|
"github.com/pingcap/tidb/pkg/util/intest"
|
|
)
|
|
|
|
// StrictFlags is a flags with a fields unset and has the most strict behavior.
|
|
const StrictFlags Flags = 0
|
|
|
|
// Flags indicate how to handle the conversion of a value.
|
|
type Flags uint16
|
|
|
|
const (
|
|
// FlagIgnoreTruncateErr indicates to ignore the truncate error.
|
|
// If this flag is set, `FlagTruncateAsWarning` will be ignored.
|
|
FlagIgnoreTruncateErr Flags = 1 << iota
|
|
// FlagTruncateAsWarning indicates to append the truncate error to warnings instead of returning it to user.
|
|
FlagTruncateAsWarning
|
|
// FlagAllowNegativeToUnsigned indicates to allow the casting from negative to unsigned int.
|
|
// When this flag is not set by default, casting a negative value to unsigned
|
|
// results an overflow error, but if SQL mode is not strict, it's converted
|
|
// to 0 with a warning.
|
|
// Otherwise, a negative value will be cast to the corresponding unsigned value without any error.
|
|
// For example, when casting -1 to an unsigned bigint with `FlagAllowNegativeToUnsigned` set,
|
|
// we will get `18446744073709551615` which is the biggest unsigned value.
|
|
FlagAllowNegativeToUnsigned
|
|
// FlagIgnoreZeroDateErr indicates to ignore the zero-date error.
|
|
// See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_date for details about the "zero-date" error.
|
|
// If this flag is set, `FlagZeroDateAsWarning` will be ignored.
|
|
//
|
|
// TODO: `FlagIgnoreZeroDateErr` and `FlagZeroDateAsWarning` don't represent the comments right now, because the
|
|
// errors related with `time` and `duration` are handled directly according to SQL mode in many places (expression,
|
|
// ddl ...). These error handling will be refined in the future. Currently, the `FlagZeroDateAsWarning` is not used,
|
|
// and the `FlagIgnoreZeroDateErr` is used to allow or disallow casting zero to date in `alter` statement. See #25728
|
|
// This flag is the reverse of `NoZeroDate` in #30507. It's set to `true` for most context, and is only set to
|
|
// `false` for `alter` (and `create`) statements.
|
|
FlagIgnoreZeroDateErr
|
|
// FlagIgnoreZeroInDateErr indicates to ignore the zero-in-date error.
|
|
// See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_in_date for details about the "zero-in-date" error.
|
|
FlagIgnoreZeroInDateErr
|
|
// FlagIgnoreInvalidDateErr indicates to ignore the invalid-date error.
|
|
// See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_allow_invalid_dates for details about the "invalid-date" error.
|
|
FlagIgnoreInvalidDateErr
|
|
// FlagSkipASCIICheck indicates to skip the ASCII check when converting the value to an ASCII string.
|
|
FlagSkipASCIICheck
|
|
// FlagSkipUTF8Check indicates to skip the UTF8 check when converting the value to an UTF8MB3 string.
|
|
FlagSkipUTF8Check
|
|
// FlagSkipUTF8MB4Check indicates to skip the UTF8MB4 check when converting the value to an UTF8 string.
|
|
FlagSkipUTF8MB4Check
|
|
// FlagCastTimeToYearThroughConcat indicates to cast time to year through concatenation. For example, `00:19:59` will be converted to '1959'
|
|
FlagCastTimeToYearThroughConcat
|
|
)
|
|
|
|
// AllowNegativeToUnsigned indicates whether the flag `FlagAllowNegativeToUnsigned` is set
|
|
func (f Flags) AllowNegativeToUnsigned() bool {
|
|
return f&FlagAllowNegativeToUnsigned != 0
|
|
}
|
|
|
|
// WithAllowNegativeToUnsigned returns a new flags with `FlagAllowNegativeToUnsigned` set/unset according to the clip parameter
|
|
func (f Flags) WithAllowNegativeToUnsigned(clip bool) Flags {
|
|
if clip {
|
|
return f | FlagAllowNegativeToUnsigned
|
|
}
|
|
return f &^ FlagAllowNegativeToUnsigned
|
|
}
|
|
|
|
// SkipASCIICheck indicates whether the flag `FlagSkipASCIICheck` is set
|
|
func (f Flags) SkipASCIICheck() bool {
|
|
return f&FlagSkipASCIICheck != 0
|
|
}
|
|
|
|
// WithSkipSACIICheck returns a new flags with `FlagSkipASCIICheck` set/unset according to the skip parameter
|
|
func (f Flags) WithSkipSACIICheck(skip bool) Flags {
|
|
if skip {
|
|
return f | FlagSkipASCIICheck
|
|
}
|
|
return f &^ FlagSkipASCIICheck
|
|
}
|
|
|
|
// SkipUTF8Check indicates whether the flag `FlagSkipUTF8Check` is set
|
|
func (f Flags) SkipUTF8Check() bool {
|
|
return f&FlagSkipUTF8Check != 0
|
|
}
|
|
|
|
// WithSkipUTF8Check returns a new flags with `FlagSkipUTF8Check` set/unset according to the skip parameter
|
|
func (f Flags) WithSkipUTF8Check(skip bool) Flags {
|
|
if skip {
|
|
return f | FlagSkipUTF8Check
|
|
}
|
|
return f &^ FlagSkipUTF8Check
|
|
}
|
|
|
|
// SkipUTF8MB4Check indicates whether the flag `FlagSkipUTF8MB4Check` is set
|
|
func (f Flags) SkipUTF8MB4Check() bool {
|
|
return f&FlagSkipUTF8MB4Check != 0
|
|
}
|
|
|
|
// WithSkipUTF8MB4Check returns a new flags with `FlagSkipUTF8MB4Check` set/unset according to the skip parameter
|
|
func (f Flags) WithSkipUTF8MB4Check(skip bool) Flags {
|
|
if skip {
|
|
return f | FlagSkipUTF8MB4Check
|
|
}
|
|
return f &^ FlagSkipUTF8MB4Check
|
|
}
|
|
|
|
// IgnoreTruncateErr indicates whether the flag `FlagIgnoreTruncateErr` is set
|
|
func (f Flags) IgnoreTruncateErr() bool {
|
|
return f&FlagIgnoreTruncateErr != 0
|
|
}
|
|
|
|
// WithIgnoreTruncateErr returns a new flags with `FlagIgnoreTruncateErr` set/unset according to the skip parameter
|
|
func (f Flags) WithIgnoreTruncateErr(ignore bool) Flags {
|
|
if ignore {
|
|
return f | FlagIgnoreTruncateErr
|
|
}
|
|
return f &^ FlagIgnoreTruncateErr
|
|
}
|
|
|
|
// TruncateAsWarning indicates whether the flag `FlagTruncateAsWarning` is set
|
|
func (f Flags) TruncateAsWarning() bool {
|
|
return f&FlagTruncateAsWarning != 0
|
|
}
|
|
|
|
// WithTruncateAsWarning returns a new flags with `FlagTruncateAsWarning` set/unset according to the skip parameter
|
|
func (f Flags) WithTruncateAsWarning(warn bool) Flags {
|
|
if warn {
|
|
return f | FlagTruncateAsWarning
|
|
}
|
|
return f &^ FlagTruncateAsWarning
|
|
}
|
|
|
|
// IgnoreZeroInDate indicates whether the flag `FlagIgnoreZeroInData` is set
|
|
func (f Flags) IgnoreZeroInDate() bool {
|
|
return f&FlagIgnoreZeroInDateErr != 0
|
|
}
|
|
|
|
// WithIgnoreZeroInDate returns a new flags with `FlagIgnoreZeroInDateErr` set/unset according to the ignore parameter
|
|
func (f Flags) WithIgnoreZeroInDate(ignore bool) Flags {
|
|
if ignore {
|
|
return f | FlagIgnoreZeroInDateErr
|
|
}
|
|
return f &^ FlagIgnoreZeroInDateErr
|
|
}
|
|
|
|
// IgnoreInvalidDateErr indicates whether the flag `FlagIgnoreInvalidDateErr` is set
|
|
func (f Flags) IgnoreInvalidDateErr() bool {
|
|
return f&FlagIgnoreInvalidDateErr != 0
|
|
}
|
|
|
|
// WithIgnoreInvalidDateErr returns a new flags with `FlagIgnoreInvalidDateErr` set/unset according to the ignore parameter
|
|
func (f Flags) WithIgnoreInvalidDateErr(ignore bool) Flags {
|
|
if ignore {
|
|
return f | FlagIgnoreInvalidDateErr
|
|
}
|
|
return f &^ FlagIgnoreInvalidDateErr
|
|
}
|
|
|
|
// IgnoreZeroDateErr indicates whether the flag `FlagIgnoreZeroDateErr` is set
|
|
func (f Flags) IgnoreZeroDateErr() bool {
|
|
return f&FlagIgnoreZeroDateErr != 0
|
|
}
|
|
|
|
// WithIgnoreZeroDateErr returns a new flags with `FlagIgnoreZeroDateErr` set/unset according to the ignore parameter
|
|
func (f Flags) WithIgnoreZeroDateErr(ignore bool) Flags {
|
|
if ignore {
|
|
return f | FlagIgnoreZeroDateErr
|
|
}
|
|
return f &^ FlagIgnoreZeroDateErr
|
|
}
|
|
|
|
// CastTimeToYearThroughConcat whether `FlagCastTimeToYearThroughConcat` is set
|
|
func (f Flags) CastTimeToYearThroughConcat() bool {
|
|
return f&FlagCastTimeToYearThroughConcat != 0
|
|
}
|
|
|
|
// WithCastTimeToYearThroughConcat returns a new flags with `FlagCastTimeToYearThroughConcat` set/unset according to the flag parameter
|
|
func (f Flags) WithCastTimeToYearThroughConcat(flag bool) Flags {
|
|
if flag {
|
|
return f | FlagCastTimeToYearThroughConcat
|
|
}
|
|
return f &^ FlagCastTimeToYearThroughConcat
|
|
}
|
|
|
|
// Context provides the information when converting between different types.
|
|
type Context struct {
|
|
flags Flags
|
|
loc *time.Location
|
|
warnHandler contextutil.WarnAppender
|
|
}
|
|
|
|
// NewContext creates a new `Context`
|
|
func NewContext(flags Flags, loc *time.Location, handler contextutil.WarnAppender) Context {
|
|
intest.Assert(loc != nil && handler != nil)
|
|
return Context{
|
|
flags: flags,
|
|
loc: loc,
|
|
warnHandler: handler,
|
|
}
|
|
}
|
|
|
|
// Flags returns the flags of the context
|
|
func (c *Context) Flags() Flags {
|
|
return c.flags
|
|
}
|
|
|
|
// WithFlags returns a new context with the flags set to the given value
|
|
func (c *Context) WithFlags(f Flags) Context {
|
|
ctx := *c
|
|
ctx.flags = f
|
|
return ctx
|
|
}
|
|
|
|
// WithLocation returns a new context with the given location
|
|
func (c *Context) WithLocation(loc *time.Location) Context {
|
|
intest.AssertNotNil(loc)
|
|
ctx := *c
|
|
ctx.loc = loc
|
|
return ctx
|
|
}
|
|
|
|
// Location returns the location of the context
|
|
func (c *Context) Location() *time.Location {
|
|
intest.AssertNotNil(c.loc)
|
|
if c.loc == nil {
|
|
// c.loc should always not be nil, just make the code safe here.
|
|
return time.UTC
|
|
}
|
|
return c.loc
|
|
}
|
|
|
|
// AppendWarning appends the error to warning. If the inner `warnHandler` is nil, do nothing.
|
|
func (c *Context) AppendWarning(err error) {
|
|
intest.Assert(c.warnHandler != nil)
|
|
if w := c.warnHandler; w != nil {
|
|
// warnHandler should always not be nil, check fn != nil here to just make code safe.
|
|
w.AppendWarning(err)
|
|
}
|
|
}
|
|
|
|
// DefaultStmtFlags is the default flags for statement context with the flag `FlagAllowNegativeToUnsigned` set.
|
|
// TODO: make DefaultStmtFlags to be equal with StrictFlags, and setting flag `FlagAllowNegativeToUnsigned`
|
|
// is only for make the code to be equivalent with the old implement during refactoring.
|
|
const DefaultStmtFlags = StrictFlags | FlagAllowNegativeToUnsigned | FlagIgnoreZeroDateErr
|
|
|
|
// DefaultStmtNoWarningContext is the context with default statement flags without any other special configuration
|
|
var DefaultStmtNoWarningContext = NewContext(DefaultStmtFlags, time.UTC, contextutil.IgnoreWarn)
|
|
|
|
// StrictContext is the most strict context which returns every error it meets
|
|
//
|
|
// this context should never append warnings
|
|
// However, the implementation of `types` may still append some warnings. TODO: remove them in the future.
|
|
var StrictContext = NewContext(StrictFlags, time.UTC, contextutil.IgnoreWarn)
|