Files
tidb/pkg/types/context.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)