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