Merge branch 'github:master' into master

This commit is contained in:
Tim Vaillancourt 2022-02-22 00:57:58 +01:00 committed by GitHub
commit ac23094497
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
101 changed files with 4172 additions and 1311 deletions

View File

@ -18,7 +18,7 @@ function build {
GOOS=$3 GOOS=$3
GOARCH=$4 GOARCH=$4
if ! go version | egrep -q 'go(1\.1[56])' ; then if ! go version | egrep -q 'go1\.(1[5-9]|[2-9][0-9]{1})' ; then
echo "go version must be 1.15 or above" echo "go version must be 1.15 or above"
exit 1 exit 1
fi fi
@ -40,8 +40,9 @@ function build {
builddir=$(setuptree) builddir=$(setuptree)
cp $buildpath/$target $builddir/gh-ost/usr/bin cp $buildpath/$target $builddir/gh-ost/usr/bin
cd $buildpath cd $buildpath
fpm -v "${RELEASE_VERSION}" --epoch 1 -f -s dir -n gh-ost -m 'shlomi-noach <shlomi-noach+gh-ost-deb@github.com>' --description "GitHub's Online Schema Migrations for MySQL " --url "https://github.com/github/gh-ost" --vendor "GitHub" --license "Apache 2.0" -C $builddir/gh-ost --prefix=/ -t rpm --rpm-rpmbuild-define "_build_id_links none" . fpm -v "${RELEASE_VERSION}" --epoch 1 -f -s dir -n gh-ost -m 'GitHub' --description "GitHub's Online Schema Migrations for MySQL " --url "https://github.com/github/gh-ost" --vendor "GitHub" --license "Apache 2.0" -C $builddir/gh-ost --prefix=/ -t rpm --rpm-rpmbuild-define "_build_id_links none" .
fpm -v "${RELEASE_VERSION}" --epoch 1 -f -s dir -n gh-ost -m 'shlomi-noach <shlomi-noach+gh-ost-deb@github.com>' --description "GitHub's Online Schema Migrations for MySQL " --url "https://github.com/github/gh-ost" --vendor "GitHub" --license "Apache 2.0" -C $builddir/gh-ost --prefix=/ -t deb --deb-no-default-config-files . fpm -v "${RELEASE_VERSION}" --epoch 1 -f -s dir -n gh-ost -m 'GitHub' --description "GitHub's Online Schema Migrations for MySQL " --url "https://github.com/github/gh-ost" --vendor "GitHub" --license "Apache 2.0" -C $builddir/gh-ost --prefix=/ -t deb --deb-no-default-config-files .
cd -
fi fi
} }
@ -62,10 +63,13 @@ main() {
mkdir -p ${buildpath} mkdir -p ${buildpath}
rm -rf ${buildpath:?}/* rm -rf ${buildpath:?}/*
build GNU/Linux linux linux amd64 build GNU/Linux linux linux amd64
# build macOS osx darwin amd64 build macOS osx darwin amd64
echo "Binaries found in:" echo "Binaries found in:"
find $buildpath/gh-ost* -type f -maxdepth 1 find $buildpath/gh-ost* -type f -maxdepth 1
echo "Checksums:"
(cd $buildpath && shasum -a256 gh-ost* 2>/dev/null)
} }
main "$@" main "$@"

View File

@ -22,7 +22,7 @@ If, for some reason, you do not wish `gh-ost` to connect to a replica, you may c
### approve-renamed-columns ### approve-renamed-columns
When your migration issues a column rename (`change column old_name new_name ...`) `gh-ost` analyzes the statement to try and associate the old column name with new column name. Otherwise the new structure may also look like some column was dropped and another was added. When your migration issues a column rename (`change column old_name new_name ...`) `gh-ost` analyzes the statement to try and associate the old column name with new column name. Otherwise, the new structure may also look like some column was dropped and another was added.
`gh-ost` will print out what it thinks the _rename_ implied, but will not issue the migration unless you provide with `--approve-renamed-columns`. `gh-ost` will print out what it thinks the _rename_ implied, but will not issue the migration unless you provide with `--approve-renamed-columns`.
@ -32,7 +32,7 @@ If you think `gh-ost` is mistaken and that there's actually no _rename_ involved
`gh-ost` infers the identity of the master server by crawling up the replication topology. You may explicitly tell `gh-ost` the identity of the master host via `--assume-master-host=the.master.com`. This is useful in: `gh-ost` infers the identity of the master server by crawling up the replication topology. You may explicitly tell `gh-ost` the identity of the master host via `--assume-master-host=the.master.com`. This is useful in:
- _master-master_ topologies (together with [`--allow-master-master`](#allow-master-master)), where `gh-ost` can arbitrarily pick one of the co-masters and you prefer that it picks a specific one - _master-master_ topologies (together with [`--allow-master-master`](#allow-master-master)), where `gh-ost` can arbitrarily pick one of the co-masters, and you prefer that it picks a specific one
- _tungsten replicator_ topologies (together with [`--tungsten`](#tungsten)), where `gh-ost` is unable to crawl and detect the master - _tungsten replicator_ topologies (together with [`--tungsten`](#tungsten)), where `gh-ost` is unable to crawl and detect the master
### assume-rbr ### assume-rbr
@ -61,7 +61,13 @@ Comma delimited status-name=threshold, same format as [`--max-load`](#max-load).
`--critical-load` defines a threshold that, when met, `gh-ost` panics and bails out. The default behavior is to bail out immediately when meeting this threshold. `--critical-load` defines a threshold that, when met, `gh-ost` panics and bails out. The default behavior is to bail out immediately when meeting this threshold.
This may sometimes lead to migrations bailing out on a very short spike, that, while in itself is impacting production and is worth investigating, isn't reason enough to kill a 10 hour migration. This may sometimes lead to migrations bailing out on a very short spike, that, while in itself is impacting production and is worth investigating, isn't reason enough to kill a 10-hour migration.
### critical-load-hibernate-seconds
When `--critical-load-hibernate-seconds` is non-zero (e.g. `--critical-load-hibernate-seconds=300`), `critical-load` does not panic and bail out; instead, `gh-ost` goes into hibernation for the specified duration. It will not read/write anything from/to any server during this time. Execution then continues upon waking from hibernation.
If `critical-load` is met again, `gh-ost` will repeat this cycle, and never panic and bail out.
### critical-load-interval-millis ### critical-load-interval-millis
@ -98,7 +104,7 @@ Noteworthy is that setting `--dml-batch-size` to higher value _does not_ mean `g
### exact-rowcount ### exact-rowcount
A `gh-ost` execution need to copy whatever rows you have in your existing table onto the ghost table. This can, and often be, a large number. Exactly what that number is? A `gh-ost` execution need to copy whatever rows you have in your existing table onto the ghost table. This can and often will be, a large number. Exactly what that number is?
`gh-ost` initially estimates the number of rows in your table by issuing an `explain select * from your_table`. This will use statistics on your table and return with a rough estimate. How rough? It might go as low as half or as high as double the actual number of rows in your table. This is the same method as used in [`pt-online-schema-change`](https://www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html). `gh-ost` initially estimates the number of rows in your table by issuing an `explain select * from your_table`. This will use statistics on your table and return with a rough estimate. How rough? It might go as low as half or as high as double the actual number of rows in your table. This is the same method as used in [`pt-online-schema-change`](https://www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html).
`gh-ost` also supports the `--exact-rowcount` flag. When this flag is given, two things happen: `gh-ost` also supports the `--exact-rowcount` flag. When this flag is given, two things happen:
@ -135,6 +141,10 @@ Add this flag when executing on a 1st generation Google Cloud Platform (GCP).
Default 100. See [`subsecond-lag`](subsecond-lag.md) for details. Default 100. See [`subsecond-lag`](subsecond-lag.md) for details.
### hooks-status-interval
Defaults to 60 seconds. Configures how often the `gh-ost-on-status` hook is called, see [`hooks`](hooks.md) for full details on how to use hooks.
### initially-drop-ghost-table ### initially-drop-ghost-table
`gh-ost` maintains two tables while migrating: the _ghost_ table (which is synced from your original table and finally replaces it) and a changelog table, which is used internally for bookkeeping. By default, it panics and aborts if it sees those tables upon startup. Provide `--initially-drop-ghost-table` and `--initially-drop-old-table` to let `gh-ost` know it's OK to drop them beforehand. `gh-ost` maintains two tables while migrating: the _ghost_ table (which is synced from your original table and finally replaces it) and a changelog table, which is used internally for bookkeeping. By default, it panics and aborts if it sees those tables upon startup. Provide `--initially-drop-ghost-table` and `--initially-drop-old-table` to let `gh-ost` know it's OK to drop them beforehand.
@ -230,7 +240,7 @@ Provide a command delimited list of replicas; `gh-ost` will throttle when any of
### throttle-http ### throttle-http
Provide a HTTP endpoint; `gh-ost` will issue `HEAD` requests on given URL and throttle whenever response status code is not `200`. The URL can be queried and updated dynamically via [interactive commands](interactive-commands.md). Empty URL disables the HTTP check. Provide an HTTP endpoint; `gh-ost` will issue `HEAD` requests on given URL and throttle whenever response status code is not `200`. The URL can be queried and updated dynamically via [interactive commands](interactive-commands.md). Empty URL disables the HTTP check.
### timestamp-old-table ### timestamp-old-table

View File

@ -68,6 +68,7 @@ The following variables are available on all hooks:
- `GH_OST_INSPECTED_LAG` - lag in seconds (floating point) of inspected server - `GH_OST_INSPECTED_LAG` - lag in seconds (floating point) of inspected server
- `GH_OST_HEARTBEAT_LAG` - lag in seconds (floating point) of heartbeat - `GH_OST_HEARTBEAT_LAG` - lag in seconds (floating point) of heartbeat
- `GH_OST_PROGRESS` - progress pct ([0..100], floating point) of migration - `GH_OST_PROGRESS` - progress pct ([0..100], floating point) of migration
- `GH_OST_ETA_SECONDS` - estimated duration until migration finishes in seconds
- `GH_OST_MIGRATED_HOST` - `GH_OST_MIGRATED_HOST`
- `GH_OST_INSPECTED_HOST` - `GH_OST_INSPECTED_HOST`
- `GH_OST_EXECUTING_HOST` - `GH_OST_EXECUTING_HOST`

View File

@ -112,7 +112,7 @@ It is also interesting to observe that `gh-ost` is the only application writing
When `gh-ost` pauses (throttles), it issues no writes on the ghost table. Because there are no triggers, write workload is decoupled from the `gh-ost` write workload. And because we're using an asynchronous approach, the algorithm already handles a time difference between a master write time and the ghost apply time. A difference of a few microseconds is no different from a difference of minutes or hours. When `gh-ost` pauses (throttles), it issues no writes on the ghost table. Because there are no triggers, write workload is decoupled from the `gh-ost` write workload. And because we're using an asynchronous approach, the algorithm already handles a time difference between a master write time and the ghost apply time. A difference of a few microseconds is no different from a difference of minutes or hours.
When `gh-ost` [throttles](throttle.md), either by replication lag, `max-load` setting or and explicit [interactive user command](interactive-commands.md), the master is back to normal. It sees no more writes on the ghost table. When `gh-ost` [throttles](throttle.md), either by replication lag, `max-load` setting or an explicit [interactive user command](interactive-commands.md), the master is back to normal. It sees no more writes on the ghost table.
An exception is the ongoing heartbeat writes onto the changelog table, which we consider to be negligible. An exception is the ongoing heartbeat writes onto the changelog table, which we consider to be negligible.
#### Testability #### Testability

7
go.mod
View File

@ -4,15 +4,14 @@ go 1.16
require ( require (
github.com/go-ini/ini v1.62.0 github.com/go-ini/ini v1.62.0
github.com/go-sql-driver/mysql v1.5.0 github.com/go-mysql-org/go-mysql v1.3.0
github.com/go-sql-driver/mysql v1.6.0
github.com/openark/golib v0.0.0-20210531070646-355f37940af8 github.com/openark/golib v0.0.0-20210531070646-355f37940af8
github.com/pkg/errors v0.9.1 // indirect
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/siddontang/go-mysql v1.1.0
github.com/smartystreets/goconvey v1.6.4 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
golang.org/x/text v0.3.5 golang.org/x/text v0.3.6
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
) )

92
go.sum
View File

@ -1,32 +1,54 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM=
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU= github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU=
github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-mysql-org/go-mysql v1.3.0 h1:lpNqkwdPzIrYSZGdqt8HIgAXZaK6VxBNfr8f7Z4FgGg=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-mysql-org/go-mysql v1.3.0/go.mod h1:3lFZKf7l95Qo70+3XB2WpiSf9wu2s3na3geLMaIIrqQ=
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/openark/golib v0.0.0-20210531070646-355f37940af8 h1:9ciIHNuyFqRWi9NpMNw9sVLB6z1ItpP5ZhTY9Q1xVu4= github.com/openark/golib v0.0.0-20210531070646-355f37940af8 h1:9ciIHNuyFqRWi9NpMNw9sVLB6z1ItpP5ZhTY9Q1xVu4=
github.com/openark/golib v0.0.0-20210531070646-355f37940af8/go.mod h1:1jj8x1eDVZxgc/Z4VyamX4qTbAdHPUQA6NeVtCd8Sl8= github.com/openark/golib v0.0.0-20210531070646-355f37940af8/go.mod h1:1jj8x1eDVZxgc/Z4VyamX4qTbAdHPUQA6NeVtCd8Sl8=
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg= 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/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.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/parser v0.0.0-20190506092653-e336082eb825/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/errors v0.11.5-0.20201029093017-5a7df2af2ac7/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI=
github.com/pingcap/tipb v0.0.0-20190428032612-535e1abaa330/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI= github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 h1:LllgC9eGfqzkfubMgjKIDyZYaa609nNWAyNZtpy2B3M=
github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI=
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pingcap/parser v0.0.0-20210415081931-48e7f467fd74/go.mod h1:xZC8I7bug4GJ5KtHhgAikjTfU4kBv1Sbo3Pf1MZ6lVw=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
@ -35,38 +57,76 @@ github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 h1:oI+RNwuC9jF2g2lP0u0cVEEZrc/AYBCuFdvwrLWM/6Q= github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 h1:oI+RNwuC9jF2g2lP0u0cVEEZrc/AYBCuFdvwrLWM/6Q=
github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4= github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4=
github.com/siddontang/go-mysql v1.1.0 h1:NfkS1skrPwUd3hsUqhc6jrv24dKTNMANxKRmDsf1fMc=
github.com/siddontang/go-mysql v1.1.0/go.mod h1:+W4RCzesQDI11HvIkaDjS8yM36SpAnGNQ7jmTLn5BnU=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
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=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -140,6 +140,7 @@ type MigrationContext struct {
HooksHintMessage string HooksHintMessage string
HooksHintOwner string HooksHintOwner string
HooksHintToken string HooksHintToken string
HooksStatusIntervalSec int64
DropServeSocket bool DropServeSocket bool
ServeSocketFile string ServeSocketFile string
@ -551,8 +552,8 @@ func (this *MigrationContext) SetMaxLagMillisecondsThrottleThreshold(maxLagMilli
} }
func (this *MigrationContext) SetChunkSize(chunkSize int64) { func (this *MigrationContext) SetChunkSize(chunkSize int64) {
if chunkSize < 100 { if chunkSize < 10 {
chunkSize = 100 chunkSize = 10
} }
if chunkSize > 100000 { if chunkSize > 100000 {
chunkSize = 100000 chunkSize = 100000

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 GitHub Inc. Copyright 2021 GitHub Inc.
See https://github.com/github/gh-ost/blob/master/LICENSE See https://github.com/github/gh-ost/blob/master/LICENSE
*/ */
@ -13,8 +13,8 @@ import (
"github.com/github/gh-ost/go/mysql" "github.com/github/gh-ost/go/mysql"
"github.com/github/gh-ost/go/sql" "github.com/github/gh-ost/go/sql"
gomysql "github.com/siddontang/go-mysql/mysql" gomysql "github.com/go-mysql-org/go-mysql/mysql"
"github.com/siddontang/go-mysql/replication" "github.com/go-mysql-org/go-mysql/replication"
"golang.org/x/net/context" "golang.org/x/net/context"
) )

View File

@ -8,6 +8,7 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"net/url"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
@ -98,7 +99,7 @@ func main() {
flag.BoolVar(&migrationContext.AssumeRBR, "assume-rbr", false, "set to 'true' when you know for certain your server uses 'ROW' binlog_format. gh-ost is unable to tell, event after reading binlog_format, whether the replication process does indeed use 'ROW', and restarts replication to be certain RBR setting is applied. Such operation requires SUPER privileges which you might not have. Setting this flag avoids restarting replication and you can proceed to use gh-ost without SUPER privileges") flag.BoolVar(&migrationContext.AssumeRBR, "assume-rbr", false, "set to 'true' when you know for certain your server uses 'ROW' binlog_format. gh-ost is unable to tell, event after reading binlog_format, whether the replication process does indeed use 'ROW', and restarts replication to be certain RBR setting is applied. Such operation requires SUPER privileges which you might not have. Setting this flag avoids restarting replication and you can proceed to use gh-ost without SUPER privileges")
flag.BoolVar(&migrationContext.CutOverExponentialBackoff, "cut-over-exponential-backoff", false, "Wait exponentially longer intervals between failed cut-over attempts. Wait intervals obey a maximum configurable with 'exponential-backoff-max-interval').") flag.BoolVar(&migrationContext.CutOverExponentialBackoff, "cut-over-exponential-backoff", false, "Wait exponentially longer intervals between failed cut-over attempts. Wait intervals obey a maximum configurable with 'exponential-backoff-max-interval').")
exponentialBackoffMaxInterval := flag.Int64("exponential-backoff-max-interval", 64, "Maximum number of seconds to wait between attempts when performing various operations with exponential backoff.") exponentialBackoffMaxInterval := flag.Int64("exponential-backoff-max-interval", 64, "Maximum number of seconds to wait between attempts when performing various operations with exponential backoff.")
chunkSize := flag.Int64("chunk-size", 1000, "amount of rows to handle in each iteration (allowed range: 100-100,000)") chunkSize := flag.Int64("chunk-size", 1000, "amount of rows to handle in each iteration (allowed range: 10-100,000)")
dmlBatchSize := flag.Int64("dml-batch-size", 10, "batch size for DML events to apply in a single transaction (range 1-100)") dmlBatchSize := flag.Int64("dml-batch-size", 10, "batch size for DML events to apply in a single transaction (range 1-100)")
defaultRetries := flag.Int64("default-retries", 60, "Default number of retries for various operations before panicking") defaultRetries := flag.Int64("default-retries", 60, "Default number of retries for various operations before panicking")
cutOverLockTimeoutSeconds := flag.Int64("cut-over-lock-timeout-seconds", 3, "Max number of seconds to hold locks on tables while attempting to cut-over (retry attempted when lock exceeds timeout)") cutOverLockTimeoutSeconds := flag.Int64("cut-over-lock-timeout-seconds", 3, "Max number of seconds to hold locks on tables while attempting to cut-over (retry attempted when lock exceeds timeout)")
@ -124,13 +125,14 @@ func main() {
flag.StringVar(&migrationContext.HooksHintMessage, "hooks-hint", "", "arbitrary message to be injected to hooks via GH_OST_HOOKS_HINT, for your convenience") flag.StringVar(&migrationContext.HooksHintMessage, "hooks-hint", "", "arbitrary message to be injected to hooks via GH_OST_HOOKS_HINT, for your convenience")
flag.StringVar(&migrationContext.HooksHintOwner, "hooks-hint-owner", "", "arbitrary name of owner to be injected to hooks via GH_OST_HOOKS_HINT_OWNER, for your convenience") flag.StringVar(&migrationContext.HooksHintOwner, "hooks-hint-owner", "", "arbitrary name of owner to be injected to hooks via GH_OST_HOOKS_HINT_OWNER, for your convenience")
flag.StringVar(&migrationContext.HooksHintToken, "hooks-hint-token", "", "arbitrary token to be injected to hooks via GH_OST_HOOKS_HINT_TOKEN, for your convenience") flag.StringVar(&migrationContext.HooksHintToken, "hooks-hint-token", "", "arbitrary token to be injected to hooks via GH_OST_HOOKS_HINT_TOKEN, for your convenience")
flag.Int64Var(&migrationContext.HooksStatusIntervalSec, "hooks-status-interval", 60, "how many seconds to wait between calling onStatus hook")
flag.UintVar(&migrationContext.ReplicaServerId, "replica-server-id", 99999, "server id used by gh-ost process. Default: 99999") flag.UintVar(&migrationContext.ReplicaServerId, "replica-server-id", 99999, "server id used by gh-ost process. Default: 99999")
maxLoad := flag.String("max-load", "", "Comma delimited status-name=threshold. e.g: 'Threads_running=100,Threads_connected=500'. When status exceeds threshold, app throttles writes") maxLoad := flag.String("max-load", "", "Comma delimited status-name=threshold. e.g: 'Threads_running=100,Threads_connected=500'. When status exceeds threshold, app throttles writes")
criticalLoad := flag.String("critical-load", "", "Comma delimited status-name=threshold, same format as --max-load. When status exceeds threshold, app panics and quits") criticalLoad := flag.String("critical-load", "", "Comma delimited status-name=threshold, same format as --max-load. When status exceeds threshold, app panics and quits")
flag.Int64Var(&migrationContext.CriticalLoadIntervalMilliseconds, "critical-load-interval-millis", 0, "When 0, migration immediately bails out upon meeting critical-load. When non-zero, a second check is done after given interval, and migration only bails out if 2nd check still meets critical load") flag.Int64Var(&migrationContext.CriticalLoadIntervalMilliseconds, "critical-load-interval-millis", 0, "When 0, migration immediately bails out upon meeting critical-load. When non-zero, a second check is done after given interval, and migration only bails out if 2nd check still meets critical load")
flag.Int64Var(&migrationContext.CriticalLoadHibernateSeconds, "critical-load-hibernate-seconds", 0, "When nonzero, critical-load does not panic and bail out; instead, gh-ost goes into hibernate for the specified duration. It will not read/write anything to from/to any server") flag.Int64Var(&migrationContext.CriticalLoadHibernateSeconds, "critical-load-hibernate-seconds", 0, "When non-zero, critical-load does not panic and bail out; instead, gh-ost goes into hibernation for the specified duration. It will not read/write anything from/to any server")
quiet := flag.Bool("quiet", false, "quiet") quiet := flag.Bool("quiet", false, "quiet")
verbose := flag.Bool("verbose", false, "verbose") verbose := flag.Bool("verbose", false, "verbose")
debug := flag.Bool("debug", false, "debug mode (very verbose)") debug := flag.Bool("debug", false, "debug mode (very verbose)")
@ -188,6 +190,11 @@ func main() {
log.Fatalf("--database must be provided and database name must not be empty, or --alter must specify database name") log.Fatalf("--database must be provided and database name must not be empty, or --alter must specify database name")
} }
} }
if err := flag.Set("database", url.QueryEscape(migrationContext.DatabaseName)); err != nil {
migrationContext.Log.Fatale(err)
}
if migrationContext.OriginalTableName == "" { if migrationContext.OriginalTableName == "" {
if parser.HasExplicitTable() { if parser.HasExplicitTable() {
migrationContext.OriginalTableName = parser.GetExplicitTable() migrationContext.OriginalTableName = parser.GetExplicitTable()

View File

@ -1016,7 +1016,7 @@ func (this *Migrator) printStatus(rule PrintStatusRule, writers ...io.Writer) {
w := io.MultiWriter(writers...) w := io.MultiWriter(writers...)
fmt.Fprintln(w, status) fmt.Fprintln(w, status)
if elapsedSeconds%60 == 0 { if elapsedSeconds%this.migrationContext.HooksStatusIntervalSec == 0 {
this.hooksExecutor.onStatus(status) this.hooksExecutor.onStatus(status)
} }
} }

View File

@ -6,9 +6,9 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/go-mysql-org/go-mysql/packet"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/siddontang/go-mysql/mysql"
"github.com/siddontang/go-mysql/packet"
) )
const defaultAuthPluginName = AUTH_NATIVE_PASSWORD const defaultAuthPluginName = AUTH_NATIVE_PASSWORD
@ -46,7 +46,7 @@ func (c *Conn) readInitialHandshake() error {
pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1
// connection id length is 4 // connection id length is 4
c.connectionID = uint32(binary.LittleEndian.Uint32(data[pos : pos+4])) c.connectionID = binary.LittleEndian.Uint32(data[pos : pos+4])
pos += 4 pos += 4
c.salt = []byte{} c.salt = []byte{}
@ -106,7 +106,7 @@ func (c *Conn) readInitialHandshake() error {
// generate auth response data according to auth plugin // generate auth response data according to auth plugin
// //
// NOTE: the returned boolean value indicates whether to add a \NUL to the end of data. // NOTE: the returned boolean value indicates whether to add a \NUL to the end of data.
// it is quite tricky because MySQl server expects different formats of responses in different auth situations. // it is quite tricky because MySQL server expects different formats of responses in different auth situations.
// here the \NUL needs to be added when sending back the empty password or cleartext password in 'sha256_password' // here the \NUL needs to be added when sending back the empty password or cleartext password in 'sha256_password'
// authentication. // authentication.
func (c *Conn) genAuthResponse(authData []byte) ([]byte, bool, error) { func (c *Conn) genAuthResponse(authData []byte) ([]byte, bool, error) {
@ -199,7 +199,7 @@ func (c *Conn) writeAuthHandshake() error {
// Charset [1 byte] // Charset [1 byte]
// use default collation id 33 here, is utf-8 // use default collation id 33 here, is utf-8
data[12] = byte(DEFAULT_COLLATION_ID) data[12] = DEFAULT_COLLATION_ID
// SSL Connection Request Packet // SSL Connection Request Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest

View File

@ -7,9 +7,9 @@ import (
"strings" "strings"
"time" "time"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/go-mysql-org/go-mysql/packet"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/siddontang/go-mysql/mysql"
"github.com/siddontang/go-mysql/packet"
) )
type Conn struct { type Conn struct {
@ -33,6 +33,9 @@ type Conn struct {
connectionID uint32 connectionID uint32
} }
// This function will be called for every row in resultset from ExecuteSelectStreaming.
type SelectPerRowCallback func(row []FieldValue) error
func getNetProto(addr string) string { func getNetProto(addr string) string {
proto := "tcp" proto := "tcp"
if strings.Contains(addr, "/") { if strings.Contains(addr, "/") {
@ -165,6 +168,28 @@ func (c *Conn) Execute(command string, args ...interface{}) (*Result, error) {
} }
} }
// ExecuteSelectStreaming will call perRowCallback for every row in resultset
// WITHOUT saving any row data to Result.{Values/RawPkg/RowDatas} fields.
//
// ExecuteSelectStreaming should be used only for SELECT queries with a large response resultset for memory preserving.
//
// Example:
//
// var result mysql.Result
// conn.ExecuteSelectStreaming(`SELECT ... LIMIT 100500`, &result, func(row []mysql.FieldValue) error {
// // Use the row as you want.
// // You must not save FieldValue.AsString() value after this callback is done. Copy it if you need.
// return nil
// })
//
func (c *Conn) ExecuteSelectStreaming(command string, result *Result, perRowCallback SelectPerRowCallback) error {
if err := c.writeCommandStr(COM_QUERY, command); err != nil {
return errors.Trace(err)
}
return c.readResultStreaming(false, result, perRowCallback)
}
func (c *Conn) Begin() error { func (c *Conn) Begin() error {
_, err := c.exec("BEGIN") _, err := c.exec("BEGIN")
return errors.Trace(err) return errors.Trace(err)

476
vendor/github.com/go-mysql-org/go-mysql/client/pool.go generated vendored Normal file
View File

@ -0,0 +1,476 @@
package client
import (
"context"
"math"
"math/rand"
"sync"
"time"
"github.com/pingcap/errors"
)
/*
Pool for efficient reuse of connections.
Usage:
pool := client.NewPool(log.Debugf, 100, 400, 5, `127.0.0.1:3306`, `username`, `userpwd`, `dbname`)
...
conn, _ := pool.GetConn(ctx)
defer pool.PutConn(conn)
conn.Execute/conn.Begin/etc...
*/
type (
Timestamp int64
LogFunc func(format string, args ...interface{})
Pool struct {
logFunc LogFunc
minAlive int
maxAlive int
maxIdle int
idleCloseTimeout Timestamp
idlePingTimeout Timestamp
connect func() (*Conn, error)
synchro struct {
sync.Mutex
idleConnections []Connection
stats ConnectionStats
}
readyConnection chan Connection
}
ConnectionStats struct {
// Uses internally
TotalCount int
// Only for stats
IdleCount int
CreatedCount int64
}
Connection struct {
conn *Conn
lastUseAt Timestamp
}
)
var (
// MaxIdleTimeoutWithoutPing - If the connection has been idle for more than this time,
// then ping will be performed before use to check if it alive
MaxIdleTimeoutWithoutPing = 10 * time.Second
// DefaultIdleTimeout - If the connection has been idle for more than this time,
// we can close it (but we should remember about Pool.minAlive)
DefaultIdleTimeout = 30 * time.Second
// MaxNewConnectionAtOnce - If we need to create new connections,
// then we will create no more than this number of connections at a time.
// This restriction will be ignored on pool initialization.
MaxNewConnectionAtOnce = 5
)
// NewPool initializes new connection pool and uses params: addr, user, password, dbName and options.
// minAlive specifies the minimum number of open connections that the pool will try to maintain.
// maxAlive specifies the maximum number of open connections
// (for internal reasons, may be greater by 1 inside newConnectionProducer).
// maxIdle specifies the maximum number of idle connections (see DefaultIdleTimeout).
func NewPool(
logFunc LogFunc,
minAlive int,
maxAlive int,
maxIdle int,
addr string,
user string,
password string,
dbName string,
options ...func(conn *Conn),
) *Pool {
if minAlive > maxAlive {
minAlive = maxAlive
}
if maxIdle > maxAlive {
maxIdle = maxAlive
}
if maxIdle <= minAlive {
maxIdle = minAlive
}
pool := &Pool{
logFunc: logFunc,
minAlive: minAlive,
maxAlive: maxAlive,
maxIdle: maxIdle,
idleCloseTimeout: Timestamp(math.Ceil(DefaultIdleTimeout.Seconds())),
idlePingTimeout: Timestamp(math.Ceil(MaxIdleTimeoutWithoutPing.Seconds())),
connect: func() (*Conn, error) {
return Connect(addr, user, password, dbName, options...)
},
readyConnection: make(chan Connection),
}
pool.synchro.idleConnections = make([]Connection, 0, pool.maxIdle)
go pool.newConnectionProducer()
if pool.minAlive > 0 {
pool.logFunc(`Pool: Setup %d new connections (minimal pool size)...`, pool.minAlive)
pool.startNewConnections(pool.minAlive)
}
go pool.closeOldIdleConnections()
return pool
}
func (pool *Pool) GetStats(stats *ConnectionStats) {
pool.synchro.Lock()
*stats = pool.synchro.stats
stats.IdleCount = len(pool.synchro.idleConnections)
pool.synchro.Unlock()
}
// GetConn returns connection from the pool or create new
func (pool *Pool) GetConn(ctx context.Context) (*Conn, error) {
for {
connection, err := pool.getConnection(ctx)
if err != nil {
return nil, err
}
// For long time idle connections, we do a ping check
if delta := pool.nowTs() - connection.lastUseAt; delta > pool.idlePingTimeout {
if err := pool.ping(connection.conn); err != nil {
pool.closeConn(connection.conn)
continue
}
}
return connection.conn, nil
}
}
// PutConn returns working connection back to pool
func (pool *Pool) PutConn(conn *Conn) {
pool.putConnection(Connection{
conn: conn,
lastUseAt: pool.nowTs(),
})
}
// DropConn closes the connection without any checks
func (pool *Pool) DropConn(conn *Conn) {
pool.closeConn(conn)
}
func (pool *Pool) putConnection(connection Connection) {
pool.synchro.Lock()
defer pool.synchro.Unlock()
// If someone is already waiting for a connection, then we return it to him
select {
case pool.readyConnection <- connection:
return
default:
}
// Nobody needs this connection
pool.putConnectionUnsafe(connection)
}
func (pool *Pool) nowTs() Timestamp {
return Timestamp(time.Now().Unix())
}
func (pool *Pool) getConnection(ctx context.Context) (Connection, error) {
pool.synchro.Lock()
connection := pool.getIdleConnectionUnsafe()
if connection.conn != nil {
pool.synchro.Unlock()
return connection, nil
}
pool.synchro.Unlock()
// No idle connections are available
select {
case connection := <-pool.readyConnection:
return connection, nil
case <-ctx.Done():
return Connection{}, ctx.Err()
}
}
func (pool *Pool) putConnectionUnsafe(connection Connection) {
if len(pool.synchro.idleConnections) == cap(pool.synchro.idleConnections) {
pool.synchro.stats.TotalCount--
_ = connection.conn.Close() // Could it be more effective to close older connections?
} else {
pool.synchro.idleConnections = append(pool.synchro.idleConnections, connection)
}
}
func (pool *Pool) newConnectionProducer() {
var connection Connection
var err error
for {
connection.conn = nil
pool.synchro.Lock()
connection = pool.getIdleConnectionUnsafe()
if connection.conn == nil {
if pool.synchro.stats.TotalCount >= pool.maxAlive {
// Can't create more connections
pool.synchro.Unlock()
time.Sleep(10 * time.Millisecond)
continue
}
pool.synchro.stats.TotalCount++ // "Reserving" new connection
}
pool.synchro.Unlock()
if connection.conn == nil {
connection, err = pool.createNewConnection()
if err != nil {
pool.synchro.Lock()
pool.synchro.stats.TotalCount-- // Bad luck, should try again
pool.synchro.Unlock()
time.Sleep(time.Duration(10+rand.Intn(90)) * time.Millisecond)
continue
}
}
pool.readyConnection <- connection
}
}
func (pool *Pool) createNewConnection() (Connection, error) {
var connection Connection
var err error
connection.conn, err = pool.connect()
if err != nil {
return Connection{}, errors.Errorf(`Could not connect to mysql: %s`, err)
}
connection.lastUseAt = pool.nowTs()
pool.synchro.Lock()
pool.synchro.stats.CreatedCount++
pool.synchro.Unlock()
return connection, nil
}
func (pool *Pool) getIdleConnectionUnsafe() Connection {
cnt := len(pool.synchro.idleConnections)
if cnt == 0 {
return Connection{}
}
last := cnt - 1
connection := pool.synchro.idleConnections[last]
pool.synchro.idleConnections[last].conn = nil
pool.synchro.idleConnections = pool.synchro.idleConnections[:last]
return connection
}
func (pool *Pool) closeOldIdleConnections() {
var toPing []Connection
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
toPing = pool.getOldIdleConnections(toPing[:0])
if len(toPing) == 0 {
continue
}
pool.recheckConnections(toPing)
if !pool.spawnConnectionsIfNeeded() {
pool.closeIdleConnectionsIfCan()
}
}
}
func (pool *Pool) getOldIdleConnections(dst []Connection) []Connection {
dst = dst[:0]
pool.synchro.Lock()
synchro := &pool.synchro
idleCnt := len(synchro.idleConnections)
checkBefore := pool.nowTs() - pool.idlePingTimeout
for i := idleCnt - 1; i >= 0; i-- {
if synchro.idleConnections[i].lastUseAt > checkBefore {
continue
}
dst = append(dst, synchro.idleConnections[i])
last := idleCnt - 1
if i < last {
// Removing an item from the middle of a slice
synchro.idleConnections[i], synchro.idleConnections[last] = synchro.idleConnections[last], synchro.idleConnections[i]
}
synchro.idleConnections[last].conn = nil
synchro.idleConnections = synchro.idleConnections[:last]
idleCnt--
}
pool.synchro.Unlock()
return dst
}
func (pool *Pool) recheckConnections(connections []Connection) {
const workerCnt = 2 // Heuristic :)
queue := make(chan Connection, len(connections))
for _, connection := range connections {
queue <- connection
}
close(queue)
var wg sync.WaitGroup
wg.Add(workerCnt)
for worker := 0; worker < workerCnt; worker++ {
go func() {
defer wg.Done()
for connection := range queue {
if err := pool.ping(connection.conn); err != nil {
pool.closeConn(connection.conn)
} else {
pool.putConnection(connection)
}
}
}()
}
wg.Wait()
}
// spawnConnectionsIfNeeded creates new connections if there are not enough of them and returns true in this case
func (pool *Pool) spawnConnectionsIfNeeded() bool {
pool.synchro.Lock()
totalCount := pool.synchro.stats.TotalCount
idleCount := len(pool.synchro.idleConnections)
needSpanNew := pool.minAlive - totalCount
pool.synchro.Unlock()
if needSpanNew <= 0 {
return false
}
// Не хватает соединений, нужно создать еще
if needSpanNew > MaxNewConnectionAtOnce {
needSpanNew = MaxNewConnectionAtOnce
}
pool.logFunc(`Pool: Setup %d new connections (total: %d idle: %d)...`, needSpanNew, totalCount, idleCount)
pool.startNewConnections(needSpanNew)
return true
}
func (pool *Pool) closeIdleConnectionsIfCan() {
pool.synchro.Lock()
canCloseCnt := pool.synchro.stats.TotalCount - pool.minAlive
canCloseCnt-- // -1 to account for an open but unused connection (pool.readyConnection <- connection in newConnectionProducer)
idleCnt := len(pool.synchro.idleConnections)
inFly := pool.synchro.stats.TotalCount - idleCnt
// We can close no more than 10% connections at a time, but at least 1, if possible
idleCanCloseCnt := idleCnt / 10
if idleCanCloseCnt == 0 {
idleCanCloseCnt = 1
}
if canCloseCnt > idleCanCloseCnt {
canCloseCnt = idleCanCloseCnt
}
if canCloseCnt <= 0 {
pool.synchro.Unlock()
return
}
closeFromIdx := idleCnt - canCloseCnt
if closeFromIdx < 0 {
// If there are enough requests in the "flight" now, then we can close all unnecessary
closeFromIdx = 0
}
toClose := append([]Connection{}, pool.synchro.idleConnections[closeFromIdx:]...)
for i := closeFromIdx; i < idleCnt; i++ {
pool.synchro.idleConnections[i].conn = nil
}
pool.synchro.idleConnections = pool.synchro.idleConnections[:closeFromIdx]
pool.synchro.Unlock()
pool.logFunc(`Pool: Close %d idle connections (in fly %d)`, len(toClose), inFly)
for _, connection := range toClose {
pool.closeConn(connection.conn)
}
}
func (pool *Pool) closeConn(conn *Conn) {
pool.synchro.Lock()
pool.synchro.stats.TotalCount--
pool.synchro.Unlock()
_ = conn.Close() // Closing is not an instant action, so do it outside the lock
}
func (pool *Pool) startNewConnections(count int) {
connections := make([]Connection, 0, count)
for i := 0; i < count; i++ {
if conn, err := pool.createNewConnection(); err == nil {
pool.synchro.Lock()
pool.synchro.stats.TotalCount++
pool.synchro.Unlock()
connections = append(connections, conn)
}
}
pool.synchro.Lock()
for _, connection := range connections {
pool.putConnectionUnsafe(connection)
}
pool.synchro.Unlock()
}
func (pool *Pool) ping(conn *Conn) error {
deadline := time.Now().Add(100 * time.Millisecond)
_ = conn.SetWriteDeadline(deadline)
_ = conn.SetReadDeadline(deadline)
err := conn.Ping()
if err != nil {
pool.logFunc(`Pool: ping query fail: %s`, err.Error())
}
return err
}

View File

@ -1,7 +1,7 @@
package client package client
import ( import (
"github.com/siddontang/go-mysql/utils" "github.com/go-mysql-org/go-mysql/utils"
) )
func (c *Conn) writeCommand(command byte) error { func (c *Conn) writeCommand(command byte) error {

View File

@ -7,9 +7,9 @@ import (
"encoding/binary" "encoding/binary"
"encoding/pem" "encoding/pem"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/go-mysql-org/go-mysql/utils"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/siddontang/go-mysql/mysql"
"github.com/siddontang/go-mysql/utils"
"github.com/siddontang/go/hack" "github.com/siddontang/go/hack"
) )
@ -39,7 +39,7 @@ func (c *Conn) handleOKPacket(data []byte) (*Result, error) {
var n int var n int
var pos = 1 var pos = 1
r := new(Result) r := &Result{Resultset: &Resultset{}}
r.AffectedRows, _, n = LengthEncodedInt(data[pos:]) r.AffectedRows, _, n = LengthEncodedInt(data[pos:])
pos += n pos += n
@ -101,6 +101,10 @@ func (c *Conn) handleAuthResult() error {
} }
c.authPluginName = switchToPlugin c.authPluginName = switchToPlugin
auth, addNull, err := c.genAuthResponse(data) auth, addNull, err := c.genAuthResponse(data)
if err != nil {
return err
}
if err = c.WriteAuthSwitchPacket(auth, addNull); err != nil { if err = c.WriteAuthSwitchPacket(auth, addNull); err != nil {
return err return err
} }
@ -138,7 +142,7 @@ func (c *Conn) handleAuthResult() error {
} }
} }
} else { } else {
errors.Errorf("invalid packet") return errors.Errorf("invalid packet %x", data[0])
} }
} else if c.authPluginName == AUTH_SHA256_PASSWORD { } else if c.authPluginName == AUTH_SHA256_PASSWORD {
if len(data) == 0 { if len(data) == 0 {
@ -169,7 +173,6 @@ func (c *Conn) readAuthResult() ([]byte, string, error) {
// see: https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/ // see: https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
// packet indicator // packet indicator
switch data[0] { switch data[0] {
case OK_HEADER: case OK_HEADER:
_, err := c.handleOKPacket(data) _, err := c.handleOKPacket(data)
return nil, "", err return nil, "", err
@ -230,6 +233,42 @@ func (c *Conn) readResult(binary bool) (*Result, error) {
return c.readResultset(firstPkgBuf, binary) return c.readResultset(firstPkgBuf, binary)
} }
func (c *Conn) readResultStreaming(binary bool, result *Result, perRowCb SelectPerRowCallback) error {
firstPkgBuf, err := c.ReadPacketReuseMem(utils.ByteSliceGet(16)[:0])
defer utils.ByteSlicePut(firstPkgBuf)
if err != nil {
return errors.Trace(err)
}
if firstPkgBuf[0] == OK_HEADER {
// https://dev.mysql.com/doc/internals/en/com-query-response.html
// 14.6.4.1 COM_QUERY Response
// If the number of columns in the resultset is 0, this is a OK_Packet.
okResult, err := c.handleOKPacket(firstPkgBuf)
if err != nil {
return errors.Trace(err)
}
result.Status = okResult.Status
result.AffectedRows = okResult.AffectedRows
result.InsertId = okResult.InsertId
if result.Resultset == nil {
result.Resultset = NewResultset(0)
} else {
result.Reset(0)
}
return nil
} else if firstPkgBuf[0] == ERR_HEADER {
return c.handleErrorPacket(append([]byte{}, firstPkgBuf...))
} else if firstPkgBuf[0] == LocalInFile_HEADER {
return ErrMalformPacket
}
return c.readResultsetStreaming(firstPkgBuf, binary, result, perRowCb)
}
func (c *Conn) readResultset(data []byte, binary bool) (*Result, error) { func (c *Conn) readResultset(data []byte, binary bool) (*Result, error) {
// column count // column count
count, _, n := LengthEncodedInt(data) count, _, n := LengthEncodedInt(data)
@ -253,6 +292,31 @@ func (c *Conn) readResultset(data []byte, binary bool) (*Result, error) {
return result, nil return result, nil
} }
func (c *Conn) readResultsetStreaming(data []byte, binary bool, result *Result, perRowCb SelectPerRowCallback) error {
columnCount, _, n := LengthEncodedInt(data)
if n-len(data) != 0 {
return ErrMalformPacket
}
if result.Resultset == nil {
result.Resultset = NewResultset(int(columnCount))
} else {
// Reuse memory if can
result.Reset(int(columnCount))
}
if err := c.readResultColumns(result); err != nil {
return errors.Trace(err)
}
if err := c.readResultRowsStreaming(result, binary, perRowCb); err != nil {
return errors.Trace(err)
}
return nil
}
func (c *Conn) readResultColumns(result *Result) (err error) { func (c *Conn) readResultColumns(result *Result) (err error) {
var i int = 0 var i int = 0
var data []byte var data []byte
@ -341,3 +405,47 @@ func (c *Conn) readResultRows(result *Result, isBinary bool) (err error) {
return nil return nil
} }
func (c *Conn) readResultRowsStreaming(result *Result, isBinary bool, perRowCb SelectPerRowCallback) (err error) {
var (
data []byte
row []FieldValue
)
for {
data, err = c.ReadPacketReuseMem(data[:0])
if err != nil {
return
}
// EOF Packet
if c.isEOFPacket(data) {
if c.capability&CLIENT_PROTOCOL_41 > 0 {
// result.Warnings = binary.LittleEndian.Uint16(data[1:])
// todo add strict_mode, warning will be treat as error
result.Status = binary.LittleEndian.Uint16(data[3:])
c.status = result.Status
}
break
}
if data[0] == ERR_HEADER {
return c.handleErrorPacket(data)
}
// Parse this row
row, err = RowData(data).Parse(result.Fields, isBinary, row)
if err != nil {
return errors.Trace(err)
}
// Send the row to "userland" code
err = perRowCb(row)
if err != nil {
return errors.Trace(err)
}
}
return nil
}

View File

@ -5,14 +5,13 @@ import (
"fmt" "fmt"
"math" "math"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/siddontang/go-mysql/mysql"
) )
type Stmt struct { type Stmt struct {
conn *Conn conn *Conn
id uint32 id uint32
query string
params int params int
columns int columns int
@ -55,7 +54,7 @@ func (s *Stmt) write(args ...interface{}) error {
//NULL-bitmap, length: (num-params+7) //NULL-bitmap, length: (num-params+7)
nullBitmap := make([]byte, (paramsNum+7)>>3) nullBitmap := make([]byte, (paramsNum+7)>>3)
var length int = int(1 + 4 + 1 + 4 + ((paramsNum + 7) >> 3) + 1 + (paramsNum << 1)) length := 1 + 4 + 1 + 4 + ((paramsNum + 7) >> 3) + 1 + (paramsNum << 1)
var newParamBoundFlag byte = 0 var newParamBoundFlag byte = 0
@ -91,11 +90,11 @@ func (s *Stmt) write(args ...interface{}) error {
case uint16: case uint16:
paramTypes[i<<1] = MYSQL_TYPE_SHORT paramTypes[i<<1] = MYSQL_TYPE_SHORT
paramTypes[(i<<1)+1] = 0x80 paramTypes[(i<<1)+1] = 0x80
paramValues[i] = Uint16ToBytes(uint16(v)) paramValues[i] = Uint16ToBytes(v)
case uint32: case uint32:
paramTypes[i<<1] = MYSQL_TYPE_LONG paramTypes[i<<1] = MYSQL_TYPE_LONG
paramTypes[(i<<1)+1] = 0x80 paramTypes[(i<<1)+1] = 0x80
paramValues[i] = Uint32ToBytes(uint32(v)) paramValues[i] = Uint32ToBytes(v)
case uint: case uint:
paramTypes[i<<1] = MYSQL_TYPE_LONGLONG paramTypes[i<<1] = MYSQL_TYPE_LONGLONG
paramTypes[(i<<1)+1] = 0x80 paramTypes[(i<<1)+1] = 0x80
@ -103,14 +102,13 @@ func (s *Stmt) write(args ...interface{}) error {
case uint64: case uint64:
paramTypes[i<<1] = MYSQL_TYPE_LONGLONG paramTypes[i<<1] = MYSQL_TYPE_LONGLONG
paramTypes[(i<<1)+1] = 0x80 paramTypes[(i<<1)+1] = 0x80
paramValues[i] = Uint64ToBytes(uint64(v)) paramValues[i] = Uint64ToBytes(v)
case bool: case bool:
paramTypes[i<<1] = MYSQL_TYPE_TINY paramTypes[i<<1] = MYSQL_TYPE_TINY
if v { if v {
paramValues[i] = []byte{1} paramValues[i] = []byte{1}
} else { } else {
paramValues[i] = []byte{0} paramValues[i] = []byte{0}
} }
case float32: case float32:
paramTypes[i<<1] = MYSQL_TYPE_FLOAT paramTypes[i<<1] = MYSQL_TYPE_FLOAT

View File

@ -591,7 +591,7 @@ var MySQLErrName = map[uint16]string{
ER_BINLOG_PURGE_EMFILE: "Too many files opened, please execute the command again", ER_BINLOG_PURGE_EMFILE: "Too many files opened, please execute the command again",
ER_EVENT_CANNOT_CREATE_IN_THE_PAST: "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation.", ER_EVENT_CANNOT_CREATE_IN_THE_PAST: "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation.",
ER_EVENT_CANNOT_ALTER_IN_THE_PAST: "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was not changed. Specify a time in the future.", ER_EVENT_CANNOT_ALTER_IN_THE_PAST: "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was not changed. Specify a time in the future.",
ER_SLAVE_INCIDENT: "The incident %s occured on the master. Message: %-.64s", ER_SLAVE_INCIDENT: "The incident %s occurred on the master. Message: %-.64s",
ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT: "Table has no partition for some existing values", ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT: "Table has no partition for some existing values",
ER_BINLOG_UNSAFE_STATEMENT: "Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. %s", ER_BINLOG_UNSAFE_STATEMENT: "Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. %s",
ER_SLAVE_FATAL_ERROR: "Fatal error: %s", ER_SLAVE_FATAL_ERROR: "Fatal error: %s",

View File

@ -3,7 +3,7 @@ package mysql
import ( import (
"encoding/binary" "encoding/binary"
"github.com/siddontang/go-mysql/utils" "github.com/go-mysql-org/go-mysql/utils"
) )
type FieldData []byte type FieldData []byte
@ -130,7 +130,7 @@ func (f *Field) Parse(p FieldData) (err error) {
f.DefaultValue = p[pos:(pos + int(f.DefaultValueLength))] f.DefaultValue = p[pos:(pos + int(f.DefaultValueLength))]
} }
return return nil
} }
func (p FieldData) Parse() (f *Field, err error) { func (p FieldData) Parse() (f *Field, err error) {

View File

@ -3,12 +3,12 @@ package mysql
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"sort"
"strconv" "strconv"
"strings" "strings"
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/siddontang/go-log/log" "github.com/siddontang/go-log/log"
"github.com/siddontang/go/hack"
) )
// MariadbGTID represent mariadb gtid, [domain ID]-[server-id]-[sequence] // MariadbGTID represent mariadb gtid, [domain ID]-[server-id]-[sequence]
@ -113,15 +113,9 @@ func ParseMariadbGTIDSet(str string) (GTIDSet, error) {
if str == "" { if str == "" {
return s, nil return s, nil
} }
err := s.Update(str)
sp := strings.Split(str, ",") if err != nil {
return nil, err
//todo, handle redundant same uuid
for i := 0; i < len(sp); i++ {
err := s.Update(sp[i])
if err != nil {
return nil, errors.Trace(err)
}
} }
return s, nil return s, nil
} }
@ -147,17 +141,29 @@ func (s *MariadbGTIDSet) AddSet(gtid *MariadbGTID) error {
// Update updates mariadb gtid set // Update updates mariadb gtid set
func (s *MariadbGTIDSet) Update(GTIDStr string) error { func (s *MariadbGTIDSet) Update(GTIDStr string) error {
gtid, err := ParseMariadbGTID(GTIDStr) sp := strings.Split(GTIDStr, ",")
if err != nil { //todo, handle redundant same uuid
return err for i := 0; i < len(sp); i++ {
gtid, err := ParseMariadbGTID(sp[i])
if err != nil {
return errors.Trace(err)
}
err = s.AddSet(gtid)
if err != nil {
return errors.Trace(err)
}
} }
return nil
err = s.AddSet(gtid)
return errors.Trace(err)
} }
func (s *MariadbGTIDSet) String() string { func (s *MariadbGTIDSet) String() string {
return hack.String(s.Encode()) sets := make([]string, 0, len(s.Sets))
for _, set := range s.Sets {
sets = append(sets, set.String())
}
sort.Strings(sets)
return strings.Join(sets, ",")
} }
// Encode encodes mariadb gtid set // Encode encodes mariadb gtid set

View File

@ -32,8 +32,10 @@ func parseInterval(str string) (i Interval, err error) {
i.Stop = i.Start + 1 i.Stop = i.Start + 1
case 2: case 2:
i.Start, err = strconv.ParseInt(p[0], 10, 64) i.Start, err = strconv.ParseInt(p[0], 10, 64)
i.Stop, err = strconv.ParseInt(p[1], 10, 64) if err == nil {
i.Stop = i.Stop + 1 i.Stop, err = strconv.ParseInt(p[1], 10, 64)
i.Stop++
}
default: default:
err = errors.Errorf("invalid interval format, must n[-n]") err = errors.Errorf("invalid interval format, must n[-n]")
} }
@ -230,14 +232,14 @@ func (s *UUIDSet) String() string {
} }
func (s *UUIDSet) encode(w io.Writer) { func (s *UUIDSet) encode(w io.Writer) {
w.Write(s.SID.Bytes()) _, _ = w.Write(s.SID.Bytes())
n := int64(len(s.Intervals)) n := int64(len(s.Intervals))
binary.Write(w, binary.LittleEndian, n) _ = binary.Write(w, binary.LittleEndian, n)
for _, i := range s.Intervals { for _, i := range s.Intervals {
binary.Write(w, binary.LittleEndian, i.Start) _ = binary.Write(w, binary.LittleEndian, i.Start)
binary.Write(w, binary.LittleEndian, i.Stop) _ = binary.Write(w, binary.LittleEndian, i.Stop)
} }
} }
@ -292,7 +294,7 @@ func (s *UUIDSet) Decode(data []byte) error {
func (s *UUIDSet) Clone() *UUIDSet { func (s *UUIDSet) Clone() *UUIDSet {
clone := new(UUIDSet) clone := new(UUIDSet)
clone.SID, _ = uuid.FromString(s.SID.String()) copy(clone.SID[:], s.SID[:])
clone.Intervals = s.Intervals.Normalize() clone.Intervals = s.Intervals.Normalize()
return clone return clone
@ -318,7 +320,6 @@ func ParseMysqlGTIDSet(str string) (GTIDSet, error) {
} else { } else {
s.AddSet(set) s.AddSet(set)
} }
} }
return s, nil return s, nil
} }
@ -362,13 +363,13 @@ func (s *MysqlGTIDSet) AddSet(set *UUIDSet) {
} }
func (s *MysqlGTIDSet) Update(GTIDStr string) error { func (s *MysqlGTIDSet) Update(GTIDStr string) error {
uuidSet, err := ParseUUIDSet(GTIDStr) gtidSet, err := ParseMysqlGTIDSet(GTIDStr)
if err != nil { if err != nil {
return err return err
} }
for _, uuidSet := range gtidSet.(*MysqlGTIDSet).Sets {
s.AddSet(uuidSet) s.AddSet(uuidSet)
}
return nil return nil
} }
@ -414,15 +415,28 @@ func (s *MysqlGTIDSet) Equal(o GTIDSet) bool {
} }
return true return true
} }
func (s *MysqlGTIDSet) String() string { func (s *MysqlGTIDSet) String() string {
// there is only one element in gtid set
if len(s.Sets) == 1 {
for _, set := range s.Sets {
return set.String()
}
}
// sort multi set
var buf bytes.Buffer var buf bytes.Buffer
sep := "" sets := make([]string, 0, len(s.Sets))
for _, set := range s.Sets { for _, set := range s.Sets {
sets = append(sets, set.String())
}
sort.Strings(sets)
sep := ""
for _, set := range sets {
buf.WriteString(sep) buf.WriteString(sep)
buf.WriteString(set.String()) buf.WriteString(set)
sep = "," sep = ","
} }
@ -432,7 +446,7 @@ func (s *MysqlGTIDSet) String() string {
func (s *MysqlGTIDSet) Encode() []byte { func (s *MysqlGTIDSet) Encode() []byte {
var buf bytes.Buffer var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, uint64(len(s.Sets))) _ = binary.Write(&buf, binary.LittleEndian, uint64(len(s.Sets)))
for i := range s.Sets { for i := range s.Sets {
s.Sets[i].encode(&buf) s.Sets[i].encode(&buf)

View File

@ -20,7 +20,7 @@ func ParseBinaryUint16(data []byte) uint16 {
} }
func ParseBinaryInt24(data []byte) int32 { func ParseBinaryInt24(data []byte) int32 {
u32 := uint32(ParseBinaryUint24(data)) u32 := ParseBinaryUint24(data)
if u32&0x00800000 != 0 { if u32&0x00800000 != 0 {
u32 |= 0xFF000000 u32 |= 0xFF000000
} }

View File

@ -0,0 +1,80 @@
package mysql
import (
"fmt"
"strconv"
"strings"
)
// For binlog filename + position based replication
type Position struct {
Name string
Pos uint32
}
func (p Position) Compare(o Position) int {
// First compare binlog name
nameCmp := CompareBinlogFileName(p.Name, o.Name)
if nameCmp != 0 {
return nameCmp
}
// Same binlog file, compare position
if p.Pos > o.Pos {
return 1
} else if p.Pos < o.Pos {
return -1
} else {
return 0
}
}
func (p Position) String() string {
return fmt.Sprintf("(%s, %d)", p.Name, p.Pos)
}
func CompareBinlogFileName(a, b string) int {
// sometimes it's convenient to construct a `Position` literal with no `Name`
if a == "" && b == "" {
return 0
} else if a == "" {
return -1
} else if b == "" {
return 1
}
splitBinlogName := func(n string) (string, int) {
// mysqld appends a numeric extension to the binary log base name to generate binary log file names
// ...
// If you supply an extension in the log name (for example, --log-bin=base_name.extension),
// the extension is silently removed and ignored.
// ref: https://dev.mysql.com/doc/refman/8.0/en/binary-log.html
i := strings.LastIndexByte(n, '.')
if i == -1 {
// try keeping backward compatibility
return n, 0
}
seq, err := strconv.Atoi(n[i+1:])
if err != nil {
panic(fmt.Sprintf("binlog file %s doesn't contain numeric extension", err))
}
return n[:i], seq
}
aBase, aSeq := splitBinlogName(a)
bBase, bSeq := splitBinlogName(b)
if aBase > bBase {
return 1
} else if aBase < bBase {
return -1
}
if aSeq > bSeq {
return 1
} else if aSeq < bSeq {
return -1
} else {
return 0
}
}

View File

@ -27,9 +27,9 @@ var (
} }
) )
func NewResultset(resultsetCount int) *Resultset { func NewResultset(fieldsCount int) *Resultset {
r := resultsetPool.Get().(*Resultset) r := resultsetPool.Get().(*Resultset)
r.reset(resultsetCount) r.Reset(fieldsCount)
return r return r
} }
@ -37,7 +37,7 @@ func (r *Resultset) returnToPool() {
resultsetPool.Put(r) resultsetPool.Put(r)
} }
func (r *Resultset) reset(count int) { func (r *Resultset) Reset(fieldsCount int) {
r.RawPkg = r.RawPkg[:0] r.RawPkg = r.RawPkg[:0]
r.Fields = r.Fields[:0] r.Fields = r.Fields[:0]
@ -52,14 +52,14 @@ func (r *Resultset) reset(count int) {
r.FieldNames = make(map[string]int) r.FieldNames = make(map[string]int)
} }
if count == 0 { if fieldsCount == 0 {
return return
} }
if cap(r.Fields) < count { if cap(r.Fields) < fieldsCount {
r.Fields = make([]*Field, count) r.Fields = make([]*Field, fieldsCount)
} else { } else {
r.Fields = r.Fields[:count] r.Fields = r.Fields[:fieldsCount]
} }
} }
@ -142,7 +142,7 @@ func (r *Resultset) GetUint(row, column int) (uint64, error) {
case uint32: case uint32:
return uint64(v), nil return uint64(v), nil
case uint64: case uint64:
return uint64(v), nil return v, nil
case float32: case float32:
return uint64(v), nil return uint64(v), nil
case float64: case float64:

View File

@ -17,7 +17,7 @@ func formatTextValue(value interface{}) ([]byte, error) {
case int32: case int32:
return strconv.AppendInt(nil, int64(v), 10), nil return strconv.AppendInt(nil, int64(v), 10), nil
case int64: case int64:
return strconv.AppendInt(nil, int64(v), 10), nil return strconv.AppendInt(nil, v, 10), nil
case int: case int:
return strconv.AppendInt(nil, int64(v), 10), nil return strconv.AppendInt(nil, int64(v), 10), nil
case uint8: case uint8:
@ -27,13 +27,13 @@ func formatTextValue(value interface{}) ([]byte, error) {
case uint32: case uint32:
return strconv.AppendUint(nil, uint64(v), 10), nil return strconv.AppendUint(nil, uint64(v), 10), nil
case uint64: case uint64:
return strconv.AppendUint(nil, uint64(v), 10), nil return strconv.AppendUint(nil, v, 10), nil
case uint: case uint:
return strconv.AppendUint(nil, uint64(v), 10), nil return strconv.AppendUint(nil, uint64(v), 10), nil
case float32: case float32:
return strconv.AppendFloat(nil, float64(v), 'f', -1, 64), nil return strconv.AppendFloat(nil, float64(v), 'f', -1, 64), nil
case float64: case float64:
return strconv.AppendFloat(nil, float64(v), 'f', -1, 64), nil return strconv.AppendFloat(nil, v, 'f', -1, 64), nil
case []byte: case []byte:
return v, nil return v, nil
case string: case string:
@ -64,7 +64,7 @@ func formatBinaryValue(value interface{}) ([]byte, error) {
case uint32: case uint32:
return Uint64ToBytes(uint64(v)), nil return Uint64ToBytes(uint64(v)), nil
case uint64: case uint64:
return Uint64ToBytes(uint64(v)), nil return Uint64ToBytes(v), nil
case uint: case uint:
return Uint64ToBytes(uint64(v)), nil return Uint64ToBytes(uint64(v)), nil
case float32: case float32:
@ -146,7 +146,10 @@ func BuildSimpleTextResultset(names []string, values [][]interface{}) (*Resultse
} }
if r.Fields[j] == nil { if r.Fields[j] == nil {
r.Fields[j] = &Field{Name: hack.Slice(names[j]), Type: typ} r.Fields[j] = &Field{Name: hack.Slice(names[j]), Type: typ}
formatField(r.Fields[j], value) err = formatField(r.Fields[j], value)
if err != nil {
return nil, errors.Trace(err)
}
} else if typ != r.Fields[j].Type { } else if typ != r.Fields[j].Type {
// we got another type in the same column. in general, we treat it as an error, except // we got another type in the same column. in general, we treat it as an error, except
// the case, when old value was null, and the new one isn't null, so we can update // the case, when old value was null, and the new one isn't null, so we can update
@ -154,7 +157,10 @@ func BuildSimpleTextResultset(names []string, values [][]interface{}) (*Resultse
oldIsNull, newIsNull := r.Fields[j].Type == MYSQL_TYPE_NULL, typ == MYSQL_TYPE_NULL oldIsNull, newIsNull := r.Fields[j].Type == MYSQL_TYPE_NULL, typ == MYSQL_TYPE_NULL
if oldIsNull && !newIsNull { // old is null, new isn't, update type info. if oldIsNull && !newIsNull { // old is null, new isn't, update type info.
r.Fields[j].Type = typ r.Fields[j].Type = typ
formatField(r.Fields[j], value) err = formatField(r.Fields[j], value)
if err != nil {
return nil, errors.Trace(err)
}
} else if !oldIsNull && !newIsNull { // different non-null types, that's an error. } else if !oldIsNull && !newIsNull { // different non-null types, that's an error.
return nil, errors.Errorf("row types aren't consistent") return nil, errors.Errorf("row types aren't consistent")
} }

View File

@ -3,8 +3,8 @@ package mysql
import ( import (
"strconv" "strconv"
"github.com/go-mysql-org/go-mysql/utils"
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/siddontang/go-mysql/utils"
) )
type RowData []byte type RowData []byte

View File

@ -114,7 +114,7 @@ func RandomBuf(size int) ([]byte, error) {
// avoid to generate '\0' // avoid to generate '\0'
for i, b := range buf { for i, b := range buf {
if uint8(b) == 0 { if b == 0 {
buf[i] = '0' buf[i] = '0'
} }
} }

View File

@ -13,9 +13,9 @@ import (
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/go-mysql-org/go-mysql/utils"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/siddontang/go-mysql/mysql"
"github.com/siddontang/go-mysql/utils"
) )
type BufPool struct { type BufPool struct {
@ -134,7 +134,7 @@ func (c *Conn) ReadPacketTo(w io.Writer) error {
} }
length := int(uint32(c.header[0]) | uint32(c.header[1])<<8 | uint32(c.header[2])<<16) length := int(uint32(c.header[0]) | uint32(c.header[1])<<8 | uint32(c.header[2])<<16)
sequence := uint8(c.header[3]) sequence := c.header[3]
if sequence != c.Sequence { if sequence != c.Sequence {
return errors.Errorf("invalid sequence %d != %d", sequence, c.Sequence) return errors.Errorf("invalid sequence %d != %d", sequence, c.Sequence)

View File

@ -7,8 +7,8 @@ import (
"path" "path"
"time" "time"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/siddontang/go-mysql/mysql"
) )
// StartBackup: Like mysqlbinlog remote raw backup // StartBackup: Like mysqlbinlog remote raw backup
@ -22,7 +22,9 @@ func (b *BinlogSyncer) StartBackup(backupDir string, p Position, timeout time.Du
// Force use raw mode // Force use raw mode
b.parser.SetRawMode(true) b.parser.SetRawMode(true)
os.MkdirAll(backupDir, 0755) if err := os.MkdirAll(backupDir, 0755); err != nil {
return errors.Trace(err)
}
s, err := b.StartSync(p) s, err := b.StartSync(p)
if err != nil { if err != nil {
@ -82,7 +84,6 @@ func (b *BinlogSyncer) StartBackup(backupDir string, p Position, timeout time.Du
if _, err = f.Write(BinLogFileHeader); err != nil { if _, err = f.Write(BinLogFileHeader); err != nil {
return errors.Trace(err) return errors.Trace(err)
} }
} }
if n, err := f.Write(e.RawData); err != nil { if n, err := f.Write(e.RawData); err != nil {

View File

@ -2,9 +2,10 @@ package replication
import ( import (
"context" "context"
"time"
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/siddontang/go-log/log" "github.com/siddontang/go-log/log"
"time"
) )
var ( var (

View File

@ -10,11 +10,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/go-mysql-org/go-mysql/client"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/pingcap/errors" "github.com/pingcap/errors"
uuid "github.com/satori/go.uuid" uuid "github.com/satori/go.uuid"
"github.com/siddontang/go-log/log" "github.com/siddontang/go-log/log"
"github.com/siddontang/go-mysql/client"
. "github.com/siddontang/go-mysql/mysql"
) )
var ( var (
@ -104,6 +104,10 @@ type BinlogSyncerConfig struct {
// https://mariadb.com/kb/en/library/com_binlog_dump/ // https://mariadb.com/kb/en/library/com_binlog_dump/
// https://mariadb.com/kb/en/library/annotate_rows_event/ // https://mariadb.com/kb/en/library/annotate_rows_event/
DumpCommandFlag uint16 DumpCommandFlag uint16
//Option function is used to set outside of BinlogSyncerConfig between mysql connection and COM_REGISTER_SLAVE
//For MariaDB: slave_gtid_ignore_duplicates、skip_replication、slave_until_gtid
Option func(*client.Conn) error
} }
// BinlogSyncer syncs binlog event from server. // BinlogSyncer syncs binlog event from server.
@ -179,7 +183,10 @@ func (b *BinlogSyncer) close() {
b.cancel() b.cancel()
if b.c != nil { if b.c != nil {
b.c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) err := b.c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
if err != nil {
log.Warnf(`could not set read deadline: %s`, err)
}
} }
// kill last connection id // kill last connection id
@ -223,18 +230,26 @@ func (b *BinlogSyncer) registerSlave() error {
return errors.Trace(err) return errors.Trace(err)
} }
if b.cfg.Option != nil {
if err = b.cfg.Option(b.c); err != nil {
return errors.Trace(err)
}
}
if len(b.cfg.Charset) != 0 { if len(b.cfg.Charset) != 0 {
b.c.SetCharset(b.cfg.Charset) if err = b.c.SetCharset(b.cfg.Charset); err != nil {
return errors.Trace(err)
}
} }
//set read timeout //set read timeout
if b.cfg.ReadTimeout > 0 { if b.cfg.ReadTimeout > 0 {
b.c.SetReadDeadline(time.Now().Add(b.cfg.ReadTimeout)) _ = b.c.SetReadDeadline(time.Now().Add(b.cfg.ReadTimeout))
} }
if b.cfg.RecvBufferSize > 0 { if b.cfg.RecvBufferSize > 0 {
if tcp, ok := b.c.Conn.Conn.(*net.TCPConn); ok { if tcp, ok := b.c.Conn.Conn.(*net.TCPConn); ok {
tcp.SetReadBuffer(b.cfg.RecvBufferSize) _ = tcp.SetReadBuffer(b.cfg.RecvBufferSize)
} }
} }
@ -268,7 +283,6 @@ func (b *BinlogSyncer) registerSlave() error {
// if _, err = b.c.Execute(`SET @master_binlog_checksum=@@global.binlog_checksum`); err != nil { // if _, err = b.c.Execute(`SET @master_binlog_checksum=@@global.binlog_checksum`); err != nil {
// return errors.Trace(err) // return errors.Trace(err)
// } // }
} }
} }
@ -697,7 +711,7 @@ func (b *BinlogSyncer) onStream(s *BinlogStreamer) {
//set read timeout //set read timeout
if b.cfg.ReadTimeout > 0 { if b.cfg.ReadTimeout > 0 {
b.c.SetReadDeadline(time.Now().Add(b.cfg.ReadTimeout)) _ = b.c.SetReadDeadline(time.Now().Add(b.cfg.ReadTimeout))
} }
// Reset retry count on successful packet receieve // Reset retry count on successful packet receieve

View File

@ -11,8 +11,9 @@ import (
"unicode" "unicode"
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/satori/go.uuid" uuid "github.com/satori/go.uuid"
. "github.com/siddontang/go-mysql/mysql"
. "github.com/go-mysql-org/go-mysql/mysql"
) )
const ( const (
@ -100,7 +101,7 @@ func (h *EventHeader) Decode(data []byte) error {
} }
func (h *EventHeader) Dump(w io.Writer) { func (h *EventHeader) Dump(w io.Writer) {
fmt.Fprintf(w, "=== %s ===\n", EventType(h.EventType)) fmt.Fprintf(w, "=== %s ===\n", h.EventType)
fmt.Fprintf(w, "Date: %s\n", time.Unix(int64(h.Timestamp), 0).Format(TimeFormat)) fmt.Fprintf(w, "Date: %s\n", time.Unix(int64(h.Timestamp), 0).Format(TimeFormat))
fmt.Fprintf(w, "Log position: %d\n", h.LogPos) fmt.Fprintf(w, "Log position: %d\n", h.LogPos)
fmt.Fprintf(w, "Event size: %d\n", h.EventSize) fmt.Fprintf(w, "Event size: %d\n", h.EventSize)
@ -309,7 +310,7 @@ func (e *QueryEvent) Decode(data []byte) error {
e.ExecutionTime = binary.LittleEndian.Uint32(data[pos:]) e.ExecutionTime = binary.LittleEndian.Uint32(data[pos:])
pos += 4 pos += 4
schemaLength := uint8(data[pos]) schemaLength := data[pos]
pos++ pos++
e.ErrorCode = binary.LittleEndian.Uint16(data[pos:]) e.ErrorCode = binary.LittleEndian.Uint16(data[pos:])
@ -368,7 +369,7 @@ type GTIDEvent struct {
func (e *GTIDEvent) Decode(data []byte) error { func (e *GTIDEvent) Decode(data []byte) error {
pos := 0 pos := 0
e.CommitFlag = uint8(data[pos]) e.CommitFlag = data[pos]
pos++ pos++
e.SID = data[pos : pos+SidLength] e.SID = data[pos : pos+SidLength]
pos += SidLength pos += SidLength
@ -376,7 +377,7 @@ func (e *GTIDEvent) Decode(data []byte) error {
pos += 8 pos += 8
if len(data) >= 42 { if len(data) >= 42 {
if uint8(data[pos]) == LogicalTimestampTypeCode { if data[pos] == LogicalTimestampTypeCode {
pos++ pos++
e.LastCommitted = int64(binary.LittleEndian.Uint64(data[pos:])) e.LastCommitted = int64(binary.LittleEndian.Uint64(data[pos:]))
pos += PartLogicalTimestampLength pos += PartLogicalTimestampLength
@ -394,11 +395,9 @@ func (e *GTIDEvent) Decode(data []byte) error {
e.ImmediateCommitTimestamp &= ^(uint64(1) << 55) e.ImmediateCommitTimestamp &= ^(uint64(1) << 55)
e.OriginalCommitTimestamp = FixedLengthInt(data[pos : pos+7]) e.OriginalCommitTimestamp = FixedLengthInt(data[pos : pos+7])
pos += 7 pos += 7
} else { } else {
// Otherwise OriginalCommitTimestamp == ImmediateCommitTimestamp // Otherwise OriginalCommitTimestamp == ImmediateCommitTimestamp
e.OriginalCommitTimestamp = e.ImmediateCommitTimestamp e.OriginalCommitTimestamp = e.ImmediateCommitTimestamp
} }
// TRANSACTION_LENGTH_MIN_LENGTH = 1 // TRANSACTION_LENGTH_MIN_LENGTH = 1
@ -422,13 +421,10 @@ func (e *GTIDEvent) Decode(data []byte) error {
e.ImmediateServerVersion &= ^(uint32(1) << 31) e.ImmediateServerVersion &= ^(uint32(1) << 31)
e.OriginalServerVersion = binary.LittleEndian.Uint32(data[pos:]) e.OriginalServerVersion = binary.LittleEndian.Uint32(data[pos:])
pos += 4 pos += 4
} else { } else {
// Otherwise OriginalServerVersion == ImmediateServerVersion // Otherwise OriginalServerVersion == ImmediateServerVersion
e.OriginalServerVersion = e.ImmediateServerVersion e.OriginalServerVersion = e.ImmediateServerVersion
} }
} }
} }
return nil return nil
@ -510,7 +506,7 @@ func (e *ExecuteLoadQueryEvent) Decode(data []byte) error {
e.ExecutionTime = binary.LittleEndian.Uint32(data[pos:]) e.ExecutionTime = binary.LittleEndian.Uint32(data[pos:])
pos += 4 pos += 4
e.SchemaLength = uint8(data[pos]) e.SchemaLength = data[pos]
pos++ pos++
e.ErrorCode = binary.LittleEndian.Uint16(data[pos:]) e.ErrorCode = binary.LittleEndian.Uint16(data[pos:])
@ -528,7 +524,7 @@ func (e *ExecuteLoadQueryEvent) Decode(data []byte) error {
e.EndPos = binary.LittleEndian.Uint32(data[pos:]) e.EndPos = binary.LittleEndian.Uint32(data[pos:])
pos += 4 pos += 4
e.DupHandlingFlags = uint8(data[pos]) e.DupHandlingFlags = data[pos]
return nil return nil
} }
@ -601,7 +597,7 @@ func (e *MariadbGTIDEvent) Decode(data []byte) error {
pos += 8 pos += 8
e.GTID.DomainID = binary.LittleEndian.Uint32(data[pos:]) e.GTID.DomainID = binary.LittleEndian.Uint32(data[pos:])
pos += 4 pos += 4
e.Flags = uint8(data[pos]) e.Flags = data[pos]
pos += 1 pos += 1
if (e.Flags & BINLOG_MARIADB_FL_GROUP_COMMIT_ID) > 0 { if (e.Flags & BINLOG_MARIADB_FL_GROUP_COMMIT_ID) > 0 {

View File

@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"math" "math"
. "github.com/go-mysql-org/go-mysql/mysql"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/siddontang/go-mysql/mysql"
"github.com/siddontang/go/hack" "github.com/siddontang/go/hack"
) )
@ -149,7 +149,7 @@ func (d *jsonBinaryDecoder) decodeObjectOrArray(data []byte, isSmall bool, isObj
count := d.decodeCount(data, isSmall) count := d.decodeCount(data, isSmall)
size := d.decodeCount(data[offsetSize:], isSmall) size := d.decodeCount(data[offsetSize:], isSmall)
if d.isDataShort(data, int(size)) { if d.isDataShort(data, size) {
// Before MySQL 5.7.22, json type generated column may have invalid value, // Before MySQL 5.7.22, json type generated column may have invalid value,
// bug ref: https://bugs.mysql.com/bug.php?id=88791 // bug ref: https://bugs.mysql.com/bug.php?id=88791
// As generated column value is not used in replication, we can just ignore // As generated column value is not used in replication, we can just ignore
@ -452,7 +452,6 @@ func (d *jsonBinaryDecoder) decodeDateTime(data []byte) interface{} {
frac := v % (1 << 24) frac := v % (1 << 24)
return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", year, month, day, hour, minute, second, frac) return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", year, month, day, hour, minute, second, frac)
} }
func (d *jsonBinaryDecoder) decodeCount(data []byte, isSmall bool) int { func (d *jsonBinaryDecoder) decodeCount(data []byte, isSmall bool) int {

View File

@ -80,7 +80,7 @@ func (p *BinlogParser) ParseFile(name string, offset int64, onEvent OnEventFunc)
offset = 4 offset = 4
} else if offset > 4 { } else if offset > 4 {
// FORMAT_DESCRIPTION event should be read by default always (despite that fact passed offset may be higher than 4) // FORMAT_DESCRIPTION event should be read by default always (despite that fact passed offset may be higher than 4)
if _, err = f.Seek(4, os.SEEK_SET); err != nil { if _, err = f.Seek(4, io.SeekStart); err != nil {
return errors.Errorf("seek %s to %d error %v", name, offset, err) return errors.Errorf("seek %s to %d error %v", name, offset, err)
} }
@ -89,7 +89,7 @@ func (p *BinlogParser) ParseFile(name string, offset int64, onEvent OnEventFunc)
} }
} }
if _, err = f.Seek(offset, os.SEEK_SET); err != nil { if _, err = f.Seek(offset, io.SeekStart); err != nil {
return errors.Errorf("seek %s to %d error %v", name, offset, err) return errors.Errorf("seek %s to %d error %v", name, offset, err)
} }
@ -157,7 +157,6 @@ func (p *BinlogParser) parseSingleEvent(r io.Reader, onEvent OnEventFunc) (bool,
} }
func (p *BinlogParser) ParseReader(r io.Reader, onEvent OnEventFunc) error { func (p *BinlogParser) ParseReader(r io.Reader, onEvent OnEventFunc) error {
for { for {
if atomic.LoadUint32(&p.stopProcessing) == 1 { if atomic.LoadUint32(&p.stopProcessing) == 1 {
break break

View File

@ -12,8 +12,9 @@ import (
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/siddontang/go-log/log" "github.com/siddontang/go-log/log"
. "github.com/siddontang/go-mysql/mysql"
"github.com/siddontang/go/hack" "github.com/siddontang/go/hack"
. "github.com/go-mysql-org/go-mysql/mysql"
) )
var errMissingTableMapEvent = errors.New("invalid table id, no corresponding table map event") var errMissingTableMapEvent = errors.New("invalid table id, no corresponding table map event")
@ -145,7 +146,7 @@ func (e *TableMapEvent) Decode(data []byte) error {
} }
func bitmapByteSize(columnCount int) int { func bitmapByteSize(columnCount int) int {
return int(columnCount+7) / 8 return (columnCount + 7) / 8
} }
// see mysql sql/log_event.h // see mysql sql/log_event.h
@ -238,10 +239,8 @@ func (e *TableMapEvent) decodeMeta(data []byte) error {
} }
func (e *TableMapEvent) decodeOptionalMeta(data []byte) (err error) { func (e *TableMapEvent) decodeOptionalMeta(data []byte) (err error) {
pos := 0 pos := 0
for pos < len(data) { for pos < len(data) {
// optional metadata fields are stored in Type, Length, Value(TLV) format // optional metadata fields are stored in Type, Length, Value(TLV) format
// Type takes 1 byte. Length is a packed integer value. Values takes Length bytes // Type takes 1 byte. Length is a packed integer value. Values takes Length bytes
t := data[pos] t := data[pos]
@ -637,7 +636,6 @@ func (e *TableMapEvent) EnumSetCollationMap() map[int]uint64 {
} }
func (e *TableMapEvent) collationMap(includeType func(int) bool, defaultCharset, columnCharset []uint64) map[int]uint64 { func (e *TableMapEvent) collationMap(includeType func(int) bool, defaultCharset, columnCharset []uint64) map[int]uint64 {
if len(defaultCharset) != 0 { if len(defaultCharset) != 0 {
defaultCollation := defaultCharset[0] defaultCollation := defaultCharset[0]
@ -666,7 +664,6 @@ func (e *TableMapEvent) collationMap(includeType func(int) bool, defaultCharset,
} }
if len(columnCharset) != 0 { if len(columnCharset) != 0 {
p := 0 p := 0
ret := make(map[int]uint64) ret := make(map[int]uint64)
for i := 0; i < int(e.ColumnCount); i++ { for i := 0; i < int(e.ColumnCount); i++ {
@ -739,7 +736,6 @@ func (e *TableMapEvent) GeometryTypeMap() map[int]uint64 {
// Table_map_log_event::print_columns in mysql-8.0/sql/log_event.cc and mariadb-10.5/sql/log_event_client.cc // Table_map_log_event::print_columns in mysql-8.0/sql/log_event.cc and mariadb-10.5/sql/log_event_client.cc
func (e *TableMapEvent) realType(i int) byte { func (e *TableMapEvent) realType(i int) byte {
typ := e.ColumnType[i] typ := e.ColumnType[i]
switch typ { switch typ {
@ -757,7 +753,6 @@ func (e *TableMapEvent) realType(i int) byte {
} }
func (e *TableMapEvent) IsNumericColumn(i int) bool { func (e *TableMapEvent) IsNumericColumn(i int) bool {
switch e.realType(i) { switch e.realType(i) {
case MYSQL_TYPE_TINY, case MYSQL_TYPE_TINY,
MYSQL_TYPE_SHORT, MYSQL_TYPE_SHORT,
@ -772,14 +767,12 @@ func (e *TableMapEvent) IsNumericColumn(i int) bool {
default: default:
return false return false
} }
} }
// IsCharacterColumn returns true if the column type is considered as character type. // IsCharacterColumn returns true if the column type is considered as character type.
// Note that JSON/GEOMETRY types are treated as character type in mariadb. // Note that JSON/GEOMETRY types are treated as character type in mariadb.
// (JSON is an alias for LONGTEXT in mariadb: https://mariadb.com/kb/en/json-data-type/) // (JSON is an alias for LONGTEXT in mariadb: https://mariadb.com/kb/en/json-data-type/)
func (e *TableMapEvent) IsCharacterColumn(i int) bool { func (e *TableMapEvent) IsCharacterColumn(i int) bool {
switch e.realType(i) { switch e.realType(i) {
case MYSQL_TYPE_STRING, case MYSQL_TYPE_STRING,
MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_VAR_STRING,
@ -796,7 +789,6 @@ func (e *TableMapEvent) IsCharacterColumn(i int) bool {
default: default:
return false return false
} }
} }
func (e *TableMapEvent) IsEnumColumn(i int) bool { func (e *TableMapEvent) IsEnumColumn(i int) bool {
@ -846,7 +838,8 @@ type RowsEvent struct {
ColumnBitmap2 []byte ColumnBitmap2 []byte
//rows: invalid: int64, float64, bool, []byte, string //rows: invalid: int64, float64, bool, []byte, string
Rows [][]interface{} Rows [][]interface{}
SkippedColumns [][]int
parseTime bool parseTime bool
timestampStringLocation *time.Location timestampStringLocation *time.Location
@ -854,7 +847,7 @@ type RowsEvent struct {
ignoreJSONDecodeErr bool ignoreJSONDecodeErr bool
} }
func (e *RowsEvent) Decode(data []byte) error { func (e *RowsEvent) Decode(data []byte) (err2 error) {
pos := 0 pos := 0
e.TableID = FixedLengthInt(data[0:e.tableIDSize]) e.TableID = FixedLengthInt(data[0:e.tableIDSize])
pos += e.tableIDSize pos += e.tableIDSize
@ -898,7 +891,9 @@ func (e *RowsEvent) Decode(data []byte) error {
// ... repeat rows until event-end // ... repeat rows until event-end
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
log.Fatalf("parse rows event panic %v, data %q, parsed rows %#v, table map %#v\n%s", r, data, e, e.Table, Pstack()) errStr := fmt.Sprintf("parse rows event panic %v, data %q, parsed rows %#v, table map %#v", r, data, e, e.Table)
log.Errorf("%s\n%s", errStr, Pstack())
err2 = errors.Trace(errors.New(errStr))
} }
}() }()
@ -907,6 +902,7 @@ func (e *RowsEvent) Decode(data []byte) error {
if e.needBitmap2 { if e.needBitmap2 {
rowsLen += e.ColumnCount rowsLen += e.ColumnCount
} }
e.SkippedColumns = make([][]int, 0, rowsLen)
e.Rows = make([][]interface{}, 0, rowsLen) e.Rows = make([][]interface{}, 0, rowsLen)
for pos < len(data) { for pos < len(data) {
@ -932,6 +928,7 @@ func isBitSet(bitmap []byte, i int) bool {
func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte) (int, error) { func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte) (int, error) {
row := make([]interface{}, e.ColumnCount) row := make([]interface{}, e.ColumnCount)
skips := make([]int, 0)
pos := 0 pos := 0
@ -953,6 +950,7 @@ func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte)
var err error var err error
for i := 0; i < int(e.ColumnCount); i++ { for i := 0; i < int(e.ColumnCount); i++ {
if !isBitSet(bitmap, i) { if !isBitSet(bitmap, i) {
skips = append(skips, i)
continue continue
} }
@ -973,6 +971,7 @@ func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte)
} }
e.Rows = append(e.Rows, row) e.Rows = append(e.Rows, row)
e.SkippedColumns = append(e.SkippedColumns, skips)
return pos, nil return pos, nil
} }
@ -1002,7 +1001,7 @@ func (e *RowsEvent) decodeValue(data []byte, tp byte, meta uint16) (v interface{
if b0&0x30 != 0x30 { if b0&0x30 != 0x30 {
length = int(uint16(b1) | (uint16((b0&0x30)^0x30) << 4)) length = int(uint16(b1) | (uint16((b0&0x30)^0x30) << 4))
tp = byte(b0 | 0x30) tp = b0 | 0x30
} else { } else {
length = int(meta & 0xFF) length = int(meta & 0xFF)
tp = b0 tp = b0
@ -1045,7 +1044,7 @@ func (e *RowsEvent) decodeValue(data []byte, tp byte, meta uint16) (v interface{
n = int(nbits+7) / 8 n = int(nbits+7) / 8
//use int64 for bit //use int64 for bit
v, err = decodeBit(data, int(nbits), int(n)) v, err = decodeBit(data, int(nbits), n)
case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIMESTAMP:
n = 4 n = 4
t := binary.LittleEndian.Uint32(data) t := binary.LittleEndian.Uint32(data)
@ -1092,11 +1091,7 @@ func (e *RowsEvent) decodeValue(data []byte, tp byte, meta uint16) (v interface{
if i32 == 0 { if i32 == 0 {
v = "00:00:00" v = "00:00:00"
} else { } else {
sign := "" v = fmt.Sprintf("%02d:%02d:%02d", i32/10000, (i32%10000)/100, i32%100)
if i32 < 0 {
sign = "-"
}
v = fmt.Sprintf("%s%02d:%02d:%02d", sign, i32/10000, (i32%10000)/100, i32%100)
} }
case MYSQL_TYPE_TIME2: case MYSQL_TYPE_TIME2:
v, n, err = decodeTime2(data, meta) v, n, err = decodeTime2(data, meta)
@ -1158,14 +1153,15 @@ func (e *RowsEvent) decodeValue(data []byte, tp byte, meta uint16) (v interface{
default: default:
err = fmt.Errorf("unsupport type %d in binlog and don't know how to handle", tp) err = fmt.Errorf("unsupport type %d in binlog and don't know how to handle", tp)
} }
return
return v, n, err
} }
func decodeString(data []byte, length int) (v string, n int) { func decodeString(data []byte, length int) (v string, n int) {
if length < 256 { if length < 256 {
length = int(data[0]) length = int(data[0])
n = int(length) + 1 n = length + 1
v = hack.String(data[1:n]) v = hack.String(data[1:n])
} else { } else {
length = int(binary.LittleEndian.Uint16(data[0:])) length = int(binary.LittleEndian.Uint16(data[0:]))
@ -1192,9 +1188,9 @@ func decodeDecimalDecompressValue(compIndx int, data []byte, mask uint8) (size i
func decodeDecimal(data []byte, precision int, decimals int, useDecimal bool) (interface{}, int, error) { func decodeDecimal(data []byte, precision int, decimals int, useDecimal bool) (interface{}, int, error) {
//see python mysql replication and https://github.com/jeremycole/mysql_binlog //see python mysql replication and https://github.com/jeremycole/mysql_binlog
integral := (precision - decimals) integral := precision - decimals
uncompIntegral := int(integral / digitsPerInteger) uncompIntegral := integral / digitsPerInteger
uncompFractional := int(decimals / digitsPerInteger) uncompFractional := decimals / digitsPerInteger
compIntegral := integral - (uncompIntegral * digitsPerInteger) compIntegral := integral - (uncompIntegral * digitsPerInteger)
compFractional := decimals - (uncompFractional * digitsPerInteger) compFractional := decimals - (uncompFractional * digitsPerInteger)

View File

@ -31,6 +31,6 @@ func ByteSlicePut(data []byte) {
select { select {
case byteSliceChan <- data: case byteSliceChan <- data:
default: default:
byteSlicePool.Put(data) byteSlicePool.Put(data) //nolint:staticcheck
} }
} }

View File

@ -1,129 +0,0 @@
sudo: false
language: go
go:
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
- master
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
before_script:
- echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf
- sudo service mysql restart
- .travis/wait_mysql.sh
- mysql -e 'create database gotest;'
matrix:
include:
- env: DB=MYSQL8
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mysql:8.0
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mysql:8.0 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- env: DB=MYSQL57
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mysql:5.7
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- env: DB=MARIA55
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mariadb:5.5
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- env: DB=MARIA10_1
sudo: required
dist: trusty
go: 1.10.x
services:
- docker
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- docker pull mariadb:10.1
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
- cp .travis/docker.cnf ~/.my.cnf
- .travis/wait_mysql.sh
before_script:
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3307
- export MYSQL_TEST_CONCURRENT=1
- os: osx
osx_image: xcode10.1
addons:
homebrew:
packages:
- mysql
update: true
go: 1.12.x
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
before_script:
- echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB\nlocal_infile=1" >> /usr/local/etc/my.cnf
- mysql.server start
- mysql -uroot -e 'CREATE USER gotest IDENTIFIED BY "secret"'
- mysql -uroot -e 'GRANT ALL ON *.* TO gotest'
- mysql -uroot -e 'create database gotest;'
- export MYSQL_TEST_USER=gotest
- export MYSQL_TEST_PASS=secret
- export MYSQL_TEST_ADDR=127.0.0.1:3306
- export MYSQL_TEST_CONCURRENT=1
script:
- go test -v -covermode=count -coverprofile=coverage.out
- go vet ./...
- .travis/gofmt.sh
after_script:
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci

View File

@ -13,11 +13,15 @@
Aaron Hopkins <go-sql-driver at die.net> Aaron Hopkins <go-sql-driver at die.net>
Achille Roussel <achille.roussel at gmail.com> Achille Roussel <achille.roussel at gmail.com>
Alex Snast <alexsn at fb.com>
Alexey Palazhchenko <alexey.palazhchenko at gmail.com> Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
Andrew Reid <andrew.reid at tixtrack.com> Andrew Reid <andrew.reid at tixtrack.com>
Animesh Ray <mail.rayanimesh at gmail.com>
Arne Hormann <arnehormann at gmail.com> Arne Hormann <arnehormann at gmail.com>
Ariel Mashraki <ariel at mashraki.co.il>
Asta Xie <xiemengjun at gmail.com> Asta Xie <xiemengjun at gmail.com>
Bulat Gaifullin <gaifullinbf at gmail.com> Bulat Gaifullin <gaifullinbf at gmail.com>
Caine Jette <jette at alum.mit.edu>
Carlos Nieto <jose.carlos at menteslibres.net> Carlos Nieto <jose.carlos at menteslibres.net>
Chris Moos <chris at tech9computers.com> Chris Moos <chris at tech9computers.com>
Craig Wilson <craiggwilson at gmail.com> Craig Wilson <craiggwilson at gmail.com>
@ -52,6 +56,7 @@ Julien Schmidt <go-sql-driver at julienschmidt.com>
Justin Li <jli at j-li.net> Justin Li <jli at j-li.net>
Justin Nuß <nuss.justin at gmail.com> Justin Nuß <nuss.justin at gmail.com>
Kamil Dziedzic <kamil at klecza.pl> Kamil Dziedzic <kamil at klecza.pl>
Kei Kamikawa <x00.x7f.x86 at gmail.com>
Kevin Malachowski <kevin at chowski.com> Kevin Malachowski <kevin at chowski.com>
Kieron Woodhouse <kieron.woodhouse at infosum.com> Kieron Woodhouse <kieron.woodhouse at infosum.com>
Lennart Rudolph <lrudolph at hmc.edu> Lennart Rudolph <lrudolph at hmc.edu>
@ -74,20 +79,26 @@ Reed Allman <rdallman10 at gmail.com>
Richard Wilkes <wilkes at me.com> Richard Wilkes <wilkes at me.com>
Robert Russell <robert at rrbrussell.com> Robert Russell <robert at rrbrussell.com>
Runrioter Wung <runrioter at gmail.com> Runrioter Wung <runrioter at gmail.com>
Sho Iizuka <sho.i518 at gmail.com>
Sho Ikeda <suicaicoca at gmail.com>
Shuode Li <elemount at qq.com> Shuode Li <elemount at qq.com>
Simon J Mudd <sjmudd at pobox.com> Simon J Mudd <sjmudd at pobox.com>
Soroush Pour <me at soroushjp.com> Soroush Pour <me at soroushjp.com>
Stan Putrya <root.vagner at gmail.com> Stan Putrya <root.vagner at gmail.com>
Stanley Gunawan <gunawan.stanley at gmail.com> Stanley Gunawan <gunawan.stanley at gmail.com>
Steven Hartland <steven.hartland at multiplay.co.uk> Steven Hartland <steven.hartland at multiplay.co.uk>
Tan Jinhua <312841925 at qq.com>
Thomas Wodarek <wodarekwebpage at gmail.com> Thomas Wodarek <wodarekwebpage at gmail.com>
Tim Ruffles <timruffles at gmail.com> Tim Ruffles <timruffles at gmail.com>
Tom Jenkinson <tom at tjenkinson.me> Tom Jenkinson <tom at tjenkinson.me>
Vladimir Kovpak <cn007b at gmail.com> Vladimir Kovpak <cn007b at gmail.com>
Vladyslav Zhelezniak <zhvladi at gmail.com>
Xiangyu Hu <xiangyu.hu at outlook.com> Xiangyu Hu <xiangyu.hu at outlook.com>
Xiaobing Jiang <s7v7nislands at gmail.com> Xiaobing Jiang <s7v7nislands at gmail.com>
Xiuming Chen <cc at cxm.cc> Xiuming Chen <cc at cxm.cc>
Xuehong Chan <chanxuehong at gmail.com>
Zhenye Xie <xiezhenye at gmail.com> Zhenye Xie <xiezhenye at gmail.com>
Zhixin Wen <john.wenzhixin at gmail.com>
# Organizations # Organizations
@ -103,3 +114,4 @@ Multiplay Ltd.
Percona LLC Percona LLC
Pivotal Inc. Pivotal Inc.
Stripe Inc. Stripe Inc.
Zendesk Inc.

View File

@ -1,3 +1,29 @@
## Version 1.6 (2021-04-01)
Changes:
- Migrate the CI service from travis-ci to GitHub Actions (#1176, #1183, #1190)
- `NullTime` is deprecated (#960, #1144)
- Reduce allocations when building SET command (#1111)
- Performance improvement for time formatting (#1118)
- Performance improvement for time parsing (#1098, #1113)
New Features:
- Implement `driver.Validator` interface (#1106, #1174)
- Support returning `uint64` from `Valuer` in `ConvertValue` (#1143)
- Add `json.RawMessage` for converter and prepared statement (#1059)
- Interpolate `json.RawMessage` as `string` (#1058)
- Implements `CheckNamedValue` (#1090)
Bugfixes:
- Stop rounding times (#1121, #1172)
- Put zero filler into the SSL handshake packet (#1066)
- Fix checking cancelled connections back into the connection pool (#1095)
- Fix remove last 0 byte for mysql_old_password when password is empty (#1133)
## Version 1.5 (2020-01-07) ## Version 1.5 (2020-01-07)
Changes: Changes:

View File

@ -35,7 +35,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
* Supports queries larger than 16MB * Supports queries larger than 16MB
* Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support. * Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support.
* Intelligent `LONG DATA` handling in prepared statements * Intelligent `LONG DATA` handling in prepared statements
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support * Secure `LOAD DATA LOCAL INFILE` support with file allowlisting and `io.Reader` support
* Optional `time.Time` parsing * Optional `time.Time` parsing
* Optional placeholder interpolation * Optional placeholder interpolation
@ -56,15 +56,37 @@ Make sure [Git is installed](https://git-scm.com/downloads) on your machine and
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then. _Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then.
Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`: Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
```go ```go
import "database/sql" import (
import _ "github.com/go-sql-driver/mysql" "database/sql"
"time"
_ "github.com/go-sql-driver/mysql"
)
// ...
db, err := sql.Open("mysql", "user:password@/dbname") db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err)
}
// See "Important settings" section.
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
``` ```
[Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples"). [Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples").
### Important settings
`db.SetConnMaxLifetime()` is required to ensure connections are closed by the driver safely before connection is closed by MySQL server, OS, or other middlewares. Since some middlewares close idle connections by 5 minutes, we recommend timeout shorter than 5 minutes. This setting helps load balancing and changing system variables too.
`db.SetMaxOpenConns()` is highly recommended to limit the number of connection used by the application. There is no recommended limit number because it depends on application and MySQL server.
`db.SetMaxIdleConns()` is recommended to be set same to (or greater than) `db.SetMaxOpenConns()`. When it is smaller than `SetMaxOpenConns()`, connections can be opened and closed very frequently than you expect. Idle connections can be closed by the `db.SetConnMaxLifetime()`. If you want to close idle connections more rapidly, you can use `db.SetConnMaxIdleTime()` since Go 1.15.
### DSN (Data Source Name) ### DSN (Data Source Name)
@ -122,7 +144,7 @@ Valid Values: true, false
Default: false Default: false
``` ```
`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. `allowAllFiles=true` disables the file allowlist for `LOAD DATA LOCAL INFILE` and allows *all* files.
[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html) [*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
##### `allowCleartextPasswords` ##### `allowCleartextPasswords`
@ -133,7 +155,7 @@ Valid Values: true, false
Default: false Default: false
``` ```
`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network. `allowCleartextPasswords=true` allows using the [cleartext client side plugin](https://dev.mysql.com/doc/en/cleartext-pluggable-authentication.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
##### `allowNativePasswords` ##### `allowNativePasswords`
@ -230,7 +252,7 @@ Default: false
If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`. If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!* *This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are rejected as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
##### `loc` ##### `loc`
@ -376,7 +398,7 @@ Rules:
Examples: Examples:
* `autocommit=1`: `SET autocommit=1` * `autocommit=1`: `SET autocommit=1`
* [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'` * [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'`
* [`tx_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `SET tx_isolation='REPEATABLE-READ'` * [`transaction_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation): `SET transaction_isolation='REPEATABLE-READ'`
#### Examples #### Examples
@ -445,7 +467,7 @@ For this feature you need direct access to the package. Therefore you must chang
import "github.com/go-sql-driver/mysql" import "github.com/go-sql-driver/mysql"
``` ```
Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore. To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
@ -459,8 +481,6 @@ However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` v
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes). **Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
Alternatively you can use the [`NullTime`](https://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
### Unicode support ### Unicode support
Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default. Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default.
@ -477,7 +497,7 @@ To run the driver tests you may need to adjust the configuration. See the [Testi
Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated. Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated.
If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls). If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls).
See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details. See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/.github/CONTRIBUTING.md) for details.
--------------------------------------- ---------------------------------------
@ -498,4 +518,3 @@ Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you
You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE). You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE).
![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow") ![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")

View File

@ -15,6 +15,7 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"fmt"
"sync" "sync"
) )
@ -136,10 +137,6 @@ func pwHash(password []byte) (result [2]uint32) {
// Hash password using insecure pre 4.1 method // Hash password using insecure pre 4.1 method
func scrambleOldPassword(scramble []byte, password string) []byte { func scrambleOldPassword(scramble []byte, password string) []byte {
if len(password) == 0 {
return nil
}
scramble = scramble[:8] scramble = scramble[:8]
hashPw := pwHash([]byte(password)) hashPw := pwHash([]byte(password))
@ -247,6 +244,9 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
if !mc.cfg.AllowOldPasswords { if !mc.cfg.AllowOldPasswords {
return nil, ErrOldPassword return nil, ErrOldPassword
} }
if len(mc.cfg.Passwd) == 0 {
return nil, nil
}
// Note: there are edge cases where this should work but doesn't; // Note: there are edge cases where this should work but doesn't;
// this is currently "wontfix": // this is currently "wontfix":
// https://github.com/go-sql-driver/mysql/issues/184 // https://github.com/go-sql-driver/mysql/issues/184
@ -372,7 +372,10 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
return err return err
} }
block, _ := pem.Decode(data[1:]) block, rest := pem.Decode(data[1:])
if block == nil {
return fmt.Errorf("No Pem data found, data: %s", rest)
}
pkix, err := x509.ParsePKIXPublicKey(block.Bytes) pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil { if err != nil {
return err return err

View File

@ -247,7 +247,7 @@ var collations = map[string]byte{
"utf8mb4_0900_ai_ci": 255, "utf8mb4_0900_ai_ci": 255,
} }
// A blacklist of collations which is unsafe to interpolate parameters. // A denylist of collations which is unsafe to interpolate parameters.
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes. // These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
var unsafeCollations = map[string]bool{ var unsafeCollations = map[string]bool{
"big5_chinese_ci": true, "big5_chinese_ci": true,

View File

@ -12,6 +12,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"encoding/json"
"io" "io"
"net" "net"
"strconv" "strconv"
@ -46,9 +47,10 @@ type mysqlConn struct {
// Handles parameters set in DSN after the connection is established // Handles parameters set in DSN after the connection is established
func (mc *mysqlConn) handleParams() (err error) { func (mc *mysqlConn) handleParams() (err error) {
var cmdSet strings.Builder
for param, val := range mc.cfg.Params { for param, val := range mc.cfg.Params {
switch param { switch param {
// Charset // Charset: character_set_connection, character_set_client, character_set_results
case "charset": case "charset":
charsets := strings.Split(val, ",") charsets := strings.Split(val, ",")
for i := range charsets { for i := range charsets {
@ -62,12 +64,25 @@ func (mc *mysqlConn) handleParams() (err error) {
return return
} }
// System Vars // Other system vars accumulated in a single SET command
default: default:
err = mc.exec("SET " + param + "=" + val + "") if cmdSet.Len() == 0 {
if err != nil { // Heuristic: 29 chars for each other key=value to reduce reallocations
return cmdSet.Grow(4 + len(param) + 1 + len(val) + 30*(len(mc.cfg.Params)-1))
cmdSet.WriteString("SET ")
} else {
cmdSet.WriteByte(',')
} }
cmdSet.WriteString(param)
cmdSet.WriteByte('=')
cmdSet.WriteString(val)
}
}
if cmdSet.Len() > 0 {
err = mc.exec(cmdSet.String())
if err != nil {
return
} }
} }
@ -230,47 +245,21 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
if v.IsZero() { if v.IsZero() {
buf = append(buf, "'0000-00-00'"...) buf = append(buf, "'0000-00-00'"...)
} else { } else {
v := v.In(mc.cfg.Loc) buf = append(buf, '\'')
v = v.Add(time.Nanosecond * 500) // To round under microsecond buf, err = appendDateTime(buf, v.In(mc.cfg.Loc))
year := v.Year() if err != nil {
year100 := year / 100 return "", err
year1 := year % 100
month := v.Month()
day := v.Day()
hour := v.Hour()
minute := v.Minute()
second := v.Second()
micro := v.Nanosecond() / 1000
buf = append(buf, []byte{
'\'',
digits10[year100], digits01[year100],
digits10[year1], digits01[year1],
'-',
digits10[month], digits01[month],
'-',
digits10[day], digits01[day],
' ',
digits10[hour], digits01[hour],
':',
digits10[minute], digits01[minute],
':',
digits10[second], digits01[second],
}...)
if micro != 0 {
micro10000 := micro / 10000
micro100 := micro / 100 % 100
micro1 := micro % 100
buf = append(buf, []byte{
'.',
digits10[micro10000], digits01[micro10000],
digits10[micro100], digits01[micro100],
digits10[micro1], digits01[micro1],
}...)
} }
buf = append(buf, '\'') buf = append(buf, '\'')
} }
case json.RawMessage:
buf = append(buf, '\'')
if mc.status&statusNoBackslashEscapes == 0 {
buf = escapeBytesBackslash(buf, v)
} else {
buf = escapeBytesQuotes(buf, v)
}
buf = append(buf, '\'')
case []byte: case []byte:
if v == nil { if v == nil {
buf = append(buf, "NULL"...) buf = append(buf, "NULL"...)
@ -480,6 +469,10 @@ func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
// BeginTx implements driver.ConnBeginTx interface // BeginTx implements driver.ConnBeginTx interface
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if mc.closed.IsSet() {
return nil, driver.ErrBadConn
}
if err := mc.watchCancel(ctx); err != nil { if err := mc.watchCancel(ctx); err != nil {
return nil, err return nil, err
} }
@ -649,3 +642,9 @@ func (mc *mysqlConn) ResetSession(ctx context.Context) error {
mc.reset = true mc.reset = true
return nil return nil
} }
// IsValid implements driver.Validator interface
// (From Go 1.15)
func (mc *mysqlConn) IsValid() bool {
return !mc.closed.IsSet()
}

View File

@ -375,7 +375,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
// cfg params // cfg params
switch value := param[1]; param[0] { switch value := param[1]; param[0] {
// Disable INFILE whitelist / enable all files // Disable INFILE allowlist / enable all files
case "allowAllFiles": case "allowAllFiles":
var isBool bool var isBool bool
cfg.AllowAllFiles, isBool = readBool(value) cfg.AllowAllFiles, isBool = readBool(value)

View File

@ -106,7 +106,7 @@ var (
scanTypeInt64 = reflect.TypeOf(int64(0)) scanTypeInt64 = reflect.TypeOf(int64(0))
scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
scanTypeNullInt = reflect.TypeOf(sql.NullInt64{}) scanTypeNullInt = reflect.TypeOf(sql.NullInt64{})
scanTypeNullTime = reflect.TypeOf(NullTime{}) scanTypeNullTime = reflect.TypeOf(nullTime{})
scanTypeUint8 = reflect.TypeOf(uint8(0)) scanTypeUint8 = reflect.TypeOf(uint8(0))
scanTypeUint16 = reflect.TypeOf(uint16(0)) scanTypeUint16 = reflect.TypeOf(uint16(0))
scanTypeUint32 = reflect.TypeOf(uint32(0)) scanTypeUint32 = reflect.TypeOf(uint32(0))

24
vendor/github.com/go-sql-driver/mysql/fuzz.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
//
// Copyright 2020 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
// +build gofuzz
package mysql
import (
"database/sql"
)
func Fuzz(data []byte) int {
db, err := sql.Open("mysql", string(data))
if err != nil {
return 0
}
db.Close()
return 1
}

View File

@ -23,7 +23,7 @@ var (
readerRegisterLock sync.RWMutex readerRegisterLock sync.RWMutex
) )
// RegisterLocalFile adds the given file to the file whitelist, // RegisterLocalFile adds the given file to the file allowlist,
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>". // so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
// Alternatively you can allow the use of all local files with // Alternatively you can allow the use of all local files with
// the DSN parameter 'allowAllFiles=true' // the DSN parameter 'allowAllFiles=true'
@ -45,7 +45,7 @@ func RegisterLocalFile(filePath string) {
fileRegisterLock.Unlock() fileRegisterLock.Unlock()
} }
// DeregisterLocalFile removes the given filepath from the whitelist. // DeregisterLocalFile removes the given filepath from the allowlist.
func DeregisterLocalFile(filePath string) { func DeregisterLocalFile(filePath string) {
fileRegisterLock.Lock() fileRegisterLock.Lock()
delete(fileRegister, strings.Trim(filePath, `"`)) delete(fileRegister, strings.Trim(filePath, `"`))

View File

@ -28,11 +28,11 @@ func (nt *NullTime) Scan(value interface{}) (err error) {
nt.Time, nt.Valid = v, true nt.Time, nt.Valid = v, true
return return
case []byte: case []byte:
nt.Time, err = parseDateTime(string(v), time.UTC) nt.Time, err = parseDateTime(v, time.UTC)
nt.Valid = (err == nil) nt.Valid = (err == nil)
return return
case string: case string:
nt.Time, err = parseDateTime(v, time.UTC) nt.Time, err = parseDateTime([]byte(v), time.UTC)
nt.Valid = (err == nil) nt.Valid = (err == nil)
return return
} }

View File

@ -28,4 +28,13 @@ import (
// } // }
// //
// This NullTime implementation is not driver-specific // This NullTime implementation is not driver-specific
//
// Deprecated: NullTime doesn't honor the loc DSN parameter.
// NullTime.Scan interprets a time as UTC, not the loc DSN parameter.
// Use sql.NullTime instead.
type NullTime sql.NullTime type NullTime sql.NullTime
// for internal use.
// the mysql package uses sql.NullTime if it is available.
// if not, the package uses mysql.NullTime.
type nullTime = sql.NullTime // sql.NullTime is available

View File

@ -32,3 +32,8 @@ type NullTime struct {
Time time.Time Time time.Time
Valid bool // Valid is true if Time is not NULL Valid bool // Valid is true if Time is not NULL
} }
// for internal use.
// the mysql package uses sql.NullTime if it is available.
// if not, the package uses mysql.NullTime.
type nullTime = NullTime // sql.NullTime is not available

View File

@ -13,6 +13,7 @@ import (
"crypto/tls" "crypto/tls"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -348,6 +349,12 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
return errors.New("unknown collation") return errors.New("unknown collation")
} }
// Filler [23 bytes] (all 0x00)
pos := 13
for ; pos < 13+23; pos++ {
data[pos] = 0
}
// SSL Connection Request Packet // SSL Connection Request Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
if mc.cfg.tls != nil { if mc.cfg.tls != nil {
@ -366,12 +373,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
mc.buf.nc = tlsConn mc.buf.nc = tlsConn
} }
// Filler [23 bytes] (all 0x00)
pos := 13
for ; pos < 13+23; pos++ {
data[pos] = 0
}
// User [null terminated string] // User [null terminated string]
if len(mc.cfg.User) > 0 { if len(mc.cfg.User) > 0 {
pos += copy(data[pos:], mc.cfg.User) pos += copy(data[pos:], mc.cfg.User)
@ -777,7 +778,7 @@ func (rows *textRows) readRow(dest []driver.Value) error {
case fieldTypeTimestamp, fieldTypeDateTime, case fieldTypeTimestamp, fieldTypeDateTime,
fieldTypeDate, fieldTypeNewDate: fieldTypeDate, fieldTypeNewDate:
dest[i], err = parseDateTime( dest[i], err = parseDateTime(
string(dest[i].([]byte)), dest[i].([]byte),
mc.cfg.Loc, mc.cfg.Loc,
) )
if err == nil { if err == nil {
@ -1003,6 +1004,9 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
continue continue
} }
if v, ok := arg.(json.RawMessage); ok {
arg = []byte(v)
}
// cache types and values // cache types and values
switch v := arg.(type) { switch v := arg.(type) {
case int64: case int64:
@ -1112,7 +1116,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
if v.IsZero() { if v.IsZero() {
b = append(b, "0000-00-00"...) b = append(b, "0000-00-00"...)
} else { } else {
b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat) b, err = appendDateTime(b, v.In(mc.cfg.Loc))
if err != nil {
return err
}
} }
paramValues = appendLengthEncodedInteger(paramValues, paramValues = appendLengthEncodedInteger(paramValues,

View File

@ -10,6 +10,7 @@ package mysql
import ( import (
"database/sql/driver" "database/sql/driver"
"encoding/json"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
@ -43,6 +44,11 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
return converter{} return converter{}
} }
func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) {
nv.Value, err = converter{}.ConvertValue(nv.Value)
return
}
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
if stmt.mc.closed.IsSet() { if stmt.mc.closed.IsSet() {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
@ -129,6 +135,8 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
return rows, err return rows, err
} }
var jsonType = reflect.TypeOf(json.RawMessage{})
type converter struct{} type converter struct{}
// ConvertValue mirrors the reference/default converter in database/sql/driver // ConvertValue mirrors the reference/default converter in database/sql/driver
@ -146,12 +154,17 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !driver.IsValue(sv) { if driver.IsValue(sv) {
return nil, fmt.Errorf("non-Value type %T returned from Value", sv) return sv, nil
} }
return sv, nil // A value returend from the Valuer interface can be "a type handled by
// a database driver's NamedValueChecker interface" so we should accept
// uint64 here as well.
if u, ok := sv.(uint64); ok {
return u, nil
}
return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
} }
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
switch rv.Kind() { switch rv.Kind() {
case reflect.Ptr: case reflect.Ptr:
@ -170,11 +183,14 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
case reflect.Bool: case reflect.Bool:
return rv.Bool(), nil return rv.Bool(), nil
case reflect.Slice: case reflect.Slice:
ek := rv.Type().Elem().Kind() switch t := rv.Type(); {
if ek == reflect.Uint8 { case t == jsonType:
return v, nil
case t.Elem().Kind() == reflect.Uint8:
return rv.Bytes(), nil return rv.Bytes(), nil
default:
return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind())
} }
return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek)
case reflect.String: case reflect.String:
return rv.String(), nil return rv.String(), nil
} }

View File

@ -106,27 +106,136 @@ func readBool(input string) (value bool, valid bool) {
* Time related utils * * Time related utils *
******************************************************************************/ ******************************************************************************/
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { func parseDateTime(b []byte, loc *time.Location) (time.Time, error) {
base := "0000-00-00 00:00:00.0000000" const base = "0000-00-00 00:00:00.000000"
switch len(str) { switch len(b) {
case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
if str == base[:len(str)] { if string(b) == base[:len(b)] {
return return time.Time{}, nil
} }
t, err = time.Parse(timeFormat[:len(str)], str)
year, err := parseByteYear(b)
if err != nil {
return time.Time{}, err
}
if year <= 0 {
year = 1
}
if b[4] != '-' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[4])
}
m, err := parseByte2Digits(b[5], b[6])
if err != nil {
return time.Time{}, err
}
if m <= 0 {
m = 1
}
month := time.Month(m)
if b[7] != '-' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[7])
}
day, err := parseByte2Digits(b[8], b[9])
if err != nil {
return time.Time{}, err
}
if day <= 0 {
day = 1
}
if len(b) == 10 {
return time.Date(year, month, day, 0, 0, 0, 0, loc), nil
}
if b[10] != ' ' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[10])
}
hour, err := parseByte2Digits(b[11], b[12])
if err != nil {
return time.Time{}, err
}
if b[13] != ':' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[13])
}
min, err := parseByte2Digits(b[14], b[15])
if err != nil {
return time.Time{}, err
}
if b[16] != ':' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[16])
}
sec, err := parseByte2Digits(b[17], b[18])
if err != nil {
return time.Time{}, err
}
if len(b) == 19 {
return time.Date(year, month, day, hour, min, sec, 0, loc), nil
}
if b[19] != '.' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[19])
}
nsec, err := parseByteNanoSec(b[20:])
if err != nil {
return time.Time{}, err
}
return time.Date(year, month, day, hour, min, sec, nsec, loc), nil
default: default:
err = fmt.Errorf("invalid time string: %s", str) return time.Time{}, fmt.Errorf("invalid time bytes: %s", b)
return
} }
}
// Adjust location func parseByteYear(b []byte) (int, error) {
if err == nil && loc != time.UTC { year, n := 0, 1000
y, mo, d := t.Date() for i := 0; i < 4; i++ {
h, mi, s := t.Clock() v, err := bToi(b[i])
t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil if err != nil {
return 0, err
}
year += v * n
n = n / 10
} }
return year, nil
}
return func parseByte2Digits(b1, b2 byte) (int, error) {
d1, err := bToi(b1)
if err != nil {
return 0, err
}
d2, err := bToi(b2)
if err != nil {
return 0, err
}
return d1*10 + d2, nil
}
func parseByteNanoSec(b []byte) (int, error) {
ns, digit := 0, 100000 // max is 6-digits
for i := 0; i < len(b); i++ {
v, err := bToi(b[i])
if err != nil {
return 0, err
}
ns += v * digit
digit /= 10
}
// nanoseconds has 10-digits. (needs to scale digits)
// 10 - 6 = 4, so we have to multiple 1000.
return ns * 1000, nil
}
func bToi(b byte) (int, error) {
if b < '0' || b > '9' {
return 0, errors.New("not [0-9]")
}
return int(b - '0'), nil
} }
func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) { func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) {
@ -167,6 +276,64 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
return nil, fmt.Errorf("invalid DATETIME packet length %d", num) return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
} }
func appendDateTime(buf []byte, t time.Time) ([]byte, error) {
year, month, day := t.Date()
hour, min, sec := t.Clock()
nsec := t.Nanosecond()
if year < 1 || year > 9999 {
return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap
}
year100 := year / 100
year1 := year % 100
var localBuf [len("2006-01-02T15:04:05.999999999")]byte // does not escape
localBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1]
localBuf[4] = '-'
localBuf[5], localBuf[6] = digits10[month], digits01[month]
localBuf[7] = '-'
localBuf[8], localBuf[9] = digits10[day], digits01[day]
if hour == 0 && min == 0 && sec == 0 && nsec == 0 {
return append(buf, localBuf[:10]...), nil
}
localBuf[10] = ' '
localBuf[11], localBuf[12] = digits10[hour], digits01[hour]
localBuf[13] = ':'
localBuf[14], localBuf[15] = digits10[min], digits01[min]
localBuf[16] = ':'
localBuf[17], localBuf[18] = digits10[sec], digits01[sec]
if nsec == 0 {
return append(buf, localBuf[:19]...), nil
}
nsec100000000 := nsec / 100000000
nsec1000000 := (nsec / 1000000) % 100
nsec10000 := (nsec / 10000) % 100
nsec100 := (nsec / 100) % 100
nsec1 := nsec % 100
localBuf[19] = '.'
// milli second
localBuf[20], localBuf[21], localBuf[22] =
digits01[nsec100000000], digits10[nsec1000000], digits01[nsec1000000]
// micro second
localBuf[23], localBuf[24], localBuf[25] =
digits10[nsec10000], digits01[nsec10000], digits10[nsec100]
// nano second
localBuf[26], localBuf[27], localBuf[28] =
digits01[nsec100], digits10[nsec1], digits01[nsec1]
// trim trailing zeros
n := len(localBuf)
for n > 0 && localBuf[n-1] == '0' {
n--
}
return append(buf, localBuf[:n]...), nil
}
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation // zeroDateTime is used in formatBinaryDateTime to avoid an allocation
// if the DATE or DATETIME has the zero value. // if the DATE or DATETIME has the zero value.
// It must never be changed. // It must never be changed.

View File

@ -6,6 +6,7 @@
# Folders # Folders
_obj _obj
_test _test
.idea
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]

99
vendor/github.com/pingcap/errors/compatible_shim.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
// Copyright 2020 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 errors
import (
"encoding/json"
"strconv"
"strings"
)
// class2RFCCode is used for compatible with old version of TiDB. When
// marshal Error to json, old version of TiDB contain a 'class' field
// which is represented for error class. In order to parse and convert
// json to errors.Error, using this map to convert error class to RFC
// error code text. here is reference:
// https://github.com/pingcap/parser/blob/release-3.0/terror/terror.go#L58
var class2RFCCode = map[int]string{
1: "autoid",
2: "ddl",
3: "domain",
4: "evaluator",
5: "executor",
6: "expression",
7: "admin",
8: "kv",
9: "meta",
10: "planner",
11: "parser",
12: "perfschema",
13: "privilege",
14: "schema",
15: "server",
16: "struct",
17: "variable",
18: "xeval",
19: "table",
20: "types",
21: "global",
22: "mocktikv",
23: "json",
24: "tikv",
25: "session",
26: "plugin",
27: "util",
}
var rfcCode2class map[string]int
func init() {
rfcCode2class = make(map[string]int)
for k, v := range class2RFCCode {
rfcCode2class[v] = k
}
}
// MarshalJSON implements json.Marshaler interface.
// aware that this function cannot save a 'registered' status,
// since we cannot access the registry when unmarshaling,
// and the original global registry would be removed here.
// This function is reserved for compatibility.
func (e *Error) MarshalJSON() ([]byte, error) {
ec := strings.Split(string(e.codeText), ":")[0]
return json.Marshal(&jsonError{
Class: rfcCode2class[ec],
Code: int(e.code),
Msg: e.GetMsg(),
RFCCode: string(e.codeText),
})
}
// UnmarshalJSON implements json.Unmarshaler interface.
// aware that this function cannot create a 'registered' error,
// since we cannot access the registry in this context,
// and the original global registry is removed.
// This function is reserved for compatibility.
func (e *Error) UnmarshalJSON(data []byte) error {
tErr := &jsonError{}
if err := json.Unmarshal(data, &tErr); err != nil {
return Trace(err)
}
e.codeText = ErrCodeText(tErr.RFCCode)
if tErr.RFCCode == "" && tErr.Class > 0 {
e.codeText = ErrCodeText(class2RFCCode[tErr.Class] + ":" + strconv.Itoa(tErr.Code))
}
e.code = ErrCode(tErr.Code)
e.message = tErr.Msg
return nil
}

11
vendor/github.com/pingcap/errors/go.mod generated vendored Normal file
View File

@ -0,0 +1,11 @@
module github.com/pingcap/errors
go 1.14
require (
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad
github.com/pkg/errors v0.9.1
go.uber.org/atomic v1.6.0
go.uber.org/zap v1.15.0
)

66
vendor/github.com/pingcap/errors/go.sum generated vendored Normal file
View File

@ -0,0 +1,66 @@
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc=
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
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=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -9,6 +9,9 @@ import (
// Trace just calls AddStack. // Trace just calls AddStack.
func Trace(err error) error { func Trace(err error) error {
if err == nil {
return nil
}
return AddStack(err) return AddStack(err)
} }
@ -52,6 +55,48 @@ func Annotatef(err error, format string, args ...interface{}) error {
} }
} }
var emptyStack stack
// NewNoStackError creates error without error stack
// later duplicate trace will no longer generate Stack too.
func NewNoStackError(msg string) error {
return &fundamental{
msg: msg,
stack: &emptyStack,
}
}
// SuspendStack suspends stack generate for error.
func SuspendStack(err error) error {
if err == nil {
return err
}
cleared := clearStack(err)
if cleared {
return err
}
return &withStack{
err,
&emptyStack,
}
}
func clearStack(err error) (cleared bool) {
switch typedErr := err.(type) {
case *withMessage:
return clearStack(typedErr.Cause())
case *fundamental:
typedErr.stack = &emptyStack
return true
case *withStack:
typedErr.stack = &emptyStack
clearStack(typedErr.Cause())
return true
default:
return false
}
}
// ErrorStack will format a stack trace if it is available, otherwise it will be Error() // ErrorStack will format a stack trace if it is available, otherwise it will be Error()
// If the error is nil, the empty string is returned // If the error is nil, the empty string is returned
// Note that this just calls fmt.Sprintf("%+v", err) // Note that this just calls fmt.Sprintf("%+v", err)

313
vendor/github.com/pingcap/errors/normalize.go generated vendored Normal file
View File

@ -0,0 +1,313 @@
// Copyright 2020 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 errors
import (
"fmt"
"runtime"
"strconv"
"go.uber.org/atomic"
)
// RedactLogEnabled defines whether the arguments of Error need to be redacted.
var RedactLogEnabled atomic.Bool
// ErrCode represents a specific error type in a error class.
// Same error code can be used in different error classes.
type ErrCode int
// ErrCodeText is a textual error code that represents a specific error type in a error class.
type ErrCodeText string
type ErrorID string
type RFCErrorCode string
// Error is the 'prototype' of a type of errors.
// Use DefineError to make a *Error:
// var ErrUnavailable = errors.Normalize("Region %d is unavailable", errors.RFCCodeText("Unavailable"))
//
// "throw" it at runtime:
// func Somewhat() error {
// ...
// if err != nil {
// // generate a stackful error use the message template at defining,
// // also see FastGen(it's stackless), GenWithStack(it uses custom message template).
// return ErrUnavailable.GenWithStackByArgs(region.ID)
// }
// }
//
// testing whether an error belongs to a prototype:
// if ErrUnavailable.Equal(err) {
// // handle this error.
// }
type Error struct {
code ErrCode
// codeText is the textual describe of the error code
codeText ErrCodeText
// message is a template of the description of this error.
// printf-style formatting is enabled.
message string
// redactArgsPos defines the positions of arguments in message that need to be redacted.
// And it is controlled by the global var RedactLogEnabled.
// For example, an original error is `Duplicate entry 'PRIMARY' for key 'key'`,
// when RedactLogEnabled is ON and redactArgsPos is [0, 1], the error is `Duplicate entry '?' for key '?'`.
redactArgsPos []int
// Cause is used to warp some third party error.
cause error
args []interface{}
file string
line int
}
// Code returns the numeric code of this error.
// ID() will return textual error if there it is,
// when you just want to get the purely numeric error
// (e.g., for mysql protocol transmission.), this would be useful.
func (e *Error) Code() ErrCode {
return e.code
}
// Code returns ErrorCode, by the RFC:
//
// The error code is a 3-tuple of abbreviated component name, error class and error code,
// joined by a colon like {Component}:{ErrorClass}:{InnerErrorCode}.
func (e *Error) RFCCode() RFCErrorCode {
return RFCErrorCode(e.ID())
}
// ID returns the ID of this error.
func (e *Error) ID() ErrorID {
if e.codeText != "" {
return ErrorID(e.codeText)
}
return ErrorID(strconv.Itoa(int(e.code)))
}
// Location returns the location where the error is created,
// implements juju/errors locationer interface.
func (e *Error) Location() (file string, line int) {
return e.file, e.line
}
// MessageTemplate returns the error message template of this error.
func (e *Error) MessageTemplate() string {
return e.message
}
// Error implements error interface.
func (e *Error) Error() string {
if e == nil {
return "<nil>"
}
describe := e.codeText
if len(describe) == 0 {
describe = ErrCodeText(strconv.Itoa(int(e.code)))
}
return fmt.Sprintf("[%s]%s", e.RFCCode(), e.GetMsg())
}
func (e *Error) GetMsg() string {
if len(e.args) > 0 {
return fmt.Sprintf(e.message, e.args...)
}
return e.message
}
func (e *Error) fillLineAndFile(skip int) {
// skip this
_, file, line, ok := runtime.Caller(skip + 1)
if !ok {
e.file = "<unknown>"
e.line = -1
return
}
e.file = file
e.line = line
}
// GenWithStack generates a new *Error with the same class and code, and a new formatted message.
func (e *Error) GenWithStack(format string, args ...interface{}) error {
// TODO: RedactErrorArg
err := *e
err.message = format
err.args = args
err.fillLineAndFile(1)
return AddStack(&err)
}
// GenWithStackByArgs generates a new *Error with the same class and code, and new arguments.
func (e *Error) GenWithStackByArgs(args ...interface{}) error {
RedactErrorArg(args, e.redactArgsPos)
err := *e
err.args = args
err.fillLineAndFile(1)
return AddStack(&err)
}
// FastGen generates a new *Error with the same class and code, and a new formatted message.
// This will not call runtime.Caller to get file and line.
func (e *Error) FastGen(format string, args ...interface{}) error {
// TODO: RedactErrorArg
err := *e
err.message = format
err.args = args
return SuspendStack(&err)
}
// FastGen generates a new *Error with the same class and code, and a new arguments.
// This will not call runtime.Caller to get file and line.
func (e *Error) FastGenByArgs(args ...interface{}) error {
RedactErrorArg(args, e.redactArgsPos)
err := *e
err.args = args
return SuspendStack(&err)
}
// Equal checks if err is equal to e.
func (e *Error) Equal(err error) bool {
originErr := Cause(err)
if originErr == nil {
return false
}
if error(e) == originErr {
return true
}
inErr, ok := originErr.(*Error)
if !ok {
return false
}
idEquals := e.ID() == inErr.ID()
return idEquals
}
// NotEqual checks if err is not equal to e.
func (e *Error) NotEqual(err error) bool {
return !e.Equal(err)
}
// RedactErrorArg redacts the args by position if RedactLogEnabled is enabled.
func RedactErrorArg(args []interface{}, position []int) {
if RedactLogEnabled.Load() {
for _, pos := range position {
if len(args) > pos {
args[pos] = "?"
}
}
}
}
// ErrorEqual returns a boolean indicating whether err1 is equal to err2.
func ErrorEqual(err1, err2 error) bool {
e1 := Cause(err1)
e2 := Cause(err2)
if e1 == e2 {
return true
}
if e1 == nil || e2 == nil {
return e1 == e2
}
te1, ok1 := e1.(*Error)
te2, ok2 := e2.(*Error)
if ok1 && ok2 {
return te1.Equal(te2)
}
return e1.Error() == e2.Error()
}
// ErrorNotEqual returns a boolean indicating whether err1 isn't equal to err2.
func ErrorNotEqual(err1, err2 error) bool {
return !ErrorEqual(err1, err2)
}
type jsonError struct {
// Deprecated field, please use `RFCCode` instead.
Class int `json:"class"`
Code int `json:"code"`
Msg string `json:"message"`
RFCCode string `json:"rfccode"`
}
func (e *Error) Wrap(err error) *Error {
if err != nil {
newErr := *e
newErr.cause = err
return &newErr
}
return nil
}
func (e *Error) Cause() error {
root := Unwrap(e.cause)
if root == nil {
return e.cause
}
return root
}
func (e *Error) FastGenWithCause(args ...interface{}) error {
err := *e
if e.cause != nil {
err.message = e.cause.Error()
}
err.args = args
return SuspendStack(&err)
}
func (e *Error) GenWithStackByCause(args ...interface{}) error {
err := *e
if e.cause != nil {
err.message = e.cause.Error()
}
err.args = args
err.fillLineAndFile(1)
return AddStack(&err)
}
type NormalizeOption func(*Error)
func RedactArgs(pos []int) NormalizeOption {
return func(e *Error) {
e.redactArgsPos = pos
}
}
// RFCCodeText returns a NormalizeOption to set RFC error code.
func RFCCodeText(codeText string) NormalizeOption {
return func(e *Error) {
e.codeText = ErrCodeText(codeText)
}
}
// MySQLErrorCode returns a NormalizeOption to set error code.
func MySQLErrorCode(code int) NormalizeOption {
return func(e *Error) {
e.code = ErrCode(code)
}
}
// Normalize creates a new Error object.
func Normalize(message string, opts ...NormalizeOption) *Error {
e := &Error{
message: message,
}
for _, opt := range opts {
opt(e)
}
return e
}

View File

@ -1,33 +0,0 @@
package mysql
import (
"fmt"
)
// For binlog filename + position based replication
type Position struct {
Name string
Pos uint32
}
func (p Position) Compare(o Position) int {
// First compare binlog name
if p.Name > o.Name {
return 1
} else if p.Name < o.Name {
return -1
} else {
// Same binlog file, compare position
if p.Pos > o.Pos {
return 1
} else if p.Pos < o.Pos {
return -1
} else {
return 0
}
}
}
func (p Position) String() string {
return fmt.Sprintf("(%s, %d)", p.Name, p.Pos)
}

19
vendor/go.uber.org/atomic/.codecov.yml generated vendored Normal file
View File

@ -0,0 +1,19 @@
coverage:
range: 80..100
round: down
precision: 2
status:
project: # measuring the overall project coverage
default: # context, you can create multiple ones with custom titles
enabled: yes # must be yes|true to enable this status
target: 100 # specify the target coverage for each commit status
# option: "auto" (must increase from parent commit or pull request base)
# option: "X%" a static target percentage to hit
if_not_found: success # if parent is not found report status as success, error, or failure
if_ci_failed: error # if ci fails report status as success, error, or failure
# Also update COVER_IGNORE_PKGS in the Makefile.
ignore:
- /internal/gen-atomicint/
- /internal/gen-valuewrapper/

12
vendor/go.uber.org/atomic/.gitignore generated vendored Normal file
View File

@ -0,0 +1,12 @@
/bin
.DS_Store
/vendor
cover.html
cover.out
lint.log
# Binaries
*.test
# Profiling output
*.prof

27
vendor/go.uber.org/atomic/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,27 @@
sudo: false
language: go
go_import_path: go.uber.org/atomic
env:
global:
- GO111MODULE=on
matrix:
include:
- go: oldstable
- go: stable
env: LINT=1
cache:
directories:
- vendor
before_install:
- go version
script:
- test -z "$LINT" || make lint
- make cover
after_success:
- bash <(curl -s https://codecov.io/bash)

76
vendor/go.uber.org/atomic/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,76 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.7.0] - 2020-09-14
### Added
- Support JSON serialization and deserialization of primitive atomic types.
- Support Text marshalling and unmarshalling for string atomics.
### Changed
- Disallow incorrect comparison of atomic values in a non-atomic way.
### Removed
- Remove dependency on `golang.org/x/{lint, tools}`.
## [1.6.0] - 2020-02-24
### Changed
- Drop library dependency on `golang.org/x/{lint, tools}`.
## [1.5.1] - 2019-11-19
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
causing `CAS` to fail even though the old value matches.
## [1.5.0] - 2019-10-29
### Changed
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
If you need to use the old import path, please add a `replace` directive to
your `go.mod`.
## [1.4.0] - 2019-05-01
### Added
- Add `atomic.Error` type for atomic operations on `error` values.
## [1.3.2] - 2018-05-02
### Added
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
## [1.3.1] - 2017-11-14
### Fixed
- Revert optimization for `atomic.String.Store("")` which caused data races.
## [1.3.0] - 2017-11-13
### Added
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
### Changed
- Optimize `atomic.String.Store("")` by avoiding an allocation.
## [1.2.0] - 2017-04-12
### Added
- Shadow `atomic.Value` from `sync/atomic`.
## [1.1.0] - 2017-03-10
### Added
- Add atomic `Float64` type.
### Changed
- Support new `go.uber.org/atomic` import path.
## [1.0.0] - 2016-07-18
- Initial release.
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0

19
vendor/go.uber.org/atomic/LICENSE.txt generated vendored Normal file
View File

@ -0,0 +1,19 @@
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.

78
vendor/go.uber.org/atomic/Makefile generated vendored Normal file
View File

@ -0,0 +1,78 @@
# Directory to place `go install`ed binaries into.
export GOBIN ?= $(shell pwd)/bin
GOLINT = $(GOBIN)/golint
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
STATICCHECK = $(GOBIN)/staticcheck
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
# Also update ignore section in .codecov.yml.
COVER_IGNORE_PKGS = \
go.uber.org/atomic/internal/gen-atomicint \
go.uber.org/atomic/internal/gen-atomicwrapper
.PHONY: build
build:
go build ./...
.PHONY: test
test:
go test -race ./...
.PHONY: gofmt
gofmt:
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
$(GOLINT):
cd tools && go install golang.org/x/lint/golint
$(STATICCHECK):
cd tools && go install honnef.co/go/tools/cmd/staticcheck
$(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
go build -o $@ ./internal/gen-atomicwrapper
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
go build -o $@ ./internal/gen-atomicint
.PHONY: golint
golint: $(GOLINT)
$(GOLINT) ./...
.PHONY: staticcheck
staticcheck: $(STATICCHECK)
$(STATICCHECK) ./...
.PHONY: lint
lint: gofmt golint staticcheck generatenodirty
# comma separated list of packages to consider for code coverage.
COVER_PKG = $(shell \
go list -find ./... | \
grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
paste -sd, -)
.PHONY: cover
cover:
go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
go tool cover -html=cover.out -o cover.html
.PHONY: generate
generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
go generate ./...
.PHONY: generatenodirty
generatenodirty:
@[ -z "$$(git status --porcelain)" ] || ( \
echo "Working tree is dirty. Commit your changes first."; \
exit 1 )
@make generate
@status=$$(git status --porcelain); \
[ -z "$$status" ] || ( \
echo "Working tree is dirty after `make generate`:"; \
echo "$$status"; \
echo "Please ensure that the generated code is up-to-date." )

63
vendor/go.uber.org/atomic/README.md generated vendored Normal file
View File

@ -0,0 +1,63 @@
# 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
```shell
$ go get -u go.uber.org/atomic@v1
```
### Legacy Import Path
As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
of using this package. If you are using Go modules, this package will fail to
compile with the legacy import path path `github.com/uber-go/atomic`.
We recommend migrating your code to the new import path but if you're unable
to do so, or if your dependencies are still using the old import path, you
will have to add a `replace` directive to your `go.mod` file downgrading the
legacy import path to an older version.
```
replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
```
You can do so automatically by running the following command.
```shell
$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
```
## 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.com/uber-go/atomic.svg?branch=master
[ci]: https://travis-ci.com/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

81
vendor/go.uber.org/atomic/bool.go generated vendored Normal file
View File

@ -0,0 +1,81 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 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 atomic
import (
"encoding/json"
)
// Bool is an atomic type-safe wrapper for bool values.
type Bool struct {
_ nocmp // disallow non-atomic comparison
v Uint32
}
var _zeroBool bool
// NewBool creates a new Bool.
func NewBool(v bool) *Bool {
x := &Bool{}
if v != _zeroBool {
x.Store(v)
}
return x
}
// Load atomically loads the wrapped bool.
func (x *Bool) Load() bool {
return truthy(x.v.Load())
}
// Store atomically stores the passed bool.
func (x *Bool) Store(v bool) {
x.v.Store(boolToInt(v))
}
// CAS is an atomic compare-and-swap for bool values.
func (x *Bool) CAS(o, n bool) bool {
return x.v.CAS(boolToInt(o), boolToInt(n))
}
// Swap atomically stores the given bool and returns the old
// value.
func (x *Bool) Swap(o bool) bool {
return truthy(x.v.Swap(boolToInt(o)))
}
// MarshalJSON encodes the wrapped bool into JSON.
func (x *Bool) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}
// UnmarshalJSON decodes a bool from JSON.
func (x *Bool) UnmarshalJSON(b []byte) error {
var v bool
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}

53
vendor/go.uber.org/atomic/bool_ext.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright (c) 2020 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 atomic
import (
"strconv"
)
//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
func truthy(n uint32) bool {
return n == 1
}
func boolToInt(b bool) uint32 {
if b {
return 1
}
return 0
}
// Toggle atomically negates the Boolean and returns the previous value.
func (b *Bool) Toggle() bool {
for {
old := b.Load()
if b.CAS(old, !old) {
return old
}
}
}
// String encodes the wrapped value as a string.
func (b *Bool) String() string {
return strconv.FormatBool(b.Load())
}

23
vendor/go.uber.org/atomic/doc.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
// Copyright (c) 2020 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 atomic provides simple wrappers around numerics to enforce atomic
// access.
package atomic

82
vendor/go.uber.org/atomic/duration.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 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 atomic
import (
"encoding/json"
"time"
)
// Duration is an atomic type-safe wrapper for time.Duration values.
type Duration struct {
_ nocmp // disallow non-atomic comparison
v Int64
}
var _zeroDuration time.Duration
// NewDuration creates a new Duration.
func NewDuration(v time.Duration) *Duration {
x := &Duration{}
if v != _zeroDuration {
x.Store(v)
}
return x
}
// Load atomically loads the wrapped time.Duration.
func (x *Duration) Load() time.Duration {
return time.Duration(x.v.Load())
}
// Store atomically stores the passed time.Duration.
func (x *Duration) Store(v time.Duration) {
x.v.Store(int64(v))
}
// CAS is an atomic compare-and-swap for time.Duration values.
func (x *Duration) CAS(o, n time.Duration) bool {
return x.v.CAS(int64(o), int64(n))
}
// Swap atomically stores the given time.Duration and returns the old
// value.
func (x *Duration) Swap(o time.Duration) time.Duration {
return time.Duration(x.v.Swap(int64(o)))
}
// MarshalJSON encodes the wrapped time.Duration into JSON.
func (x *Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}
// UnmarshalJSON decodes a time.Duration from JSON.
func (x *Duration) UnmarshalJSON(b []byte) error {
var v time.Duration
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}

40
vendor/go.uber.org/atomic/duration_ext.go generated vendored Normal file
View File

@ -0,0 +1,40 @@
// Copyright (c) 2020 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 atomic
import "time"
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
// Add atomically adds to the wrapped time.Duration and returns the new value.
func (d *Duration) Add(n time.Duration) time.Duration {
return time.Duration(d.v.Add(int64(n)))
}
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
func (d *Duration) Sub(n time.Duration) time.Duration {
return time.Duration(d.v.Sub(int64(n)))
}
// String encodes the wrapped value as a string.
func (d *Duration) String() string {
return d.Load().String()
}

51
vendor/go.uber.org/atomic/error.go generated vendored Normal file
View File

@ -0,0 +1,51 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 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 atomic
// Error is an atomic type-safe wrapper for error values.
type Error struct {
_ nocmp // disallow non-atomic comparison
v Value
}
var _zeroError error
// NewError creates a new Error.
func NewError(v error) *Error {
x := &Error{}
if v != _zeroError {
x.Store(v)
}
return x
}
// Load atomically loads the wrapped error.
func (x *Error) Load() error {
return unpackError(x.v.Load())
}
// Store atomically stores the passed error.
func (x *Error) Store(v error) {
x.v.Store(packError(v))
}

39
vendor/go.uber.org/atomic/error_ext.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
// Copyright (c) 2020 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 atomic
// atomic.Value panics on nil inputs, or if the underlying type changes.
// Stabilize by always storing a custom struct that we control.
//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -file=error.go
type packedError struct{ Value error }
func packError(v error) interface{} {
return packedError{v}
}
func unpackError(v interface{}) error {
if err, ok := v.(packedError); ok {
return err.Value
}
return nil
}

76
vendor/go.uber.org/atomic/float64.go generated vendored Normal file
View File

@ -0,0 +1,76 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 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 atomic
import (
"encoding/json"
"math"
)
// Float64 is an atomic type-safe wrapper for float64 values.
type Float64 struct {
_ nocmp // disallow non-atomic comparison
v Uint64
}
var _zeroFloat64 float64
// NewFloat64 creates a new Float64.
func NewFloat64(v float64) *Float64 {
x := &Float64{}
if v != _zeroFloat64 {
x.Store(v)
}
return x
}
// Load atomically loads the wrapped float64.
func (x *Float64) Load() float64 {
return math.Float64frombits(x.v.Load())
}
// Store atomically stores the passed float64.
func (x *Float64) Store(v float64) {
x.v.Store(math.Float64bits(v))
}
// CAS is an atomic compare-and-swap for float64 values.
func (x *Float64) CAS(o, n float64) bool {
return x.v.CAS(math.Float64bits(o), math.Float64bits(n))
}
// MarshalJSON encodes the wrapped float64 into JSON.
func (x *Float64) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}
// UnmarshalJSON decodes a float64 from JSON.
func (x *Float64) UnmarshalJSON(b []byte) error {
var v float64
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}

47
vendor/go.uber.org/atomic/float64_ext.go generated vendored Normal file
View File

@ -0,0 +1,47 @@
// Copyright (c) 2020 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 atomic
import "strconv"
//go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -cas -json -imports math -file=float64.go
// Add atomically adds to the wrapped float64 and returns the new value.
func (f *Float64) Add(s float64) float64 {
for {
old := f.Load()
new := old + s
if f.CAS(old, new) {
return new
}
}
}
// Sub atomically subtracts from the wrapped float64 and returns the new value.
func (f *Float64) Sub(s float64) float64 {
return f.Add(-s)
}
// String encodes the wrapped value as a string.
func (f *Float64) String() string {
// 'g' is the behavior for floats with %v.
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
}

26
vendor/go.uber.org/atomic/gen.go generated vendored Normal file
View File

@ -0,0 +1,26 @@
// Copyright (c) 2020 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 atomic
//go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go

8
vendor/go.uber.org/atomic/go.mod generated vendored Normal file
View File

@ -0,0 +1,8 @@
module go.uber.org/atomic
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/stretchr/testify v1.3.0
)
go 1.13

9
vendor/go.uber.org/atomic/go.sum generated vendored Normal file
View File

@ -0,0 +1,9 @@
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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=

102
vendor/go.uber.org/atomic/int32.go generated vendored Normal file
View File

@ -0,0 +1,102 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020 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 atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Int32 is an atomic wrapper around int32.
type Int32 struct {
_ nocmp // disallow non-atomic comparison
v int32
}
// NewInt32 creates a new Int32.
func NewInt32(i int32) *Int32 {
return &Int32{v: i}
}
// Load atomically loads the wrapped value.
func (i *Int32) Load() int32 {
return atomic.LoadInt32(&i.v)
}
// Add atomically adds to the wrapped int32 and returns the new value.
func (i *Int32) Add(n int32) int32 {
return atomic.AddInt32(&i.v, n)
}
// Sub atomically subtracts from the wrapped int32 and returns the new value.
func (i *Int32) Sub(n int32) int32 {
return atomic.AddInt32(&i.v, -n)
}
// Inc atomically increments the wrapped int32 and returns the new value.
func (i *Int32) Inc() int32 {
return i.Add(1)
}
// Dec atomically decrements the wrapped int32 and returns the new value.
func (i *Int32) Dec() int32 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
func (i *Int32) CAS(old, new int32) bool {
return atomic.CompareAndSwapInt32(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Int32) Store(n int32) {
atomic.StoreInt32(&i.v, n)
}
// Swap atomically swaps the wrapped int32 and returns the old value.
func (i *Int32) Swap(n int32) int32 {
return atomic.SwapInt32(&i.v, n)
}
// MarshalJSON encodes the wrapped int32 into JSON.
func (i *Int32) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped int32.
func (i *Int32) UnmarshalJSON(b []byte) error {
var v int32
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Int32) String() string {
v := i.Load()
return strconv.FormatInt(int64(v), 10)
}

102
vendor/go.uber.org/atomic/int64.go generated vendored Normal file
View File

@ -0,0 +1,102 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020 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 atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Int64 is an atomic wrapper around int64.
type Int64 struct {
_ nocmp // disallow non-atomic comparison
v int64
}
// NewInt64 creates a new Int64.
func NewInt64(i int64) *Int64 {
return &Int64{v: i}
}
// Load atomically loads the wrapped value.
func (i *Int64) Load() int64 {
return atomic.LoadInt64(&i.v)
}
// Add atomically adds to the wrapped int64 and returns the new value.
func (i *Int64) Add(n int64) int64 {
return atomic.AddInt64(&i.v, n)
}
// Sub atomically subtracts from the wrapped int64 and returns the new value.
func (i *Int64) Sub(n int64) int64 {
return atomic.AddInt64(&i.v, -n)
}
// Inc atomically increments the wrapped int64 and returns the new value.
func (i *Int64) Inc() int64 {
return i.Add(1)
}
// Dec atomically decrements the wrapped int64 and returns the new value.
func (i *Int64) Dec() int64 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
func (i *Int64) CAS(old, new int64) bool {
return atomic.CompareAndSwapInt64(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Int64) Store(n int64) {
atomic.StoreInt64(&i.v, n)
}
// Swap atomically swaps the wrapped int64 and returns the old value.
func (i *Int64) Swap(n int64) int64 {
return atomic.SwapInt64(&i.v, n)
}
// MarshalJSON encodes the wrapped int64 into JSON.
func (i *Int64) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped int64.
func (i *Int64) UnmarshalJSON(b []byte) error {
var v int64
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Int64) String() string {
v := i.Load()
return strconv.FormatInt(int64(v), 10)
}

35
vendor/go.uber.org/atomic/nocmp.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
// Copyright (c) 2020 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 atomic
// nocmp is an uncomparable struct. Embed this inside another struct to make
// it uncomparable.
//
// type Foo struct {
// nocmp
// // ...
// }
//
// This DOES NOT:
//
// - Disallow shallow copies of structs
// - Disallow comparison of pointers to uncomparable structs
type nocmp [0]func()

54
vendor/go.uber.org/atomic/string.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 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 atomic
// String is an atomic type-safe wrapper for string values.
type String struct {
_ nocmp // disallow non-atomic comparison
v Value
}
var _zeroString string
// NewString creates a new String.
func NewString(v string) *String {
x := &String{}
if v != _zeroString {
x.Store(v)
}
return x
}
// Load atomically loads the wrapped string.
func (x *String) Load() string {
if v := x.v.Load(); v != nil {
return v.(string)
}
return _zeroString
}
// Store atomically stores the passed string.
func (x *String) Store(v string) {
x.v.Store(v)
}

43
vendor/go.uber.org/atomic/string_ext.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
// Copyright (c) 2020 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 atomic
//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped=Value -file=string.go
// String returns the wrapped value.
func (s *String) String() string {
return s.Load()
}
// MarshalText encodes the wrapped string into a textual form.
//
// This makes it encodable as JSON, YAML, XML, and more.
func (s *String) MarshalText() ([]byte, error) {
return []byte(s.Load()), nil
}
// UnmarshalText decodes text and replaces the wrapped string with it.
//
// This makes it decodable from JSON, YAML, XML, and more.
func (s *String) UnmarshalText(b []byte) error {
s.Store(string(b))
return nil
}

102
vendor/go.uber.org/atomic/uint32.go generated vendored Normal file
View File

@ -0,0 +1,102 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020 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 atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Uint32 is an atomic wrapper around uint32.
type Uint32 struct {
_ nocmp // disallow non-atomic comparison
v uint32
}
// NewUint32 creates a new Uint32.
func NewUint32(i uint32) *Uint32 {
return &Uint32{v: i}
}
// Load atomically loads the wrapped value.
func (i *Uint32) Load() uint32 {
return atomic.LoadUint32(&i.v)
}
// Add atomically adds to the wrapped uint32 and returns the new value.
func (i *Uint32) Add(n uint32) uint32 {
return atomic.AddUint32(&i.v, n)
}
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
func (i *Uint32) Sub(n uint32) uint32 {
return atomic.AddUint32(&i.v, ^(n - 1))
}
// Inc atomically increments the wrapped uint32 and returns the new value.
func (i *Uint32) Inc() uint32 {
return i.Add(1)
}
// Dec atomically decrements the wrapped uint32 and returns the new value.
func (i *Uint32) Dec() uint32 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
func (i *Uint32) CAS(old, new uint32) bool {
return atomic.CompareAndSwapUint32(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Uint32) Store(n uint32) {
atomic.StoreUint32(&i.v, n)
}
// Swap atomically swaps the wrapped uint32 and returns the old value.
func (i *Uint32) Swap(n uint32) uint32 {
return atomic.SwapUint32(&i.v, n)
}
// MarshalJSON encodes the wrapped uint32 into JSON.
func (i *Uint32) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped uint32.
func (i *Uint32) UnmarshalJSON(b []byte) error {
var v uint32
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Uint32) String() string {
v := i.Load()
return strconv.FormatUint(uint64(v), 10)
}

102
vendor/go.uber.org/atomic/uint64.go generated vendored Normal file
View File

@ -0,0 +1,102 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020 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 atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Uint64 is an atomic wrapper around uint64.
type Uint64 struct {
_ nocmp // disallow non-atomic comparison
v uint64
}
// NewUint64 creates a new Uint64.
func NewUint64(i uint64) *Uint64 {
return &Uint64{v: i}
}
// Load atomically loads the wrapped value.
func (i *Uint64) Load() uint64 {
return atomic.LoadUint64(&i.v)
}
// Add atomically adds to the wrapped uint64 and returns the new value.
func (i *Uint64) Add(n uint64) uint64 {
return atomic.AddUint64(&i.v, n)
}
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
func (i *Uint64) Sub(n uint64) uint64 {
return atomic.AddUint64(&i.v, ^(n - 1))
}
// Inc atomically increments the wrapped uint64 and returns the new value.
func (i *Uint64) Inc() uint64 {
return i.Add(1)
}
// Dec atomically decrements the wrapped uint64 and returns the new value.
func (i *Uint64) Dec() uint64 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
func (i *Uint64) CAS(old, new uint64) bool {
return atomic.CompareAndSwapUint64(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Uint64) Store(n uint64) {
atomic.StoreUint64(&i.v, n)
}
// Swap atomically swaps the wrapped uint64 and returns the old value.
func (i *Uint64) Swap(n uint64) uint64 {
return atomic.SwapUint64(&i.v, n)
}
// MarshalJSON encodes the wrapped uint64 into JSON.
func (i *Uint64) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped uint64.
func (i *Uint64) UnmarshalJSON(b []byte) error {
var v uint64
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Uint64) String() string {
v := i.Load()
return strconv.FormatUint(uint64(v), 10)
}

31
vendor/go.uber.org/atomic/value.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright (c) 2020 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 atomic
import "sync/atomic"
// Value shadows the type of the same name from sync/atomic
// https://godoc.org/sync/atomic#Value
type Value struct {
atomic.Value
_ nocmp // disallow non-atomic comparison
}

Some files were not shown because too many files have changed in this diff Show More