提交 fc7d57af 编写于 作者: martianzhang's avatar martianzhang

update vendor

上级 bbe53410
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
// 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 log
import (
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
const (
defaultLogMaxSize = 300 // MB
)
// FileLogConfig serializes file log related config in toml/json.
type FileLogConfig struct {
// Log filename, leave empty to disable file log.
Filename string `toml:"filename" json:"filename"`
// Is log rotate enabled.
LogRotate bool `toml:"log-rotate" json:"log-rotate"`
// Max size for a single file, in MB.
MaxSize int `toml:"max-size" json:"max-size"`
// Max log keep days, default is never deleting.
MaxDays int `toml:"max-days" json:"max-days"`
// Maximum number of old log files to retain.
MaxBackups int `toml:"max-backups" json:"max-backups"`
}
// Config serializes log related config in toml/json.
type Config struct {
// Log level.
Level string `toml:"level" json:"level"`
// Log format. one of json, text, or console.
Format string `toml:"format" json:"format"`
// Disable automatic timestamps in output.
DisableTimestamp bool `toml:"disable-timestamp" json:"disable-timestamp"`
// File log config.
File FileLogConfig `toml:"file" json:"file"`
// Development puts the logger in development mode, which changes the
// behavior of DPanicLevel and takes stacktraces more liberally.
Development bool `toml:"development" json:"development"`
// DisableCaller stops annotating logs with the calling function's file
// name and line number. By default, all logs are annotated.
DisableCaller bool `toml:"disable-caller" json:"disable-caller"`
// DisableStacktrace completely disables automatic stacktrace capturing. By
// default, stacktraces are captured for WarnLevel and above logs in
// development and ErrorLevel and above in production.
DisableStacktrace bool `toml:"disable-stacktrace" json:"disable-stacktrace"`
// SamplingConfig sets a sampling strategy for the logger. Sampling caps the
// global CPU and I/O load that logging puts on your process while attempting
// to preserve a representative subset of your logs.
//
// Values configured here are per-second. See zapcore.NewSampler for details.
Sampling *zap.SamplingConfig `toml:"sampling" json:"sampling"`
}
// ZapProperties records some information about zap.
type ZapProperties struct {
Core zapcore.Core
Syncer zapcore.WriteSyncer
Level zap.AtomicLevel
}
func newZapTextEncoder(cfg *Config) zapcore.Encoder {
cc := zapcore.EncoderConfig{
// Keys can be anything except the empty string.
TimeKey: "time",
LevelKey: "level",
NameKey: "name",
CallerKey: "caller",
MessageKey: "message",
StacktraceKey: "stack",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: DefaultTimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
EncodeCaller: ShortCallerEncoder,
}
if cfg.DisableTimestamp {
cc.TimeKey = ""
}
return NewTextEncoder(cc)
}
func (cfg *Config) buildOptions(errSink zapcore.WriteSyncer) []zap.Option {
opts := []zap.Option{zap.ErrorOutput(errSink)}
if cfg.Development {
opts = append(opts, zap.Development())
}
if !cfg.DisableCaller {
opts = append(opts, zap.AddCaller())
}
stackLevel := zap.ErrorLevel
if cfg.Development {
stackLevel = zap.WarnLevel
}
if !cfg.DisableStacktrace {
opts = append(opts, zap.AddStacktrace(stackLevel))
}
if cfg.Sampling != nil {
opts = append(opts, zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewSampler(core, time.Second, int(cfg.Sampling.Initial), int(cfg.Sampling.Thereafter))
}))
}
return opts
}
// 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 log
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// Debug logs a message at DebugLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Debug(msg string, fields ...zap.Field) {
L().WithOptions(zap.AddCallerSkip(1)).Debug(msg, fields...)
}
// Info logs a message at InfoLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Info(msg string, fields ...zap.Field) {
L().WithOptions(zap.AddCallerSkip(1)).Info(msg, fields...)
}
// Warn logs a message at WarnLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Warn(msg string, fields ...zap.Field) {
L().WithOptions(zap.AddCallerSkip(1)).Warn(msg, fields...)
}
// Error logs a message at ErrorLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Error(msg string, fields ...zap.Field) {
L().WithOptions(zap.AddCallerSkip(1)).Error(msg, fields...)
}
// Panic logs a message at PanicLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
//
// The logger then panics, even if logging at PanicLevel is disabled.
func Panic(msg string, fields ...zap.Field) {
L().WithOptions(zap.AddCallerSkip(1)).Panic(msg, fields...)
}
// Fatal logs a message at FatalLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
//
// The logger then calls os.Exit(1), even if logging at FatalLevel is
// disabled.
func Fatal(msg string, fields ...zap.Field) {
L().WithOptions(zap.AddCallerSkip(1)).Fatal(msg, fields...)
}
// SetLevel alters the logging level.
func SetLevel(l zapcore.Level) {
_globalP.Level.SetLevel(l)
}
// GetLevel gets the logging level.
func GetLevel() zapcore.Level {
return _globalP.Level.Level()
}
module github.com/pingcap/log
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8
github.com/pingcap/errors v0.11.0
github.com/pkg/errors v0.8.1 // indirect
github.com/stretchr/testify v1.3.0 // indirect
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.2.2 // indirect
)
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg=
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ=
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
// 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 log
import (
"errors"
"os"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
// InitLogger initializes a zap logger.
func InitLogger(cfg *Config, opts ...zap.Option) (*zap.Logger, *ZapProperties, error) {
var output zapcore.WriteSyncer
if len(cfg.File.Filename) > 0 {
lg, err := initFileLog(&cfg.File)
if err != nil {
return nil, nil, err
}
output = zapcore.AddSync(lg)
} else {
stdOut, close, err := zap.Open([]string{"stdout"}...)
if err != nil {
close()
return nil, nil, err
}
output = stdOut
}
level := zap.NewAtomicLevel()
err := level.UnmarshalText([]byte(cfg.Level))
if err != nil {
return nil, nil, err
}
core := NewTextCore(newZapTextEncoder(cfg).(*textEncoder), output, level)
opts = append(opts, cfg.buildOptions(output)...)
lg := zap.New(core, opts...)
r := &ZapProperties{
Core: core,
Syncer: output,
Level: level,
}
return lg, r, nil
}
// initFileLog initializes file based logging options.
func initFileLog(cfg *FileLogConfig) (*lumberjack.Logger, error) {
if st, err := os.Stat(cfg.Filename); err == nil {
if st.IsDir() {
return nil, errors.New("can't use directory as log file name")
}
}
if cfg.MaxSize == 0 {
cfg.MaxSize = defaultLogMaxSize
}
// use lumberjack to logrotate
return &lumberjack.Logger{
Filename: cfg.Filename,
MaxSize: cfg.MaxSize,
MaxBackups: cfg.MaxBackups,
MaxAge: cfg.MaxDays,
LocalTime: true,
}, nil
}
func newStdLogger() (*zap.Logger, *ZapProperties) {
conf := &Config{Level: "info", File: FileLogConfig{}}
lg, r, _ := InitLogger(conf)
return lg, r
}
var (
_globalL, _globalP = newStdLogger()
_globalS = _globalL.Sugar()
)
// L returns the global Logger, which can be reconfigured with ReplaceGlobals.
// It's safe for concurrent use.
func L() *zap.Logger {
return _globalL
}
// S returns the global SugaredLogger, which can be reconfigured with
// ReplaceGlobals. It's safe for concurrent use.
func S() *zap.SugaredLogger {
return _globalS
}
// ReplaceGlobals replaces the global Logger and SugaredLogger.
// It's unsafe for concurrent use.
func ReplaceGlobals(logger *zap.Logger, props *ZapProperties) {
_globalL = logger
_globalS = logger.Sugar()
_globalP = props
}
// Sync flushes any buffered log entries.
func Sync() error {
err := L().Sync()
if err != nil {
return err
}
return S().Sync()
}
// 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 log
import "go.uber.org/zap/zapcore"
// NewTextCore creates a Core that writes logs to a WriteSyncer.
func NewTextCore(enc *textEncoder, ws zapcore.WriteSyncer, enab zapcore.LevelEnabler) zapcore.Core {
return &textIOCore{
LevelEnabler: enab,
enc: enc,
out: ws,
}
}
// textIOCore is a copy of zapcore.ioCore that only accept *textEncoder
// it can be removed after https://github.com/uber-go/zap/pull/685 be merged
type textIOCore struct {
zapcore.LevelEnabler
enc *textEncoder
out zapcore.WriteSyncer
}
func (c *textIOCore) With(fields []zapcore.Field) zapcore.Core {
clone := c.clone()
// it's different to ioCore, here call textEncoder#addFields to fix https://github.com/pingcap/log/issues/3
clone.enc.addFields(fields)
return clone
}
func (c *textIOCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if c.Enabled(ent.Level) {
return ce.AddCore(ent, c)
}
return ce
}
func (c *textIOCore) Write(ent zapcore.Entry, fields []zapcore.Field) error {
buf, err := c.enc.EncodeEntry(ent, fields)
if err != nil {
return err
}
_, err = c.out.Write(buf.Bytes())
buf.Free()
if err != nil {
return err
}
if ent.Level > zapcore.ErrorLevel {
// Since we may be crashing the program, sync the output. Ignore Sync
// errors, pending a clean solution to issue https://github.com/uber-go/zap/issues/370.
c.Sync()
}
return nil
}
func (c *textIOCore) Sync() error {
return c.out.Sync()
}
func (c *textIOCore) clone() *textIOCore {
return &textIOCore{
LevelEnabler: c.LevelEnabler,
enc: c.enc.Clone().(*textEncoder),
out: c.out,
}
}
// 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.
// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package log
import (
"encoding/base64"
"encoding/json"
"fmt"
"math"
"strings"
"sync"
"time"
"unicode/utf8"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
)
// DefaultTimeEncoder serializes time.Time to a human-readable formatted string
func DefaultTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
s := t.Format("2006/01/02 15:04:05.000 -07:00")
if e, ok := enc.(*textEncoder); ok {
for _, c := range []byte(s) {
e.buf.AppendByte(c)
}
return
}
enc.AppendString(s)
}
// ShortCallerEncoder serializes a caller in file:line format.
func ShortCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(getCallerString(caller))
}
func getCallerString(ec zapcore.EntryCaller) string {
if !ec.Defined {
return "<unknown>"
}
idx := strings.LastIndexByte(ec.File, '/')
buf := _pool.Get()
for i := idx + 1; i < len(ec.File); i++ {
b := ec.File[i]
switch {
case b >= 'A' && b <= 'Z':
buf.AppendByte(b)
case b >= 'a' && b <= 'z':
buf.AppendByte(b)
case b >= '0' && b <= '9':
buf.AppendByte(b)
case b == '.' || b == '-' || b == '_':
buf.AppendByte(b)
default:
}
}
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
caller := buf.String()
buf.Free()
return caller
}
// For JSON-escaping; see textEncoder.safeAddString below.
const _hex = "0123456789abcdef"
var _textPool = sync.Pool{New: func() interface{} {
return &textEncoder{}
}}
var (
_pool = buffer.NewPool()
// Get retrieves a buffer from the pool, creating one if necessary.
Get = _pool.Get
)
func getTextEncoder() *textEncoder {
return _textPool.Get().(*textEncoder)
}
func putTextEncoder(enc *textEncoder) {
if enc.reflectBuf != nil {
enc.reflectBuf.Free()
}
enc.EncoderConfig = nil
enc.buf = nil
enc.spaced = false
enc.openNamespaces = 0
enc.reflectBuf = nil
enc.reflectEnc = nil
_textPool.Put(enc)
}
type textEncoder struct {
*zapcore.EncoderConfig
buf *buffer.Buffer
spaced bool // include spaces after colons and commas
openNamespaces int
// for encoding generic values by reflection
reflectBuf *buffer.Buffer
reflectEnc *json.Encoder
}
// NewTextEncoder creates a fast, low-allocation Text encoder. The encoder
// appropriately escapes all field keys and values.
func NewTextEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder {
return &textEncoder{
EncoderConfig: &cfg,
buf: _pool.Get(),
spaced: false,
}
}
func (enc *textEncoder) AddArray(key string, arr zapcore.ArrayMarshaler) error {
enc.addKey(key)
return enc.AppendArray(arr)
}
func (enc *textEncoder) AddObject(key string, obj zapcore.ObjectMarshaler) error {
enc.addKey(key)
return enc.AppendObject(obj)
}
func (enc *textEncoder) AddBinary(key string, val []byte) {
enc.AddString(key, base64.StdEncoding.EncodeToString(val))
}
func (enc *textEncoder) AddByteString(key string, val []byte) {
enc.addKey(key)
enc.AppendByteString(val)
}
func (enc *textEncoder) AddBool(key string, val bool) {
enc.addKey(key)
enc.AppendBool(val)
}
func (enc *textEncoder) AddComplex128(key string, val complex128) {
enc.addKey(key)
enc.AppendComplex128(val)
}
func (enc *textEncoder) AddDuration(key string, val time.Duration) {
enc.addKey(key)
enc.AppendDuration(val)
}
func (enc *textEncoder) AddFloat64(key string, val float64) {
enc.addKey(key)
enc.AppendFloat64(val)
}
func (enc *textEncoder) AddInt64(key string, val int64) {
enc.addKey(key)
enc.AppendInt64(val)
}
func (enc *textEncoder) resetReflectBuf() {
if enc.reflectBuf == nil {
enc.reflectBuf = _pool.Get()
enc.reflectEnc = json.NewEncoder(enc.reflectBuf)
} else {
enc.reflectBuf.Reset()
}
}
func (enc *textEncoder) AddReflected(key string, obj interface{}) error {
enc.resetReflectBuf()
err := enc.reflectEnc.Encode(obj)
if err != nil {
return err
}
enc.reflectBuf.TrimNewline()
enc.addKey(key)
enc.AppendByteString(enc.reflectBuf.Bytes())
return nil
}
func (enc *textEncoder) OpenNamespace(key string) {
enc.addKey(key)
enc.buf.AppendByte('{')
enc.openNamespaces++
}
func (enc *textEncoder) AddString(key, val string) {
enc.addKey(key)
enc.AppendString(val)
}
func (enc *textEncoder) AddTime(key string, val time.Time) {
enc.addKey(key)
enc.AppendTime(val)
}
func (enc *textEncoder) AddUint64(key string, val uint64) {
enc.addKey(key)
enc.AppendUint64(val)
}
func (enc *textEncoder) AppendArray(arr zapcore.ArrayMarshaler) error {
enc.addElementSeparator()
ne := enc.cloned()
ne.buf.AppendByte('[')
err := arr.MarshalLogArray(ne)
ne.buf.AppendByte(']')
enc.AppendByteString(ne.buf.Bytes())
ne.buf.Free()
putTextEncoder(ne)
return err
}
func (enc *textEncoder) AppendObject(obj zapcore.ObjectMarshaler) error {
enc.addElementSeparator()
ne := enc.cloned()
ne.buf.AppendByte('{')
err := obj.MarshalLogObject(ne)
ne.buf.AppendByte('}')
enc.AppendByteString(ne.buf.Bytes())
ne.buf.Free()
putTextEncoder(ne)
return err
}
func (enc *textEncoder) AppendBool(val bool) {
enc.addElementSeparator()
enc.buf.AppendBool(val)
}
func (enc *textEncoder) AppendByteString(val []byte) {
enc.addElementSeparator()
if !enc.needDoubleQuotes(string(val)) {
enc.safeAddByteString(val)
return
}
enc.buf.AppendByte('"')
enc.safeAddByteString(val)
enc.buf.AppendByte('"')
}
func (enc *textEncoder) AppendComplex128(val complex128) {
enc.addElementSeparator()
// Cast to a platform-independent, fixed-size type.
r, i := float64(real(val)), float64(imag(val))
enc.buf.AppendFloat(r, 64)
enc.buf.AppendByte('+')
enc.buf.AppendFloat(i, 64)
enc.buf.AppendByte('i')
}
func (enc *textEncoder) AppendDuration(val time.Duration) {
cur := enc.buf.Len()
enc.EncodeDuration(val, enc)
if cur == enc.buf.Len() {
// User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep
// JSON valid.
enc.AppendInt64(int64(val))
}
}
func (enc *textEncoder) AppendInt64(val int64) {
enc.addElementSeparator()
enc.buf.AppendInt(val)
}
func (enc *textEncoder) AppendReflected(val interface{}) error {
enc.resetReflectBuf()
err := enc.reflectEnc.Encode(val)
if err != nil {
return err
}
enc.reflectBuf.TrimNewline()
enc.AppendByteString(enc.reflectBuf.Bytes())
return nil
}
func (enc *textEncoder) AppendString(val string) {
enc.addElementSeparator()
enc.safeAddStringWithQuote(val)
}
func (enc *textEncoder) AppendTime(val time.Time) {
cur := enc.buf.Len()
enc.EncodeTime(val, enc)
if cur == enc.buf.Len() {
// User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep
// output JSON valid.
enc.AppendInt64(val.UnixNano())
}
}
func (enc *textEncoder) beginQuoteFiled() {
if enc.buf.Len() > 0 {
enc.buf.AppendByte(' ')
}
enc.buf.AppendByte('[')
}
func (enc *textEncoder) endQuoteFiled() {
enc.buf.AppendByte(']')
}
func (enc *textEncoder) AppendUint64(val uint64) {
enc.addElementSeparator()
enc.buf.AppendUint(val)
}
func (enc *textEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) }
func (enc *textEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) }
func (enc *textEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) }
func (enc *textEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) }
func (enc *textEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) }
func (enc *textEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) }
func (enc *textEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) }
func (enc *textEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) }
func (enc *textEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) }
func (enc *textEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) }
func (enc *textEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) }
func (enc *textEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) }
func (enc *textEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) }
func (enc *textEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) }
func (enc *textEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) }
func (enc *textEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) }
func (enc *textEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) }
func (enc *textEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) }
func (enc *textEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) }
func (enc *textEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) }
func (enc *textEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) }
func (enc *textEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) }
func (enc *textEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) }
func (enc *textEncoder) Clone() zapcore.Encoder {
clone := enc.cloned()
clone.buf.Write(enc.buf.Bytes())
return clone
}
func (enc *textEncoder) cloned() *textEncoder {
clone := getTextEncoder()
clone.EncoderConfig = enc.EncoderConfig
clone.spaced = enc.spaced
clone.openNamespaces = enc.openNamespaces
clone.buf = _pool.Get()
return clone
}
func (enc *textEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
final := enc.cloned()
if final.TimeKey != "" {
final.beginQuoteFiled()
final.AppendTime(ent.Time)
final.endQuoteFiled()
}
if final.LevelKey != "" {
final.beginQuoteFiled()
cur := final.buf.Len()
final.EncodeLevel(ent.Level, final)
if cur == final.buf.Len() {
// User-supplied EncodeLevel was a no-op. Fall back to strings to keep
// output JSON valid.
final.AppendString(ent.Level.String())
}
final.endQuoteFiled()
}
if ent.LoggerName != "" && final.NameKey != "" {
final.beginQuoteFiled()
cur := final.buf.Len()
nameEncoder := final.EncodeName
// if no name encoder provided, fall back to FullNameEncoder for backwards
// compatibility
if nameEncoder == nil {
nameEncoder = zapcore.FullNameEncoder
}
nameEncoder(ent.LoggerName, final)
if cur == final.buf.Len() {
// User-supplied EncodeName was a no-op. Fall back to strings to
// keep output JSON valid.
final.AppendString(ent.LoggerName)
}
final.endQuoteFiled()
}
if ent.Caller.Defined && final.CallerKey != "" {
final.beginQuoteFiled()
cur := final.buf.Len()
final.EncodeCaller(ent.Caller, final)
if cur == final.buf.Len() {
// User-supplied EncodeCaller was a no-op. Fall back to strings to
// keep output JSON valid.
final.AppendString(ent.Caller.String())
}
final.endQuoteFiled()
}
// add Message
if len(ent.Message) > 0 {
final.beginQuoteFiled()
final.AppendString(ent.Message)
final.endQuoteFiled()
}
if enc.buf.Len() > 0 {
final.buf.AppendByte(' ')
final.buf.Write(enc.buf.Bytes())
}
final.addFields(fields)
final.closeOpenNamespaces()
if ent.Stack != "" && final.StacktraceKey != "" {
final.beginQuoteFiled()
final.AddString(final.StacktraceKey, ent.Stack)
final.endQuoteFiled()
}
if final.LineEnding != "" {
final.buf.AppendString(final.LineEnding)
} else {
final.buf.AppendString(zapcore.DefaultLineEnding)
}
ret := final.buf
putTextEncoder(final)
return ret, nil
}
func (enc *textEncoder) truncate() {
enc.buf.Reset()
}
func (enc *textEncoder) closeOpenNamespaces() {
for i := 0; i < enc.openNamespaces; i++ {
enc.buf.AppendByte('}')
}
}
func (enc *textEncoder) addKey(key string) {
enc.addElementSeparator()
enc.safeAddStringWithQuote(key)
enc.buf.AppendByte('=')
}
func (enc *textEncoder) addElementSeparator() {
last := enc.buf.Len() - 1
if last < 0 {
return
}
switch enc.buf.Bytes()[last] {
case '{', '[', ':', ',', ' ', '=':
return
default:
enc.buf.AppendByte(',')
}
}
func (enc *textEncoder) appendFloat(val float64, bitSize int) {
enc.addElementSeparator()
switch {
case math.IsNaN(val):
enc.buf.AppendString("NaN")
case math.IsInf(val, 1):
enc.buf.AppendString("+Inf")
case math.IsInf(val, -1):
enc.buf.AppendString("-Inf")
default:
enc.buf.AppendFloat(val, bitSize)
}
}
// safeAddString JSON-escapes a string and appends it to the internal buffer.
// Unlike the standard library's encoder, it doesn't attempt to protect the
// user from browser vulnerabilities or JSONP-related problems.
func (enc *textEncoder) safeAddString(s string) {
for i := 0; i < len(s); {
if enc.tryAddRuneSelf(s[i]) {
i++
continue
}
r, size := utf8.DecodeRuneInString(s[i:])
if enc.tryAddRuneError(r, size) {
i++
continue
}
enc.buf.AppendString(s[i : i+size])
i += size
}
}
// safeAddStringWithQuote will automatically add quotoes.
func (enc *textEncoder) safeAddStringWithQuote(s string) {
if !enc.needDoubleQuotes(s) {
enc.safeAddString(s)
return
}
enc.buf.AppendByte('"')
enc.safeAddString(s)
enc.buf.AppendByte('"')
}
// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.
func (enc *textEncoder) safeAddByteString(s []byte) {
for i := 0; i < len(s); {
if enc.tryAddRuneSelf(s[i]) {
i++
continue
}
r, size := utf8.DecodeRune(s[i:])
if enc.tryAddRuneError(r, size) {
i++
continue
}
enc.buf.Write(s[i : i+size])
i += size
}
}
// See [log-fileds](https://github.com/tikv/rfcs/blob/master/text/2018-12-19-unified-log-format.md#log-fields-section).
func (enc *textEncoder) needDoubleQuotes(s string) bool {
for i := 0; i < len(s); {
b := s[i]
if b <= 0x20 {
return true
}
switch b {
case '\\', '"', '[', ']', '=':
return true
}
i++
}
return false
}
// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte.
func (enc *textEncoder) tryAddRuneSelf(b byte) bool {
if b >= utf8.RuneSelf {
return false
}
if 0x20 <= b && b != '\\' && b != '"' {
enc.buf.AppendByte(b)
return true
}
switch b {
case '\\', '"':
enc.buf.AppendByte('\\')
enc.buf.AppendByte(b)
case '\n':
enc.buf.AppendByte('\\')
enc.buf.AppendByte('n')
case '\r':
enc.buf.AppendByte('\\')
enc.buf.AppendByte('r')
case '\t':
enc.buf.AppendByte('\\')
enc.buf.AppendByte('t')
default:
// Encode bytes < 0x20, except for the escape sequences above.
enc.buf.AppendString(`\u00`)
enc.buf.AppendByte(_hex[b>>4])
enc.buf.AppendByte(_hex[b&0xF])
}
return true
}
func (enc *textEncoder) tryAddRuneError(r rune, size int) bool {
if r == utf8.RuneError && size == 1 {
enc.buf.AppendString(`\ufffd`)
return true
}
return false
}
func (enc *textEncoder) addFields(fields []zapcore.Field) {
for _, f := range fields {
if f.Type == zapcore.ErrorType {
// handle ErrorType in pingcap/log to fix "[key=?,keyVerbose=?]" problem.
// see more detail at https://github.com/pingcap/log/pull/5
enc.encodeError(f)
continue
}
enc.beginQuoteFiled()
f.AddTo(enc)
enc.endQuoteFiled()
}
}
func (enc *textEncoder) encodeError(f zapcore.Field) {
err := f.Interface.(error)
basic := err.Error()
enc.beginQuoteFiled()
enc.AddString(f.Key, basic)
enc.endQuoteFiled()
if e, isFormatter := err.(fmt.Formatter); isFormatter {
verbose := fmt.Sprintf("%+v", e)
if verbose != basic {
// This is a rich error type, like those produced by
// github.com/pkg/errors.
enc.beginQuoteFiled()
enc.AddString(f.Key+"Verbose", verbose)
enc.endQuoteFiled()
}
}
}
......@@ -351,6 +351,7 @@ const (
ColumnOptionComment
ColumnOptionGenerated
ColumnOptionReference
ColumnOptionCollate
)
// ColumnOption is used for parsing column constraint info from SQL.
......@@ -365,7 +366,8 @@ type ColumnOption struct {
// Stored is only for ColumnOptionGenerated, default is false.
Stored bool
// Refer is used for foreign key.
Refer *ReferenceDef
Refer *ReferenceDef
StrValue string
}
// Restore implements Node interface.
......@@ -416,6 +418,12 @@ func (n *ColumnOption) Restore(ctx *RestoreCtx) error {
if err := n.Refer.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnOption ReferenceDef")
}
case ColumnOptionCollate:
if n.StrValue == "" {
return errors.New("Empty ColumnOption COLLATE")
}
ctx.WriteKeyWord("COLLATE ")
ctx.WritePlain(n.StrValue)
default:
return errors.New("An error occurred while splicing ColumnOption")
}
......@@ -1183,6 +1191,14 @@ const (
RowFormatCompressed
RowFormatRedundant
RowFormatCompact
TokuDBRowFormatDefault
TokuDBRowFormatFast
TokuDBRowFormatSmall
TokuDBRowFormatZlib
TokuDBRowFormatQuickLZ
TokuDBRowFormatLzma
TokuDBRowFormatSnappy
TokuDBRowFormatUncompressed
)
// OnDuplicateCreateTableSelectType is the option that handle unique key values in 'CREATE TABLE ... SELECT'.
......@@ -1281,6 +1297,22 @@ func (n *TableOption) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("REDUNDANT")
case RowFormatCompact:
ctx.WriteKeyWord("COMPACT")
case TokuDBRowFormatDefault:
ctx.WriteKeyWord("TOKUDB_DEFAULT")
case TokuDBRowFormatFast:
ctx.WriteKeyWord("TOKUDB_FAST")
case TokuDBRowFormatSmall:
ctx.WriteKeyWord("TOKUDB_SMALL")
case TokuDBRowFormatZlib:
ctx.WriteKeyWord("TOKUDB_ZLIB")
case TokuDBRowFormatQuickLZ:
ctx.WriteKeyWord("TOKUDB_QUICKLZ")
case TokuDBRowFormatLzma:
ctx.WriteKeyWord("TOKUDB_LZMA")
case TokuDBRowFormatSnappy:
ctx.WriteKeyWord("TOKUDB_SNAPPY")
case TokuDBRowFormatUncompressed:
ctx.WriteKeyWord("TOKUDB_UNCOMPRESSED")
default:
return errors.Errorf("invalid TableOption: TableOptionRowFormat: %d", n.UintValue)
}
......@@ -1766,6 +1798,9 @@ func (n *PartitionDefinition) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord(" VALUES LESS THAN ")
ctx.WritePlain("(")
for k, less := range n.LessThan {
if k != 0 {
ctx.WritePlain(", ")
}
if err := less.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.LessThan[%d]", k)
}
......@@ -1842,3 +1877,47 @@ func (n *PartitionOptions) Restore(ctx *RestoreCtx) error {
return nil
}
// RecoverTableStmt is a statement to recover dropped table.
type RecoverTableStmt struct {
ddlNode
JobID int64
Table *TableName
JobNum int64
}
// Restore implements Node interface.
func (n *RecoverTableStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("RECOVER TABLE ")
if n.JobID != 0 {
ctx.WriteKeyWord("BY JOB ")
ctx.WritePlainf("%d", n.JobID)
} else {
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table")
}
if n.JobNum > 0 {
ctx.WritePlainf(" %d", n.JobNum)
}
}
return nil
}
// Accept implements Node Accept interface.
func (n *RecoverTableStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*RecoverTableStmt)
if n.Table != nil {
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
}
return v.Leave(n)
}
......@@ -784,7 +784,7 @@ func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
ctx.WritePlain("/*+ ")
for i, tableHint := range n.TableHints {
if err := tableHint.Restore(ctx); err != nil {
errors.Annotatef(err, "An error occurred while restore SelectStmt.TableHints[%d]", i)
return errors.Annotatef(err, "An error occurred while restore SelectStmt.TableHints[%d]", i)
}
}
ctx.WritePlain("*/ ")
......@@ -802,7 +802,7 @@ func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
ctx.WritePlain(",")
}
if err := field.Restore(ctx); err != nil {
errors.Annotatef(err, "An error occurred while restore SelectStmt.Fields[%d]", i)
return errors.Annotatef(err, "An error occurred while restore SelectStmt.Fields[%d]", i)
}
}
}
......@@ -810,7 +810,7 @@ func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
if n.From != nil {
ctx.WriteKeyWord(" FROM ")
if err := n.From.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.From")
return errors.Annotate(err, "An error occurred while restore SelectStmt.From")
}
}
......@@ -820,21 +820,21 @@ func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
if n.Where != nil {
ctx.WriteKeyWord(" WHERE ")
if err := n.Where.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.Where")
return errors.Annotate(err, "An error occurred while restore SelectStmt.Where")
}
}
if n.GroupBy != nil {
ctx.WritePlain(" ")
if err := n.GroupBy.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.GroupBy")
return errors.Annotate(err, "An error occurred while restore SelectStmt.GroupBy")
}
}
if n.Having != nil {
ctx.WritePlain(" ")
if err := n.Having.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.Having")
return errors.Annotate(err, "An error occurred while restore SelectStmt.Having")
}
}
......@@ -845,7 +845,7 @@ func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
ctx.WritePlain(",")
}
if err := windowsSpec.Restore(ctx); err != nil {
errors.Annotatef(err, "An error occurred while restore SelectStmt.WindowSpec[%d]", i)
return errors.Annotatef(err, "An error occurred while restore SelectStmt.WindowSpec[%d]", i)
}
}
}
......@@ -853,14 +853,14 @@ func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
if n.OrderBy != nil {
ctx.WritePlain(" ")
if err := n.OrderBy.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.OrderBy")
return errors.Annotate(err, "An error occurred while restore SelectStmt.OrderBy")
}
}
if n.Limit != nil {
ctx.WritePlain(" ")
if err := n.Limit.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.Limit")
return errors.Annotate(err, "An error occurred while restore SelectStmt.Limit")
}
}
......@@ -982,7 +982,7 @@ func (n *UnionSelectList) Restore(ctx *RestoreCtx) error {
ctx.WritePlain("(")
}
if err := selectStmt.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionSelectList.SelectStmt")
return errors.Annotate(err, "An error occurred while restore UnionSelectList.SelectStmt")
}
if selectStmt.IsInBraces {
ctx.WritePlain(")")
......@@ -1022,20 +1022,20 @@ type UnionStmt struct {
// Restore implements Node interface.
func (n *UnionStmt) Restore(ctx *RestoreCtx) error {
if err := n.SelectList.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionStmt.SelectList")
return errors.Annotate(err, "An error occurred while restore UnionStmt.SelectList")
}
if n.OrderBy != nil {
ctx.WritePlain(" ")
if err := n.OrderBy.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionStmt.OrderBy")
return errors.Annotate(err, "An error occurred while restore UnionStmt.OrderBy")
}
}
if n.Limit != nil {
ctx.WritePlain(" ")
if err := n.Limit.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionStmt.Limit")
return errors.Annotate(err, "An error occurred while restore UnionStmt.Limit")
}
}
return nil
......@@ -2009,12 +2009,19 @@ type WindowSpec struct {
PartitionBy *PartitionByClause
OrderBy *OrderByClause
Frame *FrameClause
// OnlyAlias will set to true of the first following case.
// To make compatiable with MySQL, we need to distinguish `select func over w` from `select func over (w)`.
OnlyAlias bool
}
// Restore implements Node interface.
func (n *WindowSpec) Restore(ctx *RestoreCtx) error {
if name := n.Name.String(); name != "" {
ctx.WriteName(name)
if n.OnlyAlias {
return nil
}
ctx.WriteKeyWord(" AS ")
}
ctx.WritePlain("(")
......
......@@ -233,6 +233,7 @@ const (
Collation = "collation"
ConnectionID = "connection_id"
CurrentUser = "current_user"
CurrentRole = "current_role"
Database = "database"
FoundRows = "found_rows"
LastInsertId = "last_insert_id"
......
......@@ -16,6 +16,7 @@ package ast
import (
"bytes"
"fmt"
"strconv"
"strings"
"github.com/pingcap/errors"
......@@ -40,6 +41,7 @@ var (
_ StmtNode = &PrepareStmt{}
_ StmtNode = &RollbackStmt{}
_ StmtNode = &SetPwdStmt{}
_ StmtNode = &SetRoleStmt{}
_ StmtNode = &SetStmt{}
_ StmtNode = &UseStmt{}
_ StmtNode = &FlushStmt{}
......@@ -61,6 +63,8 @@ const (
// Valid formats for explain statement.
ExplainFormatROW = "row"
ExplainFormatDOT = "dot"
PumpType = "PUMP"
DrainerType = "DRAINER"
)
var (
......@@ -143,6 +147,39 @@ func (n *TraceStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
// ExplainForStmt is a statement to provite information about how is SQL statement executeing
// in connection #ConnectionID
// See https://dev.mysql.com/doc/refman/5.7/en/explain.html
type ExplainForStmt struct {
stmtNode
Format string
ConnectionID uint64
}
// Restore implements Node interface.
func (n *ExplainForStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("EXPLAIN ")
ctx.WriteKeyWord("FORMAT ")
ctx.WritePlain("= ")
ctx.WriteString(n.Format)
ctx.WritePlain(" ")
ctx.WriteKeyWord("FOR ")
ctx.WriteKeyWord("CONNECTION ")
ctx.WritePlain(strconv.FormatUint(n.ConnectionID, 10))
return nil
}
// Accept implements Node Accept interface.
func (n *ExplainForStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*ExplainForStmt)
return v.Leave(n)
}
// ExplainStmt is a statement to provide information about how is SQL statement executed
// or get columns information in a table.
// See https://dev.mysql.com/doc/refman/5.7/en/explain.html
......@@ -721,6 +758,95 @@ func (n *SetPwdStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
type ChangeStmt struct {
stmtNode
NodeType string
State string
NodeID string
}
// Restore implements Node interface.
func (n *ChangeStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("CHANGE ")
ctx.WriteKeyWord(n.NodeType)
ctx.WriteKeyWord(" TO NODE_STATE ")
ctx.WritePlain("=")
ctx.WriteString(n.State)
ctx.WriteKeyWord(" FOR NODE_ID ")
ctx.WriteString(n.NodeID)
return nil
}
// SecureText implements SensitiveStatement interface.
func (n *ChangeStmt) SecureText() string {
return fmt.Sprintf("change %s to node_state='%s' for node_id '%s'", strings.ToLower(n.NodeType), n.State, n.NodeID)
}
// Accept implements Node Accept interface.
func (n *ChangeStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*ChangeStmt)
return v.Leave(n)
}
// SetRoleStmtType is the type for FLUSH statement.
type SetRoleStmtType int
// SetRole statement types.
const (
SetRoleDefault SetRoleStmtType = iota
SetRoleNone
SetRoleAll
SetRoleAllExcept
SetRoleRegular
)
type SetRoleStmt struct {
stmtNode
SetRoleOpt SetRoleStmtType
RoleList []*auth.RoleIdentity
}
func (n *SetRoleStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("SET ROLE")
switch n.SetRoleOpt {
case SetRoleDefault:
ctx.WriteKeyWord(" DEFAULT")
case SetRoleNone:
ctx.WriteKeyWord(" NONE")
case SetRoleAll:
ctx.WriteKeyWord(" ALL")
case SetRoleAllExcept:
ctx.WriteKeyWord(" ALL EXCEPT")
}
for i, role := range n.RoleList {
ctx.WritePlain(" ")
err := role.Restore(ctx)
if err != nil {
return errors.Annotate(err, "An error occurred while restore SetRoleStmt.RoleList")
}
if i != len(n.RoleList)-1 {
ctx.WritePlain(",")
}
}
return nil
}
// Accept implements Node Accept interface.
func (n *SetRoleStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*SetRoleStmt)
return v.Leave(n)
}
// UserSpec is used for parsing create user statement.
type UserSpec struct {
User *auth.UserIdentity
......@@ -780,13 +906,18 @@ func (n *UserSpec) EncodedPassword() (string, bool) {
type CreateUserStmt struct {
stmtNode
IfNotExists bool
Specs []*UserSpec
IsCreateRole bool
IfNotExists bool
Specs []*UserSpec
}
// Restore implements Node interface.
func (n *CreateUserStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("CREATE USER ")
if n.IsCreateRole {
ctx.WriteKeyWord("CREATE ROLE ")
} else {
ctx.WriteKeyWord("CREATE USER ")
}
if n.IfNotExists {
ctx.WriteKeyWord("IF NOT EXISTS ")
}
......@@ -882,13 +1013,18 @@ func (n *AlterUserStmt) Accept(v Visitor) (Node, bool) {
type DropUserStmt struct {
stmtNode
IfExists bool
UserList []*auth.UserIdentity
IfExists bool
IsDropRole bool
UserList []*auth.UserIdentity
}
// Restore implements Node interface.
func (n *DropUserStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("DROP USER ")
if n.IsDropRole {
ctx.WriteKeyWord("DROP ROLE ")
} else {
ctx.WriteKeyWord("DROP USER ")
}
if n.IfExists {
ctx.WriteKeyWord("IF EXISTS ")
}
......@@ -1040,7 +1176,6 @@ const (
AdminChecksumTable
AdminShowSlow
AdminShowNextRowID
AdminRestoreTable
)
// HandleRange represents a range where handle value >= Begin and < End.
......@@ -1172,19 +1307,6 @@ func (n *AdminStmt) Restore(ctx *RestoreCtx) error {
return err
}
ctx.WritePlainf(" %s", n.Index)
case AdminRestoreTable:
ctx.WriteKeyWord("RESTORE TABLE ")
if n.JobIDs != nil {
ctx.WriteKeyWord("BY JOB ")
restoreJobIDs()
} else {
if err := restoreTables(); err != nil {
return err
}
if n.JobNumber != 0 {
ctx.WritePlainf(" %d", n.JobNumber)
}
}
case AdminCleanupIndex:
ctx.WriteKeyWord("CLEANUP INDEX ")
if err := restoreTables(); err != nil {
......@@ -1268,6 +1390,8 @@ func (n *PrivElem) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("CREATE")
case mysql.CreateUserPriv:
ctx.WriteKeyWord("CREATE USER")
case mysql.CreateRolePriv:
ctx.WriteKeyWord("CREATE ROLE")
case mysql.TriggerPriv:
ctx.WriteKeyWord("TRIGGER")
case mysql.DeletePriv:
......@@ -1459,6 +1583,47 @@ func (n *RevokeStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
// RevokeStmt is the struct for REVOKE statement.
type RevokeRoleStmt struct {
stmtNode
Roles []*auth.RoleIdentity
Users []*auth.UserIdentity
}
// Restore implements Node interface.
func (n *RevokeRoleStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("REVOKE ")
for i, role := range n.Roles {
if i != 0 {
ctx.WritePlain(", ")
}
if err := role.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Roles[%d]", i)
}
}
ctx.WriteKeyWord(" FROM ")
for i, v := range n.Users {
if i != 0 {
ctx.WritePlain(", ")
}
if err := v.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Users[%d]", i)
}
}
return nil
}
// Accept implements Node Accept interface.
func (n *RevokeRoleStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*RevokeRoleStmt)
return v.Leave(n)
}
// GrantStmt is the struct for GRANT statement.
type GrantStmt struct {
stmtNode
......@@ -1536,6 +1701,60 @@ func (n *GrantStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
// GrantRoleStmt is the struct for GRANT TO statement.
type GrantRoleStmt struct {
stmtNode
Roles []*auth.RoleIdentity
Users []*auth.UserIdentity
}
// Accept implements Node Accept interface.
func (n *GrantRoleStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*GrantRoleStmt)
return v.Leave(n)
}
// Restore implements Node interface.
func (n *GrantRoleStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("GRANT ")
if len(n.Roles) > 0 {
for i, role := range n.Roles {
if i != 0 {
ctx.WritePlain(", ")
}
if err := role.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore GrantRoleStmt.Roles[%d]", i)
}
}
}
ctx.WriteKeyWord(" TO ")
for i, v := range n.Users {
if i != 0 {
ctx.WritePlain(", ")
}
if err := v.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i)
}
}
return nil
}
// SecureText implements SensitiveStatement interface.
func (n *GrantRoleStmt) SecureText() string {
text := n.text
// Filter "identified by xxx" because it would expose password information.
idx := strings.Index(strings.ToLower(text), "identified")
if idx > 0 {
text = text[:idx]
}
return text
}
// Ident is the table identifier composed of schema name and table name.
type Ident struct {
Schema model.CIStr
......
......@@ -33,11 +33,6 @@ type UserIdentity struct {
AuthHostname string // Match in privs system (i.e. could be a wildcard)
}
type RoleIdentity struct {
Username string
Hostname string
}
// Restore implements Node interface.
func (user *UserIdentity) Restore(ctx *RestoreCtx) error {
if user.CurrentUser {
......@@ -55,6 +50,9 @@ func (user *UserIdentity) Restore(ctx *RestoreCtx) error {
// String converts UserIdentity to the format user@host.
func (user *UserIdentity) String() string {
// TODO: Escape username and hostname.
if user == nil {
return ""
}
return fmt.Sprintf("%s@%s", user.Username, user.Hostname)
}
......@@ -64,6 +62,26 @@ func (user *UserIdentity) AuthIdentityString() string {
return fmt.Sprintf("%s@%s", user.AuthUsername, user.AuthHostname)
}
type RoleIdentity struct {
Username string
Hostname string
}
func (role *RoleIdentity) Restore(ctx *RestoreCtx) error {
ctx.WriteName(role.Username)
if role.Hostname != "" {
ctx.WritePlain("@")
ctx.WriteName(role.Hostname)
}
return nil
}
// String converts UserIdentity to the format user@host.
func (role *RoleIdentity) String() string {
// TODO: Escape username and hostname.
return fmt.Sprintf("`%s`@`%s`", role.Username, role.Hostname)
}
// CheckScrambledPassword check scrambled password received from client.
// The new authentication is performed in following manner:
// SERVER: public_seed=create_random_string()
......
......@@ -40,6 +40,8 @@ type Collation struct {
}
var charsets = make(map[string]*Charset)
var collationsMap = make(map[int]*Collation)
var descs = make([]*Desc, 0, len(charsetInfos))
// All the supported charsets should be in the following table.
var charsetInfos = []*Charset{
......@@ -60,21 +62,6 @@ type Desc struct {
// GetAllCharsets gets all charset descriptions in the local charsets.
func GetAllCharsets() []*Desc {
descs := make([]*Desc, 0, len(charsets))
// The charsetInfos is an array, so the iterate order will be stable.
for _, ci := range charsetInfos {
c, ok := charsets[ci.Name]
if !ok {
continue
}
desc := &Desc{
Name: c.Name,
DefaultCollation: c.DefaultCollation,
Desc: c.Desc,
Maxlen: c.Maxlen,
}
descs = append(descs, desc)
}
return descs
}
......@@ -150,10 +137,8 @@ func GetCharsetInfoByID(coID int) (string, string, error) {
if coID == mysql.DefaultCollationID {
return mysql.DefaultCharset, mysql.DefaultCollationName, nil
}
for _, collation := range collations {
if coID == collation.ID {
return collation.CharsetName, collation.Name, nil
}
if collation, ok := collationsMap[coID]; ok {
return collation.CharsetName, collation.Name, nil
}
return "", "", errors.Errorf("Unknown charset id %d", coID)
}
......@@ -412,8 +397,17 @@ var collations = []*Collation{
func init() {
for _, c := range charsetInfos {
charsets[c.Name] = c
desc := &Desc{
Name: c.Name,
DefaultCollation: c.DefaultCollation,
Desc: c.Desc,
Maxlen: c.Maxlen,
}
descs = append(descs, desc)
}
for _, c := range collations {
collationsMap[c.ID] = c
charset, ok := charsets[c.CharsetName]
if !ok {
continue
......
......@@ -45,7 +45,7 @@ jobs:
cd tidb
rm go.sum
GO111MODULE=on go mod edit -replace github.com/pingcap/parser=github.com/${CIRCLE_PR_USERNAME:-$CIRCLE_PROJECT_USERNAME}/${CIRCLE_PR_REPONAME:-$CIRCLE_PROJECT_REPONAME}@$CIRCLE_SHA1
make test
make gotest
workflows:
version: 2
build_and_test:
......
......@@ -8,8 +8,8 @@ require (
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8
github.com/pingcap/errors v0.11.0
github.com/pingcap/tidb v0.0.0-20190218065808-69472bd1a6e9
github.com/pingcap/errors v0.11.1
github.com/pingcap/tidb v0.0.0-20190321025159-e8299209340c
github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7
github.com/sirupsen/logrus v1.3.0
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2
......
......@@ -2,7 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/blacktear23/go-proxyprotocol v0.0.0-20171102103907-62e368e1c470/go.mod h1:VKt7CNAQxpFpSDz3sXyj9hY/GbVsQCr0sB3w59nE7lU=
github.com/blacktear23/go-proxyprotocol v0.0.0-20180807104634-af7a81e8dd0d/go.mod h1:VKt7CNAQxpFpSDz3sXyj9hY/GbVsQCr0sB3w59nE7lU=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
......@@ -13,7 +13,6 @@ github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c h1:G8zTsaqyVfIHpgMFcGgdbhHSFhlNc77rAKkhVbQ9kQg=
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1 h1:uWcWCkSP+E1w1z8r082miT+c+9vzg+5UdrgGCo15lMo=
......@@ -36,6 +35,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
......@@ -57,6 +57,8 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/myesui/uuid v1.0.0/go.mod h1:2CDfNgU0LR8mIdO8vdWd8i9gWWxLlcoIGGpSNgafq84=
......@@ -67,25 +69,33 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg=
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ=
github.com/pingcap/errors v0.11.0 h1:DCJQB8jrHbQ1VVlMFIrbj2ApScNNotVmkSNplu2yUt4=
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.1 h1:BXFZ6MdDd2U1uJUa2sRAWTmm+nieEzuyYM0R4aUTcC8=
github.com/pingcap/errors v0.11.1/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3/go.mod h1:DazNTg0PTldtpsQiT9I5tVJwV1onHMKBBgXzmJUlMns=
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw=
github.com/pingcap/kvproto v0.0.0-20190131052532-7e329e0c9e32/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY=
github.com/pingcap/log v0.0.0-20190214045112-b37da76f67a7/go.mod h1:xsfkWVaFVV5B8e1K9seWfyJWFrIhbtUTAD8NV1Pq3+w=
github.com/pingcap/parser v0.0.0-20190218033509-9545f168ae97/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562 h1:32oF1/8lVnBR2JVcCAnKPQATTOX0+ckRDFpjQk4Ngno=
github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY=
github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596 h1:t2OQTpPJnrPDGlvA+3FwJptMTt6MEPdzK1Wt99oaefQ=
github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596/go.mod h1:WpHUKhNZ18v116SvGrmjkA9CBhYmuUTKL+p8JC9ANEw=
github.com/pingcap/parser v0.0.0-20190312024907-3f6280b08c8b/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E=
github.com/pingcap/tidb v0.0.0-20190218065808-69472bd1a6e9 h1:mFVcXY6yIB+vg0I7VsBFEjkvcSHFOA0vvwO7BcwA0Uo=
github.com/pingcap/tidb v0.0.0-20190218065808-69472bd1a6e9/go.mod h1:R6ZYnRcDpzT9N4wQxfOhGqhCdkrLIC1UNxxJAC2xWjM=
github.com/pingcap/tidb v0.0.0-20190321025159-e8299209340c h1:n3i2K6zUzXZDe6imOtdOhWltuqCLFtmLropKwS6ljeI=
github.com/pingcap/tidb v0.0.0-20190321025159-e8299209340c/go.mod h1:FcgD4o1kq3YNk08MWtMRwNZXQJpM28bFdb/go9KpmEA=
github.com/pingcap/tidb-tools v2.1.3-0.20190116051332-34c808eef588+incompatible h1:e9Gi/LP9181HT3gBfSOeSBA+5JfemuE4aEAhqNgoE4k=
github.com/pingcap/tidb-tools v2.1.3-0.20190116051332-34c808eef588+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7 h1:wnjdQRhybddDesBVBKyOLUPgDaOFdtqA92pduBgWvVQ=
github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7 h1:gGBSHPOU7g8YjTbhwn+lvFm2VDEhhA+PwDIlstkgSxE=
github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
......@@ -104,15 +114,19 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/struCoder/pidusage v0.1.2/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI=
github.com/tiancaiamao/appdash v0.0.0-20181126055449-889f96f722a2/go.mod h1:2PfKggNGDuadAa0LElHrByyrz4JPZ9fFx6Gs7nx7ZZU=
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY=
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v1.5.0/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go/codec v0.0.0-20181127175209-856da096dbdf/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1 h1:UvhxfNjNqlZ/x3cDyqxMhoiUpemd3zXkVQApN6bM/lg=
github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/unrolled/render v0.0.0-20180914162206-b9786414de4d/go.mod h1:tu82oB5W2ykJRVioYsB+IQKcft7ryBr7w12qMBUPyXg=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
......@@ -126,9 +140,11 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
......@@ -140,13 +156,16 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190130214255-bb1329dc71a0 h1:iRpjPej1fPzmfoBhMFkp3HdqzF+ytPmAwiQhJGV0zGw=
golang.org/x/tools v0.0.0-20190130214255-bb1329dc71a0/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190108161440-ae2f86662275 h1:9oFlwfEGIvmxXTcY53ygNyxIQtWciRHjrnUvZJCYXYU=
google.golang.org/genproto v0.0.0-20190108161440-ae2f86662275/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/grpc v0.0.0-20180607172857-7a6a684ca69e/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
......@@ -154,6 +173,7 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
......
......@@ -126,7 +126,7 @@ func (s *Scanner) stmtText() string {
// Errorf tells scanner something is wrong.
// Scanner satisfies yyLexer interface which need this function.
func (s *Scanner) Errorf(format string, a ...interface{}) {
func (s *Scanner) Errorf(format string, a ...interface{}) (err error) {
str := fmt.Sprintf(format, a...)
val := s.r.s[s.lastScanOffset:]
var lenStr = ""
......@@ -134,8 +134,17 @@ func (s *Scanner) Errorf(format string, a ...interface{}) {
lenStr = "(total length " + strconv.Itoa(len(val)) + ")"
val = val[:2048]
}
err := fmt.Errorf("line %d column %d near \"%s\"%s %s",
err = fmt.Errorf("line %d column %d near \"%s\"%s %s",
s.r.p.Line, s.r.p.Col, val, str, lenStr)
return
}
// AppendError sets error into scanner.
// Scanner satisfies yyLexer interface which need this function.
func (s *Scanner) AppendError(err error) {
if err == nil {
return
}
s.errs = append(s.errs, err)
}
......
......@@ -132,6 +132,7 @@ func init() {
}
var tokenMap = map[string]int{
"ACCOUNT": account,
"ACTION": action,
"ADD": add,
"ADDDATE": addDate,
......@@ -178,6 +179,7 @@ var tokenMap = map[string]int{
"CHARSET": charsetKwd,
"CHECK": check,
"CHECKSUM": checksum,
"CIPHER": cipher,
"CLEANUP": cleanup,
"CLIENT": client,
"COALESCE": coalesce,
......@@ -204,6 +206,7 @@ var tokenMap = map[string]int{
"CURRENT_TIME": currentTime,
"CURRENT_TIMESTAMP": currentTs,
"CURRENT_USER": currentUser,
"CURRENT_ROLE": currentRole,
"CURTIME": curTime,
"DATA": data,
"DATABASE": database,
......@@ -254,6 +257,7 @@ var tokenMap = map[string]int{
"EXCEPT": except,
"EXECUTE": execute,
"EXISTS": exists,
"EXPIRE": expire,
"EXPLAIN": explain,
"EXTRACT": extract,
"FALSE": falseKwd,
......@@ -308,6 +312,7 @@ var tokenMap = map[string]int{
"INTO": into,
"INVOKER": invoker,
"IS": is,
"ISSUER": issuer,
"ISOLATION": isolation,
"JOBS": jobs,
"JOB": job,
......@@ -325,6 +330,7 @@ var tokenMap = map[string]int{
"LIKE": like,
"LIMIT": limit,
"LINES": lines,
"LINEAR": linear,
"LOAD": load,
"LOCAL": local,
"LOCALTIME": localTime,
......@@ -360,9 +366,12 @@ var tokenMap = map[string]int{
"NAMES": names,
"NATIONAL": national,
"NATURAL": natural,
"NEVER": never,
"NEXT_ROW_ID": next_row_id,
"NO": no,
"NO_WRITE_TO_BINLOG": noWriteToBinLog,
"NODE_ID": nodeID,
"NODE_STATE": nodeState,
"NONE": none,
"NOT": not,
"NOW": now,
......@@ -414,8 +423,8 @@ var tokenMap = map[string]int{
"REPLACE": replace,
"RESPECT": respect,
"REPLICATION": replication,
"REQUIRE": require,
"RESTRICT": restrict,
"RESTORE": restore,
"REVERSE": reverse,
"REVOKE": revoke,
"RIGHT": right,
......@@ -449,6 +458,7 @@ var tokenMap = map[string]int{
"SQL_CACHE": sqlCache,
"SQL_CALC_FOUND_ROWS": sqlCalcFoundRows,
"SQL_NO_CACHE": sqlNoCache,
"SSL": ssl,
"START": start,
"STARTING": starting,
"STATS": stats,
......@@ -465,6 +475,7 @@ var tokenMap = map[string]int{
"STORED": stored,
"STRAIGHT_JOIN": straightJoin,
"SUBDATE": subDate,
"SUBJECT": subject,
"SUBPARTITION": subpartition,
"SUBPARTITIONS": subpartitions,
"SUBSTR": substring,
......@@ -492,6 +503,14 @@ var tokenMap = map[string]int{
"TINYINT": tinyIntType,
"TINYTEXT": tinytextType,
"TO": to,
"TOKUDB_DEFAULT": tokudbDefault,
"TOKUDB_FAST": tokudbFast,
"TOKUDB_LZMA": tokudbLzma,
"TOKUDB_QUICKLZ": tokudbQuickLZ,
"TOKUDB_SNAPPY": tokudbSnappy,
"TOKUDB_SMALL": tokudbSmall,
"TOKUDB_UNCOMPRESSED": tokudbUncompressed,
"TOKUDB_ZLIB": tokudbZlib,
"TOP": top,
"TRACE": trace,
"TRAILING": trailing,
......@@ -535,6 +554,7 @@ var tokenMap = map[string]int{
"WITH": with,
"WRITE": write,
"XOR": xor,
"X509": x509,
"YEAR": yearType,
"YEAR_MONTH": yearMonth,
"ZEROFILL": zerofill,
......
......@@ -54,7 +54,7 @@ const (
ActionModifyTableCharsetAndCollate ActionType = 22
ActionTruncateTablePartition ActionType = 23
ActionDropView ActionType = 24
ActionRestoreTable ActionType = 25
ActionRecoverTable ActionType = 25
)
// AddIndexStr is a string related to the operation of "add index".
......@@ -85,7 +85,7 @@ var actionMap = map[ActionType]string{
ActionModifyTableCharsetAndCollate: "modify table charset and collate",
ActionTruncateTablePartition: "truncate partition",
ActionDropView: "drop view",
ActionRestoreTable: "restore table",
ActionRecoverTable: "recover table",
}
// String return current ddl action in string
......
......@@ -67,6 +67,15 @@ const (
ColumnInfoVersion0 = uint64(0)
// ColumnInfoVersion1 means the column info version is 1.
ColumnInfoVersion1 = uint64(1)
// ColumnInfoVersion2 means the column info version is 2.
// This is for v2.1.7 to Compatible with older versions charset problem.
// Old version such as v2.0.8 treat utf8 as utf8mb4, because there is no UTF8 check in v2.0.8.
// After version V2.1.2 (PR#8738) , TiDB add UTF8 check, then the user upgrade from v2.0.8 insert some UTF8MB4 characters will got error.
// This is not compatibility for user. Then we try to fix this in PR #9820, and increase the version number.
ColumnInfoVersion2 = uint64(2)
// CurrLatestColumnInfoVersion means the latest column info in the current TiDB.
CurrLatestColumnInfoVersion = ColumnInfoVersion2
)
// ColumnInfo provides meta data describing of a table column.
......@@ -147,6 +156,30 @@ func FindColumnInfo(cols []*ColumnInfo, name string) *ColumnInfo {
// for use of execution phase.
const ExtraHandleID = -1
const (
// TableInfoVersion0 means the table info version is 0.
// Upgrade from v2.1.1 or v2.1.2 to v2.1.3 and later, and then execute a "change/modify column" statement
// that does not specify a charset value for column. Then the following error may be reported:
// ERROR 1105 (HY000): unsupported modify charset from utf8mb4 to utf8.
// To eliminate this error, we will not modify the charset of this column
// when executing a change/modify column statement that does not specify a charset value for column.
// This behavior is not compatible with MySQL.
TableInfoVersion0 = uint16(0)
// TableInfoVersion1 means the table info version is 1.
// When we execute a change/modify column statement that does not specify a charset value for column,
// we set the charset of this column to the charset of table. This behavior is compatible with MySQL.
TableInfoVersion1 = uint16(1)
// TableInfoVersion2 means the table info version is 2.
// This is for v2.1.7 to Compatible with older versions charset problem.
// Old version such as v2.0.8 treat utf8 as utf8mb4, because there is no UTF8 check in v2.0.8.
// After version V2.1.2 (PR#8738) , TiDB add UTF8 check, then the user upgrade from v2.0.8 insert some UTF8MB4 characters will got error.
// This is not compatibility for user. Then we try to fix this in PR #9820, and increase the version number.
TableInfoVersion2 = uint16(2)
// CurrLatestTableInfoVersion means the latest table info in the current TiDB.
CurrLatestTableInfoVersion = TableInfoVersion2
)
// ExtraHandleName is the name of ExtraHandle Column.
var ExtraHandleName = NewCIStr("_tidb_rowid")
......@@ -179,12 +212,17 @@ type TableInfo struct {
// ShardRowIDBits specify if the implicit row ID is sharded.
ShardRowIDBits uint64
// MaxShardRowIDBits uses to record the max ShardRowIDBits be used so far.
MaxShardRowIDBits uint64 `json:"max_shard_row_id_bits"`
Partition *PartitionInfo `json:"partition"`
Compression string `json:"compression"`
View *ViewInfo `json:"view"`
// Version means the version of the table info.
Version uint16 `json:"version"`
}
// GetPartitionInfo returns the partition information.
......@@ -287,6 +325,16 @@ func (t *TableInfo) Cols() []*ColumnInfo {
return publicColumns[0 : maxOffset+1]
}
// FindIndexByName finds index by name.
func (t *TableInfo) FindIndexByName(idxName string) *IndexInfo {
for _, idx := range t.Indices {
if idx.Name.L == idxName {
return idx
}
}
return nil
}
// NewExtraHandleColInfo mocks a column info for extra handle column.
func NewExtraHandleColInfo() *ColumnInfo {
colInfo := &ColumnInfo{
......
......@@ -15,6 +15,25 @@ package mysql
import "unicode"
// CharsetNameToID maps charset name to its default collation ID.
func CharsetNameToID(charset string) uint8 {
// Use quick path for TiDB to avoid access CharsetIDs map
// "SHOW CHARACTER SET;" to see all the supported character sets.
if charset == "utf8mb4" {
return UTF8MB4CollationID
} else if charset == "binary" {
return BinaryCollationID
} else if charset == "utf8" {
return UTF8CollationID
} else if charset == "ascii" {
return ASCIICollationID
} else if charset == "latin1" {
return Latin1CollationID
} else {
return CharsetIDs[charset]
}
}
// CharsetIDs maps charset name to its default collation ID.
var CharsetIDs = map[string]uint8{
"big5": 1,
......@@ -22,10 +41,10 @@ var CharsetIDs = map[string]uint8{
"cp850": 4,
"hp8": 6,
"koi8r": 7,
"latin1": 8,
"latin1": Latin1CollationID,
"latin2": 9,
"swe7": 10,
"ascii": 11,
"ascii": ASCIICollationID,
"ujis": 12,
"sjis": 13,
"hebrew": 16,
......@@ -38,7 +57,7 @@ var CharsetIDs = map[string]uint8{
"gbk": 28,
"latin5": 30,
"armscii8": 32,
"utf8": 33,
"utf8": UTF8CollationID,
"ucs2": 35,
"cp866": 36,
"keybcs2": 37,
......@@ -46,14 +65,14 @@ var CharsetIDs = map[string]uint8{
"macroman": 39,
"cp852": 40,
"latin7": 41,
"utf8mb4": 45,
"utf8mb4": UTF8MB4CollationID,
"cp1251": 51,
"utf16": 54,
"utf16le": 56,
"cp1256": 57,
"cp1257": 59,
"utf32": 60,
"binary": 63,
"binary": BinaryCollationID,
"geostd8": 92,
"cp932": 95,
"eucjpms": 97,
......@@ -556,6 +575,10 @@ const (
DefaultCharset = UTF8MB4Charset
// DefaultCollationID is utf8mb4_bin(46)
DefaultCollationID = 46
Latin1CollationID = 8
ASCIICollationID = 11
UTF8CollationID = 33
UTF8MB4CollationID = 45
BinaryCollationID = 63
UTF8DefaultCollation = "utf8_bin"
UTF8MB4DefaultCollation = "utf8mb4_bin"
......
......@@ -31,7 +31,7 @@ var (
TiDBReleaseVersion = "None"
// ServerVersion is the version information of this tidb-server in MySQL's format.
ServerVersion = fmt.Sprintf("5.7.10-TiDB-%s", TiDBReleaseVersion)
ServerVersion = fmt.Sprintf("5.7.25-TiDB-%s", TiDBReleaseVersion)
)
// Header information.
......@@ -182,6 +182,10 @@ const (
GlobalStatusTable = "GLOBAL_STATUS"
// TiDBTable is the table contains tidb info.
TiDBTable = "tidb"
// RoleEdgesTable is the table contains role relation info
RoleEdgeTable = "role_edges"
// DefaultRoleTable is the table contain default active role info
DefaultRoleTable = "default_roles"
)
// PrivilegeType privilege
......
......@@ -882,16 +882,19 @@ const (
ErrMustChangePasswordLogin = 1862
ErrRowInWrongPartition = 1863
ErrErrorLast = 1863
ErrGeneratedColumnFunctionIsNotAllowed = 3102
ErrBadGeneratedColumn = 3105
ErrUnsupportedOnGeneratedColumn = 3106
ErrGeneratedColumnNonPrior = 3107
ErrDependentByGeneratedColumn = 3108
ErrGeneratedColumnRefAutoInc = 3109
ErrInvalidJSONText = 3140
ErrInvalidJSONPath = 3143
ErrInvalidJSONData = 3146
ErrInvalidJSONPathWildcard = 3149
ErrInvalidJSONContainsPathType = 3150
ErrJSONUsedAsKey = 3152
ErrRoleNotGranted = 3530
ErrWindowNoSuchWindow = 3579
ErrWindowCircularityInWindowGraph = 3580
ErrWindowNoChildPartitioning = 3581
......
......@@ -833,7 +833,7 @@ var MySQLErrName = map[uint16]string{
ErrInternal: "Internal : %s",
ErrInnodbImport: "ALTER TABLE '%-.192s' IMPORT TABLESPACE failed with error %d : '%s'",
ErrInnodbIndexCorrupt: "Index corrupt: %s",
ErrInvalidYearColumnLength: "YEAR(%d) column type is deprecated. Creating YEAR(4) column instead.",
ErrInvalidYearColumnLength: "Supports only YEAR or YEAR(4) column",
ErrNotValidPassword: "Your password does not satisfy the current policy requirements",
ErrMustChangePassword: "You must SET PASSWORD before executing this statement",
ErrFkNoIndexChild: "Failed to add the foreign key constaint. Missing index for constraint '%s' in the foreign table '%s'",
......@@ -883,6 +883,8 @@ var MySQLErrName = map[uint16]string{
ErrUnsupportedOnGeneratedColumn: "'%s' is not supported for generated columns.",
ErrGeneratedColumnNonPrior: "Generated column can refer only to generated columns defined prior to it.",
ErrDependentByGeneratedColumn: "Column '%s' has a generated column dependency.",
ErrGeneratedColumnFunctionIsNotAllowed: "Expression of generated column '%s' contains a disallowed function.",
ErrGeneratedColumnRefAutoInc: "Generated column '%s' cannot refer to auto-increment column.",
ErrInvalidJSONText: "Invalid JSON text: %-.192s",
ErrInvalidJSONPath: "Invalid JSON path expression %s.",
ErrInvalidJSONData: "Invalid data type for JSON data",
......@@ -910,6 +912,7 @@ var MySQLErrName = map[uint16]string{
ErrWindowNoGroupOrderUnused: "ASC or DESC with GROUP BY isn't allowed with window functions; put ASC or DESC in ORDER BY",
ErrWindowExplainJson: "To get information about window functions use EXPLAIN FORMAT=JSON",
ErrWindowFunctionIgnoresFrame: "Window function '%s' ignores the frame clause of window '%s' and aggregates over the whole partition",
ErrRoleNotGranted: "%s is is not granted to %s",
// TiDB errors.
ErrMemExceedThreshold: "%s holds %dB memory, exceeds threshold %dB.%s",
......
......@@ -155,6 +155,8 @@ func (ft *FieldType) CompactStr() string {
case mysql.TypeBit, mysql.TypeShort, mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString:
// Flen is always shown.
suffix = fmt.Sprintf("(%d)", displayFlen)
case mysql.TypeYear:
suffix = fmt.Sprintf("(%d)", ft.Flen)
}
return ts + suffix
}
......@@ -317,3 +319,16 @@ func (ft *FieldType) StorageLength() int {
return VarStorageLen
}
}
// HasCharset indicates if a COLUMN has an associated charset. Returning false here prevents some information
// statements(like `SHOW CREATE TABLE`) from attaching a CHARACTER SET clause to the column.
func HasCharset(ft *FieldType) bool {
switch ft.Tp {
case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob,
mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
return !mysql.HasBinaryFlag(ft.Flag)
case mysql.TypeEnum, mysql.TypeSet:
return true
}
return false
}
......@@ -27,8 +27,10 @@ import (
)
const (
codeErrParse = terror.ErrCode(mysql.ErrParse)
codeErrSyntax = terror.ErrCode(mysql.ErrSyntax)
codeErrParse = terror.ErrCode(mysql.ErrParse)
codeErrSyntax = terror.ErrCode(mysql.ErrSyntax)
codeErrUnknownCharacterSet = terror.ErrCode(mysql.ErrUnknownCharacterSet)
codeErrInvalidYearColumnLength = terror.ErrCode(mysql.ErrInvalidYearColumnLength)
)
var (
......@@ -36,6 +38,10 @@ var (
ErrSyntax = terror.ClassParser.New(codeErrSyntax, mysql.MySQLErrName[mysql.ErrSyntax])
// ErrParse returns for sql parse error.
ErrParse = terror.ClassParser.New(codeErrParse, mysql.MySQLErrName[mysql.ErrParse])
// ErrUnknownCharacterSet returns for no character set found error.
ErrUnknownCharacterSet = terror.ClassParser.New(codeErrUnknownCharacterSet, mysql.MySQLErrName[mysql.ErrUnknownCharacterSet])
// ErrInvalidYearColumnLength returns for illegal column length for year type.
ErrInvalidYearColumnLength = terror.ClassParser.New(codeErrInvalidYearColumnLength, mysql.MySQLErrName[mysql.ErrInvalidYearColumnLength])
// SpecFieldPattern special result field pattern
SpecFieldPattern = regexp.MustCompile(`(\/\*!(M?[0-9]{5,6})?|\*\/)`)
specCodePattern = regexp.MustCompile(`\/\*!(M?[0-9]{5,6})?([^*]|\*+[^*/])*\*+\/`)
......@@ -45,8 +51,10 @@ var (
func init() {
parserMySQLErrCodes := map[terror.ErrCode]uint16{
codeErrSyntax: mysql.ErrSyntax,
codeErrParse: mysql.ErrParse,
codeErrSyntax: mysql.ErrSyntax,
codeErrParse: mysql.ErrParse,
codeErrUnknownCharacterSet: mysql.ErrUnknownCharacterSet,
codeErrInvalidYearColumnLength: mysql.ErrInvalidYearColumnLength,
}
terror.ErrClassToMySQLCodes[terror.ClassParser] = parserMySQLErrCodes
}
......@@ -200,12 +208,12 @@ func toInt(l yyLexer, lval *yySymType, str string) int {
// get value 99999999999999999999999999999999999999999999999999999999999999999
return toDecimal(l, lval, str)
}
l.Errorf("integer literal: %v", err)
l.AppendError(l.Errorf("integer literal: %v", err))
return int(unicode.ReplacementChar)
}
switch {
case n < math.MaxInt64:
case n <= math.MaxInt64:
lval.item = int64(n)
default:
lval.item = n
......@@ -216,7 +224,7 @@ func toInt(l yyLexer, lval *yySymType, str string) int {
func toDecimal(l yyLexer, lval *yySymType, str string) int {
dec, err := ast.NewDecimal(str)
if err != nil {
l.Errorf("decimal literal: %v", err)
l.AppendError(l.Errorf("decimal literal: %v", err))
}
lval.item = dec
return decLit
......@@ -225,7 +233,7 @@ func toDecimal(l yyLexer, lval *yySymType, str string) int {
func toFloat(l yyLexer, lval *yySymType, str string) int {
n, err := strconv.ParseFloat(str, 64)
if err != nil {
l.Errorf("float literal: %v", err)
l.AppendError(l.Errorf("float literal: %v", err))
return int(unicode.ReplacementChar)
}
......@@ -237,7 +245,7 @@ func toFloat(l yyLexer, lval *yySymType, str string) int {
func toHex(l yyLexer, lval *yySymType, str string) int {
h, err := ast.NewHexLiteral(str)
if err != nil {
l.Errorf("hex literal: %v", err)
l.AppendError(l.Errorf("hex literal: %v", err))
return int(unicode.ReplacementChar)
}
lval.item = h
......@@ -248,7 +256,7 @@ func toHex(l yyLexer, lval *yySymType, str string) int {
func toBit(l yyLexer, lval *yySymType, str string) int {
b, err := ast.NewBitLiteral(str)
if err != nil {
l.Errorf("bit literal: %v", err)
l.AppendError(l.Errorf("bit literal: %v", err))
return int(unicode.ReplacementChar)
}
lval.item = b
......
......@@ -18,6 +18,7 @@ import (
"sync"
"time"
"github.com/pingcap/parser"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/tidb/util/execdetails"
"github.com/pingcap/tidb/util/memory"
......@@ -115,6 +116,28 @@ type StatementContext struct {
NowTs time.Time
SysTs time.Time
StmtType string
OriginalSQL string
digestMemo struct {
sync.Once
normalized string
digest string
}
Tables []TableEntry
}
// SQLDigest gets normalized and digest for provided sql.
// it will cache result after first calling.
func (sc *StatementContext) SQLDigest() (normalized, sqlDigest string) {
sc.digestMemo.Do(func() {
sc.digestMemo.normalized, sc.digestMemo.digest = parser.NormalizeDigest(sc.OriginalSQL)
})
return sc.digestMemo.normalized, sc.digestMemo.digest
}
// TableEntry presents table in db.
type TableEntry struct {
DB string
Table string
}
// AddAffectedRows adds affected rows.
......
......@@ -37,34 +37,64 @@ func truncateStr(str string, flen int) string {
return str
}
// UnsignedUpperBound indicates the max uint64 values of different mysql types.
var UnsignedUpperBound = map[byte]uint64{
mysql.TypeTiny: math.MaxUint8,
mysql.TypeShort: math.MaxUint16,
mysql.TypeInt24: mysql.MaxUint24,
mysql.TypeLong: math.MaxUint32,
mysql.TypeLonglong: math.MaxUint64,
mysql.TypeBit: math.MaxUint64,
mysql.TypeEnum: math.MaxUint64,
mysql.TypeSet: math.MaxUint64,
// IntergerUnsignedUpperBound indicates the max uint64 values of different mysql types.
func IntergerUnsignedUpperBound(intType byte) uint64 {
switch intType {
case mysql.TypeTiny:
return math.MaxUint8
case mysql.TypeShort:
return math.MaxUint16
case mysql.TypeInt24:
return mysql.MaxUint24
case mysql.TypeLong:
return math.MaxUint32
case mysql.TypeLonglong:
return math.MaxUint64
case mysql.TypeBit:
return math.MaxUint64
case mysql.TypeEnum:
return math.MaxUint64
case mysql.TypeSet:
return math.MaxUint64
default:
panic("Input byte is not a mysql type")
}
}
// SignedUpperBound indicates the max int64 values of different mysql types.
var SignedUpperBound = map[byte]int64{
mysql.TypeTiny: math.MaxInt8,
mysql.TypeShort: math.MaxInt16,
mysql.TypeInt24: mysql.MaxInt24,
mysql.TypeLong: math.MaxInt32,
mysql.TypeLonglong: math.MaxInt64,
// IntergerSignedUpperBound indicates the max int64 values of different mysql types.
func IntergerSignedUpperBound(intType byte) int64 {
switch intType {
case mysql.TypeTiny:
return math.MaxInt8
case mysql.TypeShort:
return math.MaxInt16
case mysql.TypeInt24:
return mysql.MaxInt24
case mysql.TypeLong:
return math.MaxInt32
case mysql.TypeLonglong:
return math.MaxInt64
default:
panic("Input byte is not a mysql type")
}
}
// SignedLowerBound indicates the min int64 values of different mysql types.
var SignedLowerBound = map[byte]int64{
mysql.TypeTiny: math.MinInt8,
mysql.TypeShort: math.MinInt16,
mysql.TypeInt24: mysql.MinInt24,
mysql.TypeLong: math.MinInt32,
mysql.TypeLonglong: math.MinInt64,
// IntergerSignedLowerBound indicates the min int64 values of different mysql types.
func IntergerSignedLowerBound(intType byte) int64 {
switch intType {
case mysql.TypeTiny:
return math.MinInt8
case mysql.TypeShort:
return math.MinInt16
case mysql.TypeInt24:
return mysql.MinInt24
case mysql.TypeLong:
return math.MinInt32
case mysql.TypeLonglong:
return math.MinInt64
default:
panic("Input byte is not a mysql type")
}
}
// ConvertFloatToInt converts a float64 value to a int value.
......@@ -400,11 +430,11 @@ func ConvertJSONToInt(sc *stmtctx.StatementContext, j json.BinaryJSON, unsigned
case json.TypeCodeFloat64:
f := j.GetFloat64()
if !unsigned {
lBound := SignedLowerBound[mysql.TypeLonglong]
uBound := SignedUpperBound[mysql.TypeLonglong]
lBound := IntergerSignedLowerBound(mysql.TypeLonglong)
uBound := IntergerSignedUpperBound(mysql.TypeLonglong)
return ConvertFloatToInt(f, lBound, uBound, mysql.TypeDouble)
}
bound := UnsignedUpperBound[mysql.TypeLonglong]
bound := IntergerUnsignedUpperBound(mysql.TypeLonglong)
u, err := ConvertFloatToUint(sc, f, bound, mysql.TypeDouble)
return int64(u), errors.Trace(err)
case json.TypeCodeString:
......@@ -429,7 +459,7 @@ func ConvertJSONToFloat(sc *stmtctx.StatementContext, j json.BinaryJSON) (float6
case json.TypeCodeInt64:
return float64(j.GetInt64()), nil
case json.TypeCodeUint64:
u, err := ConvertIntToUint(sc, j.GetInt64(), UnsignedUpperBound[mysql.TypeLonglong], mysql.TypeLonglong)
u, err := ConvertIntToUint(sc, j.GetInt64(), IntergerUnsignedUpperBound(mysql.TypeLonglong), mysql.TypeLonglong)
return float64(u), errors.Trace(err)
case json.TypeCodeFloat64:
return j.GetFloat64(), nil
......
......@@ -14,6 +14,7 @@
package types
import (
"context"
"fmt"
"math"
"sort"
......@@ -29,7 +30,8 @@ import (
"github.com/pingcap/tidb/sessionctx/stmtctx"
"github.com/pingcap/tidb/types/json"
"github.com/pingcap/tidb/util/hack"
log "github.com/sirupsen/logrus"
"github.com/pingcap/tidb/util/logutil"
"go.uber.org/zap"
)
// Kind constants.
......@@ -867,7 +869,7 @@ func (d *Datum) convertToInt(sc *stmtctx.StatementContext, target *FieldType) (D
func (d *Datum) convertToUint(sc *stmtctx.StatementContext, target *FieldType) (Datum, error) {
tp := target.Tp
upperBound := UnsignedUpperBound[tp]
upperBound := IntergerUnsignedUpperBound(tp)
var (
val uint64
err error
......@@ -959,6 +961,8 @@ func (d *Datum) convertToMysqlTimestamp(sc *stmtctx.StatementContext, target *Fi
t, err = ParseTime(sc, d.GetString(), mysql.TypeTimestamp, fsp)
case KindInt64:
t, err = ParseTimeFromNum(sc, d.GetInt64(), mysql.TypeTimestamp, fsp)
case KindMysqlDecimal:
t, err = ParseTimeFromFloatString(sc, d.GetMysqlDecimal().String(), mysql.TypeTimestamp, fsp)
default:
return invalidConv(d, mysql.TypeTimestamp)
}
......@@ -996,6 +1000,8 @@ func (d *Datum) convertToMysqlTime(sc *stmtctx.StatementContext, target *FieldTy
return ret, errors.Trace(err)
}
t, err = t.RoundFrac(sc, fsp)
case KindMysqlDecimal:
t, err = ParseTimeFromFloatString(sc, d.GetMysqlDecimal().String(), tp, fsp)
case KindString, KindBytes:
t, err = ParseTime(sc, d.GetString(), tp, fsp)
case KindInt64:
......@@ -1125,7 +1131,7 @@ func (d *Datum) convertToMysqlDecimal(sc *stmtctx.StatementContext, target *Fiel
}
}
ret.SetValue(dec)
return ret, errors.Trace(err)
return ret, err
}
// ProduceDecWithSpecifiedTp produces a new decimal according to `flen` and `decimal`.
......@@ -1144,7 +1150,7 @@ func ProduceDecWithSpecifiedTp(dec *MyDecimal, tp *FieldType, sc *stmtctx.Statem
old := *dec
err = dec.Round(dec, decimal, ModeHalfEven)
if err != nil {
return nil, errors.Trace(err)
return nil, err
}
if !dec.IsZero() && frac > decimal && dec.Compare(&old) != 0 {
if sc.InInsertStmt || sc.InUpdateStmt || sc.InDeleteStmt {
......@@ -1167,7 +1173,7 @@ func ProduceDecWithSpecifiedTp(dec *MyDecimal, tp *FieldType, sc *stmtctx.Statem
if unsigned && dec.IsNegative() {
dec = dec.FromUint(0)
}
return dec, errors.Trace(err)
return dec, err
}
func (d *Datum) convertToMysqlYear(sc *stmtctx.StatementContext, target *FieldType) (Datum, error) {
......@@ -1184,7 +1190,9 @@ func (d *Datum) convertToMysqlYear(sc *stmtctx.StatementContext, target *FieldTy
if err != nil {
return ret, errors.Trace(err)
}
adjust = len(s) < 4
if len(s) != 4 && len(s) > 0 && s[0:1] == "0" {
adjust = true
}
case KindMysqlTime:
y = int64(d.GetMysqlTime().Time.Year())
case KindMysqlDuration:
......@@ -1241,7 +1249,7 @@ func (d *Datum) convertToMysqlEnum(sc *stmtctx.StatementContext, target *FieldTy
e, err = ParseEnumValue(target.Elems, uintDatum.GetUint64())
}
if err != nil {
log.Error(err)
logutil.Logger(context.Background()).Error("convert to MySQL enum failed", zap.Error(err))
err = errors.Trace(ErrTruncated)
}
ret.SetValue(e)
......@@ -1409,8 +1417,8 @@ func (d *Datum) ToInt64(sc *stmtctx.StatementContext) (int64, error) {
}
func (d *Datum) toSignedInteger(sc *stmtctx.StatementContext, tp byte) (int64, error) {
lowerBound := SignedLowerBound[tp]
upperBound := SignedUpperBound[tp]
lowerBound := IntergerSignedLowerBound(tp)
upperBound := IntergerSignedUpperBound(tp)
switch d.Kind() {
case KindInt64:
return ConvertIntToInt(d.GetInt64(), lowerBound, upperBound, tp)
......@@ -1790,7 +1798,7 @@ func handleTruncateError(sc *stmtctx.StatementContext) error {
// DatumsToString converts several datums to formatted string.
func DatumsToString(datums []Datum, handleSpecialValue bool) (string, error) {
var strs []string
strs := make([]string, 0, len(datums))
for _, datum := range datums {
if handleSpecialValue {
switch datum.Kind() {
......
......@@ -20,6 +20,7 @@ import (
"github.com/pingcap/parser/mysql"
ast "github.com/pingcap/parser/types"
"github.com/pingcap/tidb/types/json"
utilMath "github.com/pingcap/tidb/util/math"
)
// UnspecifiedLength is unspecified length.
......@@ -162,18 +163,18 @@ func DefaultTypeForValue(value interface{}, tp *FieldType) {
SetBinChsClnFlag(tp)
case int:
tp.Tp = mysql.TypeLonglong
tp.Flen = len(strconv.FormatInt(int64(x), 10))
tp.Flen = utilMath.StrLenOfInt64Fast(int64(x))
tp.Decimal = 0
SetBinChsClnFlag(tp)
case int64:
tp.Tp = mysql.TypeLonglong
tp.Flen = len(strconv.FormatInt(x, 10))
tp.Flen = utilMath.StrLenOfInt64Fast(x)
tp.Decimal = 0
SetBinChsClnFlag(tp)
case uint64:
tp.Tp = mysql.TypeLonglong
tp.Flag |= mysql.UnsignedFlag
tp.Flen = len(strconv.FormatUint(x, 10))
tp.Flen = utilMath.StrLenOfUint64Fast(x)
tp.Decimal = 0
SetBinChsClnFlag(tp)
case string:
......
......@@ -23,7 +23,7 @@ import (
)
// RoundMode is the type for round mode.
type RoundMode string
type RoundMode int32
// constant values.
const (
......@@ -49,18 +49,52 @@ const (
DivFracIncr = 4
// ModeHalfEven rounds normally.
ModeHalfEven RoundMode = "ModeHalfEven"
ModeHalfEven RoundMode = 5
// Truncate just truncates the decimal.
ModeTruncate RoundMode = "Truncate"
ModeTruncate RoundMode = 10
// Ceiling is not supported now.
modeCeiling RoundMode = "Ceiling"
modeCeiling RoundMode = 0
)
var (
wordBufLen = 9
powers10 = [10]int32{ten0, ten1, ten2, ten3, ten4, ten5, ten6, ten7, ten8, ten9}
dig2bytes = [10]int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4}
fracMax = [8]int32{
mod9 = [128]int8{
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1,
}
div9 = [128]int{
0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11,
12, 12, 12, 12, 12, 12, 12, 12, 12,
13, 13, 13, 13, 13, 13, 13, 13, 13,
14, 14,
}
powers10 = [10]int32{ten0, ten1, ten2, ten3, ten4, ten5, ten6, ten7, ten8, ten9}
dig2bytes = [10]int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4}
fracMax = [8]int32{
900000000,
990000000,
999000000,
......@@ -174,6 +208,9 @@ func countTrailingZeroes(i int, word int32) int {
}
func digitsToWords(digits int) int {
if digits+digitsPerWord-1 >= 0 && digits+digitsPerWord-1 < 128 {
return div9[digits+digitsPerWord-1]
}
return (digits + digitsPerWord - 1) / digitsPerWord
}
......@@ -756,16 +793,8 @@ func (d *MyDecimal) Round(to *MyDecimal, frac int, roundMode RoundMode) (err err
wordsFrac := digitsToWords(int(d.digitsFrac))
wordsInt := digitsToWords(int(d.digitsInt))
var roundDigit int32
roundDigit := int32(roundMode)
/* TODO - fix this code as it won't work for CEILING mode */
switch roundMode {
case modeCeiling:
roundDigit = 0
case ModeHalfEven:
roundDigit = 5
case ModeTruncate:
roundDigit = 10
}
if wordsInt+wordsFracTo > wordBufLen {
wordsFracTo = wordBufLen - wordsInt
......@@ -802,9 +831,9 @@ func (d *MyDecimal) Round(to *MyDecimal, frac int, roundMode RoundMode) (err err
toIdx := wordsInt + wordsFracTo - 1
if frac == wordsFracTo*digitsPerWord {
doInc := false
switch roundDigit {
switch roundMode {
// Notice: No support for ceiling mode now.
case 0:
case modeCeiling:
// If any word after scale is not zero, do increment.
// e.g ceiling 3.0001 to scale 1, gets 3.1
idx := toIdx + (wordsFrac - wordsFracTo)
......@@ -815,11 +844,11 @@ func (d *MyDecimal) Round(to *MyDecimal, frac int, roundMode RoundMode) (err err
}
idx--
}
case 5:
case ModeHalfEven:
digAfterScale := d.wordBuf[toIdx+1] / digMask // the first digit after scale.
// If first digit after scale is 5 and round even, do increment if digit at scale is odd.
doInc = (digAfterScale > 5) || (digAfterScale == 5)
case 10:
case ModeTruncate:
// Never round, just truncate.
doInc = false
}
......@@ -917,7 +946,7 @@ func (d *MyDecimal) Round(to *MyDecimal, frac int, roundMode RoundMode) (err err
}
}
/* Here we check 999.9 -> 1000 case when we need to increase intDigCnt */
firstDig := to.digitsInt % digitsPerWord
firstDig := mod9[to.digitsInt]
if firstDig > 0 && to.wordBuf[toIdx] >= powers10[firstDig] {
to.digitsInt++
}
......
......@@ -197,8 +197,9 @@ func (n *ValueExpr) Accept(v ast.Visitor) (ast.Node, bool) {
// Used in parsing prepare statement.
type ParamMarkerExpr struct {
ValueExpr
Offset int
Order int
Offset int
Order int
InExecute bool
}
// Restore implements Node interface.
......
......@@ -16,6 +16,7 @@ package execdetails
import (
"fmt"
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
......@@ -53,60 +54,75 @@ type CommitDetails struct {
TxnRetry int
}
const (
// ProcessTimeStr represents the sum of process time of all the coprocessor tasks.
ProcessTimeStr = "Process_time"
// WaitTimeStr means the time of all coprocessor wait.
WaitTimeStr = "Wait_time"
// BackoffTimeStr means the time of all back-off.
BackoffTimeStr = "Backoff_time"
// RequestCountStr means the request count.
RequestCountStr = "Request_count"
// TotalKeysStr means the total scan keys.
TotalKeysStr = "Total_keys"
// ProcessKeysStr means the total processed keys.
ProcessKeysStr = "Process_keys"
)
// String implements the fmt.Stringer interface.
func (d ExecDetails) String() string {
parts := make([]string, 0, 6)
if d.ProcessTime > 0 {
parts = append(parts, fmt.Sprintf("process_time:%vs", d.ProcessTime.Seconds()))
parts = append(parts, ProcessTimeStr+": "+strconv.FormatFloat(d.ProcessTime.Seconds(), 'f', -1, 64))
}
if d.WaitTime > 0 {
parts = append(parts, fmt.Sprintf("wait_time:%vs", d.WaitTime.Seconds()))
parts = append(parts, WaitTimeStr+": "+strconv.FormatFloat(d.WaitTime.Seconds(), 'f', -1, 64))
}
if d.BackoffTime > 0 {
parts = append(parts, fmt.Sprintf("backoff_time:%vs", d.BackoffTime.Seconds()))
parts = append(parts, BackoffTimeStr+": "+strconv.FormatFloat(d.BackoffTime.Seconds(), 'f', -1, 64))
}
if d.RequestCount > 0 {
parts = append(parts, fmt.Sprintf("request_count:%d", d.RequestCount))
parts = append(parts, RequestCountStr+": "+strconv.FormatInt(int64(d.RequestCount), 10))
}
if d.TotalKeys > 0 {
parts = append(parts, fmt.Sprintf("total_keys:%d", d.TotalKeys))
parts = append(parts, TotalKeysStr+": "+strconv.FormatInt(d.TotalKeys, 10))
}
if d.ProcessedKeys > 0 {
parts = append(parts, fmt.Sprintf("processed_keys:%d", d.ProcessedKeys))
parts = append(parts, ProcessKeysStr+": "+strconv.FormatInt(d.ProcessedKeys, 10))
}
commitDetails := d.CommitDetail
if commitDetails != nil {
if commitDetails.PrewriteTime > 0 {
parts = append(parts, fmt.Sprintf("prewrite_time:%vs", commitDetails.PrewriteTime.Seconds()))
parts = append(parts, fmt.Sprintf("Prewrite_time: %v", commitDetails.PrewriteTime.Seconds()))
}
if commitDetails.CommitTime > 0 {
parts = append(parts, fmt.Sprintf("commit_time:%vs", commitDetails.CommitTime.Seconds()))
parts = append(parts, fmt.Sprintf("Commit_time: %v", commitDetails.CommitTime.Seconds()))
}
if commitDetails.GetCommitTsTime > 0 {
parts = append(parts, fmt.Sprintf("get_commit_ts_time:%vs", commitDetails.GetCommitTsTime.Seconds()))
parts = append(parts, fmt.Sprintf("Get_commit_ts_time: %v", commitDetails.GetCommitTsTime.Seconds()))
}
if commitDetails.TotalBackoffTime > 0 {
parts = append(parts, fmt.Sprintf("total_backoff_time:%vs", commitDetails.TotalBackoffTime.Seconds()))
parts = append(parts, fmt.Sprintf("Total_backoff_time: %v", commitDetails.TotalBackoffTime.Seconds()))
}
resolveLockTime := atomic.LoadInt64(&commitDetails.ResolveLockTime)
if resolveLockTime > 0 {
parts = append(parts, fmt.Sprintf("resolve_lock_time:%vs", time.Duration(resolveLockTime).Seconds()))
parts = append(parts, fmt.Sprintf("Resolve_lock_time: %v", time.Duration(resolveLockTime).Seconds()))
}
if commitDetails.LocalLatchTime > 0 {
parts = append(parts, fmt.Sprintf("local_latch_wait_time:%vs", commitDetails.LocalLatchTime.Seconds()))
parts = append(parts, fmt.Sprintf("Local_latch_wait_time: %v", commitDetails.LocalLatchTime.Seconds()))
}
if commitDetails.WriteKeys > 0 {
parts = append(parts, fmt.Sprintf("write_keys:%d", commitDetails.WriteKeys))
parts = append(parts, fmt.Sprintf("Write_keys: %d", commitDetails.WriteKeys))
}
if commitDetails.WriteSize > 0 {
parts = append(parts, fmt.Sprintf("write_size:%d", commitDetails.WriteSize))
parts = append(parts, fmt.Sprintf("Write_size: %d", commitDetails.WriteSize))
}
prewriteRegionNum := atomic.LoadInt32(&commitDetails.PrewriteRegionNum)
if prewriteRegionNum > 0 {
parts = append(parts, fmt.Sprintf("prewrite_region:%d", prewriteRegionNum))
parts = append(parts, fmt.Sprintf("Prewrite_region: %d", prewriteRegionNum))
}
if commitDetails.TxnRetry > 0 {
parts = append(parts, fmt.Sprintf("txn_retry:%d", commitDetails.TxnRetry))
parts = append(parts, fmt.Sprintf("Txn_retry: %d", commitDetails.TxnRetry))
}
}
return strings.Join(parts, " ")
......
此差异已折叠。
// 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 math
import "math"
// http://cavaliercoder.com/blog/optimized-abs-for-int64-in-go.html
func abs(n int64) int64 {
y := n >> 63
return (n ^ y) - y
}
// uintSizeTable is used as a table to do comparison to get uint length is faster than doing loop on division with 10
var uintSizeTable = [21]uint64{
0, // redundant 0 here, so to make function StrLenOfUint64Fast to count from 1 and return i directly
9, 99, 999, 9999, 99999,
999999, 9999999, 99999999, 999999999, 9999999999,
99999999999, 999999999999, 9999999999999, 99999999999999, 999999999999999,
9999999999999999, 99999999999999999, 999999999999999999, 9999999999999999999,
math.MaxUint64,
} // math.MaxUint64 is 18446744073709551615 and it has 20 digits
// StrLenOfUint64Fast efficiently calculate the string character lengths of an uint64 as input
func StrLenOfUint64Fast(x uint64) int {
for i := 1; ; i++ {
if x <= uintSizeTable[i] {
return i
}
}
}
// StrLenOfInt64Fast efficiently calculate the string character lengths of an int64 as input
func StrLenOfInt64Fast(x int64) int {
size := 0
if x < 0 {
size = 1 // add "-" sign on the length count
}
return size + StrLenOfUint64Fast(uint64(abs(x)))
}
......@@ -14,11 +14,13 @@
package memory
import (
"context"
"sync"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/parser/terror"
log "github.com/sirupsen/logrus"
"github.com/pingcap/tidb/util/logutil"
"go.uber.org/zap"
)
// ActionOnExceed is the action taken when memory usage exceeds memory quota.
......@@ -41,7 +43,8 @@ func (a *LogOnExceed) Action(t *Tracker) {
defer a.mutex.Unlock()
if !a.acted {
a.acted = true
log.Warnf(errMemExceedThreshold.GenWithStackByArgs(t.label, t.BytesConsumed(), t.bytesLimit, t.String()).Error())
logutil.Logger(context.Background()).Warn("memory exceeds quota",
zap.Error(errMemExceedThreshold.GenWithStackByArgs(t.label, t.BytesConsumed(), t.bytesLimit, t.String())))
}
}
......
......@@ -17,6 +17,7 @@ import (
"bytes"
"fmt"
"sync"
"sync/atomic"
)
// Tracker is used to track the memory usage during query execution.
......@@ -37,14 +38,16 @@ import (
// 1. Only "BytesConsumed()", "Consume()", "AttachTo()" and "Detach" are thread-safe.
// 2. Other operations of a Tracker tree is not thread-safe.
type Tracker struct {
sync.Mutex // For synchronization.
mu struct {
sync.Mutex
children []*Tracker // The children memory trackers
}
label string // Label of this "Tracker".
bytesConsumed int64 // Consumed bytes.
bytesLimit int64 // Negative value means no limit.
actionOnExceed ActionOnExceed
parent *Tracker // The parent memory tracker.
children []*Tracker // The children memory trackers.
parent *Tracker // The parent memory tracker.
}
// NewTracker creates a memory tracker.
......@@ -55,7 +58,6 @@ func NewTracker(label string, bytesLimit int64) *Tracker {
label: label,
bytesLimit: bytesLimit,
actionOnExceed: &LogOnExceed{},
parent: nil,
}
}
......@@ -76,9 +78,9 @@ func (t *Tracker) AttachTo(parent *Tracker) {
if t.parent != nil {
t.parent.remove(t)
}
parent.Lock()
parent.children = append(parent.children, t)
parent.Unlock()
parent.mu.Lock()
parent.mu.children = append(parent.mu.children, t)
parent.mu.Unlock()
t.parent = parent
t.parent.Consume(t.BytesConsumed())
......@@ -90,16 +92,16 @@ func (t *Tracker) Detach() {
}
func (t *Tracker) remove(oldChild *Tracker) {
t.Lock()
defer t.Unlock()
for i, child := range t.children {
t.mu.Lock()
defer t.mu.Unlock()
for i, child := range t.mu.children {
if child != oldChild {
continue
}
t.bytesConsumed -= oldChild.BytesConsumed()
atomic.AddInt64(&t.bytesConsumed, -oldChild.BytesConsumed())
oldChild.parent = nil
t.children = append(t.children[:i], t.children[i+1:]...)
t.mu.children = append(t.mu.children[:i], t.mu.children[i+1:]...)
break
}
}
......@@ -116,18 +118,18 @@ func (t *Tracker) ReplaceChild(oldChild, newChild *Tracker) {
newConsumed := newChild.BytesConsumed()
newChild.parent = t
t.Lock()
for i, child := range t.children {
t.mu.Lock()
for i, child := range t.mu.children {
if child != oldChild {
continue
}
newConsumed -= oldChild.BytesConsumed()
oldChild.parent = nil
t.children[i] = newChild
t.mu.children[i] = newChild
break
}
t.Unlock()
t.mu.Unlock()
t.Consume(newConsumed)
}
......@@ -137,12 +139,9 @@ func (t *Tracker) ReplaceChild(oldChild, newChild *Tracker) {
func (t *Tracker) Consume(bytes int64) {
var rootExceed *Tracker
for tracker := t; tracker != nil; tracker = tracker.parent {
tracker.Lock()
tracker.bytesConsumed += bytes
if tracker.bytesLimit > 0 && tracker.bytesConsumed >= tracker.bytesLimit {
if atomic.AddInt64(&tracker.bytesConsumed, bytes) >= tracker.bytesLimit && tracker.bytesLimit > 0 {
rootExceed = tracker
}
tracker.Unlock()
}
if rootExceed != nil {
rootExceed.actionOnExceed.Action(rootExceed)
......@@ -151,9 +150,7 @@ func (t *Tracker) Consume(bytes int64) {
// BytesConsumed returns the consumed memory usage value in bytes.
func (t *Tracker) BytesConsumed() int64 {
t.Lock()
defer t.Unlock()
return t.bytesConsumed
return atomic.LoadInt64(&t.bytesConsumed)
}
// String returns the string representation of this Tracker tree.
......@@ -170,13 +167,13 @@ func (t *Tracker) toString(indent string, buffer *bytes.Buffer) {
}
fmt.Fprintf(buffer, "%s \"consumed\": %s\n", indent, t.bytesToString(t.BytesConsumed()))
t.Lock()
for i := range t.children {
if t.children[i] != nil {
t.children[i].toString(indent+" ", buffer)
t.mu.Lock()
for i := range t.mu.children {
if t.mu.children[i] != nil {
t.mu.children[i].toString(indent+" ", buffer)
}
}
t.Unlock()
t.mu.Unlock()
buffer.WriteString(indent + "}\n")
}
......
Copyright (c) 2016 Uber Technologies, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PACKAGES := $(shell glide nv)
# Many Go tools take file globs or directories as arguments instead of packages.
PACKAGE_FILES ?= *.go
# For pre go1.6
export GO15VENDOREXPERIMENT=1
.PHONY: build
build:
go build -i $(PACKAGES)
.PHONY: install
install:
glide --version || go get github.com/Masterminds/glide
glide install
.PHONY: test
test:
go test -cover -race $(PACKAGES)
.PHONY: install_ci
install_ci: install
go get github.com/wadey/gocovmerge
go get github.com/mattn/goveralls
go get golang.org/x/tools/cmd/cover
.PHONY: install_lint
install_lint:
go get golang.org/x/lint/golint
.PHONY: lint
lint:
@rm -rf lint.log
@echo "Checking formatting..."
@gofmt -d -s $(PACKAGE_FILES) 2>&1 | tee lint.log
@echo "Checking vet..."
@$(foreach dir,$(PACKAGE_FILES),go tool vet $(dir) 2>&1 | tee -a lint.log;)
@echo "Checking lint..."
@$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;)
@echo "Checking for unresolved FIXMEs..."
@git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log
@[ ! -s lint.log ]
.PHONY: test_ci
test_ci: install_ci build
./scripts/cover.sh $(shell go list $(PACKAGES))
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
Simple wrappers for primitive types to enforce atomic access.
## Installation
`go get -u go.uber.org/atomic`
## Usage
The standard library's `sync/atomic` is powerful, but it's easy to forget which
variables must be accessed atomically. `go.uber.org/atomic` preserves all the
functionality of the standard library, but wraps the primitive types to
provide a safer, more convenient API.
```go
var atom atomic.Uint32
atom.Store(42)
atom.Sub(2)
atom.CAS(40, 11)
```
See the [documentation][doc] for a complete API specification.
## Development Status
Stable.
___
Released under the [MIT License](LICENSE.txt).
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
[doc]: https://godoc.org/go.uber.org/atomic
[ci-img]: https://travis-ci.org/uber-go/atomic.svg?branch=master
[ci]: https://travis-ci.org/uber-go/atomic
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/atomic
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
[reportcard]: https://goreportcard.com/report/go.uber.org/atomic
此差异已折叠。
此差异已折叠。
hash: f14d51408e3e0e4f73b34e4039484c78059cd7fc5f4996fdd73db20dc8d24f53
updated: 2016-10-27T00:10:51.16960137-07:00
imports: []
testImports:
- name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
subpackages:
- spew
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages:
- difflib
- name: github.com/stretchr/testify
version: d77da356e56a7428ad25149ca77381849a6a5232
subpackages:
- assert
- require
package: go.uber.org/atomic
testImport:
- package: github.com/stretchr/testify
subpackages:
- assert
- require
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册