From 1cfdb27a3a37c407d6c8782563a87615971d38c4 Mon Sep 17 00:00:00 2001 From: Shlomi Noach Date: Thu, 16 Jun 2016 11:15:56 +0200 Subject: [PATCH] added siddontang dependencies --- .../siddontang/go-mysql/Godeps/Godeps.json | 51 + .../siddontang/go-mysql/Godeps/Readme | 5 + .../go-mysql/Godeps/_workspace/.gitignore | 2 + .../src/github.com/BurntSushi/toml/.gitignore | 5 + .../github.com/BurntSushi/toml/.travis.yml | 12 + .../src/github.com/BurntSushi/toml/COMPATIBLE | 3 + .../src/github.com/BurntSushi/toml/COPYING | 14 + .../src/github.com/BurntSushi/toml/Makefile | 19 + .../src/github.com/BurntSushi/toml/README.md | 220 +++ .../toml/cmd/toml-test-decoder/COPYING | 14 + .../toml/cmd/toml-test-decoder/README.md | 14 + .../toml/cmd/toml-test-decoder/main.go | 90 ++ .../toml/cmd/toml-test-encoder/COPYING | 14 + .../toml/cmd/toml-test-encoder/README.md | 14 + .../toml/cmd/toml-test-encoder/main.go | 131 ++ .../BurntSushi/toml/cmd/tomlv/COPYING | 14 + .../BurntSushi/toml/cmd/tomlv/README.md | 22 + .../BurntSushi/toml/cmd/tomlv/main.go | 61 + .../src/github.com/BurntSushi/toml/decode.go | 472 ++++++ .../github.com/BurntSushi/toml/decode_meta.go | 99 ++ .../github.com/BurntSushi/toml/decode_test.go | 540 +++++++ .../src/github.com/BurntSushi/toml/doc.go | 27 + .../src/github.com/BurntSushi/toml/encode.go | 515 +++++++ .../github.com/BurntSushi/toml/encode_test.go | 506 +++++++ .../BurntSushi/toml/encoding_types.go | 19 + .../BurntSushi/toml/encoding_types_1.1.go | 18 + .../src/github.com/BurntSushi/toml/lex.go | 734 +++++++++ .../src/github.com/BurntSushi/toml/parse.go | 417 ++++++ .../github.com/BurntSushi/toml/session.vim | 1 + .../github.com/BurntSushi/toml/type_check.go | 85 ++ .../github.com/BurntSushi/toml/type_fields.go | 241 +++ .../go-sql-driver/mysql/.gitattributes | 1 + .../github.com/go-sql-driver/mysql/.gitignore | 8 + .../go-sql-driver/mysql/.travis.yml | 4 + .../github.com/go-sql-driver/mysql/LICENSE | 373 +++++ .../github.com/go-sql-driver/mysql/README.md | 188 +++ .../github.com/go-sql-driver/mysql/buffer.go | 88 ++ .../go-sql-driver/mysql/connection.go | 241 +++ .../github.com/go-sql-driver/mysql/const.go | 133 ++ .../github.com/go-sql-driver/mysql/driver.go | 90 ++ .../go-sql-driver/mysql/driver_test.go | 1121 ++++++++++++++ .../github.com/go-sql-driver/mysql/errors.go | 104 ++ .../github.com/go-sql-driver/mysql/infile.go | 136 ++ .../github.com/go-sql-driver/mysql/packets.go | 1081 ++++++++++++++ .../github.com/go-sql-driver/mysql/result.go | 23 + .../github.com/go-sql-driver/mysql/rows.go | 77 + .../go-sql-driver/mysql/statement.go | 93 ++ .../go-sql-driver/mysql/transaction.go | 26 + .../github.com/go-sql-driver/mysql/utils.go | 428 ++++++ .../go-sql-driver/mysql/utils_test.go | 90 ++ .../src/github.com/jmoiron/sqlx/.gitignore | 24 + .../src/github.com/jmoiron/sqlx/LICENSE | 23 + .../src/github.com/jmoiron/sqlx/README.md | 258 ++++ .../src/github.com/jmoiron/sqlx/bind.go | 84 ++ .../src/github.com/jmoiron/sqlx/doc.go | 12 + .../src/github.com/jmoiron/sqlx/named.go | 321 ++++ .../src/github.com/jmoiron/sqlx/named_test.go | 227 +++ .../jmoiron/sqlx/reflectx/README.md | 17 + .../jmoiron/sqlx/reflectx/reflect.go | 250 ++++ .../jmoiron/sqlx/reflectx/reflect_test.go | 216 +++ .../src/github.com/jmoiron/sqlx/sqlx.go | 986 +++++++++++++ .../src/github.com/jmoiron/sqlx/sqlx_test.go | 1311 +++++++++++++++++ .../github.com/jmoiron/sqlx/types/README.md | 5 + .../github.com/jmoiron/sqlx/types/types.go | 95 ++ .../jmoiron/sqlx/types/types_test.go | 42 + .../src/github.com/juju/errors/.gitignore | 23 + .../src/github.com/juju/errors/LICENSE | 191 +++ .../src/github.com/juju/errors/Makefile | 11 + .../src/github.com/juju/errors/README.md | 536 +++++++ .../src/github.com/juju/errors/doc.go | 81 + .../src/github.com/juju/errors/error.go | 122 ++ .../src/github.com/juju/errors/error_test.go | 161 ++ .../src/github.com/juju/errors/errortypes.go | 284 ++++ .../github.com/juju/errors/errortypes_test.go | 173 +++ .../github.com/juju/errors/example_test.go | 23 + .../src/github.com/juju/errors/export_test.go | 12 + .../src/github.com/juju/errors/functions.go | 330 +++++ .../github.com/juju/errors/functions_test.go | 305 ++++ .../github.com/juju/errors/package_test.go | 95 ++ .../src/github.com/juju/errors/path.go | 35 + .../src/github.com/juju/errors/path_test.go | 29 + .../src/github.com/satori/go.uuid/.travis.yml | 8 + .../src/github.com/satori/go.uuid/LICENSE | 20 + .../src/github.com/satori/go.uuid/README.md | 66 + .../satori/go.uuid/benchmarks_test.go | 114 ++ .../src/github.com/satori/go.uuid/uuid.go | 361 +++++ .../github.com/satori/go.uuid/uuid_test.go | 399 +++++ .../src/github.com/siddontang/go/hack/hack.go | 27 + .../siddontang/go/hack/hack_test.go | 36 + .../siddontang/go/ioutil2/ioutil.go | 39 + .../siddontang/go/ioutil2/sectionwriter.go | 69 + .../go/ioutil2/sectionwriter_test.go | 56 + .../src/github.com/siddontang/go/log/doc.go | 21 + .../siddontang/go/log/filehandler.go | 200 +++ .../github.com/siddontang/go/log/handler.go | 49 + .../src/github.com/siddontang/go/log/log.go | 341 +++++ .../github.com/siddontang/go/log/log_test.go | 63 + .../siddontang/go/log/sockethandler.go | 65 + .../github.com/siddontang/go/sync2/atomic.go | 146 ++ .../siddontang/go/sync2/atomic_test.go | 51 + .../siddontang/go/sync2/semaphore.go | 65 + .../siddontang/go/sync2/semaphore_test.go | 41 + .../src/gopkg.in/check.v1/.gitignore | 4 + .../_workspace/src/gopkg.in/check.v1/LICENSE | 25 + .../src/gopkg.in/check.v1/README.md | 20 + .../_workspace/src/gopkg.in/check.v1/TODO | 2 + .../src/gopkg.in/check.v1/benchmark.go | 163 ++ .../src/gopkg.in/check.v1/benchmark_test.go | 91 ++ .../src/gopkg.in/check.v1/bootstrap_test.go | 82 ++ .../_workspace/src/gopkg.in/check.v1/check.go | 945 ++++++++++++ .../src/gopkg.in/check.v1/check_test.go | 207 +++ .../src/gopkg.in/check.v1/checkers.go | 458 ++++++ .../src/gopkg.in/check.v1/checkers_test.go | 272 ++++ .../src/gopkg.in/check.v1/export_test.go | 9 + .../src/gopkg.in/check.v1/fixture_test.go | 484 ++++++ .../src/gopkg.in/check.v1/foundation_test.go | 335 +++++ .../src/gopkg.in/check.v1/helpers.go | 231 +++ .../src/gopkg.in/check.v1/helpers_test.go | 519 +++++++ .../src/gopkg.in/check.v1/printer.go | 168 +++ .../src/gopkg.in/check.v1/printer_test.go | 104 ++ .../_workspace/src/gopkg.in/check.v1/run.go | 175 +++ .../src/gopkg.in/check.v1/run_test.go | 419 ++++++ .../siddontang/go-mysql/client/auth.go | 139 ++ .../siddontang/go-mysql/client/client_test.go | 331 +++++ .../siddontang/go-mysql/client/conn.go | 246 ++++ .../siddontang/go-mysql/client/req.go | 72 + .../siddontang/go-mysql/client/resp.go | 219 +++ .../siddontang/go-mysql/client/stmt.go | 216 +++ .../siddontang/go-mysql/mysql/const.go | 163 ++ .../siddontang/go-mysql/mysql/errcode.go | 870 +++++++++++ .../siddontang/go-mysql/mysql/errname.go | 868 +++++++++++ .../siddontang/go-mysql/mysql/error.go | 59 + .../siddontang/go-mysql/mysql/field.go | 157 ++ .../siddontang/go-mysql/mysql/gtid.go | 25 + .../siddontang/go-mysql/mysql/mariadb_gtid.go | 80 + .../siddontang/go-mysql/mysql/mysql_gtid.go | 409 +++++ .../siddontang/go-mysql/mysql/mysql_test.go | 153 ++ .../siddontang/go-mysql/mysql/parse_binary.go | 53 + .../siddontang/go-mysql/mysql/position.go | 33 + .../siddontang/go-mysql/mysql/result.go | 14 + .../siddontang/go-mysql/mysql/resultset.go | 400 +++++ .../go-mysql/mysql/resultset_helper.go | 205 +++ .../siddontang/go-mysql/mysql/state.go | 233 +++ .../siddontang/go-mysql/mysql/util.go | 338 +++++ .../siddontang/go-mysql/packet/conn.go | 163 ++ .../siddontang/go-mysql/replication/backup.go | 90 ++ .../go-mysql/replication/binlogstreamer.go | 72 + .../go-mysql/replication/binlogsyncer.go | 559 +++++++ .../siddontang/go-mysql/replication/bitmap.go | 38 + .../siddontang/go-mysql/replication/const.go | 185 +++ .../siddontang/go-mysql/replication/doc.go | 8 + .../siddontang/go-mysql/replication/event.go | 458 ++++++ .../go-mysql/replication/generic_event.go | 171 +++ .../siddontang/go-mysql/replication/parser.go | 266 ++++ .../go-mysql/replication/parser_test.go | 42 + .../go-mysql/replication/replication_test.go | 283 ++++ .../go-mysql/replication/row_event.go | 778 ++++++++++ .../go-mysql/replication/row_event_test.go | 328 +++++ vendor/github.com/siddontang/go/hack/hack.go | 27 + .../siddontang/go/hack/hack_test.go | 36 + 160 files changed, 30703 insertions(+) create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/Godeps.json create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/Readme create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/.gitignore create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.gitignore create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.travis.yml create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COMPATIBLE create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COPYING create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/Makefile create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types_1.1.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/lex.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/parse.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/session.vim create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_check.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_fields.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitattributes create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/.gitignore create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/LICENSE create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/doc.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/.gitignore create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/LICENSE create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/Makefile create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/doc.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/example_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/export_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/package_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/.travis.yml create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/LICENSE create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/benchmarks_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/ioutil.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/doc.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/filehandler.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/handler.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/sockethandler.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/.gitignore create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/LICENSE create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/README.md create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/TODO create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/bootstrap_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/export_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/fixture_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/foundation_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run.go create mode 100644 vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/client/auth.go create mode 100644 vendor/github.com/siddontang/go-mysql/client/client_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/client/conn.go create mode 100644 vendor/github.com/siddontang/go-mysql/client/req.go create mode 100644 vendor/github.com/siddontang/go-mysql/client/resp.go create mode 100644 vendor/github.com/siddontang/go-mysql/client/stmt.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/const.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/errcode.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/errname.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/error.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/field.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/gtid.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/mariadb_gtid.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/mysql_gtid.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/mysql_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/parse_binary.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/position.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/result.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/resultset.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/resultset_helper.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/state.go create mode 100644 vendor/github.com/siddontang/go-mysql/mysql/util.go create mode 100644 vendor/github.com/siddontang/go-mysql/packet/conn.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/backup.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/binlogstreamer.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/binlogsyncer.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/bitmap.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/const.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/doc.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/event.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/generic_event.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/parser.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/parser_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/replication_test.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/row_event.go create mode 100644 vendor/github.com/siddontang/go-mysql/replication/row_event_test.go create mode 100644 vendor/github.com/siddontang/go/hack/hack.go create mode 100644 vendor/github.com/siddontang/go/hack/hack_test.go diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/Godeps.json b/vendor/github.com/siddontang/go-mysql/Godeps/Godeps.json new file mode 100644 index 0000000..1236535 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/Godeps.json @@ -0,0 +1,51 @@ +{ + "ImportPath": "github.com/siddontang/go-mysql", + "GoVersion": "go1.5.2", + "Packages": [ + "./..." + ], + "Deps": [ + { + "ImportPath": "github.com/BurntSushi/toml", + "Rev": "2ceedfee35ad3848e49308ab0c9a4f640cfb5fb2" + }, + { + "ImportPath": "github.com/go-sql-driver/mysql", + "Comment": "v1.0.1", + "Rev": "75bb76ec381d160c6b119bd8004a12979b0f2008" + }, + { + "ImportPath": "github.com/jmoiron/sqlx", + "Comment": "sqlx-v1.0-58-gac7070c", + "Rev": "ac7070c8a115e759a26e524fd299efdac81b7e86" + }, + { + "ImportPath": "github.com/juju/errors", + "Rev": "1b5e39b83d1835fa480e0c2ddefb040ee82d58b3" + }, + { + "ImportPath": "github.com/satori/go.uuid", + "Rev": "a07c134f8289edd9ac6ee6f0aa5f3d345f4aaf27" + }, + { + "ImportPath": "github.com/siddontang/go/hack", + "Rev": "530a23162549a31baa14dfa3b647a9eccee8878f" + }, + { + "ImportPath": "github.com/siddontang/go/ioutil2", + "Rev": "530a23162549a31baa14dfa3b647a9eccee8878f" + }, + { + "ImportPath": "github.com/siddontang/go/log", + "Rev": "530a23162549a31baa14dfa3b647a9eccee8878f" + }, + { + "ImportPath": "github.com/siddontang/go/sync2", + "Rev": "530a23162549a31baa14dfa3b647a9eccee8878f" + }, + { + "ImportPath": "gopkg.in/check.v1", + "Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673" + } + ] +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/Readme b/vendor/github.com/siddontang/go-mysql/Godeps/Readme new file mode 100644 index 0000000..4cdaa53 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/.gitignore b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/.gitignore new file mode 100644 index 0000000..f037d68 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/.gitignore @@ -0,0 +1,2 @@ +/pkg +/bin diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.gitignore b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.gitignore new file mode 100644 index 0000000..0cd3800 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.gitignore @@ -0,0 +1,5 @@ +TAGS +tags +.*.swp +tomlcheck/tomlcheck +toml.test diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.travis.yml b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.travis.yml new file mode 100644 index 0000000..43caf6d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: + - 1.1 + - 1.2 + - tip +install: + - go install ./... + - go get github.com/BurntSushi/toml-test +script: + - export PATH="$PATH:$HOME/gopath/bin" + - make test + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COMPATIBLE b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COMPATIBLE new file mode 100644 index 0000000..21e0938 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COMPATIBLE @@ -0,0 +1,3 @@ +Compatible with TOML version +[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COPYING b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COPYING new file mode 100644 index 0000000..5a8e332 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/Makefile b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/Makefile new file mode 100644 index 0000000..3600848 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/Makefile @@ -0,0 +1,19 @@ +install: + go install ./... + +test: install + go test -v + toml-test toml-test-decoder + toml-test -encoder toml-test-encoder + +fmt: + gofmt -w *.go */*.go + colcheck *.go */*.go + +tags: + find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS + +push: + git push origin master + git push github master + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md new file mode 100644 index 0000000..380bb36 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md @@ -0,0 +1,220 @@ +## TOML parser and encoder for Go with reflection + +TOML stands for Tom's Obvious, Minimal Language. This Go package provides a +reflection interface similar to Go's standard library `json` and `xml` +packages. This package also supports the `encoding.TextUnmarshaler` and +`encoding.TextMarshaler` interfaces so that you can define custom data +representations. (There is an example of this below.) + +Spec: https://github.com/mojombo/toml + +Compatible with TOML version +[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + +Documentation: http://godoc.org/github.com/BurntSushi/toml + +Installation: + +```bash +go get github.com/BurntSushi/toml +``` + +Try the toml validator: + +```bash +go get github.com/BurntSushi/toml/cmd/tomlv +tomlv some-toml-file.toml +``` + +[![Build status](https://api.travis-ci.org/BurntSushi/toml.png)](https://travis-ci.org/BurntSushi/toml) + + +### Testing + +This package passes all tests in +[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder +and the encoder. + +### Examples + +This package works similarly to how the Go standard library handles `XML` +and `JSON`. Namely, data is loaded into Go values via reflection. + +For the simplest example, consider some TOML file as just a list of keys +and values: + +```toml +Age = 25 +Cats = [ "Cauchy", "Plato" ] +Pi = 3.14 +Perfection = [ 6, 28, 496, 8128 ] +DOB = 1987-07-05T05:45:00Z +``` + +Which could be defined in Go as: + +```go +type Config struct { + Age int + Cats []string + Pi float64 + Perfection []int + DOB time.Time // requires `import time` +} +``` + +And then decoded with: + +```go +var conf Config +if _, err := toml.Decode(tomlData, &conf); err != nil { + // handle error +} +``` + +You can also use struct tags if your struct field name doesn't map to a TOML +key value directly: + +```toml +some_key_NAME = "wat" +``` + +```go +type TOML struct { + ObscureKey string `toml:"some_key_NAME"` +} +``` + +### Using the `encoding.TextUnmarshaler` interface + +Here's an example that automatically parses duration strings into +`time.Duration` values: + +```toml +[[song]] +name = "Thunder Road" +duration = "4m49s" + +[[song]] +name = "Stairway to Heaven" +duration = "8m03s" +``` + +Which can be decoded with: + +```go +type song struct { + Name string + Duration duration +} +type songs struct { + Song []song +} +var favorites songs +if _, err := Decode(blob, &favorites); err != nil { + log.Fatal(err) +} + +for _, s := range favorites.Song { + fmt.Printf("%s (%s)\n", s.Name, s.Duration) +} +``` + +And you'll also need a `duration` type that satisfies the +`encoding.TextUnmarshaler` interface: + +```go +type duration struct { + time.Duration +} + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} +``` + +### More complex usage + +Here's an example of how to load the example from the official spec page: + +```toml +# This is a TOML document. Boom. + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +organization = "GitHub" +bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." +dob = 1979-05-27T07:32:00Z # First class dates? Why not? + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + + # You can indent as you please. Tabs or spaces. TOML don't care. + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] +``` + +And the corresponding Go types are: + +```go +type tomlConfig struct { + Title string + Owner ownerInfo + DB database `toml:"database"` + Servers map[string]server + Clients clients +} + +type ownerInfo struct { + Name string + Org string `toml:"organization"` + Bio string + DOB time.Time +} + +type database struct { + Server string + Ports []int + ConnMax int `toml:"connection_max"` + Enabled bool +} + +type server struct { + IP string + DC string +} + +type clients struct { + Data [][]interface{} + Hosts []string +} +``` + +Note that a case insensitive match will be tried if an exact match can't be +found. + +A working example of the above can be found in `_examples/example.{go,toml}`. + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING new file mode 100644 index 0000000..5a8e332 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md new file mode 100644 index 0000000..24421eb --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md @@ -0,0 +1,14 @@ +# Implements the TOML test suite interface + +This is an implementation of the interface expected by +[toml-test](https://github.com/BurntSushi/toml-test) for my +[toml parser written in Go](https://github.com/BurntSushi/toml). +In particular, it maps TOML data on `stdin` to a JSON format on `stdout`. + + +Compatible with TOML version +[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + +Compatible with `toml-test` version +[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0) + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go new file mode 100644 index 0000000..14e7557 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go @@ -0,0 +1,90 @@ +// Command toml-test-decoder satisfies the toml-test interface for testing +// TOML decoders. Namely, it accepts TOML on stdin and outputs JSON on stdout. +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "os" + "path" + "time" + + "github.com/BurntSushi/toml" +) + +func init() { + log.SetFlags(0) + + flag.Usage = usage + flag.Parse() +} + +func usage() { + log.Printf("Usage: %s < toml-file\n", path.Base(os.Args[0])) + flag.PrintDefaults() + + os.Exit(1) +} + +func main() { + if flag.NArg() != 0 { + flag.Usage() + } + + var tmp interface{} + if _, err := toml.DecodeReader(os.Stdin, &tmp); err != nil { + log.Fatalf("Error decoding TOML: %s", err) + } + + typedTmp := translate(tmp) + if err := json.NewEncoder(os.Stdout).Encode(typedTmp); err != nil { + log.Fatalf("Error encoding JSON: %s", err) + } +} + +func translate(tomlData interface{}) interface{} { + switch orig := tomlData.(type) { + case map[string]interface{}: + typed := make(map[string]interface{}, len(orig)) + for k, v := range orig { + typed[k] = translate(v) + } + return typed + case []map[string]interface{}: + typed := make([]map[string]interface{}, len(orig)) + for i, v := range orig { + typed[i] = translate(v).(map[string]interface{}) + } + return typed + case []interface{}: + typed := make([]interface{}, len(orig)) + for i, v := range orig { + typed[i] = translate(v) + } + + // We don't really need to tag arrays, but let's be future proof. + // (If TOML ever supports tuples, we'll need this.) + return tag("array", typed) + case time.Time: + return tag("datetime", orig.Format("2006-01-02T15:04:05Z")) + case bool: + return tag("bool", fmt.Sprintf("%v", orig)) + case int64: + return tag("integer", fmt.Sprintf("%d", orig)) + case float64: + return tag("float", fmt.Sprintf("%v", orig)) + case string: + return tag("string", orig) + } + + panic(fmt.Sprintf("Unknown type: %T", tomlData)) +} + +func tag(typeName string, data interface{}) map[string]interface{} { + return map[string]interface{}{ + "type": typeName, + "value": data, + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING new file mode 100644 index 0000000..5a8e332 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md new file mode 100644 index 0000000..45a603f --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md @@ -0,0 +1,14 @@ +# Implements the TOML test suite interface for TOML encoders + +This is an implementation of the interface expected by +[toml-test](https://github.com/BurntSushi/toml-test) for the +[TOML encoder](https://github.com/BurntSushi/toml). +In particular, it maps JSON data on `stdin` to a TOML format on `stdout`. + + +Compatible with TOML version +[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + +Compatible with `toml-test` version +[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0) + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go new file mode 100644 index 0000000..092cc68 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go @@ -0,0 +1,131 @@ +// Command toml-test-encoder satisfies the toml-test interface for testing +// TOML encoders. Namely, it accepts JSON on stdin and outputs TOML on stdout. +package main + +import ( + "encoding/json" + "flag" + "log" + "os" + "path" + "strconv" + "time" + + "github.com/BurntSushi/toml" +) + +func init() { + log.SetFlags(0) + + flag.Usage = usage + flag.Parse() +} + +func usage() { + log.Printf("Usage: %s < json-file\n", path.Base(os.Args[0])) + flag.PrintDefaults() + + os.Exit(1) +} + +func main() { + if flag.NArg() != 0 { + flag.Usage() + } + + var tmp interface{} + if err := json.NewDecoder(os.Stdin).Decode(&tmp); err != nil { + log.Fatalf("Error decoding JSON: %s", err) + } + + tomlData := translate(tmp) + if err := toml.NewEncoder(os.Stdout).Encode(tomlData); err != nil { + log.Fatalf("Error encoding TOML: %s", err) + } +} + +func translate(typedJson interface{}) interface{} { + switch v := typedJson.(type) { + case map[string]interface{}: + if len(v) == 2 && in("type", v) && in("value", v) { + return untag(v) + } + m := make(map[string]interface{}, len(v)) + for k, v2 := range v { + m[k] = translate(v2) + } + return m + case []interface{}: + tabArray := make([]map[string]interface{}, len(v)) + for i := range v { + if m, ok := translate(v[i]).(map[string]interface{}); ok { + tabArray[i] = m + } else { + log.Fatalf("JSON arrays may only contain objects. This " + + "corresponds to only tables being allowed in " + + "TOML table arrays.") + } + } + return tabArray + } + log.Fatalf("Unrecognized JSON format '%T'.", typedJson) + panic("unreachable") +} + +func untag(typed map[string]interface{}) interface{} { + t := typed["type"].(string) + v := typed["value"] + switch t { + case "string": + return v.(string) + case "integer": + v := v.(string) + n, err := strconv.Atoi(v) + if err != nil { + log.Fatalf("Could not parse '%s' as integer: %s", v, err) + } + return n + case "float": + v := v.(string) + f, err := strconv.ParseFloat(v, 64) + if err != nil { + log.Fatalf("Could not parse '%s' as float64: %s", v, err) + } + return f + case "datetime": + v := v.(string) + t, err := time.Parse("2006-01-02T15:04:05Z", v) + if err != nil { + log.Fatalf("Could not parse '%s' as a datetime: %s", v, err) + } + return t + case "bool": + v := v.(string) + switch v { + case "true": + return true + case "false": + return false + } + log.Fatalf("Could not parse '%s' as a boolean.", v) + case "array": + v := v.([]interface{}) + array := make([]interface{}, len(v)) + for i := range v { + if m, ok := v[i].(map[string]interface{}); ok { + array[i] = untag(m) + } else { + log.Fatalf("Arrays may only contain other arrays or "+ + "primitive values, but found a '%T'.", m) + } + } + return array + } + log.Fatalf("Unrecognized tag type '%s'.", t) + panic("unreachable") +} + +func in(key string, m map[string]interface{}) bool { + _, ok := m[key] + return ok +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING new file mode 100644 index 0000000..5a8e332 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md new file mode 100644 index 0000000..5df0dc3 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md @@ -0,0 +1,22 @@ +# TOML Validator + +If Go is installed, it's simple to try it out: + +```bash +go get github.com/BurntSushi/toml/cmd/tomlv +tomlv some-toml-file.toml +``` + +You can see the types of every key in a TOML file with: + +```bash +tomlv -types some-toml-file.toml +``` + +At the moment, only one error message is reported at a time. Error messages +include line numbers. No output means that the files given are valid TOML, or +there is a bug in `tomlv`. + +Compatible with TOML version +[v0.1.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.1.0.md) + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go new file mode 100644 index 0000000..c7d689a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go @@ -0,0 +1,61 @@ +// Command tomlv validates TOML documents and prints each key's type. +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path" + "strings" + "text/tabwriter" + + "github.com/BurntSushi/toml" +) + +var ( + flagTypes = false +) + +func init() { + log.SetFlags(0) + + flag.BoolVar(&flagTypes, "types", flagTypes, + "When set, the types of every defined key will be shown.") + + flag.Usage = usage + flag.Parse() +} + +func usage() { + log.Printf("Usage: %s toml-file [ toml-file ... ]\n", + path.Base(os.Args[0])) + flag.PrintDefaults() + + os.Exit(1) +} + +func main() { + if flag.NArg() < 1 { + flag.Usage() + } + for _, f := range flag.Args() { + var tmp interface{} + md, err := toml.DecodeFile(f, &tmp) + if err != nil { + log.Fatalf("Error in '%s': %s", f, err) + } + if flagTypes { + printTypes(md) + } + } +} + +func printTypes(md toml.MetaData) { + tabw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + for _, key := range md.Keys() { + fmt.Fprintf(tabw, "%s%s\t%s\n", + strings.Repeat(" ", len(key)-1), key, md.Type(key...)) + } + tabw.Flush() +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go new file mode 100644 index 0000000..b6d75d0 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go @@ -0,0 +1,472 @@ +package toml + +import ( + "fmt" + "io" + "io/ioutil" + "math" + "reflect" + "strings" + "time" +) + +var e = fmt.Errorf + +// Primitive is a TOML value that hasn't been decoded into a Go value. +// When using the various `Decode*` functions, the type `Primitive` may +// be given to any value, and its decoding will be delayed. +// +// A `Primitive` value can be decoded using the `PrimitiveDecode` function. +// +// The underlying representation of a `Primitive` value is subject to change. +// Do not rely on it. +// +// N.B. Primitive values are still parsed, so using them will only avoid +// the overhead of reflection. They can be useful when you don't know the +// exact type of TOML data until run time. +type Primitive struct { + undecoded interface{} + context Key +} + +// DEPRECATED! +// +// Use MetaData.PrimitiveDecode instead. +func PrimitiveDecode(primValue Primitive, v interface{}) error { + md := MetaData{decoded: make(map[string]bool)} + return md.unify(primValue.undecoded, rvalue(v)) +} + +// PrimitiveDecode is just like the other `Decode*` functions, except it +// decodes a TOML value that has already been parsed. Valid primitive values +// can *only* be obtained from values filled by the decoder functions, +// including this method. (i.e., `v` may contain more `Primitive` +// values.) +// +// Meta data for primitive values is included in the meta data returned by +// the `Decode*` functions with one exception: keys returned by the Undecoded +// method will only reflect keys that were decoded. Namely, any keys hidden +// behind a Primitive will be considered undecoded. Executing this method will +// update the undecoded keys in the meta data. (See the example.) +func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { + md.context = primValue.context + defer func() { md.context = nil }() + return md.unify(primValue.undecoded, rvalue(v)) +} + +// Decode will decode the contents of `data` in TOML format into a pointer +// `v`. +// +// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be +// used interchangeably.) +// +// TOML arrays of tables correspond to either a slice of structs or a slice +// of maps. +// +// TOML datetimes correspond to Go `time.Time` values. +// +// All other TOML types (float, string, int, bool and array) correspond +// to the obvious Go types. +// +// An exception to the above rules is if a type implements the +// encoding.TextUnmarshaler interface. In this case, any primitive TOML value +// (floats, strings, integers, booleans and datetimes) will be converted to +// a byte string and given to the value's UnmarshalText method. See the +// Unmarshaler example for a demonstration with time duration strings. +// +// Key mapping +// +// TOML keys can map to either keys in a Go map or field names in a Go +// struct. The special `toml` struct tag may be used to map TOML keys to +// struct fields that don't match the key name exactly. (See the example.) +// A case insensitive match to struct names will be tried if an exact match +// can't be found. +// +// The mapping between TOML values and Go values is loose. That is, there +// may exist TOML values that cannot be placed into your representation, and +// there may be parts of your representation that do not correspond to +// TOML values. This loose mapping can be made stricter by using the IsDefined +// and/or Undecoded methods on the MetaData returned. +// +// This decoder will not handle cyclic types. If a cyclic type is passed, +// `Decode` will not terminate. +func Decode(data string, v interface{}) (MetaData, error) { + p, err := parse(data) + if err != nil { + return MetaData{}, err + } + md := MetaData{ + p.mapping, p.types, p.ordered, + make(map[string]bool, len(p.ordered)), nil, + } + return md, md.unify(p.mapping, rvalue(v)) +} + +// DecodeFile is just like Decode, except it will automatically read the +// contents of the file at `fpath` and decode it for you. +func DecodeFile(fpath string, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadFile(fpath) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// DecodeReader is just like Decode, except it will consume all bytes +// from the reader and decode it for you. +func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadAll(r) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// unify performs a sort of type unification based on the structure of `rv`, +// which is the client representation. +// +// Any type mismatch produces an error. Finding a type that we don't know +// how to handle produces an unsupported type error. +func (md *MetaData) unify(data interface{}, rv reflect.Value) error { + // Special case. Look for a `Primitive` value. + if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { + // Save the undecoded data and the key context into the primitive + // value. + context := make(Key, len(md.context)) + copy(context, md.context) + rv.Set(reflect.ValueOf(Primitive{ + undecoded: data, + context: context, + })) + return nil + } + + // Special case. Handle time.Time values specifically. + // TODO: Remove this code when we decide to drop support for Go 1.1. + // This isn't necessary in Go 1.2 because time.Time satisfies the encoding + // interfaces. + if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) { + return md.unifyDatetime(data, rv) + } + + // Special case. Look for a value satisfying the TextUnmarshaler interface. + if v, ok := rv.Interface().(TextUnmarshaler); ok { + return md.unifyText(data, v) + } + // BUG(burntsushi) + // The behavior here is incorrect whenever a Go type satisfies the + // encoding.TextUnmarshaler interface but also corresponds to a TOML + // hash or array. In particular, the unmarshaler should only be applied + // to primitive TOML values. But at this point, it will be applied to + // all kinds of values and produce an incorrect error whenever those values + // are hashes or arrays (including arrays of tables). + + k := rv.Kind() + + // laziness + if k >= reflect.Int && k <= reflect.Uint64 { + return md.unifyInt(data, rv) + } + switch k { + case reflect.Ptr: + elem := reflect.New(rv.Type().Elem()) + err := md.unify(data, reflect.Indirect(elem)) + if err != nil { + return err + } + rv.Set(elem) + return nil + case reflect.Struct: + return md.unifyStruct(data, rv) + case reflect.Map: + return md.unifyMap(data, rv) + case reflect.Array: + return md.unifyArray(data, rv) + case reflect.Slice: + return md.unifySlice(data, rv) + case reflect.String: + return md.unifyString(data, rv) + case reflect.Bool: + return md.unifyBool(data, rv) + case reflect.Interface: + // we only support empty interfaces. + if rv.NumMethod() > 0 { + return e("Unsupported type '%s'.", rv.Kind()) + } + return md.unifyAnything(data, rv) + case reflect.Float32: + fallthrough + case reflect.Float64: + return md.unifyFloat64(data, rv) + } + return e("Unsupported type '%s'.", rv.Kind()) +} + +func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + return mismatch(rv, "map", mapping) + } + + for key, datum := range tmap { + var f *field + fields := cachedTypeFields(rv.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break + } + if f == nil && strings.EqualFold(ff.name, key) { + f = ff + } + } + if f != nil { + subv := rv + for _, i := range f.index { + subv = indirect(subv.Field(i)) + } + if isUnifiable(subv) { + md.decoded[md.context.add(key).String()] = true + md.context = append(md.context, key) + if err := md.unify(datum, subv); err != nil { + return e("Type mismatch for '%s.%s': %s", + rv.Type().String(), f.name, err) + } + md.context = md.context[0 : len(md.context)-1] + } else if f.name != "" { + // Bad user! No soup for you! + return e("Field '%s.%s' is unexported, and therefore cannot "+ + "be loaded with reflection.", rv.Type().String(), f.name) + } + } + } + return nil +} + +func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + return badtype("map", mapping) + } + if rv.IsNil() { + rv.Set(reflect.MakeMap(rv.Type())) + } + for k, v := range tmap { + md.decoded[md.context.add(k).String()] = true + md.context = append(md.context, k) + + rvkey := indirect(reflect.New(rv.Type().Key())) + rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) + if err := md.unify(v, rvval); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + + rvkey.SetString(k) + rv.SetMapIndex(rvkey, rvval) + } + return nil +} + +func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + return badtype("slice", data) + } + sliceLen := datav.Len() + if sliceLen != rv.Len() { + return e("expected array length %d; got TOML array of length %d", + rv.Len(), sliceLen) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + return badtype("slice", data) + } + sliceLen := datav.Len() + if rv.IsNil() { + rv.Set(reflect.MakeSlice(rv.Type(), sliceLen, sliceLen)) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { + sliceLen := data.Len() + for i := 0; i < sliceLen; i++ { + v := data.Index(i).Interface() + sliceval := indirect(rv.Index(i)) + if err := md.unify(v, sliceval); err != nil { + return err + } + } + return nil +} + +func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { + if _, ok := data.(time.Time); ok { + rv.Set(reflect.ValueOf(data)) + return nil + } + return badtype("time.Time", data) +} + +func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { + if s, ok := data.(string); ok { + rv.SetString(s) + return nil + } + return badtype("string", data) +} + +func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { + if num, ok := data.(float64); ok { + switch rv.Kind() { + case reflect.Float32: + fallthrough + case reflect.Float64: + rv.SetFloat(num) + default: + panic("bug") + } + return nil + } + return badtype("float", data) +} + +func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { + if num, ok := data.(int64); ok { + if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { + switch rv.Kind() { + case reflect.Int, reflect.Int64: + // No bounds checking necessary. + case reflect.Int8: + if num < math.MinInt8 || num > math.MaxInt8 { + return e("Value '%d' is out of range for int8.", num) + } + case reflect.Int16: + if num < math.MinInt16 || num > math.MaxInt16 { + return e("Value '%d' is out of range for int16.", num) + } + case reflect.Int32: + if num < math.MinInt32 || num > math.MaxInt32 { + return e("Value '%d' is out of range for int32.", num) + } + } + rv.SetInt(num) + } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { + unum := uint64(num) + switch rv.Kind() { + case reflect.Uint, reflect.Uint64: + // No bounds checking necessary. + case reflect.Uint8: + if num < 0 || unum > math.MaxUint8 { + return e("Value '%d' is out of range for uint8.", num) + } + case reflect.Uint16: + if num < 0 || unum > math.MaxUint16 { + return e("Value '%d' is out of range for uint16.", num) + } + case reflect.Uint32: + if num < 0 || unum > math.MaxUint32 { + return e("Value '%d' is out of range for uint32.", num) + } + } + rv.SetUint(unum) + } else { + panic("unreachable") + } + return nil + } + return badtype("integer", data) +} + +func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { + if b, ok := data.(bool); ok { + rv.SetBool(b) + return nil + } + return badtype("boolean", data) +} + +func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { + rv.Set(reflect.ValueOf(data)) + return nil +} + +func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error { + var s string + switch sdata := data.(type) { + case TextMarshaler: + text, err := sdata.MarshalText() + if err != nil { + return err + } + s = string(text) + case fmt.Stringer: + s = sdata.String() + case string: + s = sdata + case bool: + s = fmt.Sprintf("%v", sdata) + case int64: + s = fmt.Sprintf("%d", sdata) + case float64: + s = fmt.Sprintf("%f", sdata) + default: + return badtype("primitive (string-like)", data) + } + if err := v.UnmarshalText([]byte(s)); err != nil { + return err + } + return nil +} + +// rvalue returns a reflect.Value of `v`. All pointers are resolved. +func rvalue(v interface{}) reflect.Value { + return indirect(reflect.ValueOf(v)) +} + +// indirect returns the value pointed to by a pointer. +// Pointers are followed until the value is not a pointer. +// New values are allocated for each nil pointer. +// +// An exception to this rule is if the value satisfies an interface of +// interest to us (like encoding.TextUnmarshaler). +func indirect(v reflect.Value) reflect.Value { + if v.Kind() != reflect.Ptr { + if v.CanAddr() { + pv := v.Addr() + if _, ok := pv.Interface().(TextUnmarshaler); ok { + return pv + } + } + return v + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + return indirect(reflect.Indirect(v)) +} + +func isUnifiable(rv reflect.Value) bool { + if rv.CanSet() { + return true + } + if _, ok := rv.Interface().(TextUnmarshaler); ok { + return true + } + return false +} + +func badtype(expected string, data interface{}) error { + return e("Expected %s but found '%T'.", expected, data) +} + +func mismatch(user reflect.Value, expected string, data interface{}) error { + return e("Type mismatch for %s. Expected %s but found '%T'.", + user.Type().String(), expected, data) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go new file mode 100644 index 0000000..c811445 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go @@ -0,0 +1,99 @@ +package toml + +import "strings" + +// MetaData allows access to meta information about TOML data that may not +// be inferrable via reflection. In particular, whether a key has been defined +// and the TOML type of a key. +type MetaData struct { + mapping map[string]interface{} + types map[string]tomlType + keys []Key + decoded map[string]bool + context Key // Used only during decoding. +} + +// IsDefined returns true if the key given exists in the TOML data. The key +// should be specified hierarchially. e.g., +// +// // access the TOML key 'a.b.c' +// IsDefined("a", "b", "c") +// +// IsDefined will return false if an empty key given. Keys are case sensitive. +func (md *MetaData) IsDefined(key ...string) bool { + if len(key) == 0 { + return false + } + + var hash map[string]interface{} + var ok bool + var hashOrVal interface{} = md.mapping + for _, k := range key { + if hash, ok = hashOrVal.(map[string]interface{}); !ok { + return false + } + if hashOrVal, ok = hash[k]; !ok { + return false + } + } + return true +} + +// Type returns a string representation of the type of the key specified. +// +// Type will return the empty string if given an empty key or a key that +// does not exist. Keys are case sensitive. +func (md *MetaData) Type(key ...string) string { + fullkey := strings.Join(key, ".") + if typ, ok := md.types[fullkey]; ok { + return typ.typeString() + } + return "" +} + +// Key is the type of any TOML key, including key groups. Use (MetaData).Keys +// to get values of this type. +type Key []string + +func (k Key) String() string { + return strings.Join(k, ".") +} + +func (k Key) add(piece string) Key { + newKey := make(Key, len(k)+1) + copy(newKey, k) + newKey[len(k)] = piece + return newKey +} + +// Keys returns a slice of every key in the TOML data, including key groups. +// Each key is itself a slice, where the first element is the top of the +// hierarchy and the last is the most specific. +// +// The list will have the same order as the keys appeared in the TOML data. +// +// All keys returned are non-empty. +func (md *MetaData) Keys() []Key { + return md.keys +} + +// Undecoded returns all keys that have not been decoded in the order in which +// they appear in the original TOML document. +// +// This includes keys that haven't been decoded because of a Primitive value. +// Once the Primitive value is decoded, the keys will be considered decoded. +// +// Also note that decoding into an empty interface will result in no decoding, +// and so no keys will be considered decoded. +// +// In this sense, the Undecoded keys correspond to keys in the TOML document +// that do not have a concrete type in your representation. +func (md *MetaData) Undecoded() []Key { + undecoded := make([]Key, 0, len(md.keys)) + for _, key := range md.keys { + if !md.decoded[key.String()] { + undecoded = append(undecoded, key) + } + } + return undecoded +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go new file mode 100644 index 0000000..b940333 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go @@ -0,0 +1,540 @@ +package toml + +import ( + "fmt" + "log" + "reflect" + "testing" + "time" +) + +func init() { + log.SetFlags(0) +} + +func TestDecodeSimple(t *testing.T) { + var testSimple = ` +age = 250 +andrew = "gallant" +kait = "brady" +now = 1987-07-05T05:45:00Z +yesOrNo = true +pi = 3.14 +colors = [ + ["red", "green", "blue"], + ["cyan", "magenta", "yellow", "black"], +] + +[My.Cats] +plato = "cat 1" +cauchy = "cat 2" +` + + type cats struct { + Plato string + Cauchy string + } + type simple struct { + Age int + Colors [][]string + Pi float64 + YesOrNo bool + Now time.Time + Andrew string + Kait string + My map[string]cats + } + + var val simple + _, err := Decode(testSimple, &val) + if err != nil { + t.Fatal(err) + } + + now, err := time.Parse("2006-01-02T15:04:05", "1987-07-05T05:45:00") + if err != nil { + panic(err) + } + var answer = simple{ + Age: 250, + Andrew: "gallant", + Kait: "brady", + Now: now, + YesOrNo: true, + Pi: 3.14, + Colors: [][]string{ + {"red", "green", "blue"}, + {"cyan", "magenta", "yellow", "black"}, + }, + My: map[string]cats{ + "Cats": cats{Plato: "cat 1", Cauchy: "cat 2"}, + }, + } + if !reflect.DeepEqual(val, answer) { + t.Fatalf("Expected\n-----\n%#v\n-----\nbut got\n-----\n%#v\n", + answer, val) + } +} + +func TestDecodeEmbedded(t *testing.T) { + type Dog struct{ Name string } + type Age int + + tests := map[string]struct { + input string + decodeInto interface{} + wantDecoded interface{} + }{ + "embedded struct": { + input: `Name = "milton"`, + decodeInto: &struct{ Dog }{}, + wantDecoded: &struct{ Dog }{Dog{"milton"}}, + }, + "embedded non-nil pointer to struct": { + input: `Name = "milton"`, + decodeInto: &struct{ *Dog }{}, + wantDecoded: &struct{ *Dog }{&Dog{"milton"}}, + }, + "embedded nil pointer to struct": { + input: ``, + decodeInto: &struct{ *Dog }{}, + wantDecoded: &struct{ *Dog }{nil}, + }, + "embedded int": { + input: `Age = -5`, + decodeInto: &struct{ Age }{}, + wantDecoded: &struct{ Age }{-5}, + }, + } + + for label, test := range tests { + _, err := Decode(test.input, test.decodeInto) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(test.wantDecoded, test.decodeInto) { + t.Errorf("%s: want decoded == %+v, got %+v", + label, test.wantDecoded, test.decodeInto) + } + } +} + +func TestTableArrays(t *testing.T) { + var tomlTableArrays = ` +[[albums]] +name = "Born to Run" + + [[albums.songs]] + name = "Jungleland" + + [[albums.songs]] + name = "Meeting Across the River" + +[[albums]] +name = "Born in the USA" + + [[albums.songs]] + name = "Glory Days" + + [[albums.songs]] + name = "Dancing in the Dark" +` + + type Song struct { + Name string + } + + type Album struct { + Name string + Songs []Song + } + + type Music struct { + Albums []Album + } + + expected := Music{[]Album{ + {"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}}, + {"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}}, + }} + var got Music + if _, err := Decode(tomlTableArrays, &got); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(expected, got) { + t.Fatalf("\n%#v\n!=\n%#v\n", expected, got) + } +} + +// Case insensitive matching tests. +// A bit more comprehensive than needed given the current implementation, +// but implementations change. +// Probably still missing demonstrations of some ugly corner cases regarding +// case insensitive matching and multiple fields. +func TestCase(t *testing.T) { + var caseToml = ` +tOpString = "string" +tOpInt = 1 +tOpFloat = 1.1 +tOpBool = true +tOpdate = 2006-01-02T15:04:05Z +tOparray = [ "array" ] +Match = "i should be in Match only" +MatcH = "i should be in MatcH only" +once = "just once" +[nEst.eD] +nEstedString = "another string" +` + + type InsensitiveEd struct { + NestedString string + } + + type InsensitiveNest struct { + Ed InsensitiveEd + } + + type Insensitive struct { + TopString string + TopInt int + TopFloat float64 + TopBool bool + TopDate time.Time + TopArray []string + Match string + MatcH string + Once string + OncE string + Nest InsensitiveNest + } + + tme, err := time.Parse(time.RFC3339, time.RFC3339[:len(time.RFC3339)-5]) + if err != nil { + panic(err) + } + expected := Insensitive{ + TopString: "string", + TopInt: 1, + TopFloat: 1.1, + TopBool: true, + TopDate: tme, + TopArray: []string{"array"}, + MatcH: "i should be in MatcH only", + Match: "i should be in Match only", + Once: "just once", + OncE: "", + Nest: InsensitiveNest{ + Ed: InsensitiveEd{NestedString: "another string"}, + }, + } + var got Insensitive + if _, err := Decode(caseToml, &got); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(expected, got) { + t.Fatalf("\n%#v\n!=\n%#v\n", expected, got) + } +} + +func TestPointers(t *testing.T) { + type Object struct { + Type string + Description string + } + + type Dict struct { + NamedObject map[string]*Object + BaseObject *Object + Strptr *string + Strptrs []*string + } + s1, s2, s3 := "blah", "abc", "def" + expected := &Dict{ + Strptr: &s1, + Strptrs: []*string{&s2, &s3}, + NamedObject: map[string]*Object{ + "foo": {"FOO", "fooooo!!!"}, + "bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"}, + }, + BaseObject: &Object{"BASE", "da base"}, + } + + ex1 := ` +Strptr = "blah" +Strptrs = ["abc", "def"] + +[NamedObject.foo] +Type = "FOO" +Description = "fooooo!!!" + +[NamedObject.bar] +Type = "BAR" +Description = "ba-ba-ba-ba-barrrr!!!" + +[BaseObject] +Type = "BASE" +Description = "da base" +` + dict := new(Dict) + _, err := Decode(ex1, dict) + if err != nil { + t.Errorf("Decode error: %v", err) + } + if !reflect.DeepEqual(expected, dict) { + t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict) + } +} + +type sphere struct { + Center [3]float64 + Radius float64 +} + +func TestDecodeSimpleArray(t *testing.T) { + var s1 sphere + if _, err := Decode(`center = [0.0, 1.5, 0.0]`, &s1); err != nil { + t.Fatal(err) + } +} + +func TestDecodeArrayWrongSize(t *testing.T) { + var s1 sphere + if _, err := Decode(`center = [0.1, 2.3]`, &s1); err == nil { + t.Fatal("Expected array type mismatch error") + } +} + +func TestDecodeLargeIntoSmallInt(t *testing.T) { + type table struct { + Value int8 + } + var tab table + if _, err := Decode(`value = 500`, &tab); err == nil { + t.Fatal("Expected integer out-of-bounds error.") + } +} + +func TestDecodeSizedInts(t *testing.T) { + type table struct { + U8 uint8 + U16 uint16 + U32 uint32 + U64 uint64 + U uint + I8 int8 + I16 int16 + I32 int32 + I64 int64 + I int + } + answer := table{1, 1, 1, 1, 1, -1, -1, -1, -1, -1} + toml := ` + u8 = 1 + u16 = 1 + u32 = 1 + u64 = 1 + u = 1 + i8 = -1 + i16 = -1 + i32 = -1 + i64 = -1 + i = -1 + ` + var tab table + if _, err := Decode(toml, &tab); err != nil { + t.Fatal(err.Error()) + } + if answer != tab { + t.Fatalf("Expected %#v but got %#v", answer, tab) + } +} + +func ExampleMetaData_PrimitiveDecode() { + var md MetaData + var err error + + var tomlBlob = ` +ranking = ["Springsteen", "J Geils"] + +[bands.Springsteen] +started = 1973 +albums = ["Greetings", "WIESS", "Born to Run", "Darkness"] + +[bands.J Geils] +started = 1970 +albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"] +` + + type band struct { + Started int + Albums []string + } + type classics struct { + Ranking []string + Bands map[string]Primitive + } + + // Do the initial decode. Reflection is delayed on Primitive values. + var music classics + if md, err = Decode(tomlBlob, &music); err != nil { + log.Fatal(err) + } + + // MetaData still includes information on Primitive values. + fmt.Printf("Is `bands.Springsteen` defined? %v\n", + md.IsDefined("bands", "Springsteen")) + + // Decode primitive data into Go values. + for _, artist := range music.Ranking { + // A band is a primitive value, so we need to decode it to get a + // real `band` value. + primValue := music.Bands[artist] + + var aBand band + if err = md.PrimitiveDecode(primValue, &aBand); err != nil { + log.Fatal(err) + } + fmt.Printf("%s started in %d.\n", artist, aBand.Started) + } + // Check to see if there were any fields left undecoded. + // Note that this won't be empty before decoding the Primitive value! + fmt.Printf("Undecoded: %q\n", md.Undecoded()) + + // Output: + // Is `bands.Springsteen` defined? true + // Springsteen started in 1973. + // J Geils started in 1970. + // Undecoded: [] +} + +func ExampleDecode() { + var tomlBlob = ` +# Some comments. +[alpha] +ip = "10.0.0.1" + + [alpha.config] + Ports = [ 8001, 8002 ] + Location = "Toronto" + Created = 1987-07-05T05:45:00Z + +[beta] +ip = "10.0.0.2" + + [beta.config] + Ports = [ 9001, 9002 ] + Location = "New Jersey" + Created = 1887-01-05T05:55:00Z +` + + type serverConfig struct { + Ports []int + Location string + Created time.Time + } + + type server struct { + IP string `toml:"ip"` + Config serverConfig `toml:"config"` + } + + type servers map[string]server + + var config servers + if _, err := Decode(tomlBlob, &config); err != nil { + log.Fatal(err) + } + + for _, name := range []string{"alpha", "beta"} { + s := config[name] + fmt.Printf("Server: %s (ip: %s) in %s created on %s\n", + name, s.IP, s.Config.Location, + s.Config.Created.Format("2006-01-02")) + fmt.Printf("Ports: %v\n", s.Config.Ports) + } + + // Output: + // Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05 + // Ports: [8001 8002] + // Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05 + // Ports: [9001 9002] +} + +type duration struct { + time.Duration +} + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} + +// Example Unmarshaler shows how to decode TOML strings into your own +// custom data type. +func Example_unmarshaler() { + blob := ` +[[song]] +name = "Thunder Road" +duration = "4m49s" + +[[song]] +name = "Stairway to Heaven" +duration = "8m03s" +` + type song struct { + Name string + Duration duration + } + type songs struct { + Song []song + } + var favorites songs + if _, err := Decode(blob, &favorites); err != nil { + log.Fatal(err) + } + + // Code to implement the TextUnmarshaler interface for `duration`: + // + // type duration struct { + // time.Duration + // } + // + // func (d *duration) UnmarshalText(text []byte) error { + // var err error + // d.Duration, err = time.ParseDuration(string(text)) + // return err + // } + + for _, s := range favorites.Song { + fmt.Printf("%s (%s)\n", s.Name, s.Duration) + } + // Output: + // Thunder Road (4m49s) + // Stairway to Heaven (8m3s) +} + +// Example StrictDecoding shows how to detect whether there are keys in the +// TOML document that weren't decoded into the value given. This is useful +// for returning an error to the user if they've included extraneous fields +// in their configuration. +func Example_strictDecoding() { + var blob = ` +key1 = "value1" +key2 = "value2" +key3 = "value3" +` + type config struct { + Key1 string + Key3 string + } + + var conf config + md, err := Decode(blob, &conf) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Undecoded keys: %q\n", md.Undecoded()) + // Output: + // Undecoded keys: ["key2"] +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go new file mode 100644 index 0000000..fe26800 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go @@ -0,0 +1,27 @@ +/* +Package toml provides facilities for decoding and encoding TOML configuration +files via reflection. There is also support for delaying decoding with +the Primitive type, and querying the set of keys in a TOML document with the +MetaData type. + +The specification implemented: https://github.com/mojombo/toml + +The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify +whether a file is a valid TOML document. It can also be used to print the +type of each key in a TOML document. + +Testing + +There are two important types of tests used for this package. The first is +contained inside '*_test.go' files and uses the standard Go unit testing +framework. These tests are primarily devoted to holistically testing the +decoder and encoder. + +The second type of testing is used to verify the implementation's adherence +to the TOML specification. These tests have been factored into their own +project: https://github.com/BurntSushi/toml-test + +The reason the tests are in a separate project is so that they can be used by +any implementation of TOML. Namely, it is language agnostic. +*/ +package toml diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go new file mode 100644 index 0000000..3618713 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go @@ -0,0 +1,515 @@ +package toml + +import ( + "bufio" + "errors" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + "time" +) + +type tomlEncodeError struct{ error } + +var ( + errArrayMixedElementTypes = errors.New( + "can't encode array with mixed element types") + errArrayNilElement = errors.New( + "can't encode array with nil element") + errNonString = errors.New( + "can't encode a map with non-string key type") + errAnonNonStruct = errors.New( + "can't encode an anonymous field that is not a struct") + errArrayNoTable = errors.New( + "TOML array element can't contain a table") + errNoKey = errors.New( + "top-level values must be a Go map or struct") + errAnything = errors.New("") // used in testing +) + +var quotedReplacer = strings.NewReplacer( + "\t", "\\t", + "\n", "\\n", + "\r", "\\r", + "\"", "\\\"", + "\\", "\\\\", +) + +// Encoder controls the encoding of Go values to a TOML document to some +// io.Writer. +// +// The indentation level can be controlled with the Indent field. +type Encoder struct { + // A single indentation level. By default it is two spaces. + Indent string + + // hasWritten is whether we have written any output to w yet. + hasWritten bool + w *bufio.Writer +} + +// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer +// given. By default, a single indentation level is 2 spaces. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: bufio.NewWriter(w), + Indent: " ", + } +} + +// Encode writes a TOML representation of the Go value to the underlying +// io.Writer. If the value given cannot be encoded to a valid TOML document, +// then an error is returned. +// +// The mapping between Go values and TOML values should be precisely the same +// as for the Decode* functions. Similarly, the TextMarshaler interface is +// supported by encoding the resulting bytes as strings. (If you want to write +// arbitrary binary data then you will need to use something like base64 since +// TOML does not have any binary types.) +// +// When encoding TOML hashes (i.e., Go maps or structs), keys without any +// sub-hashes are encoded first. +// +// If a Go map is encoded, then its keys are sorted alphabetically for +// deterministic output. More control over this behavior may be provided if +// there is demand for it. +// +// Encoding Go values without a corresponding TOML representation---like map +// types with non-string keys---will cause an error to be returned. Similarly +// for mixed arrays/slices, arrays/slices with nil elements, embedded +// non-struct types and nested slices containing maps or structs. +// (e.g., [][]map[string]string is not allowed but []map[string]string is OK +// and so is []map[string][]string.) +func (enc *Encoder) Encode(v interface{}) error { + rv := eindirect(reflect.ValueOf(v)) + if err := enc.safeEncode(Key([]string{}), rv); err != nil { + return err + } + return enc.w.Flush() +} + +func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { + defer func() { + if r := recover(); r != nil { + if terr, ok := r.(tomlEncodeError); ok { + err = terr.error + return + } + panic(r) + } + }() + enc.encode(key, rv) + return nil +} + +func (enc *Encoder) encode(key Key, rv reflect.Value) { + // Special case. Time needs to be in ISO8601 format. + // Special case. If we can marshal the type to text, then we used that. + // Basically, this prevents the encoder for handling these types as + // generic structs (or whatever the underlying type of a TextMarshaler is). + switch rv.Interface().(type) { + case time.Time, TextMarshaler: + enc.keyEqElement(key, rv) + return + } + + k := rv.Kind() + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: + enc.keyEqElement(key, rv) + case reflect.Array, reflect.Slice: + if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { + enc.eArrayOfTables(key, rv) + } else { + enc.keyEqElement(key, rv) + } + case reflect.Interface: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Map: + if rv.IsNil() { + return + } + enc.eTable(key, rv) + case reflect.Ptr: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Struct: + enc.eTable(key, rv) + default: + panic(e("Unsupported type for key '%s': %s", key, k)) + } +} + +// eElement encodes any value that can be an array element (primitives and +// arrays). +func (enc *Encoder) eElement(rv reflect.Value) { + switch v := rv.Interface().(type) { + case time.Time: + // Special case time.Time as a primitive. Has to come before + // TextMarshaler below because time.Time implements + // encoding.TextMarshaler, but we need to always use UTC. + enc.wf(v.In(time.FixedZone("UTC", 0)).Format("2006-01-02T15:04:05Z")) + return + case TextMarshaler: + // Special case. Use text marshaler if it's available for this value. + if s, err := v.MarshalText(); err != nil { + encPanic(err) + } else { + enc.writeQuoted(string(s)) + } + return + } + switch rv.Kind() { + case reflect.Bool: + enc.wf(strconv.FormatBool(rv.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + enc.wf(strconv.FormatInt(rv.Int(), 10)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64: + enc.wf(strconv.FormatUint(rv.Uint(), 10)) + case reflect.Float32: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32))) + case reflect.Float64: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64))) + case reflect.Array, reflect.Slice: + enc.eArrayOrSliceElement(rv) + case reflect.Interface: + enc.eElement(rv.Elem()) + case reflect.String: + enc.writeQuoted(rv.String()) + default: + panic(e("Unexpected primitive type: %s", rv.Kind())) + } +} + +// By the TOML spec, all floats must have a decimal with at least one +// number on either side. +func floatAddDecimal(fstr string) string { + if !strings.Contains(fstr, ".") { + return fstr + ".0" + } + return fstr +} + +func (enc *Encoder) writeQuoted(s string) { + enc.wf("\"%s\"", quotedReplacer.Replace(s)) +} + +func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { + length := rv.Len() + enc.wf("[") + for i := 0; i < length; i++ { + elem := rv.Index(i) + enc.eElement(elem) + if i != length-1 { + enc.wf(", ") + } + } + enc.wf("]") +} + +func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + panicIfInvalidKey(key, true) + for i := 0; i < rv.Len(); i++ { + trv := rv.Index(i) + if isNil(trv) { + continue + } + enc.newline() + enc.wf("%s[[%s]]", enc.indentStr(key), key.String()) + enc.newline() + enc.eMapOrStruct(key, trv) + } +} + +func (enc *Encoder) eTable(key Key, rv reflect.Value) { + if len(key) == 1 { + // Output an extra new line between top-level tables. + // (The newline isn't written if nothing else has been written though.) + enc.newline() + } + if len(key) > 0 { + panicIfInvalidKey(key, true) + enc.wf("%s[%s]", enc.indentStr(key), key.String()) + enc.newline() + } + enc.eMapOrStruct(key, rv) +} + +func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) { + switch rv := eindirect(rv); rv.Kind() { + case reflect.Map: + enc.eMap(key, rv) + case reflect.Struct: + enc.eStruct(key, rv) + default: + panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) + } +} + +func (enc *Encoder) eMap(key Key, rv reflect.Value) { + rt := rv.Type() + if rt.Key().Kind() != reflect.String { + encPanic(errNonString) + } + + // Sort keys so that we have deterministic output. And write keys directly + // underneath this key first, before writing sub-structs or sub-maps. + var mapKeysDirect, mapKeysSub []string + for _, mapKey := range rv.MapKeys() { + k := mapKey.String() + if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { + mapKeysSub = append(mapKeysSub, k) + } else { + mapKeysDirect = append(mapKeysDirect, k) + } + } + + var writeMapKeys = func(mapKeys []string) { + sort.Strings(mapKeys) + for _, mapKey := range mapKeys { + mrv := rv.MapIndex(reflect.ValueOf(mapKey)) + if isNil(mrv) { + // Don't write anything for nil fields. + continue + } + enc.encode(key.add(mapKey), mrv) + } + } + writeMapKeys(mapKeysDirect) + writeMapKeys(mapKeysSub) +} + +func (enc *Encoder) eStruct(key Key, rv reflect.Value) { + // Write keys for fields directly under this key first, because if we write + // a field that creates a new table, then all keys under it will be in that + // table (not the one we're writing here). + rt := rv.Type() + var fieldsDirect, fieldsSub [][]int + var addFields func(rt reflect.Type, rv reflect.Value, start []int) + addFields = func(rt reflect.Type, rv reflect.Value, start []int) { + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + // skip unexporded fields + if f.PkgPath != "" { + continue + } + frv := rv.Field(i) + if f.Anonymous { + frv := eindirect(frv) + t := frv.Type() + if t.Kind() != reflect.Struct { + encPanic(errAnonNonStruct) + } + addFields(t, frv, f.Index) + } else if typeIsHash(tomlTypeOfGo(frv)) { + fieldsSub = append(fieldsSub, append(start, f.Index...)) + } else { + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + } + } + } + addFields(rt, rv, nil) + + var writeFields = func(fields [][]int) { + for _, fieldIndex := range fields { + sft := rt.FieldByIndex(fieldIndex) + sf := rv.FieldByIndex(fieldIndex) + if isNil(sf) { + // Don't write anything for nil fields. + continue + } + + keyName := sft.Tag.Get("toml") + if keyName == "-" { + continue + } + if keyName == "" { + keyName = sft.Name + } + enc.encode(key.add(keyName), sf) + } + } + writeFields(fieldsDirect) + writeFields(fieldsSub) +} + +// tomlTypeName returns the TOML type name of the Go value's type. It is used to +// determine whether the types of array elements are mixed (which is forbidden). +// If the Go value is nil, then it is illegal for it to be an array element, and +// valueIsNil is returned as true. + +// Returns the TOML type of a Go value. The type may be `nil`, which means +// no concrete TOML type could be found. +func tomlTypeOfGo(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() { + return nil + } + switch rv.Kind() { + case reflect.Bool: + return tomlBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + return tomlInteger + case reflect.Float32, reflect.Float64: + return tomlFloat + case reflect.Array, reflect.Slice: + if typeEqual(tomlHash, tomlArrayType(rv)) { + return tomlArrayHash + } else { + return tomlArray + } + case reflect.Ptr, reflect.Interface: + return tomlTypeOfGo(rv.Elem()) + case reflect.String: + return tomlString + case reflect.Map: + return tomlHash + case reflect.Struct: + switch rv.Interface().(type) { + case time.Time: + return tomlDatetime + case TextMarshaler: + return tomlString + default: + return tomlHash + } + default: + panic("unexpected reflect.Kind: " + rv.Kind().String()) + } +} + +// tomlArrayType returns the element type of a TOML array. The type returned +// may be nil if it cannot be determined (e.g., a nil slice or a zero length +// slize). This function may also panic if it finds a type that cannot be +// expressed in TOML (such as nil elements, heterogeneous arrays or directly +// nested arrays of tables). +func tomlArrayType(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { + return nil + } + firstType := tomlTypeOfGo(rv.Index(0)) + if firstType == nil { + encPanic(errArrayNilElement) + } + + rvlen := rv.Len() + for i := 1; i < rvlen; i++ { + elem := rv.Index(i) + switch elemType := tomlTypeOfGo(elem); { + case elemType == nil: + encPanic(errArrayNilElement) + case !typeEqual(firstType, elemType): + encPanic(errArrayMixedElementTypes) + } + } + // If we have a nested array, then we must make sure that the nested + // array contains ONLY primitives. + // This checks arbitrarily nested arrays. + if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) { + nest := tomlArrayType(eindirect(rv.Index(0))) + if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) { + encPanic(errArrayNoTable) + } + } + return firstType +} + +func (enc *Encoder) newline() { + if enc.hasWritten { + enc.wf("\n") + } +} + +func (enc *Encoder) keyEqElement(key Key, val reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + panicIfInvalidKey(key, false) + enc.wf("%s%s = ", enc.indentStr(key), key[len(key)-1]) + enc.eElement(val) + enc.newline() +} + +func (enc *Encoder) wf(format string, v ...interface{}) { + if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { + encPanic(err) + } + enc.hasWritten = true +} + +func (enc *Encoder) indentStr(key Key) string { + return strings.Repeat(enc.Indent, len(key)-1) +} + +func encPanic(err error) { + panic(tomlEncodeError{err}) +} + +func eindirect(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + return eindirect(v.Elem()) + default: + return v + } +} + +func isNil(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return rv.IsNil() + default: + return false + } +} + +func panicIfInvalidKey(key Key, hash bool) { + if hash { + for _, k := range key { + if !isValidTableName(k) { + encPanic(e("Key '%s' is not a valid table name. Table names "+ + "cannot contain '[', ']' or '.'.", key.String())) + } + } + } else { + if !isValidKeyName(key[len(key)-1]) { + encPanic(e("Key '%s' is not a name. Key names "+ + "cannot contain whitespace.", key.String())) + } + } +} + +func isValidTableName(s string) bool { + if len(s) == 0 { + return false + } + for _, r := range s { + if r == '[' || r == ']' || r == '.' { + return false + } + } + return true +} + +func isValidKeyName(s string) bool { + if len(s) == 0 { + return false + } + return true +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode_test.go new file mode 100644 index 0000000..74a5ee5 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encode_test.go @@ -0,0 +1,506 @@ +package toml + +import ( + "bytes" + "fmt" + "log" + "net" + "testing" + "time" +) + +func TestEncodeRoundTrip(t *testing.T) { + type Config struct { + Age int + Cats []string + Pi float64 + Perfection []int + DOB time.Time + Ipaddress net.IP + } + + var inputs = Config{ + 13, + []string{"one", "two", "three"}, + 3.145, + []int{11, 2, 3, 4}, + time.Now(), + net.ParseIP("192.168.59.254"), + } + + var firstBuffer bytes.Buffer + e := NewEncoder(&firstBuffer) + err := e.Encode(inputs) + if err != nil { + t.Fatal(err) + } + var outputs Config + if _, err := Decode(firstBuffer.String(), &outputs); err != nil { + log.Printf("Could not decode:\n-----\n%s\n-----\n", + firstBuffer.String()) + t.Fatal(err) + } + + // could test each value individually, but I'm lazy + var secondBuffer bytes.Buffer + e2 := NewEncoder(&secondBuffer) + err = e2.Encode(outputs) + if err != nil { + t.Fatal(err) + } + if firstBuffer.String() != secondBuffer.String() { + t.Error( + firstBuffer.String(), + "\n\n is not identical to\n\n", + secondBuffer.String()) + } +} + +// XXX(burntsushi) +// I think these tests probably should be removed. They are good, but they +// ought to be obsolete by toml-test. +func TestEncode(t *testing.T) { + type Embedded struct { + Int int `toml:"_int"` + } + type NonStruct int + + date := time.Date(2014, 5, 11, 20, 30, 40, 0, time.FixedZone("IST", 3600)) + dateStr := "2014-05-11T19:30:40Z" + + tests := map[string]struct { + input interface{} + wantOutput string + wantError error + }{ + "bool field": { + input: struct { + BoolTrue bool + BoolFalse bool + }{true, false}, + wantOutput: "BoolTrue = true\nBoolFalse = false\n", + }, + "int fields": { + input: struct { + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + }{1, 2, 3, 4, 5}, + wantOutput: "Int = 1\nInt8 = 2\nInt16 = 3\nInt32 = 4\nInt64 = 5\n", + }, + "uint fields": { + input: struct { + Uint uint + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + }{1, 2, 3, 4, 5}, + wantOutput: "Uint = 1\nUint8 = 2\nUint16 = 3\nUint32 = 4" + + "\nUint64 = 5\n", + }, + "float fields": { + input: struct { + Float32 float32 + Float64 float64 + }{1.5, 2.5}, + wantOutput: "Float32 = 1.5\nFloat64 = 2.5\n", + }, + "string field": { + input: struct{ String string }{"foo"}, + wantOutput: "String = \"foo\"\n", + }, + "string field and unexported field": { + input: struct { + String string + unexported int + }{"foo", 0}, + wantOutput: "String = \"foo\"\n", + }, + "datetime field in UTC": { + input: struct{ Date time.Time }{date}, + wantOutput: fmt.Sprintf("Date = %s\n", dateStr), + }, + "datetime field as primitive": { + // Using a map here to fail if isStructOrMap() returns true for + // time.Time. + input: map[string]interface{}{ + "Date": date, + "Int": 1, + }, + wantOutput: fmt.Sprintf("Date = %s\nInt = 1\n", dateStr), + }, + "array fields": { + input: struct { + IntArray0 [0]int + IntArray3 [3]int + }{[0]int{}, [3]int{1, 2, 3}}, + wantOutput: "IntArray0 = []\nIntArray3 = [1, 2, 3]\n", + }, + "slice fields": { + input: struct{ IntSliceNil, IntSlice0, IntSlice3 []int }{ + nil, []int{}, []int{1, 2, 3}, + }, + wantOutput: "IntSlice0 = []\nIntSlice3 = [1, 2, 3]\n", + }, + "datetime slices": { + input: struct{ DatetimeSlice []time.Time }{ + []time.Time{date, date}, + }, + wantOutput: fmt.Sprintf("DatetimeSlice = [%s, %s]\n", + dateStr, dateStr), + }, + "nested arrays and slices": { + input: struct { + SliceOfArrays [][2]int + ArrayOfSlices [2][]int + SliceOfArraysOfSlices [][2][]int + ArrayOfSlicesOfArrays [2][][2]int + SliceOfMixedArrays [][2]interface{} + ArrayOfMixedSlices [2][]interface{} + }{ + [][2]int{{1, 2}, {3, 4}}, + [2][]int{{1, 2}, {3, 4}}, + [][2][]int{ + { + {1, 2}, {3, 4}, + }, + { + {5, 6}, {7, 8}, + }, + }, + [2][][2]int{ + { + {1, 2}, {3, 4}, + }, + { + {5, 6}, {7, 8}, + }, + }, + [][2]interface{}{ + {1, 2}, {"a", "b"}, + }, + [2][]interface{}{ + {1, 2}, {"a", "b"}, + }, + }, + wantOutput: `SliceOfArrays = [[1, 2], [3, 4]] +ArrayOfSlices = [[1, 2], [3, 4]] +SliceOfArraysOfSlices = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] +ArrayOfSlicesOfArrays = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] +SliceOfMixedArrays = [[1, 2], ["a", "b"]] +ArrayOfMixedSlices = [[1, 2], ["a", "b"]] +`, + }, + "empty slice": { + input: struct{ Empty []interface{} }{[]interface{}{}}, + wantOutput: "Empty = []\n", + }, + "(error) slice with element type mismatch (string and integer)": { + input: struct{ Mixed []interface{} }{[]interface{}{1, "a"}}, + wantError: errArrayMixedElementTypes, + }, + "(error) slice with element type mismatch (integer and float)": { + input: struct{ Mixed []interface{} }{[]interface{}{1, 2.5}}, + wantError: errArrayMixedElementTypes, + }, + "slice with elems of differing Go types, same TOML types": { + input: struct { + MixedInts []interface{} + MixedFloats []interface{} + }{ + []interface{}{ + int(1), int8(2), int16(3), int32(4), int64(5), + uint(1), uint8(2), uint16(3), uint32(4), uint64(5), + }, + []interface{}{float32(1.5), float64(2.5)}, + }, + wantOutput: "MixedInts = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]\n" + + "MixedFloats = [1.5, 2.5]\n", + }, + "(error) slice w/ element type mismatch (one is nested array)": { + input: struct{ Mixed []interface{} }{ + []interface{}{1, []interface{}{2}}, + }, + wantError: errArrayMixedElementTypes, + }, + "(error) slice with 1 nil element": { + input: struct{ NilElement1 []interface{} }{[]interface{}{nil}}, + wantError: errArrayNilElement, + }, + "(error) slice with 1 nil element (and other non-nil elements)": { + input: struct{ NilElement []interface{} }{ + []interface{}{1, nil}, + }, + wantError: errArrayNilElement, + }, + "simple map": { + input: map[string]int{"a": 1, "b": 2}, + wantOutput: "a = 1\nb = 2\n", + }, + "map with interface{} value type": { + input: map[string]interface{}{"a": 1, "b": "c"}, + wantOutput: "a = 1\nb = \"c\"\n", + }, + "map with interface{} value type, some of which are structs": { + input: map[string]interface{}{ + "a": struct{ Int int }{2}, + "b": 1, + }, + wantOutput: "b = 1\n\n[a]\n Int = 2\n", + }, + "nested map": { + input: map[string]map[string]int{ + "a": {"b": 1}, + "c": {"d": 2}, + }, + wantOutput: "[a]\n b = 1\n\n[c]\n d = 2\n", + }, + "nested struct": { + input: struct{ Struct struct{ Int int } }{ + struct{ Int int }{1}, + }, + wantOutput: "[Struct]\n Int = 1\n", + }, + "nested struct and non-struct field": { + input: struct { + Struct struct{ Int int } + Bool bool + }{struct{ Int int }{1}, true}, + wantOutput: "Bool = true\n\n[Struct]\n Int = 1\n", + }, + "2 nested structs": { + input: struct{ Struct1, Struct2 struct{ Int int } }{ + struct{ Int int }{1}, struct{ Int int }{2}, + }, + wantOutput: "[Struct1]\n Int = 1\n\n[Struct2]\n Int = 2\n", + }, + "deeply nested structs": { + input: struct { + Struct1, Struct2 struct{ Struct3 *struct{ Int int } } + }{ + struct{ Struct3 *struct{ Int int } }{&struct{ Int int }{1}}, + struct{ Struct3 *struct{ Int int } }{nil}, + }, + wantOutput: "[Struct1]\n [Struct1.Struct3]\n Int = 1" + + "\n\n[Struct2]\n", + }, + "nested struct with nil struct elem": { + input: struct { + Struct struct{ Inner *struct{ Int int } } + }{ + struct{ Inner *struct{ Int int } }{nil}, + }, + wantOutput: "[Struct]\n", + }, + "nested struct with no fields": { + input: struct { + Struct struct{ Inner struct{} } + }{ + struct{ Inner struct{} }{struct{}{}}, + }, + wantOutput: "[Struct]\n [Struct.Inner]\n", + }, + "struct with tags": { + input: struct { + Struct struct { + Int int `toml:"_int"` + } `toml:"_struct"` + Bool bool `toml:"_bool"` + }{ + struct { + Int int `toml:"_int"` + }{1}, true, + }, + wantOutput: "_bool = true\n\n[_struct]\n _int = 1\n", + }, + "embedded struct": { + input: struct{ Embedded }{Embedded{1}}, + wantOutput: "_int = 1\n", + }, + "embedded *struct": { + input: struct{ *Embedded }{&Embedded{1}}, + wantOutput: "_int = 1\n", + }, + "nested embedded struct": { + input: struct { + Struct struct{ Embedded } `toml:"_struct"` + }{struct{ Embedded }{Embedded{1}}}, + wantOutput: "[_struct]\n _int = 1\n", + }, + "nested embedded *struct": { + input: struct { + Struct struct{ *Embedded } `toml:"_struct"` + }{struct{ *Embedded }{&Embedded{1}}}, + wantOutput: "[_struct]\n _int = 1\n", + }, + "array of tables": { + input: struct { + Structs []*struct{ Int int } `toml:"struct"` + }{ + []*struct{ Int int }{{1}, {3}}, + }, + wantOutput: "[[struct]]\n Int = 1\n\n[[struct]]\n Int = 3\n", + }, + "array of tables order": { + input: map[string]interface{}{ + "map": map[string]interface{}{ + "zero": 5, + "arr": []map[string]int{ + map[string]int{ + "friend": 5, + }, + }, + }, + }, + wantOutput: "[map]\n zero = 5\n\n [[map.arr]]\n friend = 5\n", + }, + "(error) top-level slice": { + input: []struct{ Int int }{{1}, {2}, {3}}, + wantError: errNoKey, + }, + "(error) slice of slice": { + input: struct { + Slices [][]struct{ Int int } + }{ + [][]struct{ Int int }{{{1}}, {{2}}, {{3}}}, + }, + wantError: errArrayNoTable, + }, + "(error) map no string key": { + input: map[int]string{1: ""}, + wantError: errNonString, + }, + "(error) anonymous non-struct": { + input: struct{ NonStruct }{5}, + wantError: errAnonNonStruct, + }, + "(error) empty key name": { + input: map[string]int{"": 1}, + wantError: errAnything, + }, + "(error) empty map name": { + input: map[string]interface{}{ + "": map[string]int{"v": 1}, + }, + wantError: errAnything, + }, + } + for label, test := range tests { + encodeExpected(t, label, test.input, test.wantOutput, test.wantError) + } +} + +func TestEncodeNestedTableArrays(t *testing.T) { + type song struct { + Name string `toml:"name"` + } + type album struct { + Name string `toml:"name"` + Songs []song `toml:"songs"` + } + type springsteen struct { + Albums []album `toml:"albums"` + } + value := springsteen{ + []album{ + {"Born to Run", + []song{{"Jungleland"}, {"Meeting Across the River"}}}, + {"Born in the USA", + []song{{"Glory Days"}, {"Dancing in the Dark"}}}, + }, + } + expected := `[[albums]] + name = "Born to Run" + + [[albums.songs]] + name = "Jungleland" + + [[albums.songs]] + name = "Meeting Across the River" + +[[albums]] + name = "Born in the USA" + + [[albums.songs]] + name = "Glory Days" + + [[albums.songs]] + name = "Dancing in the Dark" +` + encodeExpected(t, "nested table arrays", value, expected, nil) +} + +func TestEncodeArrayHashWithNormalHashOrder(t *testing.T) { + type Alpha struct { + V int + } + type Beta struct { + V int + } + type Conf struct { + V int + A Alpha + B []Beta + } + + val := Conf{ + V: 1, + A: Alpha{2}, + B: []Beta{{3}}, + } + expected := "V = 1\n\n[A]\n V = 2\n\n[[B]]\n V = 3\n" + encodeExpected(t, "array hash with normal hash order", val, expected, nil) +} + +func encodeExpected( + t *testing.T, label string, val interface{}, wantStr string, wantErr error, +) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + err := enc.Encode(val) + if err != wantErr { + if wantErr != nil { + if wantErr == errAnything && err != nil { + return + } + t.Errorf("%s: want Encode error %v, got %v", label, wantErr, err) + } else { + t.Errorf("%s: Encode failed: %s", label, err) + } + } + if err != nil { + return + } + if got := buf.String(); wantStr != got { + t.Errorf("%s: want\n-----\n%q\n-----\nbut got\n-----\n%q\n-----\n", + label, wantStr, got) + } +} + +func ExampleEncoder_Encode() { + date, _ := time.Parse(time.RFC822, "14 Mar 10 18:00 UTC") + var config = map[string]interface{}{ + "date": date, + "counts": []int{1, 1, 2, 3, 5, 8}, + "hash": map[string]string{ + "key1": "val1", + "key2": "val2", + }, + } + buf := new(bytes.Buffer) + if err := NewEncoder(buf).Encode(config); err != nil { + log.Fatal(err) + } + fmt.Println(buf.String()) + + // Output: + // counts = [1, 1, 2, 3, 5, 8] + // date = 2010-03-14T18:00:00Z + // + // [hash] + // key1 = "val1" + // key2 = "val2" +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types.go new file mode 100644 index 0000000..140c44c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types.go @@ -0,0 +1,19 @@ +// +build go1.2 + +package toml + +// In order to support Go 1.1, we define our own TextMarshaler and +// TextUnmarshaler types. For Go 1.2+, we just alias them with the +// standard library interfaces. + +import ( + "encoding" +) + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler encoding.TextMarshaler + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextUnmarshaler encoding.TextUnmarshaler diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types_1.1.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types_1.1.go new file mode 100644 index 0000000..fb285e7 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/encoding_types_1.1.go @@ -0,0 +1,18 @@ +// +build !go1.2 + +package toml + +// These interfaces were introduced in Go 1.2, so we add them manually when +// compiling for Go 1.1. + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/lex.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/lex.go new file mode 100644 index 0000000..3821fa2 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/lex.go @@ -0,0 +1,734 @@ +package toml + +import ( + "fmt" + "strings" + "unicode/utf8" +) + +type itemType int + +const ( + itemError itemType = iota + itemNIL // used in the parser to indicate no type + itemEOF + itemText + itemString + itemBool + itemInteger + itemFloat + itemDatetime + itemArray // the start of an array + itemArrayEnd + itemTableStart + itemTableEnd + itemArrayTableStart + itemArrayTableEnd + itemKeyStart + itemCommentStart +) + +const ( + eof = 0 + tableStart = '[' + tableEnd = ']' + arrayTableStart = '[' + arrayTableEnd = ']' + tableSep = '.' + keySep = '=' + arrayStart = '[' + arrayEnd = ']' + arrayValTerm = ',' + commentStart = '#' + stringStart = '"' + stringEnd = '"' +) + +type stateFn func(lx *lexer) stateFn + +type lexer struct { + input string + start int + pos int + width int + line int + state stateFn + items chan item + + // A stack of state functions used to maintain context. + // The idea is to reuse parts of the state machine in various places. + // For example, values can appear at the top level or within arbitrarily + // nested arrays. The last state on the stack is used after a value has + // been lexed. Similarly for comments. + stack []stateFn +} + +type item struct { + typ itemType + val string + line int +} + +func (lx *lexer) nextItem() item { + for { + select { + case item := <-lx.items: + return item + default: + lx.state = lx.state(lx) + } + } +} + +func lex(input string) *lexer { + lx := &lexer{ + input: input + "\n", + state: lexTop, + line: 1, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + } + return lx +} + +func (lx *lexer) push(state stateFn) { + lx.stack = append(lx.stack, state) +} + +func (lx *lexer) pop() stateFn { + if len(lx.stack) == 0 { + return lx.errorf("BUG in lexer: no states to pop.") + } + last := lx.stack[len(lx.stack)-1] + lx.stack = lx.stack[0 : len(lx.stack)-1] + return last +} + +func (lx *lexer) current() string { + return lx.input[lx.start:lx.pos] +} + +func (lx *lexer) emit(typ itemType) { + lx.items <- item{typ, lx.current(), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) emitTrim(typ itemType) { + lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) next() (r rune) { + if lx.pos >= len(lx.input) { + lx.width = 0 + return eof + } + + if lx.input[lx.pos] == '\n' { + lx.line++ + } + r, lx.width = utf8.DecodeRuneInString(lx.input[lx.pos:]) + lx.pos += lx.width + return r +} + +// ignore skips over the pending input before this point. +func (lx *lexer) ignore() { + lx.start = lx.pos +} + +// backup steps back one rune. Can be called only once per call of next. +func (lx *lexer) backup() { + lx.pos -= lx.width + if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { + lx.line-- + } +} + +// accept consumes the next rune if it's equal to `valid`. +func (lx *lexer) accept(valid rune) bool { + if lx.next() == valid { + return true + } + lx.backup() + return false +} + +// peek returns but does not consume the next rune in the input. +func (lx *lexer) peek() rune { + r := lx.next() + lx.backup() + return r +} + +// errorf stops all lexing by emitting an error and returning `nil`. +// Note that any value that is a character is escaped if it's a special +// character (new lines, tabs, etc.). +func (lx *lexer) errorf(format string, values ...interface{}) stateFn { + lx.items <- item{ + itemError, + fmt.Sprintf(format, values...), + lx.line, + } + return nil +} + +// lexTop consumes elements at the top level of TOML data. +func lexTop(lx *lexer) stateFn { + r := lx.next() + if isWhitespace(r) || isNL(r) { + return lexSkip(lx, lexTop) + } + + switch r { + case commentStart: + lx.push(lexTop) + return lexCommentStart + case tableStart: + return lexTableStart + case eof: + if lx.pos > lx.start { + return lx.errorf("Unexpected EOF.") + } + lx.emit(itemEOF) + return nil + } + + // At this point, the only valid item can be a key, so we back up + // and let the key lexer do the rest. + lx.backup() + lx.push(lexTopEnd) + return lexKeyStart +} + +// lexTopEnd is entered whenever a top-level item has been consumed. (A value +// or a table.) It must see only whitespace, and will turn back to lexTop +// upon a new line. If it sees EOF, it will quit the lexer successfully. +func lexTopEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case r == commentStart: + // a comment will read to a new line for us. + lx.push(lexTop) + return lexCommentStart + case isWhitespace(r): + return lexTopEnd + case isNL(r): + lx.ignore() + return lexTop + case r == eof: + lx.ignore() + return lexTop + } + return lx.errorf("Expected a top-level item to end with a new line, "+ + "comment or EOF, but got %q instead.", r) +} + +// lexTable lexes the beginning of a table. Namely, it makes sure that +// it starts with a character other than '.' and ']'. +// It assumes that '[' has already been consumed. +// It also handles the case that this is an item in an array of tables. +// e.g., '[[name]]'. +func lexTableStart(lx *lexer) stateFn { + if lx.peek() == arrayTableStart { + lx.next() + lx.emit(itemArrayTableStart) + lx.push(lexArrayTableEnd) + } else { + lx.emit(itemTableStart) + lx.push(lexTableEnd) + } + return lexTableNameStart +} + +func lexTableEnd(lx *lexer) stateFn { + lx.emit(itemTableEnd) + return lexTopEnd +} + +func lexArrayTableEnd(lx *lexer) stateFn { + if r := lx.next(); r != arrayTableEnd { + return lx.errorf("Expected end of table array name delimiter %q, "+ + "but got %q instead.", arrayTableEnd, r) + } + lx.emit(itemArrayTableEnd) + return lexTopEnd +} + +func lexTableNameStart(lx *lexer) stateFn { + switch lx.next() { + case tableEnd, eof: + return lx.errorf("Unexpected end of table. (Tables cannot " + + "be empty.)") + case tableSep: + return lx.errorf("Unexpected table separator. (Tables cannot " + + "be empty.)") + } + return lexTableName +} + +// lexTableName lexes the name of a table. It assumes that at least one +// valid character for the table has already been read. +func lexTableName(lx *lexer) stateFn { + switch lx.peek() { + case eof: + return lx.errorf("Unexpected end of table name %q.", lx.current()) + case tableStart: + return lx.errorf("Table names cannot contain %q or %q.", + tableStart, tableEnd) + case tableEnd: + lx.emit(itemText) + lx.next() + return lx.pop() + case tableSep: + lx.emit(itemText) + lx.next() + lx.ignore() + return lexTableNameStart + } + lx.next() + return lexTableName +} + +// lexKeyStart consumes a key name up until the first non-whitespace character. +// lexKeyStart will ignore whitespace. +func lexKeyStart(lx *lexer) stateFn { + r := lx.peek() + switch { + case r == keySep: + return lx.errorf("Unexpected key separator %q.", keySep) + case isWhitespace(r) || isNL(r): + lx.next() + return lexSkip(lx, lexKeyStart) + } + + lx.ignore() + lx.emit(itemKeyStart) + lx.next() + return lexKey +} + +// lexKey consumes the text of a key. Assumes that the first character (which +// is not whitespace) has already been consumed. +func lexKey(lx *lexer) stateFn { + r := lx.peek() + + // Keys cannot contain a '#' character. + if r == commentStart { + return lx.errorf("Key cannot contain a '#' character.") + } + + // XXX: Possible divergence from spec? + // "Keys start with the first non-whitespace character and end with the + // last non-whitespace character before the equals sign." + // Note here that whitespace is either a tab or a space. + // But we'll call it quits if we see a new line too. + if isNL(r) { + lx.emitTrim(itemText) + return lexKeyEnd + } + + // Let's also call it quits if we see an equals sign. + if r == keySep { + lx.emitTrim(itemText) + return lexKeyEnd + } + + lx.next() + return lexKey +} + +// lexKeyEnd consumes the end of a key (up to the key separator). +// Assumes that any whitespace after a key has been consumed. +func lexKeyEnd(lx *lexer) stateFn { + r := lx.next() + if r == keySep { + return lexSkip(lx, lexValue) + } + return lx.errorf("Expected key separator %q, but got %q instead.", + keySep, r) +} + +// lexValue starts the consumption of a value anywhere a value is expected. +// lexValue will ignore whitespace. +// After a value is lexed, the last state on the next is popped and returned. +func lexValue(lx *lexer) stateFn { + // We allow whitespace to precede a value, but NOT new lines. + // In array syntax, the array states are responsible for ignoring new lines. + r := lx.next() + if isWhitespace(r) { + return lexSkip(lx, lexValue) + } + + switch { + case r == arrayStart: + lx.ignore() + lx.emit(itemArray) + return lexArrayValue + case r == stringStart: + lx.ignore() // ignore the '"' + return lexString + case r == 't': + return lexTrue + case r == 'f': + return lexFalse + case r == '-': + return lexNumberStart + case isDigit(r): + lx.backup() // avoid an extra state and use the same as above + return lexNumberOrDateStart + case r == '.': // special error case, be kind to users + return lx.errorf("Floats must start with a digit, not '.'.") + } + return lx.errorf("Expected value but found %q instead.", r) +} + +// lexArrayValue consumes one value in an array. It assumes that '[' or ',' +// have already been consumed. All whitespace and new lines are ignored. +func lexArrayValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValue) + case r == commentStart: + lx.push(lexArrayValue) + return lexCommentStart + case r == arrayValTerm: + return lx.errorf("Unexpected array value terminator %q.", + arrayValTerm) + case r == arrayEnd: + return lexArrayEnd + } + + lx.backup() + lx.push(lexArrayValueEnd) + return lexValue +} + +// lexArrayValueEnd consumes the cruft between values of an array. Namely, +// it ignores whitespace and expects either a ',' or a ']'. +func lexArrayValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValueEnd) + case r == commentStart: + lx.push(lexArrayValueEnd) + return lexCommentStart + case r == arrayValTerm: + lx.ignore() + return lexArrayValue // move on to the next value + case r == arrayEnd: + return lexArrayEnd + } + return lx.errorf("Expected an array value terminator %q or an array "+ + "terminator %q, but got %q instead.", arrayValTerm, arrayEnd, r) +} + +// lexArrayEnd finishes the lexing of an array. It assumes that a ']' has +// just been consumed. +func lexArrayEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemArrayEnd) + return lx.pop() +} + +// lexString consumes the inner contents of a string. It assumes that the +// beginning '"' has already been consumed and ignored. +func lexString(lx *lexer) stateFn { + r := lx.next() + switch { + case isNL(r): + return lx.errorf("Strings cannot contain new lines.") + case r == '\\': + return lexStringEscape + case r == stringEnd: + lx.backup() + lx.emit(itemString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexString +} + +// lexStringEscape consumes an escaped character. It assumes that the preceding +// '\\' has already been consumed. +func lexStringEscape(lx *lexer) stateFn { + r := lx.next() + switch r { + case 'b': + fallthrough + case 't': + fallthrough + case 'n': + fallthrough + case 'f': + fallthrough + case 'r': + fallthrough + case '"': + fallthrough + case '/': + fallthrough + case '\\': + return lexString + case 'u': + return lexStringUnicode + } + return lx.errorf("Invalid escape character %q. Only the following "+ + "escape characters are allowed: "+ + "\\b, \\t, \\n, \\f, \\r, \\\", \\/, \\\\, and \\uXXXX.", r) +} + +// lexStringBinary consumes two hexadecimal digits following '\x'. It assumes +// that the '\x' has already been consumed. +func lexStringUnicode(lx *lexer) stateFn { + var r rune + + for i := 0; i < 4; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf("Expected four hexadecimal digits after '\\x', "+ + "but got '%s' instead.", lx.current()) + } + } + return lexString +} + +// lexNumberOrDateStart consumes either a (positive) integer, float or datetime. +// It assumes that NO negative sign has been consumed. +func lexNumberOrDateStart(lx *lexer) stateFn { + r := lx.next() + if !isDigit(r) { + if r == '.' { + return lx.errorf("Floats must start with a digit, not '.'.") + } else { + return lx.errorf("Expected a digit but got %q.", r) + } + } + return lexNumberOrDate +} + +// lexNumberOrDate consumes either a (positive) integer, float or datetime. +func lexNumberOrDate(lx *lexer) stateFn { + r := lx.next() + switch { + case r == '-': + if lx.pos-lx.start != 5 { + return lx.errorf("All ISO8601 dates must be in full Zulu form.") + } + return lexDateAfterYear + case isDigit(r): + return lexNumberOrDate + case r == '.': + return lexFloatStart + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDateAfterYear consumes a full Zulu Datetime in ISO8601 format. +// It assumes that "YYYY-" has already been consumed. +func lexDateAfterYear(lx *lexer) stateFn { + formats := []rune{ + // digits are '0'. + // everything else is direct equality. + '0', '0', '-', '0', '0', + 'T', + '0', '0', ':', '0', '0', ':', '0', '0', + 'Z', + } + for _, f := range formats { + r := lx.next() + if f == '0' { + if !isDigit(r) { + return lx.errorf("Expected digit in ISO8601 datetime, "+ + "but found %q instead.", r) + } + } else if f != r { + return lx.errorf("Expected %q in ISO8601 datetime, "+ + "but found %q instead.", f, r) + } + } + lx.emit(itemDatetime) + return lx.pop() +} + +// lexNumberStart consumes either an integer or a float. It assumes that a +// negative sign has already been read, but that *no* digits have been consumed. +// lexNumberStart will move to the appropriate integer or float states. +func lexNumberStart(lx *lexer) stateFn { + // we MUST see a digit. Even floats have to start with a digit. + r := lx.next() + if !isDigit(r) { + if r == '.' { + return lx.errorf("Floats must start with a digit, not '.'.") + } else { + return lx.errorf("Expected a digit but got %q.", r) + } + } + return lexNumber +} + +// lexNumber consumes an integer or a float after seeing the first digit. +func lexNumber(lx *lexer) stateFn { + r := lx.next() + switch { + case isDigit(r): + return lexNumber + case r == '.': + return lexFloatStart + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexFloatStart starts the consumption of digits of a float after a '.'. +// Namely, at least one digit is required. +func lexFloatStart(lx *lexer) stateFn { + r := lx.next() + if !isDigit(r) { + return lx.errorf("Floats must have a digit after the '.', but got "+ + "%q instead.", r) + } + return lexFloat +} + +// lexFloat consumes the digits of a float after a '.'. +// Assumes that one digit has been consumed after a '.' already. +func lexFloat(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexFloat + } + + lx.backup() + lx.emit(itemFloat) + return lx.pop() +} + +// lexConst consumes the s[1:] in s. It assumes that s[0] has already been +// consumed. +func lexConst(lx *lexer, s string) stateFn { + for i := range s[1:] { + if r := lx.next(); r != rune(s[i+1]) { + return lx.errorf("Expected %q, but found %q instead.", s[:i+1], + s[:i]+string(r)) + } + } + return nil +} + +// lexTrue consumes the "rue" in "true". It assumes that 't' has already +// been consumed. +func lexTrue(lx *lexer) stateFn { + if fn := lexConst(lx, "true"); fn != nil { + return fn + } + lx.emit(itemBool) + return lx.pop() +} + +// lexFalse consumes the "alse" in "false". It assumes that 'f' has already +// been consumed. +func lexFalse(lx *lexer) stateFn { + if fn := lexConst(lx, "false"); fn != nil { + return fn + } + lx.emit(itemBool) + return lx.pop() +} + +// lexCommentStart begins the lexing of a comment. It will emit +// itemCommentStart and consume no characters, passing control to lexComment. +func lexCommentStart(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemCommentStart) + return lexComment +} + +// lexComment lexes an entire comment. It assumes that '#' has been consumed. +// It will consume *up to* the first new line character, and pass control +// back to the last state on the stack. +func lexComment(lx *lexer) stateFn { + r := lx.peek() + if isNL(r) || r == eof { + lx.emit(itemText) + return lx.pop() + } + lx.next() + return lexComment +} + +// lexSkip ignores all slurped input and moves on to the next state. +func lexSkip(lx *lexer, nextState stateFn) stateFn { + return func(lx *lexer) stateFn { + lx.ignore() + return nextState + } +} + +// isWhitespace returns true if `r` is a whitespace character according +// to the spec. +func isWhitespace(r rune) bool { + return r == '\t' || r == ' ' +} + +func isNL(r rune) bool { + return r == '\n' || r == '\r' +} + +func isDigit(r rune) bool { + return r >= '0' && r <= '9' +} + +func isHexadecimal(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} + +func (itype itemType) String() string { + switch itype { + case itemError: + return "Error" + case itemNIL: + return "NIL" + case itemEOF: + return "EOF" + case itemText: + return "Text" + case itemString: + return "String" + case itemBool: + return "Bool" + case itemInteger: + return "Integer" + case itemFloat: + return "Float" + case itemDatetime: + return "DateTime" + case itemTableStart: + return "TableStart" + case itemTableEnd: + return "TableEnd" + case itemKeyStart: + return "KeyStart" + case itemArray: + return "Array" + case itemArrayEnd: + return "ArrayEnd" + case itemCommentStart: + return "CommentStart" + } + panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) +} + +func (item item) String() string { + return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/parse.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/parse.go new file mode 100644 index 0000000..43afe3c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/parse.go @@ -0,0 +1,417 @@ +package toml + +import ( + "fmt" + "log" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +type parser struct { + mapping map[string]interface{} + types map[string]tomlType + lx *lexer + + // A list of keys in the order that they appear in the TOML data. + ordered []Key + + // the full key for the current hash in scope + context Key + + // the base key name for everything except hashes + currentKey string + + // rough approximation of line number + approxLine int + + // A map of 'key.group.names' to whether they were created implicitly. + implicits map[string]bool +} + +type parseError string + +func (pe parseError) Error() string { + return string(pe) +} + +func parse(data string) (p *parser, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(parseError); ok { + return + } + panic(r) + } + }() + + p = &parser{ + mapping: make(map[string]interface{}), + types: make(map[string]tomlType), + lx: lex(data), + ordered: make([]Key, 0), + implicits: make(map[string]bool), + } + for { + item := p.next() + if item.typ == itemEOF { + break + } + p.topLevel(item) + } + + return p, nil +} + +func (p *parser) panicf(format string, v ...interface{}) { + msg := fmt.Sprintf("Near line %d, key '%s': %s", + p.approxLine, p.current(), fmt.Sprintf(format, v...)) + panic(parseError(msg)) +} + +func (p *parser) next() item { + it := p.lx.nextItem() + if it.typ == itemError { + p.panicf("Near line %d: %s", it.line, it.val) + } + return it +} + +func (p *parser) bug(format string, v ...interface{}) { + log.Fatalf("BUG: %s\n\n", fmt.Sprintf(format, v...)) +} + +func (p *parser) expect(typ itemType) item { + it := p.next() + p.assertEqual(typ, it.typ) + return it +} + +func (p *parser) assertEqual(expected, got itemType) { + if expected != got { + p.bug("Expected '%s' but got '%s'.", expected, got) + } +} + +func (p *parser) topLevel(item item) { + switch item.typ { + case itemCommentStart: + p.approxLine = item.line + p.expect(itemText) + case itemTableStart: + kg := p.expect(itemText) + p.approxLine = kg.line + + key := make(Key, 0) + for ; kg.typ == itemText; kg = p.next() { + key = append(key, kg.val) + } + p.assertEqual(itemTableEnd, kg.typ) + + p.establishContext(key, false) + p.setType("", tomlHash) + p.ordered = append(p.ordered, key) + case itemArrayTableStart: + kg := p.expect(itemText) + p.approxLine = kg.line + + key := make(Key, 0) + for ; kg.typ == itemText; kg = p.next() { + key = append(key, kg.val) + } + p.assertEqual(itemArrayTableEnd, kg.typ) + + p.establishContext(key, true) + p.setType("", tomlArrayHash) + p.ordered = append(p.ordered, key) + case itemKeyStart: + kname := p.expect(itemText) + p.currentKey = kname.val + p.approxLine = kname.line + + val, typ := p.value(p.next()) + p.setValue(p.currentKey, val) + p.setType(p.currentKey, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + + p.currentKey = "" + default: + p.bug("Unexpected type at top level: %s", item.typ) + } +} + +// value translates an expected value from the lexer into a Go value wrapped +// as an empty interface. +func (p *parser) value(it item) (interface{}, tomlType) { + switch it.typ { + case itemString: + return p.replaceUnicode(replaceEscapes(it.val)), p.typeOfPrimitive(it) + case itemBool: + switch it.val { + case "true": + return true, p.typeOfPrimitive(it) + case "false": + return false, p.typeOfPrimitive(it) + } + p.bug("Expected boolean value, but got '%s'.", it.val) + case itemInteger: + num, err := strconv.ParseInt(it.val, 10, 64) + if err != nil { + // See comment below for floats describing why we make a + // distinction between a bug and a user error. + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Integer '%s' is out of the range of 64-bit "+ + "signed integers.", it.val) + } else { + p.bug("Expected integer value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemFloat: + num, err := strconv.ParseFloat(it.val, 64) + if err != nil { + // Distinguish float values. Normally, it'd be a bug if the lexer + // provides an invalid float, but it's possible that the float is + // out of range of valid values (which the lexer cannot determine). + // So mark the former as a bug but the latter as a legitimate user + // error. + // + // This is also true for integers. + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Float '%s' is out of the range of 64-bit "+ + "IEEE-754 floating-point numbers.", it.val) + } else { + p.bug("Expected float value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemDatetime: + t, err := time.Parse("2006-01-02T15:04:05Z", it.val) + if err != nil { + p.bug("Expected Zulu formatted DateTime, but got '%s'.", it.val) + } + return t, p.typeOfPrimitive(it) + case itemArray: + array := make([]interface{}, 0) + types := make([]tomlType, 0) + + for it = p.next(); it.typ != itemArrayEnd; it = p.next() { + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + val, typ := p.value(it) + array = append(array, val) + types = append(types, typ) + } + return array, p.typeOfArray(types) + } + p.bug("Unexpected value type: %s", it.typ) + panic("unreachable") +} + +// establishContext sets the current context of the parser, +// where the context is either a hash or an array of hashes. Which one is +// set depends on the value of the `array` parameter. +// +// Establishing the context also makes sure that the key isn't a duplicate, and +// will create implicit hashes automatically. +func (p *parser) establishContext(key Key, array bool) { + var ok bool + + // Always start at the top level and drill down for our context. + hashContext := p.mapping + keyContext := make(Key, 0) + + // We only need implicit hashes for key[0:-1] + for _, k := range key[0 : len(key)-1] { + _, ok = hashContext[k] + keyContext = append(keyContext, k) + + // No key? Make an implicit hash and move on. + if !ok { + p.addImplicit(keyContext) + hashContext[k] = make(map[string]interface{}) + } + + // If the hash context is actually an array of tables, then set + // the hash context to the last element in that array. + // + // Otherwise, it better be a table, since this MUST be a key group (by + // virtue of it not being the last element in a key). + switch t := hashContext[k].(type) { + case []map[string]interface{}: + hashContext = t[len(t)-1] + case map[string]interface{}: + hashContext = t + default: + p.panicf("Key '%s' was already created as a hash.", keyContext) + } + } + + p.context = keyContext + if array { + // If this is the first element for this array, then allocate a new + // list of tables for it. + k := key[len(key)-1] + if _, ok := hashContext[k]; !ok { + hashContext[k] = make([]map[string]interface{}, 0, 5) + } + + // Add a new table. But make sure the key hasn't already been used + // for something else. + if hash, ok := hashContext[k].([]map[string]interface{}); ok { + hashContext[k] = append(hash, make(map[string]interface{})) + } else { + p.panicf("Key '%s' was already created and cannot be used as "+ + "an array.", keyContext) + } + } else { + p.setValue(key[len(key)-1], make(map[string]interface{})) + } + p.context = append(p.context, key[len(key)-1]) +} + +// setValue sets the given key to the given value in the current context. +// It will make sure that the key hasn't already been defined, account for +// implicit key groups. +func (p *parser) setValue(key string, value interface{}) { + var tmpHash interface{} + var ok bool + + hash := p.mapping + keyContext := make(Key, 0) + for _, k := range p.context { + keyContext = append(keyContext, k) + if tmpHash, ok = hash[k]; !ok { + p.bug("Context for key '%s' has not been established.", keyContext) + } + switch t := tmpHash.(type) { + case []map[string]interface{}: + // The context is a table of hashes. Pick the most recent table + // defined as the current hash. + hash = t[len(t)-1] + case map[string]interface{}: + hash = t + default: + p.bug("Expected hash to have type 'map[string]interface{}', but "+ + "it has '%T' instead.", tmpHash) + } + } + keyContext = append(keyContext, key) + + if _, ok := hash[key]; ok { + // Typically, if the given key has already been set, then we have + // to raise an error since duplicate keys are disallowed. However, + // it's possible that a key was previously defined implicitly. In this + // case, it is allowed to be redefined concretely. (See the + // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.) + // + // But we have to make sure to stop marking it as an implicit. (So that + // another redefinition provokes an error.) + // + // Note that since it has already been defined (as a hash), we don't + // want to overwrite it. So our business is done. + if p.isImplicit(keyContext) { + p.removeImplicit(keyContext) + return + } + + // Otherwise, we have a concrete key trying to override a previous + // key, which is *always* wrong. + p.panicf("Key '%s' has already been defined.", keyContext) + } + hash[key] = value +} + +// setType sets the type of a particular value at a given key. +// It should be called immediately AFTER setValue. +// +// Note that if `key` is empty, then the type given will be applied to the +// current context (which is either a table or an array of tables). +func (p *parser) setType(key string, typ tomlType) { + keyContext := make(Key, 0, len(p.context)+1) + for _, k := range p.context { + keyContext = append(keyContext, k) + } + if len(key) > 0 { // allow type setting for hashes + keyContext = append(keyContext, key) + } + p.types[keyContext.String()] = typ +} + +// addImplicit sets the given Key as having been created implicitly. +func (p *parser) addImplicit(key Key) { + p.implicits[key.String()] = true +} + +// removeImplicit stops tagging the given key as having been implicitly created. +func (p *parser) removeImplicit(key Key) { + p.implicits[key.String()] = false +} + +// isImplicit returns true if the key group pointed to by the key was created +// implicitly. +func (p *parser) isImplicit(key Key) bool { + return p.implicits[key.String()] +} + +// current returns the full key name of the current context. +func (p *parser) current() string { + if len(p.currentKey) == 0 { + return p.context.String() + } + if len(p.context) == 0 { + return p.currentKey + } + return fmt.Sprintf("%s.%s", p.context, p.currentKey) +} + +func replaceEscapes(s string) string { + return strings.NewReplacer( + "\\b", "\u0008", + "\\t", "\u0009", + "\\n", "\u000A", + "\\f", "\u000C", + "\\r", "\u000D", + "\\\"", "\u0022", + "\\/", "\u002F", + "\\\\", "\u005C", + ).Replace(s) +} + +func (p *parser) replaceUnicode(s string) string { + indexEsc := func() int { + return strings.Index(s, "\\u") + } + for i := indexEsc(); i != -1; i = indexEsc() { + asciiBytes := s[i+2 : i+6] + s = strings.Replace(s, s[i:i+6], p.asciiEscapeToUnicode(asciiBytes), -1) + } + return s +} + +func (p *parser) asciiEscapeToUnicode(s string) string { + hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) + if err != nil { + p.bug("Could not parse '%s' as a hexadecimal number, but the "+ + "lexer claims it's OK: %s", s, err) + } + + // BUG(burntsushi) + // I honestly don't understand how this works. I can't seem + // to find a way to make this fail. I figured this would fail on invalid + // UTF-8 characters like U+DCFF, but it doesn't. + r := string(rune(hex)) + if !utf8.ValidString(r) { + p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) + } + return string(r) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/session.vim b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/session.vim new file mode 100644 index 0000000..562164b --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/session.vim @@ -0,0 +1 @@ +au BufWritePost *.go silent!make tags > /dev/null 2>&1 diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_check.go new file mode 100644 index 0000000..79dac6b --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_check.go @@ -0,0 +1,85 @@ +package toml + +// tomlType represents any Go type that corresponds to a TOML type. +// While the first draft of the TOML spec has a simplistic type system that +// probably doesn't need this level of sophistication, we seem to be militating +// toward adding real composite types. +type tomlType interface { + typeString() string +} + +// typeEqual accepts any two types and returns true if they are equal. +func typeEqual(t1, t2 tomlType) bool { + if t1 == nil || t2 == nil { + return false + } + return t1.typeString() == t2.typeString() +} + +func typeIsHash(t tomlType) bool { + return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) +} + +type tomlBaseType string + +func (btype tomlBaseType) typeString() string { + return string(btype) +} + +func (btype tomlBaseType) String() string { + return btype.typeString() +} + +var ( + tomlInteger tomlBaseType = "Integer" + tomlFloat tomlBaseType = "Float" + tomlDatetime tomlBaseType = "Datetime" + tomlString tomlBaseType = "String" + tomlBool tomlBaseType = "Bool" + tomlArray tomlBaseType = "Array" + tomlHash tomlBaseType = "Hash" + tomlArrayHash tomlBaseType = "ArrayHash" +) + +// typeOfPrimitive returns a tomlType of any primitive value in TOML. +// Primitive values are: Integer, Float, Datetime, String and Bool. +// +// Passing a lexer item other than the following will cause a BUG message +// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. +func (p *parser) typeOfPrimitive(lexItem item) tomlType { + switch lexItem.typ { + case itemInteger: + return tomlInteger + case itemFloat: + return tomlFloat + case itemDatetime: + return tomlDatetime + case itemString: + return tomlString + case itemBool: + return tomlBool + } + p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) + panic("unreachable") +} + +// typeOfArray returns a tomlType for an array given a list of types of its +// values. +// +// In the current spec, if an array is homogeneous, then its type is always +// "Array". If the array is not homogeneous, an error is generated. +func (p *parser) typeOfArray(types []tomlType) tomlType { + // Empty arrays are cool. + if len(types) == 0 { + return tomlArray + } + + theType := types[0] + for _, t := range types[1:] { + if !typeEqual(theType, t) { + p.panicf("Array contains values of type '%s' and '%s', but arrays "+ + "must be homogeneous.", theType, t) + } + } + return tomlArray +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_fields.go new file mode 100644 index 0000000..7592f87 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/BurntSushi/toml/type_fields.go @@ -0,0 +1,241 @@ +package toml + +// Struct field handling is adapted from code in encoding/json: +// +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the Go distribution. + +import ( + "reflect" + "sort" + "sync" +) + +// A field represents a single field found in a struct. +type field struct { + name string // the name of the field (`toml` tag included) + tag bool // whether field has a `toml` tag + index []int // represents the depth of an anonymous field + typ reflect.Type // the type of the field +} + +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from toml tag", then +// breaking ties with index sequence. +type byName []field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false + } + if xik != x[j].index[k] { + return xik < x[j].index[k] + } + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that TOML should recognize for the given +// type. The algorithm is breadth-first search over the set of structs to +// include - the top struct and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} + + for _, f := range current { + if visited[f.typ] { + continue + } + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" { // unexported + continue + } + name := sf.Tag.Get("toml") + if name == "-" { + continue + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := name != "" + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + f := field{name: ft.Name(), index: index, typ: ft} + next = append(next, f) + } + } + } + } + + sort.Sort(byName(fields)) + + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with TOML tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. + out := fields[:0] + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + + fields = out + sort.Sort(byIndex(fields)) + + return fields +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// TOML tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitattributes b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitattributes new file mode 100644 index 0000000..b0ff811 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitattributes @@ -0,0 +1 @@ +README.md merge=ours diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore new file mode 100644 index 0000000..ba8e0cb --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml new file mode 100644 index 0000000..0305748 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml @@ -0,0 +1,4 @@ +language: go + +before_script: + - mysql -e 'create database gotest;' diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE new file mode 100644 index 0000000..14e2f77 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + 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/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md new file mode 100644 index 0000000..4407086 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/README.md @@ -0,0 +1,188 @@ +# Go-MySQL-Driver + +A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package + +![Go-MySQL-Driver logo](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow") + +**Current tagged Release:** June 03, 2013 (Version 1.0.1) + +--------------------------------------- + * [Features](#features) + * [Requirements](#requirements) + * [Installation](#installation) + * [Usage](#usage) + * [DSN (Data Source Name)](#dsn-data-source-name) + * [Password](#password) + * [Protocol](#protocol) + * [Address](#address) + * [Parameters](#parameters) + * [Examples](#examples) + * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support) + * [time.Time support](#timetime-support) + * [Testing / Development](#testing--development) + * [License](#license) + +--------------------------------------- + +## Features + * Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance") + * Native Go implementation. No C-bindings, just pure Go + * Connections over TCP/IPv4, TCP/IPv6 or Unix domain sockets + * Automatic handling of broken connections + * Automatic Connection Pooling *(by database/sql package)* + * Supports queries larger than 16MB + * Full [`sql.RawBytes`](http://golang.org/pkg/database/sql/#RawBytes) support. + * Intelligent `LONG DATA` handling in prepared statements + * Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support + * Optional `time.Time` parsing + +## Requirements + * Go 1.0.3 or higher + * MySQL (Version 4.1 or higher), MariaDB or Percona Server + +--------------------------------------- + +## Installation +Simple install the package to your [$GOPATH](http://code.google.com/p/go-wiki/wiki/GOPATH "GOPATH") with the [go tool](http://golang.org/cmd/go/ "go command") from shell: +```bash +$ go get github.com/go-sql-driver/mysql +``` +Make sure [Git is installed](http://git-scm.com/downloads) on your machine and in your system's `PATH`. + +## Usage +_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`](http://golang.org/pkg/database/sql) API then. + +Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`: +```go +import "database/sql" +import _ "github.com/go-sql-driver/mysql" + +db, e := sql.Open("mysql", "user:password@/dbname?charset=utf8") +``` + +[Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples"). + + +### DSN (Data Source Name) + +The Data Source Name has a common format, like e.g. [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php) uses it, but without type-prefix (optional parts marked by squared brackets): +``` +[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] +``` + +A DSN in its fullest form: +``` +username:password@protocol(address)/dbname?param=value +``` + +Except of the databasename, all values are optional. So the minimal DSN is: +``` +/dbname +``` + +If you do not want to preselect a database, leave `dbname` empty: +``` +/ +``` + +#### Password +Passwords can consist of any character. Escaping is **not** necessary. + +#### Protocol +See [net.Dial](http://golang.org/pkg/net/#Dial) for more information which networks are available. +In general you should use an Unix domain socket if available and TCP otherwise for best performance. + +#### Address +For TCP and UDP networks, addresses have the form `host:port`. +If `host` is a literal IPv6 address, it must be enclosed in square brackets. +The functions [net.JoinHostPort](http://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](http://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form. + +For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`. + +#### Parameters +***Parameters are case-sensitive!*** + +Possible Parameters are: + * `timeout`: **Driver** side connection timeout. The value must be a string of decimal numbers, each with optional fraction and a unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout). + * `charset`: Sets the charset used for client-server interaction ("SET NAMES `value`"). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`). + * `allowAllFiles`: `allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. *Might be insecure!* + * `parseTime`: `parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string` + * `loc`: Sets the location for time.Time values (when using `parseTime=true`). The default is `UTC`. *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details. + * `strict`: Enable strict mode. MySQL warnings are treated as errors. + +All other parameters are interpreted as system variables: + * `autocommit`: *"SET autocommit=`value`"* + * `time_zone`: *"SET time_zone=`value`"* + * `tx_isolation`: *"SET [tx_isolation](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation)=`value`"* + * `param`: *"SET `param`=`value`"* + +#### Examples +``` +user@unix(/path/to/socket)/dbname +``` + +``` +user:password@tcp(localhost:5555)/dbname?charset=utf8&autocommit=true +``` + +``` +user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname?charset=utf8mb4,utf8 +``` + +``` +user:password@/dbname +``` + +No Database preselected: +``` +user:password@/ +``` + +### `LOAD DATA LOCAL INFILE` support +For this feature you need direct access to the package. Therefore you must change the import path (no `_`): +```go +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). + +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::` then. + +See also the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") + +### `time.Time` support +The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your programm. + +However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](http://golang.org/pkg/time/#Location) with the `loc` DSN parameter. + +**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`](http://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`. + + + +## Testing / Development +To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details. + +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). + +Code changes must be proposed via a Pull Request and must be reviewed. Only *LGTM*-ed (" *Looks good to me* ") code may be committed to the master branch. + +--------------------------------------- + +## License +Go-MySQL-Driver is licensed under the [Mozilla Public License Version 2.0](https://raw.github.com/go-sql-driver/mysql/master/LICENSE) + +Mozilla summarizes the license scope as follows: +> MPL: The copyleft applies to any files containing MPLed code. + + +That means: + * You can **use** the **unchanged** source code both in private as also commercial + * You **needn't publish** the source code of your library as long the files licensed under the MPL 2.0 are **unchanged** + * You **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0) + +Please read the [MPL 2.0 FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license. + +You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE) diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go new file mode 100644 index 0000000..99b8ab7 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go @@ -0,0 +1,88 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import "io" + +const defaultBufSize = 4096 + +type buffer struct { + buf []byte + rd io.Reader + idx int + length int +} + +func newBuffer(rd io.Reader) *buffer { + return &buffer{ + buf: make([]byte, defaultBufSize), + rd: rd, + } +} + +// fill reads into the buffer until at least _need_ bytes are in it +func (b *buffer) fill(need int) (err error) { + // move existing data to the beginning + if b.length > 0 && b.idx > 0 { + copy(b.buf[0:b.length], b.buf[b.idx:]) + } + + // grow buffer if necessary + if need > len(b.buf) { + b.grow(need) + } + + b.idx = 0 + + var n int + for b.length < need { + n, err = b.rd.Read(b.buf[b.length:]) + b.length += n + + if err == nil { + continue + } + return // err + } + + return +} + +// grow the buffer to at least the given size +// credit for this code snippet goes to Maxim Khitrov +// https://groups.google.com/forum/#!topic/golang-nuts/ETbw1ECDgRs +func (b *buffer) grow(size int) { + // If append would be too expensive, alloc a new slice + if size > 2*cap(b.buf) { + newBuf := make([]byte, size) + copy(newBuf, b.buf) + b.buf = newBuf + return + } + + for cap(b.buf) < size { + b.buf = append(b.buf[:cap(b.buf)], 0) + } + b.buf = b.buf[:cap(b.buf)] +} + +// returns next N bytes from buffer. +// The returned slice is only guaranteed to be valid until the next read +func (b *buffer) readNext(need int) (p []byte, err error) { + if b.length < need { + // refill + err = b.fill(need) // err deferred + } + + p = b.buf[b.idx : b.idx+need] + b.idx += need + b.length -= need + return +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go new file mode 100644 index 0000000..07b212e --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go @@ -0,0 +1,241 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "database/sql/driver" + "errors" + "net" + "strings" + "time" +) + +type mysqlConn struct { + cfg *config + flags clientFlag + charset byte + cipher []byte + netConn net.Conn + buf *buffer + protocol uint8 + sequence uint8 + affectedRows uint64 + insertId uint64 + maxPacketAllowed int + maxWriteSize int + parseTime bool + strict bool +} + +type config struct { + user string + passwd string + net string + addr string + dbname string + params map[string]string + loc *time.Location +} + +// Handles parameters set in DSN +func (mc *mysqlConn) handleParams() (err error) { + for param, val := range mc.cfg.params { + switch param { + // Charset + case "charset": + charsets := strings.Split(val, ",") + for i := range charsets { + // ignore errors here - a charset may not exist + err = mc.exec("SET NAMES " + charsets[i]) + if err == nil { + break + } + } + if err != nil { + return + } + + // handled elsewhere + case "timeout", "allowAllFiles", "loc": + continue + + // time.Time parsing + case "parseTime": + mc.parseTime = readBool(val) + + // Strict mode + case "strict": + mc.strict = readBool(val) + + // TLS-Encryption + case "tls": + err = errors.New("TLS-Encryption not implemented yet") + return + + // Compression + case "compress": + err = errors.New("Compression not implemented yet") + + // System Vars + default: + err = mc.exec("SET " + param + "=" + val + "") + if err != nil { + return + } + } + } + + return +} + +func (mc *mysqlConn) Begin() (driver.Tx, error) { + err := mc.exec("START TRANSACTION") + if err == nil { + return &mysqlTx{mc}, err + } + + return nil, err +} + +func (mc *mysqlConn) Close() (err error) { + mc.writeCommandPacket(comQuit) + mc.cfg = nil + mc.buf = nil + mc.netConn.Close() + mc.netConn = nil + return +} + +func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { + // Send command + err := mc.writeCommandPacketStr(comStmtPrepare, query) + if err != nil { + return nil, err + } + + stmt := &mysqlStmt{ + mc: mc, + } + + // Read Result + columnCount, err := stmt.readPrepareResultPacket() + if err == nil { + if stmt.paramCount > 0 { + stmt.params, err = stmt.mc.readColumns(stmt.paramCount) + if err != nil { + return nil, err + } + } + + if columnCount > 0 { + err = stmt.mc.readUntilEOF() + } + } + + return stmt, err +} + +func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { + if len(args) == 0 { // no args, fastpath + mc.affectedRows = 0 + mc.insertId = 0 + + err := mc.exec(query) + if err == nil { + return &mysqlResult{ + affectedRows: int64(mc.affectedRows), + insertId: int64(mc.insertId), + }, err + } + return nil, err + } + + // with args, must use prepared stmt + return nil, driver.ErrSkip + +} + +// Internal function to execute commands +func (mc *mysqlConn) exec(query string) (err error) { + // Send command + err = mc.writeCommandPacketStr(comQuery, query) + if err != nil { + return + } + + // Read Result + var resLen int + resLen, err = mc.readResultSetHeaderPacket() + if err == nil && resLen > 0 { + err = mc.readUntilEOF() + if err != nil { + return + } + + err = mc.readUntilEOF() + } + + return +} + +func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) { + if len(args) == 0 { // no args, fastpath + // Send command + err := mc.writeCommandPacketStr(comQuery, query) + if err == nil { + // Read Result + var resLen int + resLen, err = mc.readResultSetHeaderPacket() + if err == nil { + rows := &mysqlRows{mc, false, nil, false} + + if resLen > 0 { + // Columns + rows.columns, err = mc.readColumns(resLen) + } + return rows, err + } + } + + return nil, err + } + + // with args, must use prepared stmt + return nil, driver.ErrSkip +} + +// Gets the value of the given MySQL System Variable +// The returned byte slice is only valid until the next read +func (mc *mysqlConn) getSystemVar(name string) (val []byte, err error) { + // Send command + err = mc.writeCommandPacketStr(comQuery, "SELECT @@"+name) + if err == nil { + // Read Result + var resLen int + resLen, err = mc.readResultSetHeaderPacket() + if err == nil { + rows := &mysqlRows{mc, false, nil, false} + + if resLen > 0 { + // Columns + rows.columns, err = mc.readColumns(resLen) + } + + dest := make([]driver.Value, resLen) + err = rows.readRow(dest) + if err == nil { + val = dest[0].([]byte) + err = mc.readUntilEOF() + } + } + } + + return +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go new file mode 100644 index 0000000..3822e3d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go @@ -0,0 +1,133 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +const ( + minProtocolVersion byte = 10 + maxPacketSize = 1<<24 - 1 + timeFormat = "2006-01-02 15:04:05" +) + +// MySQL constants documentation: +// http://dev.mysql.com/doc/internals/en/client-server-protocol.html + +const ( + iOK byte = 0x00 + iLocalInFile byte = 0xfb + iEOF byte = 0xfe + iERR byte = 0xff +) + +type clientFlag uint32 + +const ( + clientLongPassword clientFlag = 1 << iota + clientFoundRows + clientLongFlag + clientConnectWithDB + clientNoSchema + clientCompress + clientODBC + clientLocalFiles + clientIgnoreSpace + clientProtocol41 + clientInteractive + clientSSL + clientIgnoreSIGPIPE + clientTransactions + clientReserved + clientSecureConn + clientMultiStatements + clientMultiResults +) + +const ( + comQuit byte = iota + 1 + comInitDB + comQuery + comFieldList + comCreateDB + comDropDB + comRefresh + comShutdown + comStatistics + comProcessInfo + comConnect + comProcessKill + comDebug + comPing + comTime + comDelayedInsert + comChangeUser + comBinlogDump + comTableDump + comConnectOut + comRegiserSlave + comStmtPrepare + comStmtExecute + comStmtSendLongData + comStmtClose + comStmtReset + comSetOption + comStmtFetch +) + +const ( + fieldTypeDecimal byte = iota + fieldTypeTiny + fieldTypeShort + fieldTypeLong + fieldTypeFloat + fieldTypeDouble + fieldTypeNULL + fieldTypeTimestamp + fieldTypeLongLong + fieldTypeInt24 + fieldTypeDate + fieldTypeTime + fieldTypeDateTime + fieldTypeYear + fieldTypeNewDate + fieldTypeVarChar + fieldTypeBit +) +const ( + fieldTypeNewDecimal byte = iota + 0xf6 + fieldTypeEnum + fieldTypeSet + fieldTypeTinyBLOB + fieldTypeMediumBLOB + fieldTypeLongBLOB + fieldTypeBLOB + fieldTypeVarString + fieldTypeString + fieldTypeGeometry +) + +type fieldFlag uint16 + +const ( + flagNotNULL fieldFlag = 1 << iota + flagPriKey + flagUniqueKey + flagMultipleKey + flagBLOB + flagUnsigned + flagZeroFill + flagBinary + flagEnum + flagAutoIncrement + flagTimestamp + flagSet + flagUnknown1 + flagUnknown2 + flagUnknown3 + flagUnknown4 +) diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go new file mode 100644 index 0000000..6fd6714 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go @@ -0,0 +1,90 @@ +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +package mysql + +import ( + "database/sql" + "database/sql/driver" + "net" + "time" +) + +type mysqlDriver struct{} + +// Open new Connection. +// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how +// the DSN string is formated +func (d *mysqlDriver) Open(dsn string) (driver.Conn, error) { + var err error + + // New mysqlConn + mc := &mysqlConn{ + maxPacketAllowed: maxPacketSize, + maxWriteSize: maxPacketSize - 1, + } + mc.cfg, err = parseDSN(dsn) + if err != nil { + return nil, err + } + + // Connect to Server + if _, ok := mc.cfg.params["timeout"]; ok { // with timeout + var timeout time.Duration + timeout, err = time.ParseDuration(mc.cfg.params["timeout"]) + if err == nil { + mc.netConn, err = net.DialTimeout(mc.cfg.net, mc.cfg.addr, timeout) + } + } else { // no timeout + mc.netConn, err = net.Dial(mc.cfg.net, mc.cfg.addr) + } + if err != nil { + return nil, err + } + mc.buf = newBuffer(mc.netConn) + + // Reading Handshake Initialization Packet + err = mc.readInitPacket() + if err != nil { + return nil, err + } + + // Send Client Authentication Packet + err = mc.writeAuthPacket() + if err != nil { + return nil, err + } + + // Read Result Packet + err = mc.readResultOK() + if err != nil { + return nil, err + } + + // Get max allowed packet size + maxap, err := mc.getSystemVar("max_allowed_packet") + if err != nil { + return nil, err + } + mc.maxPacketAllowed = stringToInt(maxap) - 1 + if mc.maxPacketAllowed < maxPacketSize { + mc.maxWriteSize = mc.maxPacketAllowed + } + + // Handle DSN Params + err = mc.handleParams() + if err != nil { + return nil, err + } + + return mc, err +} + +func init() { + sql.Register("mysql", &mysqlDriver{}) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go new file mode 100644 index 0000000..30aa4ff --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go @@ -0,0 +1,1121 @@ +package mysql + +import ( + "database/sql" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "strings" + "testing" + "time" +) + +var ( + charset string + dsn string + netAddr string + available bool +) + +var ( + tDate = time.Date(2012, 6, 14, 0, 0, 0, 0, time.UTC) + sDate = "2012-06-14" + tDateTime = time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC) + sDateTime = "2011-11-20 21:27:37" + tDate0 = time.Time{} + sDate0 = "0000-00-00" + sDateTime0 = "0000-00-00 00:00:00" +) + +// See https://github.com/go-sql-driver/mysql/wiki/Testing +func init() { + env := func(key, defaultValue string) string { + if value := os.Getenv(key); value != "" { + return value + } + return defaultValue + } + user := env("MYSQL_TEST_USER", "root") + pass := env("MYSQL_TEST_PASS", "") + prot := env("MYSQL_TEST_PROT", "tcp") + addr := env("MYSQL_TEST_ADDR", "localhost:3306") + dbname := env("MYSQL_TEST_DBNAME", "gotest") + charset = "charset=utf8" + netAddr = fmt.Sprintf("%s(%s)", prot, addr) + dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&strict=true&"+charset, user, pass, netAddr, dbname) + c, err := net.Dial(prot, addr) + if err == nil { + available = true + c.Close() + } +} + +type DBTest struct { + *testing.T + db *sql.DB +} + +func runTests(t *testing.T, name, dsn string, tests ...func(dbt *DBTest)) { + if !available { + t.Logf("MySQL-Server not running on %s. Skipping %s", netAddr, name) + return + } + + db, err := sql.Open("mysql", dsn) + if err != nil { + t.Fatalf("Error connecting: %v", err) + } + defer db.Close() + + db.Exec("DROP TABLE IF EXISTS test") + + dbt := &DBTest{t, db} + for _, test := range tests { + test(dbt) + dbt.db.Exec("DROP TABLE IF EXISTS test") + } +} + +func (dbt *DBTest) fail(method, query string, err error) { + if len(query) > 300 { + query = "[query too large to print]" + } + dbt.Fatalf("Error on %s %s: %v", method, query, err) +} + +func (dbt *DBTest) mustExec(query string, args ...interface{}) (res sql.Result) { + res, err := dbt.db.Exec(query, args...) + if err != nil { + dbt.fail("Exec", query, err) + } + return res +} + +func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows) { + rows, err := dbt.db.Query(query, args...) + if err != nil { + dbt.fail("Query", query, err) + } + return rows +} + +func TestCharset(t *testing.T) { + mustSetCharset := func(charsetParam, expected string) { + db, err := sql.Open("mysql", strings.Replace(dsn, charset, charsetParam, 1)) + if err != nil { + t.Fatalf("Error on Open: %v", err) + } + defer db.Close() + + dbt := &DBTest{t, db} + rows := dbt.mustQuery("SELECT @@character_set_connection") + defer rows.Close() + + if !rows.Next() { + dbt.Fatalf("Error getting connection charset: %v", err) + } + + var got string + rows.Scan(&got) + + if got != expected { + dbt.Fatalf("Expected connection charset %s but got %s", expected, got) + } + } + + if !available { + t.Logf("MySQL-Server not running on %s. Skipping TestCharset", netAddr) + return + } + + // non utf8 test + mustSetCharset("charset=ascii", "ascii") + + // when the first charset is invalid, use the second + mustSetCharset("charset=none,utf8", "utf8") + + // when the first charset is valid, use it + mustSetCharset("charset=ascii,utf8", "ascii") + mustSetCharset("charset=utf8,ascii", "utf8") +} + +func TestFailingCharset(t *testing.T) { + if !available { + t.Logf("MySQL-Server not running on %s. Skipping TestFailingCharset", netAddr) + return + } + db, err := sql.Open("mysql", strings.Replace(dsn, charset, "charset=none", 1)) + if err != nil { + t.Fatalf("Error on Open: %v", err) + } + defer db.Close() + + // run query to really establish connection... + _, err = db.Exec("SELECT 1") + if err == nil { + db.Close() + t.Fatalf("Connection must not succeed without a valid charset") + } +} + +func TestRawBytesResultExceedsBuffer(t *testing.T) { + runTests(t, "TestRawBytesResultExceedsBuffer", dsn, func(dbt *DBTest) { + // defaultBufSize from buffer.go + expected := strings.Repeat("abc", defaultBufSize) + rows := dbt.mustQuery("SELECT '" + expected + "'") + defer rows.Close() + if !rows.Next() { + dbt.Error("expected result, got none") + } + var result sql.RawBytes + rows.Scan(&result) + if expected != string(result) { + dbt.Error("result did not match expected value") + } + }) +} + +func TestCRUD(t *testing.T) { + runTests(t, "TestCRUD", dsn, func(dbt *DBTest) { + // Create Table + dbt.mustExec("CREATE TABLE test (value BOOL)") + + // Test for unexpected data + var out bool + rows := dbt.mustQuery("SELECT * FROM test") + if rows.Next() { + dbt.Error("unexpected data in empty table") + } + + // Create Data + res := dbt.mustExec("INSERT INTO test VALUES (1)") + count, err := res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %v", err) + } + if count != 1 { + dbt.Fatalf("Expected 1 affected row, got %d", count) + } + + id, err := res.LastInsertId() + if err != nil { + dbt.Fatalf("res.LastInsertId() returned error: %v", err) + } + if id != 0 { + dbt.Fatalf("Expected InsertID 0, got %d", id) + } + + // Read + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if true != out { + dbt.Errorf("true != %t", out) + } + + if rows.Next() { + dbt.Error("unexpected data") + } + } else { + dbt.Error("no data") + } + + // Update + res = dbt.mustExec("UPDATE test SET value = ? WHERE value = ?", false, true) + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %v", err) + } + if count != 1 { + dbt.Fatalf("Expected 1 affected row, got %d", count) + } + + // Check Update + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if false != out { + dbt.Errorf("false != %t", out) + } + + if rows.Next() { + dbt.Error("unexpected data") + } + } else { + dbt.Error("no data") + } + + // Delete + res = dbt.mustExec("DELETE FROM test WHERE value = ?", false) + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %v", err) + } + if count != 1 { + dbt.Fatalf("Expected 1 affected row, got %d", count) + } + + // Check for unexpected rows + res = dbt.mustExec("DELETE FROM test") + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %v", err) + } + if count != 0 { + dbt.Fatalf("Expected 0 affected row, got %d", count) + } + }) +} + +func TestInt(t *testing.T) { + runTests(t, "TestInt", dsn, func(dbt *DBTest) { + types := [5]string{"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT"} + in := int64(42) + var out int64 + var rows *sql.Rows + + // SIGNED + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + ")") + + dbt.mustExec("INSERT INTO test VALUES (?)", in) + + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s: %d != %d", v, in, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + + dbt.mustExec("DROP TABLE IF EXISTS test") + } + + // UNSIGNED ZEROFILL + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + " ZEROFILL)") + + dbt.mustExec("INSERT INTO test VALUES (?)", in) + + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s ZEROFILL: %d != %d", v, in, out) + } + } else { + dbt.Errorf("%s ZEROFILL: no data", v) + } + + dbt.mustExec("DROP TABLE IF EXISTS test") + } + }) +} + +func TestFloat(t *testing.T) { + runTests(t, "TestFloat", dsn, func(dbt *DBTest) { + types := [2]string{"FLOAT", "DOUBLE"} + in := float32(42.23) + var out float32 + var rows *sql.Rows + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + ")") + dbt.mustExec("INSERT INTO test VALUES (?)", in) + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s: %g != %g", v, in, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + dbt.mustExec("DROP TABLE IF EXISTS test") + } + }) +} + +func TestString(t *testing.T) { + runTests(t, "TestString", dsn, func(dbt *DBTest) { + types := [6]string{"CHAR(255)", "VARCHAR(255)", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"} + in := "κόσμε üöäßñóùéàâÿœ'îë Árvíztűrő いろはにほへとちりぬるを イロハニホヘト דג סקרן чащах น่าฟังเอย" + var out string + var rows *sql.Rows + + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + ") CHARACTER SET utf8") + + dbt.mustExec("INSERT INTO test VALUES (?)", in) + + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s: %s != %s", v, in, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + + dbt.mustExec("DROP TABLE IF EXISTS test") + } + + // BLOB + dbt.mustExec("CREATE TABLE test (id int, value BLOB) CHARACTER SET utf8") + + id := 2 + in = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " + + "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " + + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " + + "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + dbt.mustExec("INSERT INTO test VALUES (?, ?)", id, in) + + err := dbt.db.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&out) + if err != nil { + dbt.Fatalf("Error on BLOB-Query: %v", err) + } else if out != in { + dbt.Errorf("BLOB: %s != %s", in, out) + } + }) +} + +func TestDateTime(t *testing.T) { + type testmode struct { + selectSuffix string + args []interface{} + } + type timetest struct { + in interface{} + sOut string + tOut time.Time + tIsZero bool + } + type tester func(dbt *DBTest, rows *sql.Rows, + test *timetest, sqltype, resulttype, mode string) + type setup struct { + vartype string + dsnSuffix string + test tester + } + var ( + modes = map[string]*testmode{ + "text": &testmode{}, + "binary": &testmode{" WHERE 1 = ?", []interface{}{1}}, + } + timetests = map[string][]*timetest{ + "DATE": { + {sDate, sDate, tDate, false}, + {sDate0, sDate0, tDate0, true}, + {tDate, sDate, tDate, false}, + {tDate0, sDate0, tDate0, true}, + }, + "DATETIME": { + {sDateTime, sDateTime, tDateTime, false}, + {sDateTime0, sDateTime0, tDate0, true}, + {tDateTime, sDateTime, tDateTime, false}, + {tDate0, sDateTime0, tDate0, true}, + }, + } + setups = []*setup{ + {"string", "&parseTime=false", func( + dbt *DBTest, rows *sql.Rows, test *timetest, sqltype, resulttype, mode string) { + var sOut string + if err := rows.Scan(&sOut); err != nil { + dbt.Errorf("%s (%s %s): %v", sqltype, resulttype, mode, err) + } else if test.sOut != sOut { + dbt.Errorf("%s (%s %s): %s != %s", sqltype, resulttype, mode, test.sOut, sOut) + } + }}, + {"time.Time", "&parseTime=true", func( + dbt *DBTest, rows *sql.Rows, test *timetest, sqltype, resulttype, mode string) { + var tOut time.Time + if err := rows.Scan(&tOut); err != nil { + dbt.Errorf("%s (%s %s): %v", sqltype, resulttype, mode, err) + } else if test.tOut != tOut || test.tIsZero != tOut.IsZero() { + dbt.Errorf("%s (%s %s): %s [%t] != %s [%t]", sqltype, resulttype, mode, test.tOut, test.tIsZero, tOut, tOut.IsZero()) + } + }}, + } + ) + + var s *setup + testTime := func(dbt *DBTest) { + var rows *sql.Rows + for sqltype, tests := range timetests { + dbt.mustExec("CREATE TABLE test (value " + sqltype + ")") + for _, test := range tests { + for mode, q := range modes { + dbt.mustExec("TRUNCATE test") + dbt.mustExec("INSERT INTO test VALUES (?)", test.in) + rows = dbt.mustQuery("SELECT value FROM test"+q.selectSuffix, q.args...) + if rows.Next() { + s.test(dbt, rows, test, sqltype, s.vartype, mode) + } else { + if err := rows.Err(); err != nil { + dbt.Errorf("%s (%s %s): %v", + sqltype, s.vartype, mode, err) + } else { + dbt.Errorf("%s (%s %s): no data", + sqltype, s.vartype, mode) + } + } + } + } + dbt.mustExec("DROP TABLE IF EXISTS test") + } + } + + timeDsn := dsn + "&sql_mode=ALLOW_INVALID_DATES" + for _, v := range setups { + s = v + runTests(t, "TestDateTime", timeDsn+s.dsnSuffix, testTime) + } +} + +func TestNULL(t *testing.T) { + runTests(t, "TestNULL", dsn, func(dbt *DBTest) { + nullStmt, err := dbt.db.Prepare("SELECT NULL") + if err != nil { + dbt.Fatal(err) + } + defer nullStmt.Close() + + nonNullStmt, err := dbt.db.Prepare("SELECT 1") + if err != nil { + dbt.Fatal(err) + } + defer nonNullStmt.Close() + + // NullBool + var nb sql.NullBool + // Invalid + err = nullStmt.QueryRow().Scan(&nb) + if err != nil { + dbt.Fatal(err) + } + if nb.Valid { + dbt.Error("Valid NullBool which should be invalid") + } + // Valid + err = nonNullStmt.QueryRow().Scan(&nb) + if err != nil { + dbt.Fatal(err) + } + if !nb.Valid { + dbt.Error("Invalid NullBool which should be valid") + } else if nb.Bool != true { + dbt.Errorf("Unexpected NullBool value: %t (should be true)", nb.Bool) + } + + // NullFloat64 + var nf sql.NullFloat64 + // Invalid + err = nullStmt.QueryRow().Scan(&nf) + if err != nil { + dbt.Fatal(err) + } + if nf.Valid { + dbt.Error("Valid NullFloat64 which should be invalid") + } + // Valid + err = nonNullStmt.QueryRow().Scan(&nf) + if err != nil { + dbt.Fatal(err) + } + if !nf.Valid { + dbt.Error("Invalid NullFloat64 which should be valid") + } else if nf.Float64 != float64(1) { + dbt.Errorf("Unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64) + } + + // NullInt64 + var ni sql.NullInt64 + // Invalid + err = nullStmt.QueryRow().Scan(&ni) + if err != nil { + dbt.Fatal(err) + } + if ni.Valid { + dbt.Error("Valid NullInt64 which should be invalid") + } + // Valid + err = nonNullStmt.QueryRow().Scan(&ni) + if err != nil { + dbt.Fatal(err) + } + if !ni.Valid { + dbt.Error("Invalid NullInt64 which should be valid") + } else if ni.Int64 != int64(1) { + dbt.Errorf("Unexpected NullInt64 value: %d (should be 1)", ni.Int64) + } + + // NullString + var ns sql.NullString + // Invalid + err = nullStmt.QueryRow().Scan(&ns) + if err != nil { + dbt.Fatal(err) + } + if ns.Valid { + dbt.Error("Valid NullString which should be invalid") + } + // Valid + err = nonNullStmt.QueryRow().Scan(&ns) + if err != nil { + dbt.Fatal(err) + } + if !ns.Valid { + dbt.Error("Invalid NullString which should be valid") + } else if ns.String != `1` { + dbt.Error("Unexpected NullString value:" + ns.String + " (should be `1`)") + } + + // Insert NULL + dbt.mustExec("CREATE TABLE test (dummmy1 int, value int, dummy2 int)") + + dbt.mustExec("INSERT INTO test VALUES (?, ?, ?)", 1, nil, 2) + + var out interface{} + rows := dbt.mustQuery("SELECT * FROM test") + if rows.Next() { + rows.Scan(&out) + if out != nil { + dbt.Errorf("%v != nil", out) + } + } else { + dbt.Error("no data") + } + }) +} + +func TestLongData(t *testing.T) { + runTests(t, "TestLongData", dsn, func(dbt *DBTest) { + var maxAllowedPacketSize int + err := dbt.db.QueryRow("select @@max_allowed_packet").Scan(&maxAllowedPacketSize) + if err != nil { + dbt.Fatal(err) + } + maxAllowedPacketSize-- + + // don't get too ambitious + if maxAllowedPacketSize > 1<<25 { + maxAllowedPacketSize = 1 << 25 + } + + dbt.mustExec("CREATE TABLE test (value LONGBLOB)") + + in := strings.Repeat(`0`, maxAllowedPacketSize+1) + var out string + var rows *sql.Rows + + // Long text data + const nonDataQueryLen = 28 // length query w/o value + inS := in[:maxAllowedPacketSize-nonDataQueryLen] + dbt.mustExec("INSERT INTO test VALUES('" + inS + "')") + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if inS != out { + dbt.Fatalf("LONGBLOB: length in: %d, length out: %d", len(inS), len(out)) + } + if rows.Next() { + dbt.Error("LONGBLOB: unexpexted row") + } + } else { + dbt.Fatalf("LONGBLOB: no data") + } + + // Empty table + dbt.mustExec("TRUNCATE TABLE test") + + // Long binary data + dbt.mustExec("INSERT INTO test VALUES(?)", in) + rows = dbt.mustQuery("SELECT value FROM test WHERE 1=?", 1) + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Fatalf("LONGBLOB: length in: %d, length out: %d", len(in), len(out)) + } + if rows.Next() { + dbt.Error("LONGBLOB: unexpexted row") + } + } else { + dbt.Fatalf("LONGBLOB: no data") + } + }) +} + +func TestLoadData(t *testing.T) { + runTests(t, "TestLoadData", dsn, func(dbt *DBTest) { + verifyLoadDataResult := func() { + rows, err := dbt.db.Query("SELECT * FROM test") + if err != nil { + dbt.Fatal(err.Error()) + } + + i := 0 + values := [4]string{ + "a string", + "a string containing a \t", + "a string containing a \n", + "a string containing both \t\n", + } + + var id int + var value string + + for rows.Next() { + i++ + err = rows.Scan(&id, &value) + if err != nil { + dbt.Fatal(err.Error()) + } + if i != id { + dbt.Fatalf("%d != %d", i, id) + } + if values[i-1] != value { + dbt.Fatalf("%s != %s", values[i-1], value) + } + } + err = rows.Err() + if err != nil { + dbt.Fatal(err.Error()) + } + + if i != 4 { + dbt.Fatalf("Rows count mismatch. Got %d, want 4", i) + } + } + file, err := ioutil.TempFile("", "gotest") + defer os.Remove(file.Name()) + if err != nil { + dbt.Fatal(err) + } + file.WriteString("1\ta string\n2\ta string containing a \\t\n3\ta string containing a \\n\n4\ta string containing both \\t\\n\n") + file.Close() + + dbt.db.Exec("DROP TABLE IF EXISTS test") + dbt.mustExec("CREATE TABLE test (id INT NOT NULL PRIMARY KEY, value TEXT NOT NULL) CHARACTER SET utf8") + + // Local File + RegisterLocalFile(file.Name()) + dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE '%q' INTO TABLE test", file.Name())) + verifyLoadDataResult() + // negative test + _, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'doesnotexist' INTO TABLE test") + if err == nil { + dbt.Fatal("Load non-existent file didn't fail") + } else if err.Error() != "Local File 'doesnotexist' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files" { + dbt.Fatal(err.Error()) + } + + // Empty table + dbt.mustExec("TRUNCATE TABLE test") + + // Reader + RegisterReaderHandler("test", func() io.Reader { + file, err = os.Open(file.Name()) + if err != nil { + dbt.Fatal(err) + } + return file + }) + dbt.mustExec("LOAD DATA LOCAL INFILE 'Reader::test' INTO TABLE test") + verifyLoadDataResult() + // negative test + _, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'Reader::doesnotexist' INTO TABLE test") + if err == nil { + dbt.Fatal("Load non-existent Reader didn't fail") + } else if err.Error() != "Reader 'doesnotexist' is not registered" { + dbt.Fatal(err.Error()) + } + }) +} + +func TestStrict(t *testing.T) { + // ALLOW_INVALID_DATES to get rid of stricter modes - we want to test for warnings, not errors + relaxedDsn := dsn + "&sql_mode=ALLOW_INVALID_DATES" + runTests(t, "TestStrict", relaxedDsn, func(dbt *DBTest) { + dbt.mustExec("CREATE TABLE test (a TINYINT NOT NULL, b CHAR(4))") + + var queries = [...]struct { + in string + codes []string + }{ + {"DROP TABLE IF EXISTS no_such_table", []string{"1051"}}, + {"INSERT INTO test VALUES(10,'mysql'),(NULL,'test'),(300,'Open Source')", []string{"1265", "1048", "1264", "1265"}}, + } + var err error + + var checkWarnings = func(err error, mode string, idx int) { + if err == nil { + dbt.Errorf("Expected STRICT error on query [%s] %s", mode, queries[idx].in) + } + + if warnings, ok := err.(MySQLWarnings); ok { + var codes = make([]string, len(warnings)) + for i := range warnings { + codes[i] = warnings[i].Code + } + if len(codes) != len(queries[idx].codes) { + dbt.Errorf("Unexpected STRICT error count on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes) + } + + for i := range warnings { + if codes[i] != queries[idx].codes[i] { + dbt.Errorf("Unexpected STRICT error codes on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes) + return + } + } + + } else { + dbt.Errorf("Unexpected error on query [%s] %s: %s", mode, queries[idx].in, err.Error()) + } + } + + // text protocol + for i := range queries { + _, err = dbt.db.Exec(queries[i].in) + checkWarnings(err, "text", i) + } + + var stmt *sql.Stmt + + // binary protocol + for i := range queries { + stmt, err = dbt.db.Prepare(queries[i].in) + if err != nil { + dbt.Error("Error on preparing query %: ", queries[i].in, err.Error()) + } + + _, err = stmt.Exec() + checkWarnings(err, "binary", i) + + err = stmt.Close() + if err != nil { + dbt.Error("Error on closing stmt for query %: ", queries[i].in, err.Error()) + } + } + }) +} + +// Special cases + +func TestRowsClose(t *testing.T) { + runTests(t, "TestRowsClose", dsn, func(dbt *DBTest) { + rows, err := dbt.db.Query("SELECT 1") + if err != nil { + dbt.Fatal(err) + } + + err = rows.Close() + if err != nil { + dbt.Fatal(err) + } + + if rows.Next() { + dbt.Fatal("Unexpected row after rows.Close()") + } + + err = rows.Err() + if err != nil { + dbt.Fatal(err) + } + }) +} + +// dangling statements +// http://code.google.com/p/go/issues/detail?id=3865 +func TestCloseStmtBeforeRows(t *testing.T) { + runTests(t, "TestCloseStmtBeforeRows", dsn, func(dbt *DBTest) { + stmt, err := dbt.db.Prepare("SELECT 1") + if err != nil { + dbt.Fatal(err) + } + + rows, err := stmt.Query() + if err != nil { + stmt.Close() + dbt.Fatal(err) + } + defer rows.Close() + + err = stmt.Close() + if err != nil { + dbt.Fatal(err) + } + + if !rows.Next() { + dbt.Fatal("Getting row failed") + } else { + err = rows.Err() + if err != nil { + dbt.Fatal(err) + } + + var out bool + err = rows.Scan(&out) + if err != nil { + dbt.Fatalf("Error on rows.Scan(): %v", err) + } + if out != true { + dbt.Errorf("true != %t", out) + } + } + }) +} + +// It is valid to have multiple Rows for the same Stmt +// http://code.google.com/p/go/issues/detail?id=3734 +func TestStmtMultiRows(t *testing.T) { + runTests(t, "TestStmtMultiRows", dsn, func(dbt *DBTest) { + stmt, err := dbt.db.Prepare("SELECT 1 UNION SELECT 0") + if err != nil { + dbt.Fatal(err) + } + + rows1, err := stmt.Query() + if err != nil { + stmt.Close() + dbt.Fatal(err) + } + defer rows1.Close() + + rows2, err := stmt.Query() + if err != nil { + stmt.Close() + dbt.Fatal(err) + } + defer rows2.Close() + + var out bool + + // 1 + if !rows1.Next() { + dbt.Fatal("1st rows1.Next failed") + } else { + err = rows1.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows1.Scan(&out) + if err != nil { + dbt.Fatalf("Error on rows.Scan(): %v", err) + } + if out != true { + dbt.Errorf("true != %t", out) + } + } + + if !rows2.Next() { + dbt.Fatal("1st rows2.Next failed") + } else { + err = rows2.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows2.Scan(&out) + if err != nil { + dbt.Fatalf("Error on rows.Scan(): %v", err) + } + if out != true { + dbt.Errorf("true != %t", out) + } + } + + // 2 + if !rows1.Next() { + dbt.Fatal("2nd rows1.Next failed") + } else { + err = rows1.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows1.Scan(&out) + if err != nil { + dbt.Fatalf("Error on rows.Scan(): %v", err) + } + if out != false { + dbt.Errorf("false != %t", out) + } + + if rows1.Next() { + dbt.Fatal("Unexpected row on rows1") + } + err = rows1.Close() + if err != nil { + dbt.Fatal(err) + } + } + + if !rows2.Next() { + dbt.Fatal("2nd rows2.Next failed") + } else { + err = rows2.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows2.Scan(&out) + if err != nil { + dbt.Fatalf("Error on rows.Scan(): %v", err) + } + if out != false { + dbt.Errorf("false != %t", out) + } + + if rows2.Next() { + dbt.Fatal("Unexpected row on rows2") + } + err = rows2.Close() + if err != nil { + dbt.Fatal(err) + } + } + }) +} + +func TestConcurrent(t *testing.T) { + if readBool(os.Getenv("MYSQL_TEST_CONCURRENT")) != true { + t.Log("CONCURRENT env var not set. Skipping TestConcurrent") + return + } + runTests(t, "TestConcurrent", dsn, func(dbt *DBTest) { + var max int + err := dbt.db.QueryRow("SELECT @@max_connections").Scan(&max) + if err != nil { + dbt.Fatalf("%v", err) + } + dbt.Logf("Testing up to %d concurrent connections \r\n", max) + canStop := false + c := make(chan struct{}, max) + for i := 0; i < max; i++ { + go func(id int) { + tx, err := dbt.db.Begin() + if err != nil { + canStop = true + if err.Error() == "Error 1040: Too many connections" { + max-- + return + } else { + dbt.Fatalf("Error on Con %d: %s", id, err.Error()) + } + } + c <- struct{}{} + for !canStop { + _, err = tx.Exec("SELECT 1") + if err != nil { + canStop = true + dbt.Fatalf("Error on Con %d: %s", id, err.Error()) + } + } + err = tx.Commit() + if err != nil { + canStop = true + dbt.Fatalf("Error on Con %d: %s", id, err.Error()) + } + }(i) + } + for i := 0; i < max; i++ { + <-c + } + canStop = true + + dbt.Logf("Reached %d concurrent connections \r\n", max) + }) +} + +// BENCHMARKS +var sample []byte + +func initBenchmarks() ([]byte, int, int) { + if sample == nil { + sample = []byte(strings.Repeat("0123456789abcdef", 1024*1024)) + } + return sample, 16, len(sample) +} + +func BenchmarkRoundtripText(b *testing.B) { + sample, min, max := initBenchmarks() + db, err := sql.Open("mysql", dsn) + if err != nil { + b.Fatalf("crashed") + } + defer db.Close() + var result string + for i := 0; i < b.N; i++ { + length := min + i + if length > max { + length = max + } + test := string(sample[0:length]) + rows, err := db.Query("SELECT \"" + test + "\"") + if err != nil { + b.Fatalf("crashed") + } + if !rows.Next() { + rows.Close() + b.Fatalf("crashed") + } + err = rows.Scan(&result) + if err != nil { + rows.Close() + b.Fatalf("crashed") + } + if result != test { + rows.Close() + b.Errorf("mismatch") + } + rows.Close() + } +} + +func BenchmarkRoundtripPrepared(b *testing.B) { + sample, min, max := initBenchmarks() + db, err := sql.Open("mysql", dsn) + if err != nil { + b.Fatalf("crashed") + } + defer db.Close() + var result string + stmt, err := db.Prepare("SELECT ?") + if err != nil { + b.Fatalf("crashed") + } + for i := 0; i < b.N; i++ { + length := min + i + if length > max { + length = max + } + test := string(sample[0:length]) + rows, err := stmt.Query(test) + if err != nil { + b.Fatalf("crashed") + } + if !rows.Next() { + rows.Close() + b.Fatalf("crashed") + } + err = rows.Scan(&result) + if err != nil { + rows.Close() + b.Fatalf("crashed") + } + if result != test { + rows.Close() + b.Errorf("mismatch") + } + rows.Close() + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go new file mode 100644 index 0000000..615e9cb --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go @@ -0,0 +1,104 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "database/sql/driver" + "errors" + "fmt" + "io" +) + +var ( + errMalformPkt = errors.New("Malformed Packet") + errPktSync = errors.New("Commands out of sync. You can't run this command now") + errPktSyncMul = errors.New("Commands out of sync. Did you run multiple statements at once?") + errOldPassword = errors.New("It seems like you are using old_passwords, which is unsupported. See https://github.com/go-sql-driver/mysql/wiki/old_passwords") + errPktTooLarge = errors.New("Packet for query is too large. You can change this value on the server by adjusting the 'max_allowed_packet' variable.") +) + +// error type which represents a single MySQL error +type MySQLError struct { + Number uint16 + Message string +} + +func (me *MySQLError) Error() string { + return fmt.Sprintf("Error %d: %s", me.Number, me.Message) +} + +// error type which represents a group of one or more MySQL warnings +type MySQLWarnings []mysqlWarning + +func (mws MySQLWarnings) Error() string { + var msg string + for i, warning := range mws { + if i > 0 { + msg += "\r\n" + } + msg += fmt.Sprintf("%s %s: %s", warning.Level, warning.Code, warning.Message) + } + return msg +} + +// error type which represents a single MySQL warning +type mysqlWarning struct { + Level string + Code string + Message string +} + +func (mc *mysqlConn) getWarnings() (err error) { + rows, err := mc.Query("SHOW WARNINGS", []driver.Value{}) + if err != nil { + return + } + + var warnings = MySQLWarnings{} + var values = make([]driver.Value, 3) + + var warning mysqlWarning + var raw []byte + var ok bool + + for { + err = rows.Next(values) + switch err { + case nil: + warning = mysqlWarning{} + + if raw, ok = values[0].([]byte); ok { + warning.Level = string(raw) + } else { + warning.Level = fmt.Sprintf("%s", values[0]) + } + if raw, ok = values[1].([]byte); ok { + warning.Code = string(raw) + } else { + warning.Code = fmt.Sprintf("%s", values[1]) + } + if raw, ok = values[2].([]byte); ok { + warning.Message = string(raw) + } else { + warning.Message = fmt.Sprintf("%s", values[0]) + } + + warnings = append(warnings, warning) + + case io.EOF: + return warnings + + default: + rows.Close() + return + } + } + return +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go new file mode 100644 index 0000000..635dbd5 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go @@ -0,0 +1,136 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "database/sql/driver" + "fmt" + "io" + "os" + "strings" +) + +var ( + fileRegister map[string]bool + readerRegister map[string]func() io.Reader +) + +func init() { + fileRegister = make(map[string]bool) + readerRegister = make(map[string]func() io.Reader) +} + +// RegisterLocalFile adds the given file to the file whitelist, +// so that it can be used by "LOAD DATA LOCAL INFILE ". +// Alternatively you can allow the use of all local files with +// the DSN parameter 'allowAllFiles=true' +func RegisterLocalFile(filepath string) { + fileRegister[strings.Trim(filepath, `"`)] = true +} + +// DeregisterLocalFile removes the given filepath from the whitelist. +func DeregisterLocalFile(filepath string) { + delete(fileRegister, strings.Trim(filepath, `"`)) +} + +// RegisterReaderHandler registers a handler function which is used +// to receive a io.Reader. +// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::". +// If the handler returns a io.ReadCloser Close() is called when the +// request is finished. +func RegisterReaderHandler(name string, handler func() io.Reader) { + readerRegister[name] = handler +} + +// DeregisterReaderHandler removes the ReaderHandler function with +// the given name from the registry. +func DeregisterReaderHandler(name string) { + delete(readerRegister, name) +} + +func (mc *mysqlConn) handleInFileRequest(name string) (err error) { + var rdr io.Reader + data := make([]byte, 4+mc.maxWriteSize) + + if strings.HasPrefix(name, "Reader::") { // io.Reader + name = name[8:] + handler, inMap := readerRegister[name] + if handler != nil { + rdr = handler() + } + if rdr == nil { + if !inMap { + err = fmt.Errorf("Reader '%s' is not registered", name) + } else { + err = fmt.Errorf("Reader '%s' is ", name) + } + } + } else { // File + name = strings.Trim(name, `"`) + if fileRegister[name] || mc.cfg.params[`allowAllFiles`] == `true` { + rdr, err = os.Open(name) + } else { + err = fmt.Errorf("Local File '%s' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files", name) + } + } + + if rdc, ok := rdr.(io.ReadCloser); ok { + defer func() { + if err == nil { + err = rdc.Close() + } else { + rdc.Close() + } + }() + } + + // send content packets + var ioErr error + if err == nil { + var n int + for err == nil && ioErr == nil { + n, err = rdr.Read(data[4:]) + if n > 0 { + data[0] = byte(n) + data[1] = byte(n >> 8) + data[2] = byte(n >> 16) + data[3] = mc.sequence + ioErr = mc.writePacket(data[:4+n]) + } + } + if err == io.EOF { + err = nil + } + if ioErr != nil { + errLog.Print(ioErr.Error()) + return driver.ErrBadConn + } + } + + // send empty packet (termination) + ioErr = mc.writePacket([]byte{ + 0x00, + 0x00, + 0x00, + mc.sequence, + }) + if ioErr != nil { + errLog.Print(ioErr.Error()) + return driver.ErrBadConn + } + + // read OK packet + if err == nil { + return mc.readResultOK() + } else { + mc.readPacket() + } + return err +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go new file mode 100644 index 0000000..b8fda32 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go @@ -0,0 +1,1081 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "bytes" + "database/sql/driver" + "encoding/binary" + "errors" + "fmt" + "io" + "math" + "time" +) + +// Packets documentation: +// http://dev.mysql.com/doc/internals/en/client-server-protocol.html + +// Read packet to buffer 'data' +func (mc *mysqlConn) readPacket() (data []byte, err error) { + // Read packet header + data, err = mc.buf.readNext(4) + if err != nil { + errLog.Print(err.Error()) + return nil, driver.ErrBadConn + } + + // Packet Length [24 bit] + pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16) + + if pktLen < 1 { + errLog.Print(errMalformPkt.Error()) + return nil, driver.ErrBadConn + } + + // Check Packet Sync [8 bit] + if data[3] != mc.sequence { + if data[3] > mc.sequence { + return nil, errPktSyncMul + } else { + return nil, errPktSync + } + } + mc.sequence++ + + // Read packet body [pktLen bytes] + data, err = mc.buf.readNext(pktLen) + if err == nil { + if pktLen < maxPacketSize { + return data, nil + } + + var buf []byte + buf = append(buf, data...) + + // More data + data, err = mc.readPacket() + if err == nil { + return append(buf, data...), nil + } + } + errLog.Print(err.Error()) + return nil, driver.ErrBadConn +} + +// Write packet buffer 'data' +// The packet header must be already included +func (mc *mysqlConn) writePacket(data []byte) error { + if len(data)-4 <= mc.maxWriteSize { // Can send data at once + // Write packet + n, err := mc.netConn.Write(data) + if err == nil && n == len(data) { + mc.sequence++ + return nil + } + + // Handle error + if err == nil { // n != len(data) + errLog.Print(errMalformPkt.Error()) + } else { + errLog.Print(err.Error()) + } + return driver.ErrBadConn + } + + // Must split packet + return mc.splitPacket(data) +} + +func (mc *mysqlConn) splitPacket(data []byte) (err error) { + pktLen := len(data) - 4 + + if pktLen > mc.maxPacketAllowed { + return errPktTooLarge + } + + for pktLen >= maxPacketSize { + data[0] = 0xff + data[1] = 0xff + data[2] = 0xff + data[3] = mc.sequence + + // Write packet + n, err := mc.netConn.Write(data[:4+maxPacketSize]) + if err == nil && n == 4+maxPacketSize { + mc.sequence++ + data = data[maxPacketSize:] + pktLen -= maxPacketSize + continue + } + + // Handle error + if err == nil { // n != len(data) + errLog.Print(errMalformPkt.Error()) + } else { + errLog.Print(err.Error()) + } + return driver.ErrBadConn + } + + data[0] = byte(pktLen) + data[1] = byte(pktLen >> 8) + data[2] = byte(pktLen >> 16) + data[3] = mc.sequence + return mc.writePacket(data) +} + +/****************************************************************************** +* Initialisation Process * +******************************************************************************/ + +// Handshake Initialization Packet +// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::Handshake +func (mc *mysqlConn) readInitPacket() (err error) { + data, err := mc.readPacket() + if err != nil { + return + } + + if data[0] == iERR { + return mc.handleErrorPacket(data) + } + + // protocol version [1 byte] + if data[0] < minProtocolVersion { + err = fmt.Errorf( + "Unsupported MySQL Protocol Version %d. Protocol Version %d or higher is required", + data[0], + minProtocolVersion) + } + + // server version [null terminated string] + // connection id [4 bytes] + pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + 4 + + // first part of the password cipher [8 bytes] + mc.cipher = append(mc.cipher, data[pos:pos+8]...) + + // (filler) always 0x00 [1 byte] + pos += 8 + 1 + + // capability flags (lower 2 bytes) [2 bytes] + mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2])) + if mc.flags&clientProtocol41 == 0 { + err = errors.New("MySQL-Server does not support required Protocol 41+") + } + pos += 2 + + if len(data) > pos { + // character set [1 byte] + mc.charset = data[pos] + + // status flags [2 bytes] + // capability flags (upper 2 bytes) [2 bytes] + // length of auth-plugin-data [1 byte] + // reserved (all [00]) [10 bytes] + pos += 1 + 2 + 2 + 1 + 10 + + // second part of the password cipher [12? bytes] + // The documentation is ambiguous about the length. + // The official Python library uses the fixed length 12 + // which is not documented but seems to work. + mc.cipher = append(mc.cipher, data[pos:pos+12]...) + + // TODO: Verify string termination + // EOF if version (>= 5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2) + // \NUL otherwise + // http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::Handshake + // + //if data[len(data)-1] == 0 { + // return + //} + //return errMalformPkt + } + + return +} + +// Client Authentication Packet +// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::HandshakeResponse +func (mc *mysqlConn) writeAuthPacket() error { + // Adjust client flags based on server support + clientFlags := uint32( + clientProtocol41 | + clientSecureConn | + clientLongPassword | + clientTransactions | + clientLocalFiles, + ) + if mc.flags&clientLongFlag > 0 { + clientFlags |= uint32(clientLongFlag) + } + + // User Password + scrambleBuff := scramblePassword(mc.cipher, []byte(mc.cfg.passwd)) + mc.cipher = nil + + pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.user) + 1 + 1 + len(scrambleBuff) + + // To specify a db name + if len(mc.cfg.dbname) > 0 { + clientFlags |= uint32(clientConnectWithDB) + pktLen += len(mc.cfg.dbname) + 1 + } + + // Calculate packet length and make buffer with that size + data := make([]byte, pktLen+4) + + // Add the packet header [24bit length + 1 byte sequence] + data[0] = byte(pktLen) + data[1] = byte(pktLen >> 8) + data[2] = byte(pktLen >> 16) + data[3] = mc.sequence + + // ClientFlags [32 bit] + data[4] = byte(clientFlags) + data[5] = byte(clientFlags >> 8) + data[6] = byte(clientFlags >> 16) + data[7] = byte(clientFlags >> 24) + + // MaxPacketSize [32 bit] (none) + //data[8] = 0x00 + //data[9] = 0x00 + //data[10] = 0x00 + //data[11] = 0x00 + + // Charset [1 byte] + data[12] = mc.charset + + // Filler [23 bytes] (all 0x00) + pos := 13 + 23 + + // User [null terminated string] + if len(mc.cfg.user) > 0 { + pos += copy(data[pos:], mc.cfg.user) + } + //data[pos] = 0x00 + pos++ + + // ScrambleBuffer [length encoded integer] + data[pos] = byte(len(scrambleBuff)) + pos += 1 + copy(data[pos+1:], scrambleBuff) + + // Databasename [null terminated string] + if len(mc.cfg.dbname) > 0 { + pos += copy(data[pos:], mc.cfg.dbname) + //data[pos] = 0x00 + } + + // Send Auth packet + return mc.writePacket(data) +} + +/****************************************************************************** +* Command Packets * +******************************************************************************/ + +func (mc *mysqlConn) writeCommandPacket(command byte) error { + // Reset Packet Sequence + mc.sequence = 0 + + // Send CMD packet + return mc.writePacket([]byte{ + // Add the packet header [24bit length + 1 byte sequence] + 0x01, // 1 byte long + 0x00, + 0x00, + 0x00, // mc.sequence + + // Add command byte + command, + }) +} + +func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error { + // Reset Packet Sequence + mc.sequence = 0 + + pktLen := 1 + len(arg) + data := make([]byte, pktLen+4) + + // Add the packet header [24bit length + 1 byte sequence] + data[0] = byte(pktLen) + data[1] = byte(pktLen >> 8) + data[2] = byte(pktLen >> 16) + //data[3] = mc.sequence + + // Add command byte + data[4] = command + + // Add arg + copy(data[5:], arg) + + // Send CMD packet + return mc.writePacket(data) +} + +func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error { + // Reset Packet Sequence + mc.sequence = 0 + + // Send CMD packet + return mc.writePacket([]byte{ + // Add the packet header [24bit length + 1 byte sequence] + 0x05, // 5 bytes long + 0x00, + 0x00, + 0x00, // mc.sequence + + // Add command byte + command, + + // Add arg [32 bit] + byte(arg), + byte(arg >> 8), + byte(arg >> 16), + byte(arg >> 24), + }) +} + +/****************************************************************************** +* Result Packets * +******************************************************************************/ + +// Returns error if Packet is not an 'Result OK'-Packet +func (mc *mysqlConn) readResultOK() error { + data, err := mc.readPacket() + if err == nil { + // packet indicator + switch data[0] { + + case iOK: + return mc.handleOkPacket(data) + + case iEOF: // someone is using old_passwords + return errOldPassword + + default: // Error otherwise + return mc.handleErrorPacket(data) + } + } + return err +} + +// Result Set Header Packet +// http://dev.mysql.com/doc/internals/en/text-protocol.html#packet-ProtocolText::Resultset +func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) { + data, err := mc.readPacket() + if err == nil { + switch data[0] { + + case iOK: + return 0, mc.handleOkPacket(data) + + case iERR: + return 0, mc.handleErrorPacket(data) + + case iLocalInFile: + return 0, mc.handleInFileRequest(string(data[1:])) + } + + // column count + num, _, n := readLengthEncodedInteger(data) + if n-len(data) == 0 { + return int(num), nil + } + + return 0, errMalformPkt + } + return 0, err +} + +// Error Packet +// http://dev.mysql.com/doc/internals/en/overview.html#packet-ERR_Packet +func (mc *mysqlConn) handleErrorPacket(data []byte) error { + if data[0] != iERR { + return errMalformPkt + } + + // 0xff [1 byte] + + // Error Number [16 bit uint] + errno := binary.LittleEndian.Uint16(data[1:3]) + + pos := 3 + + // SQL State [optional: # + 5bytes string] + //sqlstate := string(data[pos : pos+6]) + if data[pos] == 0x23 { + pos = 9 + } + + // Error Message [string] + return &MySQLError{ + Number: errno, + Message: string(data[pos:]), + } +} + +// Ok Packet +// http://dev.mysql.com/doc/internals/en/overview.html#packet-OK_Packet +func (mc *mysqlConn) handleOkPacket(data []byte) (err error) { + var n, m int + + // 0x00 [1 byte] + + // Affected rows [Length Coded Binary] + mc.affectedRows, _, n = readLengthEncodedInteger(data[1:]) + + // Insert id [Length Coded Binary] + mc.insertId, _, m = readLengthEncodedInteger(data[1+n:]) + + // server_status [2 bytes] + + // warning count [2 bytes] + if !mc.strict { + return + } else { + pos := 1 + n + m + 2 + if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 { + err = mc.getWarnings() + } + } + + // message [until end of packet] + return +} + +// Read Packets as Field Packets until EOF-Packet or an Error appears +// http://dev.mysql.com/doc/internals/en/text-protocol.html#packet-Protocol::ColumnDefinition41 +func (mc *mysqlConn) readColumns(count int) (columns []mysqlField, err error) { + var data []byte + var i, pos, n int + var name []byte + + columns = make([]mysqlField, count) + + for { + data, err = mc.readPacket() + if err != nil { + return + } + + // EOF Packet + if data[0] == iEOF && len(data) == 5 { + if i != count { + err = fmt.Errorf("ColumnsCount mismatch n:%d len:%d", count, len(columns)) + } + return + } + + // Catalog + pos, err = skipLengthEnodedString(data) + if err != nil { + return + } + + // Database [len coded string] + n, err = skipLengthEnodedString(data[pos:]) + if err != nil { + return + } + pos += n + + // Table [len coded string] + n, err = skipLengthEnodedString(data[pos:]) + if err != nil { + return + } + pos += n + + // Original table [len coded string] + n, err = skipLengthEnodedString(data[pos:]) + if err != nil { + return + } + pos += n + + // Name [len coded string] + name, _, n, err = readLengthEnodedString(data[pos:]) + if err != nil { + return + } + columns[i].name = string(name) + pos += n + + // Original name [len coded string] + n, err = skipLengthEnodedString(data[pos:]) + if err != nil { + return + } + + // Filler [1 byte] + // Charset [16 bit uint] + // Length [32 bit uint] + pos += n + 1 + 2 + 4 + + // Field type [byte] + columns[i].fieldType = data[pos] + pos++ + + // Flags [16 bit uint] + columns[i].flags = fieldFlag(binary.LittleEndian.Uint16(data[pos : pos+2])) + //pos += 2 + + // Decimals [8 bit uint] + //pos++ + + // Default value [len coded binary] + //if pos < len(data) { + // defaultVal, _, err = bytesToLengthCodedBinary(data[pos:]) + //} + + i++ + } + + return +} + +// Read Packets as Field Packets until EOF-Packet or an Error appears +// http://dev.mysql.com/doc/internals/en/text-protocol.html#packet-ProtocolText::ResultsetRow +func (rows *mysqlRows) readRow(dest []driver.Value) (err error) { + data, err := rows.mc.readPacket() + if err != nil { + return + } + + // EOF Packet + if data[0] == iEOF && len(data) == 5 { + return io.EOF + } + + // RowSet Packet + var n int + var isNull bool + pos := 0 + + for i := range dest { + // Read bytes and convert to string + dest[i], isNull, n, err = readLengthEnodedString(data[pos:]) + pos += n + if err == nil { + if !isNull { + if !rows.mc.parseTime { + continue + } else { + switch rows.columns[i].fieldType { + case fieldTypeTimestamp, fieldTypeDateTime, + fieldTypeDate, fieldTypeNewDate: + dest[i], err = parseDateTime(string(dest[i].([]byte)), rows.mc.cfg.loc) + if err == nil { + continue + } + default: + continue + } + } + + } else { + dest[i] = nil + continue + } + } + return // err + } + + return +} + +// Reads Packets until EOF-Packet or an Error appears. Returns count of Packets read +func (mc *mysqlConn) readUntilEOF() (err error) { + var data []byte + + for { + data, err = mc.readPacket() + + // No Err and no EOF Packet + if err == nil && (data[0] != iEOF || len(data) != 5) { + continue + } + return // Err or EOF + } + return +} + +/****************************************************************************** +* Prepared Statements * +******************************************************************************/ + +// Prepare Result Packets +// http://dev.mysql.com/doc/internals/en/prepared-statements.html#com-stmt-prepare-response +func (stmt *mysqlStmt) readPrepareResultPacket() (columnCount uint16, err error) { + data, err := stmt.mc.readPacket() + if err == nil { + // Position + pos := 0 + + // packet indicator [1 byte] + if data[pos] != iOK { + err = stmt.mc.handleErrorPacket(data) + return + } + pos++ + + // statement id [4 bytes] + stmt.id = binary.LittleEndian.Uint32(data[pos : pos+4]) + pos += 4 + + // Column count [16 bit uint] + columnCount = binary.LittleEndian.Uint16(data[pos : pos+2]) + pos += 2 + + // Param count [16 bit uint] + stmt.paramCount = int(binary.LittleEndian.Uint16(data[pos : pos+2])) + pos += 2 + + // Warning count [16 bit uint] + if !stmt.mc.strict { + return + } else { + if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 { + err = stmt.mc.getWarnings() + } + } + } + return +} + +// http://dev.mysql.com/doc/internals/en/prepared-statements.html#com-stmt-send-long-data +func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) (err error) { + maxLen := stmt.mc.maxPacketAllowed - 1 + pktLen := maxLen + argLen := len(arg) + data := make([]byte, 4+1+4+2+argLen) + copy(data[4+1+4+2:], arg) + + for argLen > 0 { + if 1+4+2+argLen < maxLen { + pktLen = 1 + 4 + 2 + argLen + } + + // Add the packet header [24bit length + 1 byte sequence] + data[0] = byte(pktLen) + data[1] = byte(pktLen >> 8) + data[2] = byte(pktLen >> 16) + data[3] = 0x00 // mc.sequence + + // Add command byte [1 byte] + data[4] = comStmtSendLongData + + // Add stmtID [32 bit] + data[5] = byte(stmt.id) + data[6] = byte(stmt.id >> 8) + data[7] = byte(stmt.id >> 16) + data[8] = byte(stmt.id >> 24) + + // Add paramID [16 bit] + data[9] = byte(paramID) + data[10] = byte(paramID >> 8) + + // Send CMD packet + err = stmt.mc.writePacket(data[:4+pktLen]) + if err == nil { + argLen -= pktLen - (1 + 4 + 2) + data = data[pktLen-(1+4+2):] + continue + } + return err + + } + + // Reset Packet Sequence + stmt.mc.sequence = 0 + return nil +} + +// Execute Prepared Statement +// http://dev.mysql.com/doc/internals/en/prepared-statements.html#com-stmt-execute +func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { + if len(args) != stmt.paramCount { + return fmt.Errorf( + "Arguments count mismatch (Got: %d Has: %d)", + len(args), + stmt.paramCount) + } + + // Reset packet-sequence + stmt.mc.sequence = 0 + + pktLen := 1 + 4 + 1 + 4 + ((stmt.paramCount + 7) >> 3) + 1 + (stmt.paramCount << 1) + paramValues := make([][]byte, stmt.paramCount) + paramTypes := make([]byte, (stmt.paramCount << 1)) + bitMask := uint64(0) + var i int + + for i = range args { + // build NULL-bitmap + if args[i] == nil { + bitMask += 1 << uint(i) + paramTypes[i<<1] = fieldTypeNULL + continue + } + + // cache types and values + switch v := args[i].(type) { + case int64: + paramTypes[i<<1] = fieldTypeLongLong + paramValues[i] = uint64ToBytes(uint64(v)) + pktLen += 8 + continue + + case float64: + paramTypes[i<<1] = fieldTypeDouble + paramValues[i] = uint64ToBytes(math.Float64bits(v)) + pktLen += 8 + continue + + case bool: + paramTypes[i<<1] = fieldTypeTiny + pktLen++ + if v { + paramValues[i] = []byte{0x01} + } else { + paramValues[i] = []byte{0x00} + } + continue + + case []byte: + paramTypes[i<<1] = fieldTypeString + if len(v) < stmt.mc.maxPacketAllowed-pktLen-(stmt.paramCount-(i+1))*64 { + paramValues[i] = append( + lengthEncodedIntegerToBytes(uint64(len(v))), + v..., + ) + pktLen += len(paramValues[i]) + continue + } else { + err := stmt.writeCommandLongData(i, v) + if err == nil { + continue + } + return err + } + + case string: + paramTypes[i<<1] = fieldTypeString + if len(v) < stmt.mc.maxPacketAllowed-pktLen-(stmt.paramCount-(i+1))*64 { + paramValues[i] = append( + lengthEncodedIntegerToBytes(uint64(len(v))), + []byte(v)..., + ) + pktLen += len(paramValues[i]) + continue + } else { + err := stmt.writeCommandLongData(i, []byte(v)) + if err == nil { + continue + } + return err + } + + case time.Time: + paramTypes[i<<1] = fieldTypeString + + var val []byte + if v.IsZero() { + val = []byte("0000-00-00") + } else { + val = []byte(v.Format(timeFormat)) + } + + paramValues[i] = append( + lengthEncodedIntegerToBytes(uint64(len(val))), + val..., + ) + pktLen += len(paramValues[i]) + continue + + default: + return fmt.Errorf("Can't convert type: %T", args[i]) + } + } + + data := make([]byte, pktLen+4) + + // packet header [4 bytes] + data[0] = byte(pktLen) + data[1] = byte(pktLen >> 8) + data[2] = byte(pktLen >> 16) + data[3] = stmt.mc.sequence + + // command [1 byte] + data[4] = comStmtExecute + + // statement_id [4 bytes] + data[5] = byte(stmt.id) + data[6] = byte(stmt.id >> 8) + data[7] = byte(stmt.id >> 16) + data[8] = byte(stmt.id >> 24) + + // flags (0: CURSOR_TYPE_NO_CURSOR) [1 byte] + //data[9] = 0x00 + + // iteration_count (uint32(1)) [4 bytes] + data[10] = 0x01 + //data[11] = 0x00 + //data[12] = 0x00 + //data[13] = 0x00 + + if stmt.paramCount > 0 { + // NULL-bitmap [(param_count+7)/8 bytes] + pos := 14 + ((stmt.paramCount + 7) >> 3) + // Convert bitMask to bytes + for i = 14; i < pos; i++ { + data[i] = byte(bitMask >> uint((i-14)<<3)) + } + + // newParameterBoundFlag 1 [1 byte] + data[pos] = 0x01 + pos++ + + // type of parameters [param_count*2 bytes] + pos += copy(data[pos:], paramTypes) + + // values for the parameters [n bytes] + for i = range paramValues { + pos += copy(data[pos:], paramValues[i]) + } + } + + return stmt.mc.writePacket(data) +} + +// http://dev.mysql.com/doc/internals/en/prepared-statements.html#packet-ProtocolBinary::ResultsetRow +func (rows *mysqlRows) readBinaryRow(dest []driver.Value) (err error) { + data, err := rows.mc.readPacket() + if err != nil { + return + } + + // packet indicator [1 byte] + if data[0] != iOK { + // EOF Packet + if data[0] == iEOF && len(data) == 5 { + return io.EOF + } else { + // Error otherwise + return rows.mc.handleErrorPacket(data) + } + } + + // NULL-bitmap, [(column-count + 7 + 2) / 8 bytes] + pos := 1 + (len(dest)+7+2)>>3 + nullBitMap := data[1:pos] + + // values [rest] + var n int + var unsigned bool + + for i := range dest { + // Field is NULL + // (byte >> bit-pos) % 2 == 1 + if ((nullBitMap[(i+2)>>3] >> uint((i+2)&7)) & 1) == 1 { + dest[i] = nil + continue + } + + unsigned = rows.columns[i].flags&flagUnsigned != 0 + + // Convert to byte-coded string + switch rows.columns[i].fieldType { + case fieldTypeNULL: + dest[i] = nil + continue + + // Numeric Types + case fieldTypeTiny: + if unsigned { + dest[i] = int64(data[pos]) + } else { + dest[i] = int64(int8(data[pos])) + } + pos++ + continue + + case fieldTypeShort, fieldTypeYear: + if unsigned { + dest[i] = int64(binary.LittleEndian.Uint16(data[pos : pos+2])) + } else { + dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2]))) + } + pos += 2 + continue + + case fieldTypeInt24, fieldTypeLong: + if unsigned { + dest[i] = int64(binary.LittleEndian.Uint32(data[pos : pos+4])) + } else { + dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4]))) + } + pos += 4 + continue + + case fieldTypeLongLong: + if unsigned { + val := binary.LittleEndian.Uint64(data[pos : pos+8]) + if val > math.MaxInt64 { + dest[i] = uint64ToString(val) + } else { + dest[i] = int64(val) + } + } else { + dest[i] = int64(binary.LittleEndian.Uint64(data[pos : pos+8])) + } + pos += 8 + continue + + case fieldTypeFloat: + dest[i] = float64(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4]))) + pos += 4 + continue + + case fieldTypeDouble: + dest[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[pos : pos+8])) + pos += 8 + continue + + // Length coded Binary Strings + case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar, + fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB, + fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB, + fieldTypeVarString, fieldTypeString, fieldTypeGeometry: + var isNull bool + dest[i], isNull, n, err = readLengthEnodedString(data[pos:]) + pos += n + if err == nil { + if !isNull { + continue + } else { + dest[i] = nil + continue + } + } + return // err + + // Date YYYY-MM-DD + case fieldTypeDate, fieldTypeNewDate: + var num uint64 + var isNull bool + num, isNull, n = readLengthEncodedInteger(data[pos:]) + + pos += n + + if isNull { + dest[i] = nil + continue + } + + if rows.mc.parseTime { + dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.loc) + } else { + dest[i], err = formatBinaryDate(num, data[pos:]) + } + + if err == nil { + pos += int(num) + continue + } else { + return err + } + + // Time [-][H]HH:MM:SS[.fractal] + case fieldTypeTime: + var num uint64 + var isNull bool + num, isNull, n = readLengthEncodedInteger(data[pos:]) + + pos += n + + if num == 0 { + if isNull { + dest[i] = nil + continue + } else { + dest[i] = []byte("00:00:00") + continue + } + } + + var sign byte + if data[pos] == 1 { + sign = byte('-') + } + + switch num { + case 8: + dest[i] = []byte(fmt.Sprintf( + "%c%02d:%02d:%02d", + sign, + uint16(data[pos+1])*24+uint16(data[pos+5]), + data[pos+6], + data[pos+7], + )) + pos += 8 + continue + case 12: + dest[i] = []byte(fmt.Sprintf( + "%c%02d:%02d:%02d.%06d", + sign, + uint16(data[pos+1])*24+uint16(data[pos+5]), + data[pos+6], + data[pos+7], + binary.LittleEndian.Uint32(data[pos+8:pos+12]), + )) + pos += 12 + continue + default: + return fmt.Errorf("Invalid TIME-packet length %d", num) + } + + // Timestamp YYYY-MM-DD HH:MM:SS[.fractal] + case fieldTypeTimestamp, fieldTypeDateTime: + var num uint64 + var isNull bool + num, isNull, n = readLengthEncodedInteger(data[pos:]) + + pos += n + + if isNull { + dest[i] = nil + continue + } + + if rows.mc.parseTime { + dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.loc) + } else { + dest[i], err = formatBinaryDateTime(num, data[pos:]) + } + + if err == nil { + pos += int(num) + continue + } else { + return err + } + + // Please report if this happens! + default: + return fmt.Errorf("Unknown FieldType %d", rows.columns[i].fieldType) + } + } + + return +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go new file mode 100644 index 0000000..dd5e656 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go @@ -0,0 +1,23 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +type mysqlResult struct { + affectedRows int64 + insertId int64 +} + +func (res *mysqlResult) LastInsertId() (int64, error) { + return res.insertId, nil +} + +func (res *mysqlResult) RowsAffected() (int64, error) { + return res.affectedRows, nil +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go new file mode 100644 index 0000000..fda7599 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go @@ -0,0 +1,77 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "database/sql/driver" + "errors" + "io" +) + +type mysqlField struct { + name string + fieldType byte + flags fieldFlag +} + +type mysqlRows struct { + mc *mysqlConn + binary bool + columns []mysqlField + eof bool +} + +func (rows *mysqlRows) Columns() (columns []string) { + columns = make([]string, len(rows.columns)) + for i := range columns { + columns[i] = rows.columns[i].name + } + return +} + +func (rows *mysqlRows) Close() (err error) { + defer func() { + rows.mc = nil + }() + + // Remove unread packets from stream + if !rows.eof { + if rows.mc == nil { + return errors.New("Invalid Connection") + } + + err = rows.mc.readUntilEOF() + } + + return +} + +func (rows *mysqlRows) Next(dest []driver.Value) error { + if rows.eof { + return io.EOF + } + + if rows.mc == nil { + return errors.New("Invalid Connection") + } + + // Fetch next row from stream + var err error + if rows.binary { + err = rows.readBinaryRow(dest) + } else { + err = rows.readRow(dest) + } + + if err == io.EOF { + rows.eof = true + } + return err +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go new file mode 100644 index 0000000..faa1ad0 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go @@ -0,0 +1,93 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "database/sql/driver" +) + +type mysqlStmt struct { + mc *mysqlConn + id uint32 + paramCount int + params []mysqlField +} + +func (stmt *mysqlStmt) Close() (err error) { + err = stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id) + stmt.mc = nil + return +} + +func (stmt *mysqlStmt) NumInput() int { + return stmt.paramCount +} + +func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { + stmt.mc.affectedRows = 0 + stmt.mc.insertId = 0 + + // Send command + err := stmt.writeExecutePacket(args) + if err != nil { + return nil, err + } + + // Read Result + var resLen int + resLen, err = stmt.mc.readResultSetHeaderPacket() + if err == nil { + if resLen > 0 { + // Columns + err = stmt.mc.readUntilEOF() + if err != nil { + return nil, err + } + + // Rows + err = stmt.mc.readUntilEOF() + } + if err == nil { + return &mysqlResult{ + affectedRows: int64(stmt.mc.affectedRows), + insertId: int64(stmt.mc.insertId), + }, nil + } + } + + return nil, err +} + +func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { + // Send command + err := stmt.writeExecutePacket(args) + if err != nil { + return nil, err + } + + // Read Result + var resLen int + resLen, err = stmt.mc.readResultSetHeaderPacket() + if err != nil { + return nil, err + } + + rows := &mysqlRows{stmt.mc, true, nil, false} + + if resLen > 0 { + // Columns + rows.columns, err = stmt.mc.readColumns(resLen) + if err != nil { + return nil, err + } + } + + return rows, err +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go new file mode 100644 index 0000000..fb8b32e --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go @@ -0,0 +1,26 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +type mysqlTx struct { + mc *mysqlConn +} + +func (tx *mysqlTx) Commit() (err error) { + err = tx.mc.exec("COMMIT") + tx.mc = nil + return +} + +func (tx *mysqlTx) Rollback() (err error) { + err = tx.mc.exec("ROLLBACK") + tx.mc = nil + return +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go new file mode 100644 index 0000000..83a30d1 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go @@ -0,0 +1,428 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2012 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "crypto/sha1" + "database/sql/driver" + "encoding/binary" + "fmt" + "io" + "log" + "os" + "regexp" + "strings" + "time" +) + +// NullTime represents a time.Time that may be NULL. +// NullTime implements the Scanner interface so +// it can be used as a scan destination: +// +// var nt NullTime +// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt) +// ... +// if nt.Valid { +// // use nt.Time +// } else { +// // NULL value +// } +// +// This NullTime implementation is not driver-specific +type NullTime struct { + Time time.Time + Valid bool // Valid is true if Time is not NULL +} + +// Scan implements the Scanner interface. +// The value type must be time.Time or string / []byte (formatted time-string), +// otherwise Scan fails. +func (nt *NullTime) Scan(value interface{}) (err error) { + if value == nil { + nt.Time, nt.Valid = time.Time{}, false + return + } + + switch v := value.(type) { + case time.Time: + nt.Time, nt.Valid = v, true + return + case []byte: + nt.Time, err = parseDateTime(string(v), time.UTC) + nt.Valid = (err == nil) + return + case string: + nt.Time, err = parseDateTime(v, time.UTC) + nt.Valid = (err == nil) + return + } + + nt.Valid = false + return fmt.Errorf("Can't convert %T to time.Time", value) +} + +// Value implements the driver Valuer interface. +func (nt NullTime) Value() (driver.Value, error) { + if !nt.Valid { + return nil, nil + } + return nt.Time, nil +} + +// Logger +var ( + errLog *log.Logger +) + +func init() { + errLog = log.New(os.Stderr, "[MySQL] ", log.Ldate|log.Ltime|log.Lshortfile) + + dsnPattern = regexp.MustCompile( + `^(?:(?P.*?)(?::(?P.*))?@)?` + // [user[:password]@] + `(?:(?P[^\(]*)(?:\((?P[^\)]*)\))?)?` + // [net[(addr)]] + `\/(?P.*?)` + // /dbname + `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] +} + +// Data Source Name Parser +var dsnPattern *regexp.Regexp + +func parseDSN(dsn string) (cfg *config, err error) { + cfg = new(config) + cfg.params = make(map[string]string) + + matches := dsnPattern.FindStringSubmatch(dsn) + names := dsnPattern.SubexpNames() + + for i, match := range matches { + switch names[i] { + case "user": + cfg.user = match + case "passwd": + cfg.passwd = match + case "net": + cfg.net = match + case "addr": + cfg.addr = match + case "dbname": + cfg.dbname = match + case "params": + for _, v := range strings.Split(match, "&") { + param := strings.SplitN(v, "=", 2) + if len(param) != 2 { + continue + } + cfg.params[param[0]] = param[1] + } + } + } + + // Set default network if empty + if cfg.net == "" { + cfg.net = "tcp" + } + + // Set default adress if empty + if cfg.addr == "" { + cfg.addr = "127.0.0.1:3306" + } + + cfg.loc, err = time.LoadLocation(cfg.params["loc"]) + + return +} + +// Encrypt password using 4.1+ method +// http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#4.1_and_later +func scramblePassword(scramble, password []byte) []byte { + if len(password) == 0 { + return nil + } + + // stage1Hash = SHA1(password) + crypt := sha1.New() + crypt.Write(password) + stage1 := crypt.Sum(nil) + + // scrambleHash = SHA1(scramble + SHA1(stage1Hash)) + // inner Hash + crypt.Reset() + crypt.Write(stage1) + hash := crypt.Sum(nil) + + // outer Hash + crypt.Reset() + crypt.Write(scramble) + crypt.Write(hash) + scramble = crypt.Sum(nil) + + // token = scrambleHash XOR stage1Hash + for i := range scramble { + scramble[i] ^= stage1[i] + } + return scramble +} + +func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { + switch len(str) { + case 10: // YYYY-MM-DD + if str == "0000-00-00" { + return + } + t, err = time.Parse(timeFormat[:10], str) + case 19: // YYYY-MM-DD HH:MM:SS + if str == "0000-00-00 00:00:00" { + return + } + t, err = time.Parse(timeFormat, str) + default: + err = fmt.Errorf("Invalid Time-String: %s", str) + return + } + + // Adjust location + if err == nil && loc != time.UTC { + y, mo, d := t.Date() + h, mi, s := t.Clock() + t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil + } + + return +} + +func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) { + switch num { + case 0: + return time.Time{}, nil + case 4: + return time.Date( + int(binary.LittleEndian.Uint16(data[:2])), // year + time.Month(data[2]), // month + int(data[3]), // day + 0, 0, 0, 0, + loc, + ), nil + case 7: + return time.Date( + int(binary.LittleEndian.Uint16(data[:2])), // year + time.Month(data[2]), // month + int(data[3]), // day + int(data[4]), // hour + int(data[5]), // minutes + int(data[6]), // seconds + 0, + loc, + ), nil + case 11: + return time.Date( + int(binary.LittleEndian.Uint16(data[:2])), // year + time.Month(data[2]), // month + int(data[3]), // day + int(data[4]), // hour + int(data[5]), // minutes + int(data[6]), // seconds + int(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds + loc, + ), nil + } + return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num) +} + +func formatBinaryDate(num uint64, data []byte) (driver.Value, error) { + switch num { + case 0: + return []byte("0000-00-00"), nil + case 4: + return []byte(fmt.Sprintf( + "%04d-%02d-%02d", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3], + )), nil + } + return nil, fmt.Errorf("Invalid DATE-packet length %d", num) +} + +func formatBinaryDateTime(num uint64, data []byte) (driver.Value, error) { + switch num { + case 0: + return []byte("0000-00-00 00:00:00"), nil + case 4: + return []byte(fmt.Sprintf( + "%04d-%02d-%02d 00:00:00", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3], + )), nil + case 7: + return []byte(fmt.Sprintf( + "%04d-%02d-%02d %02d:%02d:%02d", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3], + data[4], + data[5], + data[6], + )), nil + case 11: + return []byte(fmt.Sprintf( + "%04d-%02d-%02d %02d:%02d:%02d.%06d", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3], + data[4], + data[5], + data[6], + binary.LittleEndian.Uint32(data[7:11]), + )), nil + } + return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num) +} + +func readBool(value string) bool { + switch strings.ToLower(value) { + case "true": + return true + case "1": + return true + } + return false +} + +/****************************************************************************** +* Convert from and to bytes * +******************************************************************************/ + +func uint64ToBytes(n uint64) []byte { + return []byte{ + byte(n), + byte(n >> 8), + byte(n >> 16), + byte(n >> 24), + byte(n >> 32), + byte(n >> 40), + byte(n >> 48), + byte(n >> 56), + } +} + +func uint64ToString(n uint64) []byte { + var a [20]byte + i := 20 + + // U+0030 = 0 + // ... + // U+0039 = 9 + + var q uint64 + for n >= 10 { + i-- + q = n / 10 + a[i] = uint8(n-q*10) + 0x30 + n = q + } + + i-- + a[i] = uint8(n) + 0x30 + + return a[i:] +} + +// treats string value as unsigned integer representation +func stringToInt(b []byte) int { + val := 0 + for i := range b { + val *= 10 + val += int(b[i] - 0x30) + } + return val +} + +func readLengthEnodedString(b []byte) ([]byte, bool, int, error) { + // Get length + num, isNull, n := readLengthEncodedInteger(b) + if num < 1 { + return nil, isNull, n, nil + } + + n += int(num) + + // Check data length + if len(b) >= n { + return b[n-int(num) : n], false, n, nil + } + return nil, false, n, io.EOF +} + +func skipLengthEnodedString(b []byte) (int, error) { + // Get length + num, _, n := readLengthEncodedInteger(b) + if num < 1 { + return n, nil + } + + n += int(num) + + // Check data length + if len(b) >= n { + return n, nil + } + return n, io.EOF +} + +func readLengthEncodedInteger(b []byte) (num uint64, isNull bool, n int) { + switch b[0] { + + // 251: NULL + case 0xfb: + n = 1 + isNull = true + return + + // 252: value of following 2 + case 0xfc: + num = uint64(b[1]) | uint64(b[2])<<8 + n = 3 + return + + // 253: value of following 3 + case 0xfd: + num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 + n = 4 + return + + // 254: value of following 8 + case 0xfe: + num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 | + uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 | + uint64(b[7])<<48 | uint64(b[8])<<54 + n = 9 + return + } + + // 0-250: value of first byte + num = uint64(b[0]) + n = 1 + return +} + +func lengthEncodedIntegerToBytes(n uint64) []byte { + switch { + case n <= 250: + return []byte{byte(n)} + + case n <= 0xffff: + return []byte{0xfc, byte(n), byte(n >> 8)} + + case n <= 0xffffff: + return []byte{0xfd, byte(n), byte(n >> 8), byte(n >> 16)} + } + return nil +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go new file mode 100644 index 0000000..3a53166 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go @@ -0,0 +1,90 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 Julien Schmidt. All rights reserved. +// http://www.julienschmidt.com +// +// 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/. + +package mysql + +import ( + "fmt" + "testing" + "time" +) + +func TestDSNParser(t *testing.T) { + var testDSNs = []struct { + in string + out string + loc *time.Location + }{ + {"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p}", time.UTC}, + {"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p}", time.UTC}, + {"user:password@tcp(localhost:5555)/dbname?charset=utf8", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p}", time.UTC}, + {"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p}", time.UTC}, + {"user:password@/dbname?loc=UTC", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[loc:UTC] loc:%p}", time.UTC}, + {"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[loc:Local] loc:%p}", time.Local}, + {"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p}", time.UTC}, + {"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p}", time.UTC}, + {"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p}", time.UTC}, + } + + var cfg *config + var err error + var res string + + for i, tst := range testDSNs { + cfg, err = parseDSN(tst.in) + if err != nil { + t.Error(err.Error()) + } + + res = fmt.Sprintf("%+v", cfg) + if res != fmt.Sprintf(tst.out, tst.loc) { + t.Errorf("%d. parseDSN(%q) => %q, want %q", i, tst.in, res, fmt.Sprintf(tst.out, tst.loc)) + } + } +} + +func TestScanNullTime(t *testing.T) { + var scanTests = []struct { + in interface{} + error bool + valid bool + time time.Time + }{ + {tDate, false, true, tDate}, + {sDate, false, true, tDate}, + {[]byte(sDate), false, true, tDate}, + {tDateTime, false, true, tDateTime}, + {sDateTime, false, true, tDateTime}, + {[]byte(sDateTime), false, true, tDateTime}, + {tDate0, false, true, tDate0}, + {sDate0, false, true, tDate0}, + {[]byte(sDate0), false, true, tDate0}, + {sDateTime0, false, true, tDate0}, + {[]byte(sDateTime0), false, true, tDate0}, + {"", true, false, tDate0}, + {"1234", true, false, tDate0}, + {0, true, false, tDate0}, + } + + var nt = NullTime{} + var err error + + for _, tst := range scanTests { + err = nt.Scan(tst.in) + if (err != nil) != tst.error { + t.Errorf("%v: expected error status %b, got %b", tst.in, tst.error, (err != nil)) + } + if nt.Valid != tst.valid { + t.Errorf("%v: expected valid status %b, got %b", tst.in, tst.valid, nt.Valid) + } + if nt.Time != tst.time { + t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time) + } + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/.gitignore b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/.gitignore new file mode 100644 index 0000000..529841c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +tags +environ diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/LICENSE b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/LICENSE new file mode 100644 index 0000000..0d31edf --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/LICENSE @@ -0,0 +1,23 @@ + Copyright (c) 2013, Jason Moiron + + 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. + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md new file mode 100644 index 0000000..4e3eb6d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md @@ -0,0 +1,258 @@ +#sqlx + +[![Build Status](https://drone.io/github.com/jmoiron/sqlx/status.png)](https://drone.io/github.com/jmoiron/sqlx/latest) [![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/jmoiron/sqlx) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/jmoiron/sqlx/master/LICENSE) + +sqlx is a library which provides a set of extensions on go's standard +`database/sql` library. The sqlx versions of `sql.DB`, `sql.TX`, `sql.Stmt`, +et al. all leave the underlying interfaces untouched, so that their interfaces +are a superset on the standard ones. This makes it relatively painless to +integrate existing codebases using database/sql with sqlx. + +Major additional concepts are: + +* Marshal rows into structs (with embedded struct support), maps, and slices +* Named parameter support including prepared statements +* `Get` and `Select` to go quickly from query to struct/slice +* `LoadFile` for executing statements from a file + +There is now some [fairly comprehensive documentation](http://jmoiron.github.io/sqlx/) for sqlx. +You can also read the usage below for a quick sample on how sqlx works, or check out the [API +documentation on godoc](http://godoc.org/github.com/jmoiron/sqlx). + +## Recent Changes + +The ability to use basic types as Select and Get destinations was added. This +is only valid when there is one column in the result set, and both functions +return an error if this isn't the case. This allows for much simpler patterns +of access for single column results: + +```go +var count int +err := db.Get(&count, "SELECT count(*) FROM person;") + +var names []string +err := db.Select(&names, "SELECT name FROM person;") +``` + +See the note on Scannability at the bottom of this README for some more info. + +### Backwards Compatibility + +There is no Go1-like promise of absolute stability, but I take the issue +seriously and will maintain the library in a compatible state unless vital +bugs prevent me from doing so. Since [#59](https://github.com/jmoiron/sqlx/issues/59) and [#60](https://github.com/jmoiron/sqlx/issues/60) necessitated +breaking behavior, a wider API cleanup was done at the time of fixing. + +## install + + go get github.com/jmoiron/sqlx + +## issues + +Row headers can be ambiguous (`SELECT 1 AS a, 2 AS a`), and the result of +`Columns()` can have duplicate names on queries like: + +```sql +SELECT a.id, a.name, b.id, b.name FROM foos AS a JOIN foos AS b ON a.parent = b.id; +``` + +making a struct or map destination ambiguous. Use `AS` in your queries +to give rows distinct names, `rows.Scan` to scan them manually, or +`SliceScan` to get a slice of results. + +## usage + +Below is an example which shows some common use cases for sqlx. Check +[sqlx_test.go](https://github.com/jmoiron/sqlx/blob/master/sqlx_test.go) for more +usage. + + +```go +package main + +import ( + _ "github.com/lib/pq" + "database/sql" + "github.com/jmoiron/sqlx" + "log" +) + +var schema = ` +CREATE TABLE person ( + first_name text, + last_name text, + email text +); + +CREATE TABLE place ( + country text, + city text NULL, + telcode integer +)` + +type Person struct { + FirstName string `db:"first_name"` + LastName string `db:"last_name"` + Email string +} + +type Place struct { + Country string + City sql.NullString + TelCode int +} + +func main() { + // this connects & tries a simple 'SELECT 1', panics on error + // use sqlx.Open() for sql.Open() semantics + db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable") + if err != nil { + log.Fatalln(err) + } + + // exec the schema or fail; multi-statement Exec behavior varies between + // database drivers; pq will exec them all, sqlite3 won't, ymmv + db.MustExec(schema) + + tx := db.MustBegin() + tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "jmoiron@jmoiron.net") + tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "John", "Doe", "johndoeDNE@gmail.net") + tx.MustExec("INSERT INTO place (country, city, telcode) VALUES ($1, $2, $3)", "United States", "New York", "1") + tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Hong Kong", "852") + tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Singapore", "65") + // Named queries can use structs, so if you have an existing struct (i.e. person := &Person{}) that you have populated, you can pass it in as &person + tx.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", &Person{"Jane", "Citizen", "jane.citzen@example.com"}) + tx.Commit() + + // Query the database, storing results in a []Person (wrapped in []interface{}) + people := []Person{} + db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC") + jason, john := people[0], people[1] + + fmt.Printf("%#v\n%#v", jason, john) + // Person{FirstName:"Jason", LastName:"Moiron", Email:"jmoiron@jmoiron.net"} + // Person{FirstName:"John", LastName:"Doe", Email:"johndoeDNE@gmail.net"} + + // You can also get a single result, a la QueryRow + jason = Person{} + err = db.Get(&jason, "SELECT * FROM person WHERE first_name=$1", "Jason") + fmt.Printf("%#v\n", jason) + // Person{FirstName:"Jason", LastName:"Moiron", Email:"jmoiron@jmoiron.net"} + + // if you have null fields and use SELECT *, you must use sql.Null* in your struct + places := []Place{} + err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC") + if err != nil { + fmt.Println(err) + return + } + usa, singsing, honkers := places[0], places[1], places[2] + + fmt.Printf("%#v\n%#v\n%#v\n", usa, singsing, honkers) + // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1} + // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65} + // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852} + + // Loop through rows using only one struct + place := Place{} + rows, err := db.Queryx("SELECT * FROM place") + for rows.Next() { + err := rows.StructScan(&place) + if err != nil { + log.Fatalln(err) + } + fmt.Printf("%#v\n", place) + } + // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1} + // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852} + // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65} + + // Named queries, using `:name` as the bindvar. Automatic bindvar support + // which takes into account the dbtype based on the driverName on sqlx.Open/Connect + _, err = db.NamedExec(`INSERT INTO person (first_name,last_name,email) VALUES (:first,:last,:email)`, + map[string]interface{}{ + "first": "Bin", + "last": "Smuth", + "email": "bensmith@allblacks.nz", + }) + + // Selects Mr. Smith from the database + rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"}) + + // Named queries can also use structs. Their bind names follow the same rules + // as the name -> db mapping, so struct fields are lowercased and the `db` tag + // is taken into consideration. + rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason) +} +``` + +## Scannability + +Get and Select are able to take base types, so the following is now possible: + +```go +var name string +db.Get(&name, "SELECT first_name FROM person WHERE id=$1", 10) + +var ids []int64 +db.Select(&ids, "SELECT id FROM person LIMIT 20;") +``` + +This can get complicated with destination types which are structs, like `sql.NullString`. Because of this, straightforward rules for *scannability* had to be developed. Iff something is "Scannable", then it is used directly in `rows.Scan`; if it's not, then the standard sqlx struct rules apply. + +Something is scannable if any of the following are true: + +* It is not a struct, ie. `reflect.ValueOf(v).Kind() != reflect.Struct` +* It implements the `sql.Scanner` interface +* It has no exported fields (eg. `time.Time`) + +## embedded structs + +Scan targets obey Go attribute rules directly, including nested embedded structs. Older versions of sqlx would attempt to also descend into non-embedded structs, but this is no longer supported. + +Go makes *accessing* '[ambiguous selectors](http://play.golang.org/p/MGRxdjLaUc)' a compile time error, defining structs with ambiguous selectors is legal. Sqlx will decide which field to use on a struct based on a breadth first search of the struct and any structs it embeds, as specified by the order of the fields as accessible by `reflect`, which generally means in source-order. This means that sqlx chooses the outer-most, top-most matching name for targets, even when the selector might technically be ambiguous. + +## scan safety + +By default, scanning into structs requires the structs to have fields for all of the +columns in the query. This was done for a few reasons: + +* A mistake in naming during development could lead you to believe that data is + being written to a field when actually it can't be found and it is being dropped +* This behavior mirrors the behavior of the Go compiler with respect to unused + variables +* Selecting more data than you need is wasteful (more data on the wire, more time + marshalling, etc) + +Unlike Marshallers in the stdlib, the programmer scanning an sql result into a struct +will generally have a full understanding of what the underlying data model is *and* +full control over the SQL statement. + +Despite this, there are use cases where it's convenient to be able to ignore unknown +columns. In most of these cases, you might be better off with `ScanSlice`, but where +you want to still use structs, there is now the `Unsafe` method. Its usage is most +simply shown in an example: + +```go + db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable") + if err != nil { + log.Fatal(err) + } + + type Person { + Name string + } + var p Person + + // This fails, because there is no destination for location in Person + err = db.Get(&p, "SELECT name, location FROM person LIMIT 1") + + udb := db.Unsafe() + + // This succeeds and just sets `Name` in the p struct + err = udb.Get(&p, "SELECT name, location FROM person LIMIT 1") +``` + +The `Unsafe` method is implemented on `Tx`, `DB`, and `Stmt`. When you use an unsafe +`Tx` or `DB` to create a new `Tx` or `Stmt`, those inherit its lack of safety. + diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go new file mode 100644 index 0000000..2f1ec2b --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go @@ -0,0 +1,84 @@ +package sqlx + +import ( + "bytes" + "strconv" +) + +// Bindvar types supported by Rebind, BindMap and BindStruct. +const ( + UNKNOWN = iota + QUESTION + DOLLAR + NAMED +) + +// BindType returns the bindtype for a given database given a drivername. +func BindType(driverName string) int { + switch driverName { + case "postgres", "pgx": + return DOLLAR + case "mysql": + return QUESTION + case "sqlite3": + return QUESTION + case "oci8": + return NAMED + } + return UNKNOWN +} + +// FIXME: this should be able to be tolerant of escaped ?'s in queries without +// losing much speed, and should be to avoid confusion. + +// FIXME: this is now produces the wrong results for oracle's NAMED bindtype + +// Rebind a query from the default bindtype (QUESTION) to the target bindtype. +func Rebind(bindType int, query string) string { + if bindType != DOLLAR { + return query + } + + qb := []byte(query) + // Add space enough for 10 params before we have to allocate + rqb := make([]byte, 0, len(qb)+10) + j := 1 + for _, b := range qb { + if b == '?' { + rqb = append(rqb, '$') + for _, b := range strconv.Itoa(j) { + rqb = append(rqb, byte(b)) + } + j++ + } else { + rqb = append(rqb, b) + } + } + return string(rqb) +} + +// Experimental implementation of Rebind which uses a bytes.Buffer. The code is +// much simpler and should be more resistant to odd unicode, but it is twice as +// slow. Kept here for benchmarking purposes and to possibly replace Rebind if +// problems arise with its somewhat naive handling of unicode. + +func rebindBuff(bindType int, query string) string { + if bindType != DOLLAR { + return query + } + + b := make([]byte, 0, len(query)) + rqb := bytes.NewBuffer(b) + j := 1 + for _, r := range query { + if r == '?' { + rqb.WriteRune('$') + rqb.WriteString(strconv.Itoa(j)) + j++ + } else { + rqb.WriteRune(r) + } + } + + return rqb.String() +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/doc.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/doc.go new file mode 100644 index 0000000..e2b4e60 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/doc.go @@ -0,0 +1,12 @@ +// Package sqlx provides general purpose extensions to database/sql. +// +// It is intended to seamlessly wrap database/sql and provide convenience +// methods which are useful in the development of database driven applications. +// None of the underlying database/sql methods are changed. Instead all extended +// behavior is implemented through new methods defined on wrapper types. +// +// Additions include scanning into structs, named query support, rebinding +// queries for different drivers, convenient shorthands for common error handling +// and more. +// +package sqlx diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go new file mode 100644 index 0000000..d753518 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go @@ -0,0 +1,321 @@ +package sqlx + +// Named Query Support +// +// * BindMap - bind query bindvars to map/struct args +// * NamedExec, NamedQuery - named query w/ struct or map +// * NamedStmt - a pre-compiled named query which is a prepared statement +// +// Internal Interfaces: +// +// * compileNamedQuery - rebind a named query, returning a query and list of names +// * bindArgs, bindMapArgs, bindAnyArgs - given a list of names, return an arglist +// +import ( + "database/sql" + "errors" + "fmt" + "reflect" + "strconv" + "unicode" + + "github.com/jmoiron/sqlx/reflectx" +) + +// NamedStmt is a prepared statement that executes named queries. Prepare it +// how you would execute a NamedQuery, but pass in a struct or map when executing. +type NamedStmt struct { + Params []string + QueryString string + Stmt *Stmt +} + +// Close closes the named statement. +func (n *NamedStmt) Close() error { + return n.Stmt.Close() +} + +// Exec executes a named statement using the struct passed. +func (n *NamedStmt) Exec(arg interface{}) (sql.Result, error) { + args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper) + if err != nil { + return *new(sql.Result), err + } + return n.Stmt.Exec(args...) +} + +// Query executes a named statement using the struct argument, returning rows. +func (n *NamedStmt) Query(arg interface{}) (*sql.Rows, error) { + args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper) + if err != nil { + return nil, err + } + return n.Stmt.Query(args...) +} + +// QueryRow executes a named statement against the database. Because sqlx cannot +// create a *sql.Row with an error condition pre-set for binding errors, sqlx +// returns a *sqlx.Row instead. +func (n *NamedStmt) QueryRow(arg interface{}) *Row { + args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper) + if err != nil { + return &Row{err: err} + } + return n.Stmt.QueryRowx(args...) +} + +// MustExec execs a NamedStmt, panicing on error +func (n *NamedStmt) MustExec(arg interface{}) sql.Result { + res, err := n.Exec(arg) + if err != nil { + panic(err) + } + return res +} + +// Queryx using this NamedStmt +func (n *NamedStmt) Queryx(arg interface{}) (*Rows, error) { + r, err := n.Query(arg) + if err != nil { + return nil, err + } + return &Rows{Rows: r, Mapper: n.Stmt.Mapper}, err +} + +// QueryRowx this NamedStmt. Because of limitations with QueryRow, this is +// an alias for QueryRow. +func (n *NamedStmt) QueryRowx(arg interface{}) *Row { + return n.QueryRow(arg) +} + +// Select using this NamedStmt +func (n *NamedStmt) Select(dest interface{}, arg interface{}) error { + rows, err := n.Query(arg) + if err != nil { + return err + } + // if something happens here, we want to make sure the rows are Closed + defer rows.Close() + return scanAll(rows, dest, false) +} + +// Get using this NamedStmt +func (n *NamedStmt) Get(dest interface{}, arg interface{}) error { + r := n.QueryRowx(arg) + return r.scanAny(dest, false) +} + +// A union interface of preparer and binder, required to be able to prepare +// named statements (as the bindtype must be determined). +type namedPreparer interface { + Preparer + binder +} + +func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) { + bindType := BindType(p.DriverName()) + q, args, err := compileNamedQuery([]byte(query), bindType) + if err != nil { + return nil, err + } + stmt, err := Preparex(p, q) + if err != nil { + return nil, err + } + return &NamedStmt{ + QueryString: q, + Params: args, + Stmt: stmt, + }, nil +} + +func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) { + if maparg, ok := arg.(map[string]interface{}); ok { + return bindMapArgs(names, maparg) + } + return bindArgs(names, arg, m) +} + +// private interface to generate a list of interfaces from a given struct +// type, given a list of names to pull out of the struct. Used by public +// BindStruct interface. +func bindArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) { + arglist := make([]interface{}, 0, len(names)) + + // grab the indirected value of arg + v := reflect.ValueOf(arg) + for v = reflect.ValueOf(arg); v.Kind() == reflect.Ptr; { + v = v.Elem() + } + + fields := m.TraversalsByName(v.Type(), names) + for i, t := range fields { + if len(t) == 0 { + return arglist, fmt.Errorf("could not find name %s in %#v", names[i], arg) + } + val := reflectx.FieldByIndexesReadOnly(v, t) + arglist = append(arglist, val.Interface()) + } + + return arglist, nil +} + +// like bindArgs, but for maps. +func bindMapArgs(names []string, arg map[string]interface{}) ([]interface{}, error) { + arglist := make([]interface{}, 0, len(names)) + + for _, name := range names { + val, ok := arg[name] + if !ok { + return arglist, fmt.Errorf("could not find name %s in %#v", name, arg) + } + arglist = append(arglist, val) + } + return arglist, nil +} + +// bindStruct binds a named parameter query with fields from a struct argument. +// The rules for binding field names to parameter names follow the same +// conventions as for StructScan, including obeying the `db` struct tags. +func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) { + bound, names, err := compileNamedQuery([]byte(query), bindType) + if err != nil { + return "", []interface{}{}, err + } + + arglist, err := bindArgs(names, arg, m) + if err != nil { + return "", []interface{}{}, err + } + + return bound, arglist, nil +} + +// bindMap binds a named parameter query with a map of arguments. +func bindMap(bindType int, query string, args map[string]interface{}) (string, []interface{}, error) { + bound, names, err := compileNamedQuery([]byte(query), bindType) + if err != nil { + return "", []interface{}{}, err + } + + arglist, err := bindMapArgs(names, args) + return bound, arglist, err +} + +// -- Compilation of Named Queries + +// Allow digits and letters in bind params; additionally runes are +// checked against underscores, meaning that bind params can have be +// alphanumeric with underscores. Mind the difference between unicode +// digits and numbers, where '5' is a digit but '五' is not. +var allowedBindRunes = []*unicode.RangeTable{unicode.Letter, unicode.Digit} + +// FIXME: this function isn't safe for unicode named params, as a failing test +// can testify. This is not a regression but a failure of the original code +// as well. It should be modified to range over runes in a string rather than +// bytes, even though this is less convenient and slower. Hopefully the +// addition of the prepared NamedStmt (which will only do this once) will make +// up for the slightly slower ad-hoc NamedExec/NamedQuery. + +// compile a NamedQuery into an unbound query (using the '?' bindvar) and +// a list of names. +func compileNamedQuery(qs []byte, bindType int) (query string, names []string, err error) { + names = make([]string, 0, 10) + rebound := make([]byte, 0, len(qs)) + + inName := false + last := len(qs) - 1 + currentVar := 1 + name := make([]byte, 0, 10) + + for i, b := range qs { + // a ':' while we're in a name is an error + if b == ':' { + // if this is the second ':' in a '::' escape sequence, append a ':' + if inName && i > 0 && qs[i-1] == ':' { + rebound = append(rebound, ':') + inName = false + continue + } else if inName { + err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i)) + return query, names, err + } + inName = true + name = []byte{} + // if we're in a name, and this is an allowed character, continue + } else if inName && (unicode.IsOneOf(allowedBindRunes, rune(b)) || b == '_') && i != last { + // append the byte to the name if we are in a name and not on the last byte + name = append(name, b) + // if we're in a name and it's not an allowed character, the name is done + } else if inName { + inName = false + // if this is the final byte of the string and it is part of the name, then + // make sure to add it to the name + if i == last && unicode.IsOneOf(allowedBindRunes, rune(b)) { + name = append(name, b) + } + // add the string representation to the names list + names = append(names, string(name)) + // add a proper bindvar for the bindType + switch bindType { + // oracle only supports named type bind vars even for positional + case NAMED: + rebound = append(rebound, ':') + rebound = append(rebound, name...) + case QUESTION, UNKNOWN: + rebound = append(rebound, '?') + case DOLLAR: + rebound = append(rebound, '$') + for _, b := range strconv.Itoa(currentVar) { + rebound = append(rebound, byte(b)) + } + currentVar++ + } + // add this byte to string unless it was not part of the name + if i != last { + rebound = append(rebound, b) + } else if !unicode.IsOneOf(allowedBindRunes, rune(b)) { + rebound = append(rebound, b) + } + } else { + // this is a normal byte and should just go onto the rebound query + rebound = append(rebound, b) + } + } + + return string(rebound), names, err +} + +// Bind binds a struct or a map to a query with named parameters. +func BindNamed(bindType int, query string, arg interface{}) (string, []interface{}, error) { + return bindNamedMapper(bindType, query, arg, mapper()) +} + +func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) { + if maparg, ok := arg.(map[string]interface{}); ok { + return bindMap(bindType, query, maparg) + } + return bindStruct(bindType, query, arg, m) +} + +// NamedQuery binds a named query and then runs Query on the result using the +// provided Ext (sqlx.Tx, sqlx.Db). It works with both structs and with +// map[string]interface{} types. +func NamedQuery(e Ext, query string, arg interface{}) (*Rows, error) { + q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e)) + if err != nil { + return nil, err + } + return e.Queryx(q, args...) +} + +// NamedExec uses BindStruct to get a query executable by the driver and +// then runs Exec on the result. Returns an error from the binding +// or the query excution itself. +func NamedExec(e Ext, query string, arg interface{}) (sql.Result, error) { + q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e)) + if err != nil { + return nil, err + } + return e.Exec(q, args...) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named_test.go new file mode 100644 index 0000000..d3459a8 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/named_test.go @@ -0,0 +1,227 @@ +package sqlx + +import ( + "database/sql" + "testing" +) + +func TestCompileQuery(t *testing.T) { + table := []struct { + Q, R, D, N string + V []string + }{ + // basic test for named parameters, invalid char ',' terminating + { + Q: `INSERT INTO foo (a,b,c,d) VALUES (:name, :age, :first, :last)`, + R: `INSERT INTO foo (a,b,c,d) VALUES (?, ?, ?, ?)`, + D: `INSERT INTO foo (a,b,c,d) VALUES ($1, $2, $3, $4)`, + N: `INSERT INTO foo (a,b,c,d) VALUES (:name, :age, :first, :last)`, + V: []string{"name", "age", "first", "last"}, + }, + // This query tests a named parameter ending the string as well as numbers + { + Q: `SELECT * FROM a WHERE first_name=:name1 AND last_name=:name2`, + R: `SELECT * FROM a WHERE first_name=? AND last_name=?`, + D: `SELECT * FROM a WHERE first_name=$1 AND last_name=$2`, + N: `SELECT * FROM a WHERE first_name=:name1 AND last_name=:name2`, + V: []string{"name1", "name2"}, + }, + { + Q: `SELECT "::foo" FROM a WHERE first_name=:name1 AND last_name=:name2`, + R: `SELECT ":foo" FROM a WHERE first_name=? AND last_name=?`, + D: `SELECT ":foo" FROM a WHERE first_name=$1 AND last_name=$2`, + N: `SELECT ":foo" FROM a WHERE first_name=:name1 AND last_name=:name2`, + V: []string{"name1", "name2"}, + }, + { + Q: `SELECT 'a::b::c' || first_name, '::::ABC::_::' FROM person WHERE first_name=:first_name AND last_name=:last_name`, + R: `SELECT 'a:b:c' || first_name, '::ABC:_:' FROM person WHERE first_name=? AND last_name=?`, + D: `SELECT 'a:b:c' || first_name, '::ABC:_:' FROM person WHERE first_name=$1 AND last_name=$2`, + N: `SELECT 'a:b:c' || first_name, '::ABC:_:' FROM person WHERE first_name=:first_name AND last_name=:last_name`, + V: []string{"first_name", "last_name"}, + }, + /* This unicode awareness test sadly fails, because of our byte-wise worldview. + * We could certainly iterate by Rune instead, though it's a great deal slower, + * it's probably the RightWay(tm) + { + Q: `INSERT INTO foo (a,b,c,d) VALUES (:あ, :b, :キコ, :名前)`, + R: `INSERT INTO foo (a,b,c,d) VALUES (?, ?, ?, ?)`, + D: `INSERT INTO foo (a,b,c,d) VALUES ($1, $2, $3, $4)`, + N: []string{"name", "age", "first", "last"}, + }, + */ + } + + for _, test := range table { + qr, names, err := compileNamedQuery([]byte(test.Q), QUESTION) + if err != nil { + t.Error(err) + } + if qr != test.R { + t.Errorf("expected %s, got %s", test.R, qr) + } + if len(names) != len(test.V) { + t.Errorf("expected %#v, got %#v", test.V, names) + } else { + for i, name := range names { + if name != test.V[i] { + t.Errorf("expected %dth name to be %s, got %s", i+1, test.V[i], name) + } + } + } + qd, _, _ := compileNamedQuery([]byte(test.Q), DOLLAR) + if qd != test.D { + t.Errorf("\nexpected: `%s`\ngot: `%s`", test.D, qd) + } + + qq, _, _ := compileNamedQuery([]byte(test.Q), NAMED) + if qq != test.N { + t.Errorf("\nexpected: `%s`\ngot: `%s`\n(len: %d vs %d)", test.N, qq, len(test.N), len(qq)) + } + } +} + +type Test struct { + t *testing.T +} + +func (t Test) Error(err error, msg ...interface{}) { + if err != nil { + if len(msg) == 0 { + t.t.Error(err) + } else { + t.t.Error(msg...) + } + } +} + +func (t Test) Errorf(err error, format string, args ...interface{}) { + if err != nil { + t.t.Errorf(format, args...) + } +} + +func TestNamedQueries(t *testing.T) { + RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T) { + loadDefaultFixture(db, t) + test := Test{t} + var ns *NamedStmt + var err error + + // Check that invalid preparations fail + ns, err = db.PrepareNamed("SELECT * FROM person WHERE first_name=:first:name") + if err == nil { + t.Error("Expected an error with invalid prepared statement.") + } + + ns, err = db.PrepareNamed("invalid sql") + if err == nil { + t.Error("Expected an error with invalid prepared statement.") + } + + // Check closing works as anticipated + ns, err = db.PrepareNamed("SELECT * FROM person WHERE first_name=:first_name") + test.Error(err) + err = ns.Close() + test.Error(err) + + ns, err = db.PrepareNamed(` + SELECT first_name, last_name, email + FROM person WHERE first_name=:first_name AND email=:email`) + test.Error(err) + + // test Queryx w/ uses Query + p := Person{FirstName: "Jason", LastName: "Moiron", Email: "jmoiron@jmoiron.net"} + + rows, err := ns.Queryx(p) + test.Error(err) + for rows.Next() { + var p2 Person + rows.StructScan(&p2) + if p.FirstName != p2.FirstName { + t.Errorf("got %s, expected %s", p.FirstName, p2.FirstName) + } + if p.LastName != p2.LastName { + t.Errorf("got %s, expected %s", p.LastName, p2.LastName) + } + if p.Email != p2.Email { + t.Errorf("got %s, expected %s", p.Email, p2.Email) + } + } + + // test Select + people := make([]Person, 0, 5) + err = ns.Select(&people, p) + test.Error(err) + + if len(people) != 1 { + t.Errorf("got %d results, expected %d", len(people), 1) + } + if p.FirstName != people[0].FirstName { + t.Errorf("got %s, expected %s", p.FirstName, people[0].FirstName) + } + if p.LastName != people[0].LastName { + t.Errorf("got %s, expected %s", p.LastName, people[0].LastName) + } + if p.Email != people[0].Email { + t.Errorf("got %s, expected %s", p.Email, people[0].Email) + } + + // test Exec + ns, err = db.PrepareNamed(` + INSERT INTO person (first_name, last_name, email) + VALUES (:first_name, :last_name, :email)`) + test.Error(err) + + js := Person{ + FirstName: "Julien", + LastName: "Savea", + Email: "jsavea@ab.co.nz", + } + _, err = ns.Exec(js) + test.Error(err) + + // Make sure we can pull him out again + p2 := Person{} + db.Get(&p2, db.Rebind("SELECT * FROM person WHERE email=?"), js.Email) + if p2.Email != js.Email { + t.Errorf("expected %s, got %s", js.Email, p2.Email) + } + + // test Txn NamedStmts + tx := db.MustBegin() + txns := tx.NamedStmt(ns) + + // We're going to add Steven in this txn + sl := Person{ + FirstName: "Steven", + LastName: "Luatua", + Email: "sluatua@ab.co.nz", + } + + _, err = txns.Exec(sl) + test.Error(err) + // then rollback... + tx.Rollback() + // looking for Steven after a rollback should fail + err = db.Get(&p2, db.Rebind("SELECT * FROM person WHERE email=?"), sl.Email) + if err != sql.ErrNoRows { + t.Errorf("expected no rows error, got %v", err) + } + + // now do the same, but commit + tx = db.MustBegin() + txns = tx.NamedStmt(ns) + _, err = txns.Exec(sl) + test.Error(err) + tx.Commit() + + // looking for Steven after a Commit should succeed + err = db.Get(&p2, db.Rebind("SELECT * FROM person WHERE email=?"), sl.Email) + test.Error(err) + if p2.Email != sl.Email { + t.Errorf("expected %s, got %s", sl.Email, p2.Email) + } + + }) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/README.md new file mode 100644 index 0000000..76f1b5d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/README.md @@ -0,0 +1,17 @@ +# reflectx + +The sqlx package has special reflect needs. In particular, it needs to: + +* be able to map a name to a field +* understand embedded structs +* understand mapping names to fields by a particular tag +* user specified name -> field mapping functions + +These behaviors mimic the behaviors by the standard library marshallers and also the +behavior of standard Go accessors. + +The first two are amply taken care of by `Reflect.Value.FieldByName`, and the third is +addressed by `Reflect.Value.FieldByNameFunc`, but these don't quite understand struct +tags in the ways that are vital to most marshalers, and they are slow. + +This reflectx package extends reflect to achieve these goals. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go new file mode 100644 index 0000000..847b760 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go @@ -0,0 +1,250 @@ +// Package reflect implements extensions to the standard reflect lib suitable +// for implementing marshaling and unmarshaling packages. The main Mapper type +// allows for Go-compatible named atribute access, including accessing embedded +// struct attributes and the ability to use functions and struct tags to +// customize field names. +// +package reflectx + +import "sync" + +import ( + "reflect" + "runtime" +) + +type fieldMap map[string][]int + +// Mapper is a general purpose mapper of names to struct fields. A Mapper +// behaves like most marshallers, optionally obeying a field tag for name +// mapping and a function to provide a basic mapping of fields to names. +type Mapper struct { + cache map[reflect.Type]fieldMap + tagName string + mapFunc func(string) string + mutex sync.Mutex +} + +// NewMapper returns a new mapper which optionally obeys the field tag given +// by tagName. If tagName is the empty string, it is ignored. +func NewMapper(tagName string) *Mapper { + return &Mapper{ + cache: make(map[reflect.Type]fieldMap), + tagName: tagName, + } +} + +// NewMapperFunc returns a new mapper which optionally obeys a field tag and +// a struct field name mapper func given by f. Tags will take precedence, but +// for any other field, the mapped name will be f(field.Name) +func NewMapperFunc(tagName string, f func(string) string) *Mapper { + return &Mapper{ + cache: make(map[reflect.Type]fieldMap), + tagName: tagName, + mapFunc: f, + } +} + +// TypeMap returns a mapping of field strings to int slices representing +// the traversal down the struct to reach the field. +func (m *Mapper) TypeMap(t reflect.Type) fieldMap { + m.mutex.Lock() + mapping, ok := m.cache[t] + if !ok { + mapping = getMapping(t, m.tagName, m.mapFunc) + m.cache[t] = mapping + } + m.mutex.Unlock() + return mapping +} + +// FieldMap returns the mapper's mapping of field names to reflect values. Panics +// if v's Kind is not Struct, or v is not Indirectable to a struct kind. +func (m *Mapper) FieldMap(v reflect.Value) map[string]reflect.Value { + v = reflect.Indirect(v) + mustBe(v, reflect.Struct) + + r := map[string]reflect.Value{} + nm := m.TypeMap(v.Type()) + for tagName, indexes := range nm { + r[tagName] = FieldByIndexes(v, indexes) + } + return r +} + +// FieldByName returns a field by the its mapped name as a reflect.Value. +// Panics if v's Kind is not Struct or v is not Indirectable to a struct Kind. +// Returns zero Value if the name is not found. +func (m *Mapper) FieldByName(v reflect.Value, name string) reflect.Value { + v = reflect.Indirect(v) + mustBe(v, reflect.Struct) + + nm := m.TypeMap(v.Type()) + traversal, ok := nm[name] + if !ok { + return *new(reflect.Value) + } + return FieldByIndexes(v, traversal) +} + +// FieldsByName returns a slice of values corresponding to the slice of names +// for the value. Panics if v's Kind is not Struct or v is not Indirectable +// to a struct Kind. Returns zero Value for each name not found. +func (m *Mapper) FieldsByName(v reflect.Value, names []string) []reflect.Value { + v = reflect.Indirect(v) + mustBe(v, reflect.Struct) + + nm := m.TypeMap(v.Type()) + + vals := make([]reflect.Value, 0, len(names)) + for _, name := range names { + traversal, ok := nm[name] + if !ok { + vals = append(vals, *new(reflect.Value)) + } else { + vals = append(vals, FieldByIndexes(v, traversal)) + } + } + return vals +} + +// Traversals by name returns a slice of int slices which represent the struct +// traversals for each mapped name. Panics if t is not a struct or Indirectable +// to a struct. Returns empty int slice for each name not found. +func (m *Mapper) TraversalsByName(t reflect.Type, names []string) [][]int { + t = Deref(t) + mustBe(t, reflect.Struct) + nm := m.TypeMap(t) + + r := make([][]int, 0, len(names)) + for _, name := range names { + traversal, ok := nm[name] + if !ok { + r = append(r, []int{}) + } else { + r = append(r, traversal) + } + } + return r +} + +// FieldByIndexes returns a value for a particular struct traversal. +func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value { + for _, i := range indexes { + v = reflect.Indirect(v).Field(i) + // if this is a pointer, it's possible it is nil + if v.Kind() == reflect.Ptr && v.IsNil() { + alloc := reflect.New(Deref(v.Type())) + v.Set(alloc) + } + } + return v +} + +// FieldByIndexesReadOnly returns a value for a particular struct traversal, +// but is not concerned with allocating nil pointers because the value is +// going to be used for reading and not setting. +func FieldByIndexesReadOnly(v reflect.Value, indexes []int) reflect.Value { + for _, i := range indexes { + v = reflect.Indirect(v).Field(i) + } + return v +} + +// Deref is Indirect for reflect.Types +func Deref(t reflect.Type) reflect.Type { + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t +} + +// -- helpers & utilities -- + +type Kinder interface { + Kind() reflect.Kind +} + +// mustBe checks a value against a kind, panicing with a reflect.ValueError +// if the kind isn't that which is required. +func mustBe(v Kinder, expected reflect.Kind) { + k := v.Kind() + if k != expected { + panic(&reflect.ValueError{Method: methodName(), Kind: k}) + } +} + +// methodName is returns the caller of the function calling methodName +func methodName() string { + pc, _, _, _ := runtime.Caller(2) + f := runtime.FuncForPC(pc) + if f == nil { + return "unknown method" + } + return f.Name() +} + +type typeQueue struct { + t reflect.Type + p []int +} + +// A copying append that creates a new slice each time. +func apnd(is []int, i int) []int { + x := make([]int, len(is)+1) + for p, n := range is { + x[p] = n + } + x[len(x)-1] = i + return x +} + +// getMapping returns a mapping for the t type, using the tagName and the mapFunc +// to determine the canonical names of fields. +func getMapping(t reflect.Type, tagName string, mapFunc func(string) string) fieldMap { + queue := []typeQueue{} + queue = append(queue, typeQueue{Deref(t), []int{}}) + m := fieldMap{} + for len(queue) != 0 { + // pop the first item off of the queue + tq := queue[0] + queue = queue[1:] + // iterate through all of its fields + for fieldPos := 0; fieldPos < tq.t.NumField(); fieldPos++ { + f := tq.t.Field(fieldPos) + + name := f.Tag.Get(tagName) + if len(name) == 0 { + if mapFunc != nil { + name = mapFunc(f.Name) + } else { + name = f.Name + } + } + + // if the name is "-", disabled via a tag, skip it + if name == "-" { + continue + } + + // skip unexported fields + if len(f.PkgPath) != 0 { + continue + } + + // bfs search of anonymous embedded structs + if f.Anonymous { + queue = append(queue, typeQueue{Deref(f.Type), apnd(tq.p, fieldPos)}) + continue + } + + // if the name is shadowed by an earlier identical name in the search, skip it + if _, ok := m[name]; ok { + continue + } + // add it to the map at the current position + m[name] = apnd(tq.p, fieldPos) + } + } + return m +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect_test.go new file mode 100644 index 0000000..e0a40f6 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect_test.go @@ -0,0 +1,216 @@ +package reflectx + +import ( + "reflect" + "strings" + "testing" +) + +func ival(v reflect.Value) int { + return v.Interface().(int) +} + +func TestBasic(t *testing.T) { + type Foo struct { + A int + B int + C int + } + + f := Foo{1, 2, 3} + fv := reflect.ValueOf(f) + m := NewMapper("") + + v := m.FieldByName(fv, "A") + if ival(v) != f.A { + t.Errorf("Expecting %d, got %d", ival(v), f.A) + } + v = m.FieldByName(fv, "B") + if ival(v) != f.B { + t.Errorf("Expecting %d, got %d", f.B, ival(v)) + } + v = m.FieldByName(fv, "C") + if ival(v) != f.C { + t.Errorf("Expecting %d, got %d", f.C, ival(v)) + } +} + +func TestEmbedded(t *testing.T) { + type Foo struct { + A int + } + + type Bar struct { + Foo + B int + } + + type Baz struct { + A int + Bar + } + + m := NewMapper("") + + z := Baz{} + z.A = 1 + z.B = 2 + z.Bar.Foo.A = 3 + zv := reflect.ValueOf(z) + + v := m.FieldByName(zv, "A") + if ival(v) != z.A { + t.Errorf("Expecting %d, got %d", ival(v), z.A) + } + v = m.FieldByName(zv, "B") + if ival(v) != z.B { + t.Errorf("Expecting %d, got %d", ival(v), z.B) + } +} + +func TestMapping(t *testing.T) { + type Person struct { + ID int + Name string + WearsGlasses bool `db:"wears_glasses"` + } + + m := NewMapperFunc("db", strings.ToLower) + p := Person{1, "Jason", true} + mapping := m.TypeMap(reflect.TypeOf(p)) + + for _, key := range []string{"id", "name", "wears_glasses"} { + if _, ok := mapping[key]; !ok { + t.Errorf("Expecting to find key %s in mapping but did not.", key) + } + } + + type SportsPerson struct { + Weight int + Age int + Person + } + s := SportsPerson{Weight: 100, Age: 30, Person: p} + mapping = m.TypeMap(reflect.TypeOf(s)) + for _, key := range []string{"id", "name", "wears_glasses", "weight", "age"} { + if _, ok := mapping[key]; !ok { + t.Errorf("Expecting to find key %s in mapping but did not.", key) + } + + } + + type RugbyPlayer struct { + Position int + IsIntense bool `db:"is_intense"` + IsAllBlack bool `db:"-"` + SportsPerson + } + r := RugbyPlayer{12, true, false, s} + mapping = m.TypeMap(reflect.TypeOf(r)) + for _, key := range []string{"id", "name", "wears_glasses", "weight", "age", "position", "is_intense"} { + if _, ok := mapping[key]; !ok { + t.Errorf("Expecting to find key %s in mapping but did not.", key) + } + } + + if _, ok := mapping["isallblack"]; ok { + t.Errorf("Expecting to ignore `IsAllBlack` field") + } + + type EmbeddedLiteral struct { + Embedded struct { + Person string + Position int + } + IsIntense bool + } + + e := EmbeddedLiteral{} + mapping = m.TypeMap(reflect.TypeOf(e)) + //fmt.Printf("Mapping: %#v\n", mapping) + + //f := FieldByIndexes(reflect.ValueOf(e), mapping["isintense"]) + //fmt.Println(f, f.Interface()) + + //tbn := m.TraversalsByName(reflect.TypeOf(e), []string{"isintense"}) + //fmt.Printf("%#v\n", tbn) + +} + +type E1 struct { + A int +} +type E2 struct { + E1 + B int +} +type E3 struct { + E2 + C int +} +type E4 struct { + E3 + D int +} + +func BenchmarkFieldNameL1(b *testing.B) { + e4 := E4{D: 1} + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.FieldByName("D") + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} + +func BenchmarkFieldNameL4(b *testing.B) { + e4 := E4{} + e4.A = 1 + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.FieldByName("A") + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} + +func BenchmarkFieldPosL1(b *testing.B) { + e4 := E4{D: 1} + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.Field(1) + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} + +func BenchmarkFieldPosL4(b *testing.B) { + e4 := E4{} + e4.A = 1 + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.Field(0) + f = f.Field(0) + f = f.Field(0) + f = f.Field(0) + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} + +func BenchmarkFieldByIndexL4(b *testing.B) { + e4 := E4{} + e4.A = 1 + idx := []int{0, 0, 0, 0} + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := FieldByIndexes(v, idx) + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx.go new file mode 100644 index 0000000..d195705 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx.go @@ -0,0 +1,986 @@ +package sqlx + +import ( + "database/sql" + "database/sql/driver" + "errors" + "fmt" + + "io/ioutil" + "path/filepath" + "reflect" + "strings" + + "github.com/jmoiron/sqlx/reflectx" +) + +// Although the NameMapper is convenient, in practice it should not +// be relied on except for application code. If you are writing a library +// that uses sqlx, you should be aware that the name mappings you expect +// can be overridded by your user's application. + +// NameMapper is used to map column names to struct field names. By default, +// it uses strings.ToLower to lowercase struct field names. It can be set +// to whatever you want, but it is encouraged to be set before sqlx is used +// as name-to-field mappings are cached after first use on a type. +var NameMapper = strings.ToLower +var origMapper = reflect.ValueOf(NameMapper) + +// Rather than creating on init, this is created when necessary so that +// importers have time to customize the NameMapper. +var mpr *reflectx.Mapper + +// mapper returns a valid mapper using the configured NameMapper func. +func mapper() *reflectx.Mapper { + if mpr == nil { + mpr = reflectx.NewMapperFunc("db", NameMapper) + } else if origMapper != reflect.ValueOf(NameMapper) { + // if NameMapper has changed, create a new mapper + mpr = reflectx.NewMapperFunc("db", NameMapper) + origMapper = reflect.ValueOf(NameMapper) + } + return mpr +} + +// isScannable takes the reflect.Type and the actual dest value and returns +// whether or not it's Scannable. Something is scannable if: +// * it is not a struct +// * it implements sql.Scanner +// * it has no exported fields +func isScannable(t reflect.Type) bool { + if reflect.PtrTo(t).Implements(_scannerInterface) { + return true + } + if t.Kind() != reflect.Struct { + return true + } + + // it's not important that we use the right mapper for this particular object, + // we're only concerned on how many exported fields this struct has + m := mapper() + if len(m.TypeMap(t)) == 0 { + return true + } + return false +} + +// ColScanner is an interface used by MapScan and SliceScan +type ColScanner interface { + Columns() ([]string, error) + Scan(dest ...interface{}) error + Err() error +} + +// Queryer is an interface used by Get and Select +type Queryer interface { + Query(query string, args ...interface{}) (*sql.Rows, error) + Queryx(query string, args ...interface{}) (*Rows, error) + QueryRowx(query string, args ...interface{}) *Row +} + +// Execer is an interface used by MustExec and LoadFile +type Execer interface { + Exec(query string, args ...interface{}) (sql.Result, error) +} + +// Binder is an interface for something which can bind queries (Tx, DB) +type binder interface { + DriverName() string + Rebind(string) string + BindNamed(string, interface{}) (string, []interface{}, error) +} + +// Ext is a union interface which can bind, query, and exec, used by +// NamedQuery and NamedExec. +type Ext interface { + binder + Queryer + Execer +} + +// Preparer is an interface used by Preparex. +type Preparer interface { + Prepare(query string) (*sql.Stmt, error) +} + +// determine if any of our extensions are unsafe +func isUnsafe(i interface{}) bool { + switch i.(type) { + case Row: + return i.(Row).unsafe + case *Row: + return i.(*Row).unsafe + case Rows: + return i.(Rows).unsafe + case *Rows: + return i.(*Rows).unsafe + case Stmt: + return i.(Stmt).unsafe + case qStmt: + return i.(qStmt).Stmt.unsafe + case *qStmt: + return i.(*qStmt).Stmt.unsafe + case DB: + return i.(DB).unsafe + case *DB: + return i.(*DB).unsafe + case Tx: + return i.(Tx).unsafe + case *Tx: + return i.(*Tx).unsafe + case sql.Rows, *sql.Rows: + return false + default: + return false + } +} + +func mapperFor(i interface{}) *reflectx.Mapper { + switch i.(type) { + case DB: + return i.(DB).Mapper + case *DB: + return i.(*DB).Mapper + case Tx: + return i.(Tx).Mapper + case *Tx: + return i.(*Tx).Mapper + default: + return mapper() + } +} + +var _scannerInterface = reflect.TypeOf((*sql.Scanner)(nil)).Elem() +var _valuerInterface = reflect.TypeOf((*driver.Valuer)(nil)).Elem() + +// Row is a reimplementation of sql.Row in order to gain access to the underlying +// sql.Rows.Columns() data, necessary for StructScan. +type Row struct { + err error + unsafe bool + rows *sql.Rows + Mapper *reflectx.Mapper +} + +// Scan is a fixed implementation of sql.Row.Scan, which does not discard the +// underlying error from the internal rows object if it exists. +func (r *Row) Scan(dest ...interface{}) error { + if r.err != nil { + return r.err + } + + // TODO(bradfitz): for now we need to defensively clone all + // []byte that the driver returned (not permitting + // *RawBytes in Rows.Scan), since we're about to close + // the Rows in our defer, when we return from this function. + // the contract with the driver.Next(...) interface is that it + // can return slices into read-only temporary memory that's + // only valid until the next Scan/Close. But the TODO is that + // for a lot of drivers, this copy will be unnecessary. We + // should provide an optional interface for drivers to + // implement to say, "don't worry, the []bytes that I return + // from Next will not be modified again." (for instance, if + // they were obtained from the network anyway) But for now we + // don't care. + defer r.rows.Close() + for _, dp := range dest { + if _, ok := dp.(*sql.RawBytes); ok { + return errors.New("sql: RawBytes isn't allowed on Row.Scan") + } + } + + if !r.rows.Next() { + if err := r.rows.Err(); err != nil { + return err + } + return sql.ErrNoRows + } + err := r.rows.Scan(dest...) + if err != nil { + return err + } + // Make sure the query can be processed to completion with no errors. + if err := r.rows.Close(); err != nil { + return err + } + return nil +} + +// Columns returns the underlying sql.Rows.Columns(), or the deferred error usually +// returned by Row.Scan() +func (r *Row) Columns() ([]string, error) { + if r.err != nil { + return []string{}, r.err + } + return r.rows.Columns() +} + +// Err returns the error encountered while scanning. +func (r *Row) Err() error { + return r.err +} + +// DB is a wrapper around sql.DB which keeps track of the driverName upon Open, +// used mostly to automatically bind named queries using the right bindvars. +type DB struct { + *sql.DB + driverName string + unsafe bool + Mapper *reflectx.Mapper +} + +// NewDb returns a new sqlx DB wrapper for a pre-existing *sql.DB. The +// driverName of the original database is required for named query support. +func NewDb(db *sql.DB, driverName string) *DB { + return &DB{DB: db, driverName: driverName, Mapper: mapper()} +} + +// DriverName returns the driverName passed to the Open function for this DB. +func (db *DB) DriverName() string { + return db.driverName +} + +// Open is the same as sql.Open, but returns an *sqlx.DB instead. +func Open(driverName, dataSourceName string) (*DB, error) { + db, err := sql.Open(driverName, dataSourceName) + if err != nil { + return nil, err + } + return &DB{DB: db, driverName: driverName, Mapper: mapper()}, err +} + +// MustOpen is the same as sql.Open, but returns an *sqlx.DB instead and panics on error. +func MustOpen(driverName, dataSourceName string) *DB { + db, err := Open(driverName, dataSourceName) + if err != nil { + panic(err) + } + return db +} + +// MapperFunc sets a new mapper for this db using the default sqlx struct tag +// and the provided mapper function. +func (db *DB) MapperFunc(mf func(string) string) { + db.Mapper = reflectx.NewMapperFunc("db", mf) +} + +// Rebind transforms a query from QUESTION to the DB driver's bindvar type. +func (db *DB) Rebind(query string) string { + return Rebind(BindType(db.driverName), query) +} + +// Unsafe returns a version of DB which will silently succeed to scan when +// columns in the SQL result have no fields in the destination struct. +// sqlx.Stmt and sqlx.Tx which are created from this DB will inherit its +// safety behavior. +func (db *DB) Unsafe() *DB { + return &DB{DB: db.DB, driverName: db.driverName, unsafe: true, Mapper: db.Mapper} +} + +// BindNamed binds a query using the DB driver's bindvar type. +func (db *DB) BindNamed(query string, arg interface{}) (string, []interface{}, error) { + return BindNamed(BindType(db.driverName), query, arg) +} + +// NamedQuery using this DB. +func (db *DB) NamedQuery(query string, arg interface{}) (*Rows, error) { + return NamedQuery(db, query, arg) +} + +// NamedExec using this DB. +func (db *DB) NamedExec(query string, arg interface{}) (sql.Result, error) { + return NamedExec(db, query, arg) +} + +// Select using this DB. +func (db *DB) Select(dest interface{}, query string, args ...interface{}) error { + return Select(db, dest, query, args...) +} + +// Get using this DB. +func (db *DB) Get(dest interface{}, query string, args ...interface{}) error { + return Get(db, dest, query, args...) +} + +// MustBegin starts a transaction, and panics on error. Returns an *sqlx.Tx instead +// of an *sql.Tx. +func (db *DB) MustBegin() *Tx { + tx, err := db.Beginx() + if err != nil { + panic(err) + } + return tx +} + +// Beginx begins a transaction and returns an *sqlx.Tx instead of an *sql.Tx. +func (db *DB) Beginx() (*Tx, error) { + tx, err := db.DB.Begin() + if err != nil { + return nil, err + } + return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err +} + +// Queryx queries the database and returns an *sqlx.Rows. +func (db *DB) Queryx(query string, args ...interface{}) (*Rows, error) { + r, err := db.DB.Query(query, args...) + if err != nil { + return nil, err + } + return &Rows{Rows: r, unsafe: db.unsafe, Mapper: db.Mapper}, err +} + +// QueryRowx queries the database and returns an *sqlx.Row. +func (db *DB) QueryRowx(query string, args ...interface{}) *Row { + rows, err := db.DB.Query(query, args...) + return &Row{rows: rows, err: err, unsafe: db.unsafe, Mapper: db.Mapper} +} + +// MustExec (panic) runs MustExec using this database. +func (db *DB) MustExec(query string, args ...interface{}) sql.Result { + return MustExec(db, query, args...) +} + +// Preparex returns an sqlx.Stmt instead of a sql.Stmt +func (db *DB) Preparex(query string) (*Stmt, error) { + return Preparex(db, query) +} + +// PrepareNamed returns an sqlx.NamedStmt +func (db *DB) PrepareNamed(query string) (*NamedStmt, error) { + return prepareNamed(db, query) +} + +// Tx is an sqlx wrapper around sql.Tx with extra functionality +type Tx struct { + *sql.Tx + driverName string + unsafe bool + Mapper *reflectx.Mapper +} + +// DriverName returns the driverName used by the DB which began this transaction. +func (tx *Tx) DriverName() string { + return tx.driverName +} + +// Rebind a query within a transaction's bindvar type. +func (tx *Tx) Rebind(query string) string { + return Rebind(BindType(tx.driverName), query) +} + +// Unsafe returns a version of Tx which will silently succeed to scan when +// columns in the SQL result have no fields in the destination struct. +func (tx *Tx) Unsafe() *Tx { + return &Tx{Tx: tx.Tx, driverName: tx.driverName, unsafe: true, Mapper: tx.Mapper} +} + +// BindNamed binds a query within a transaction's bindvar type. +func (tx *Tx) BindNamed(query string, arg interface{}) (string, []interface{}, error) { + return BindNamed(BindType(tx.driverName), query, arg) +} + +// NamedQuery within a transaction. +func (tx *Tx) NamedQuery(query string, arg interface{}) (*Rows, error) { + return NamedQuery(tx, query, arg) +} + +// NamedExec a named query within a transaction. +func (tx *Tx) NamedExec(query string, arg interface{}) (sql.Result, error) { + return NamedExec(tx, query, arg) +} + +// Select within a transaction. +func (tx *Tx) Select(dest interface{}, query string, args ...interface{}) error { + return Select(tx, dest, query, args...) +} + +// Queryx within a transaction. +func (tx *Tx) Queryx(query string, args ...interface{}) (*Rows, error) { + r, err := tx.Tx.Query(query, args...) + if err != nil { + return nil, err + } + return &Rows{Rows: r, unsafe: tx.unsafe, Mapper: tx.Mapper}, err +} + +// QueryRowx within a transaction. +func (tx *Tx) QueryRowx(query string, args ...interface{}) *Row { + rows, err := tx.Tx.Query(query, args...) + return &Row{rows: rows, err: err, unsafe: tx.unsafe, Mapper: tx.Mapper} +} + +// Get within a transaction. +func (tx *Tx) Get(dest interface{}, query string, args ...interface{}) error { + return Get(tx, dest, query, args...) +} + +// MustExec runs MustExec within a transaction. +func (tx *Tx) MustExec(query string, args ...interface{}) sql.Result { + return MustExec(tx, query, args...) +} + +// Preparex a statement within a transaction. +func (tx *Tx) Preparex(query string) (*Stmt, error) { + return Preparex(tx, query) +} + +// Stmtx returns a version of the prepared statement which runs within a transaction. Provided +// stmt can be either *sql.Stmt or *sqlx.Stmt. +func (tx *Tx) Stmtx(stmt interface{}) *Stmt { + var st sql.Stmt + var s *sql.Stmt + switch stmt.(type) { + case sql.Stmt: + st = stmt.(sql.Stmt) + s = &st + case Stmt: + s = stmt.(Stmt).Stmt + case *Stmt: + s = stmt.(*Stmt).Stmt + case *sql.Stmt: + s = stmt.(*sql.Stmt) + } + return &Stmt{Stmt: tx.Stmt(s), Mapper: tx.Mapper} +} + +// NamedStmt returns a version of the prepared statement which runs within a transaction. +func (tx *Tx) NamedStmt(stmt *NamedStmt) *NamedStmt { + return &NamedStmt{ + QueryString: stmt.QueryString, + Params: stmt.Params, + Stmt: tx.Stmtx(stmt.Stmt), + } +} + +// PrepareNamed returns an sqlx.NamedStmt +func (tx *Tx) PrepareNamed(query string) (*NamedStmt, error) { + return prepareNamed(tx, query) +} + +// Stmt is an sqlx wrapper around sql.Stmt with extra functionality +type Stmt struct { + *sql.Stmt + unsafe bool + Mapper *reflectx.Mapper +} + +// Unsafe returns a version of Stmt which will silently succeed to scan when +// columns in the SQL result have no fields in the destination struct. +func (s *Stmt) Unsafe() *Stmt { + return &Stmt{Stmt: s.Stmt, unsafe: true, Mapper: s.Mapper} +} + +// Select using the prepared statement. +func (s *Stmt) Select(dest interface{}, args ...interface{}) error { + return Select(&qStmt{*s}, dest, "", args...) +} + +// Get using the prepared statement. +func (s *Stmt) Get(dest interface{}, args ...interface{}) error { + return Get(&qStmt{*s}, dest, "", args...) +} + +// MustExec (panic) using this statement. Note that the query portion of the error +// output will be blank, as Stmt does not expose its query. +func (s *Stmt) MustExec(args ...interface{}) sql.Result { + return MustExec(&qStmt{*s}, "", args...) +} + +// QueryRowx using this statement. +func (s *Stmt) QueryRowx(args ...interface{}) *Row { + qs := &qStmt{*s} + return qs.QueryRowx("", args...) +} + +// Queryx using this statement. +func (s *Stmt) Queryx(args ...interface{}) (*Rows, error) { + qs := &qStmt{*s} + return qs.Queryx("", args...) +} + +// qStmt is an unexposed wrapper which lets you use a Stmt as a Queryer & Execer by +// implementing those interfaces and ignoring the `query` argument. +type qStmt struct{ Stmt } + +func (q *qStmt) Query(query string, args ...interface{}) (*sql.Rows, error) { + return q.Stmt.Query(args...) +} + +func (q *qStmt) Queryx(query string, args ...interface{}) (*Rows, error) { + r, err := q.Stmt.Query(args...) + if err != nil { + return nil, err + } + return &Rows{Rows: r, unsafe: q.Stmt.unsafe, Mapper: q.Stmt.Mapper}, err +} + +func (q *qStmt) QueryRowx(query string, args ...interface{}) *Row { + rows, err := q.Stmt.Query(args...) + return &Row{rows: rows, err: err, unsafe: q.Stmt.unsafe, Mapper: q.Stmt.Mapper} +} + +func (q *qStmt) Exec(query string, args ...interface{}) (sql.Result, error) { + return q.Stmt.Exec(args...) +} + +// Rows is a wrapper around sql.Rows which caches costly reflect operations +// during a looped StructScan +type Rows struct { + *sql.Rows + unsafe bool + Mapper *reflectx.Mapper + // these fields cache memory use for a rows during iteration w/ structScan + started bool + fields [][]int + values []interface{} +} + +// SliceScan using this Rows. +func (r *Rows) SliceScan() ([]interface{}, error) { + return SliceScan(r) +} + +// MapScan using this Rows. +func (r *Rows) MapScan(dest map[string]interface{}) error { + return MapScan(r, dest) +} + +// StructScan is like sql.Rows.Scan, but scans a single Row into a single Struct. +// Use this and iterate over Rows manually when the memory load of Select() might be +// prohibitive. *Rows.StructScan caches the reflect work of matching up column +// positions to fields to avoid that overhead per scan, which means it is not safe +// to run StructScan on the same Rows instance with different struct types. +func (r *Rows) StructScan(dest interface{}) error { + v := reflect.ValueOf(dest) + + if v.Kind() != reflect.Ptr { + return errors.New("must pass a pointer, not a value, to StructScan destination") + } + + v = reflect.Indirect(v) + + if !r.started { + columns, err := r.Columns() + if err != nil { + return err + } + m := r.Mapper + + r.fields = m.TraversalsByName(v.Type(), columns) + // if we are not unsafe and are missing fields, return an error + if f, err := missingFields(r.fields); err != nil && !r.unsafe { + return fmt.Errorf("missing destination name %s", columns[f]) + } + r.values = make([]interface{}, len(columns)) + r.started = true + } + + err := fieldsByTraversal(v, r.fields, r.values, true) + if err != nil { + return err + } + // scan into the struct field pointers and append to our results + err = r.Scan(r.values...) + if err != nil { + return err + } + return r.Err() +} + +// Connect to a database and verify with a ping. +func Connect(driverName, dataSourceName string) (*DB, error) { + db, err := Open(driverName, dataSourceName) + if err != nil { + return db, err + } + err = db.Ping() + return db, err +} + +// MustConnect connects to a database and panics on error. +func MustConnect(driverName, dataSourceName string) *DB { + db, err := Connect(driverName, dataSourceName) + if err != nil { + panic(err) + } + return db +} + +// Preparex prepares a statement. +func Preparex(p Preparer, query string) (*Stmt, error) { + s, err := p.Prepare(query) + if err != nil { + return nil, err + } + return &Stmt{Stmt: s, unsafe: isUnsafe(p), Mapper: mapperFor(p)}, err +} + +// Select executes a query using the provided Queryer, and StructScans each row +// into dest, which must be a slice. If the slice elements are scannable, then +// the result set must have only one column. Otherwise, StructScan is used. +// The *sql.Rows are closed automatically. +func Select(q Queryer, dest interface{}, query string, args ...interface{}) error { + rows, err := q.Queryx(query, args...) + if err != nil { + return err + } + // if something happens here, we want to make sure the rows are Closed + defer rows.Close() + return scanAll(rows, dest, false) +} + +// Get does a QueryRow using the provided Queryer, and scans the resulting row +// to dest. If dest is scannable, the result must only have one column. Otherwise, +// StructScan is used. Get will return sql.ErrNoRows like row.Scan would. +func Get(q Queryer, dest interface{}, query string, args ...interface{}) error { + r := q.QueryRowx(query, args...) + return r.scanAny(dest, false) +} + +// LoadFile exec's every statement in a file (as a single call to Exec). +// LoadFile may return a nil *sql.Result if errors are encountered locating or +// reading the file at path. LoadFile reads the entire file into memory, so it +// is not suitable for loading large data dumps, but can be useful for initializing +// schemas or loading indexes. +// +// FIXME: this does not really work with multi-statement files for mattn/go-sqlite3 +// or the go-mysql-driver/mysql drivers; pq seems to be an exception here. Detecting +// this by requiring something with DriverName() and then attempting to split the +// queries will be difficult to get right, and its current driver-specific behavior +// is deemed at least not complex in its incorrectness. +func LoadFile(e Execer, path string) (*sql.Result, error) { + realpath, err := filepath.Abs(path) + if err != nil { + return nil, err + } + contents, err := ioutil.ReadFile(realpath) + if err != nil { + return nil, err + } + res, err := e.Exec(string(contents)) + return &res, err +} + +// MustExec execs the query using e and panics if there was an error. +func MustExec(e Execer, query string, args ...interface{}) sql.Result { + res, err := e.Exec(query, args...) + if err != nil { + panic(err) + } + return res +} + +// SliceScan using this Rows. +func (r *Row) SliceScan() ([]interface{}, error) { + return SliceScan(r) +} + +// MapScan using this Rows. +func (r *Row) MapScan(dest map[string]interface{}) error { + return MapScan(r, dest) +} + +func (r *Row) scanAny(dest interface{}, structOnly bool) error { + if r.err != nil { + return r.err + } + defer r.rows.Close() + + v := reflect.ValueOf(dest) + if v.Kind() != reflect.Ptr { + return errors.New("must pass a pointer, not a value, to StructScan destination") + } + if v.IsNil() { + return errors.New("nil pointer passed to StructScan destination") + } + + base := reflectx.Deref(v.Type()) + scannable := isScannable(base) + + if structOnly && scannable { + return structOnlyError(base) + } + + columns, err := r.Columns() + if err != nil { + return err + } + + if scannable && len(columns) > 1 { + return fmt.Errorf("scannable dest type %s with >1 columns (%d) in result", base.Kind(), len(columns)) + } + + if scannable { + return r.Scan(dest) + } + + m := r.Mapper + + fields := m.TraversalsByName(v.Type(), columns) + // if we are not unsafe and are missing fields, return an error + if f, err := missingFields(fields); err != nil && !r.unsafe { + return fmt.Errorf("missing destination name %s", columns[f]) + } + values := make([]interface{}, len(columns)) + + err = fieldsByTraversal(v, fields, values, true) + if err != nil { + return err + } + // scan into the struct field pointers and append to our results + return r.Scan(values...) +} + +// StructScan a single Row into dest. +func (r *Row) StructScan(dest interface{}) error { + return r.scanAny(dest, true) +} + +// SliceScan a row, returning a []interface{} with values similar to MapScan. +// This function is primarly intended for use where the number of columns +// is not known. Because you can pass an []interface{} directly to Scan, +// it's recommended that you do that as it will not have to allocate new +// slices per row. +func SliceScan(r ColScanner) ([]interface{}, error) { + // ignore r.started, since we needn't use reflect for anything. + columns, err := r.Columns() + if err != nil { + return []interface{}{}, err + } + + values := make([]interface{}, len(columns)) + for i := range values { + values[i] = new(interface{}) + } + + err = r.Scan(values...) + + if err != nil { + return values, err + } + + for i := range columns { + values[i] = *(values[i].(*interface{})) + } + + return values, r.Err() +} + +// MapScan scans a single Row into the dest map[string]interface{}. +// Use this to get results for SQL that might not be under your control +// (for instance, if you're building an interface for an SQL server that +// executes SQL from input). Please do not use this as a primary interface! +// This will modify the map sent to it in place, so reuse the same map with +// care. Columns which occur more than once in the result will overwrite +// eachother! +func MapScan(r ColScanner, dest map[string]interface{}) error { + // ignore r.started, since we needn't use reflect for anything. + columns, err := r.Columns() + if err != nil { + return err + } + + values := make([]interface{}, len(columns)) + for i := range values { + values[i] = new(interface{}) + } + + err = r.Scan(values...) + if err != nil { + return err + } + + for i, column := range columns { + dest[column] = *(values[i].(*interface{})) + } + + return r.Err() +} + +type rowsi interface { + Close() error + Columns() ([]string, error) + Err() error + Next() bool + Scan(...interface{}) error +} + +// structOnlyError returns an error appropriate for type when a non-scannable +// struct is expected but something else is given +func structOnlyError(t reflect.Type) error { + isStruct := t.Kind() == reflect.Struct + isScanner := reflect.PtrTo(t).Implements(_scannerInterface) + if !isStruct { + return fmt.Errorf("expected %s but got %s", reflect.Struct, t.Kind()) + } + if isScanner { + return fmt.Errorf("structscan expects a struct dest but the provided struct type %s implements scanner", t.Name()) + } + return fmt.Errorf("expected a struct, but struct %s has no exported fields", t.Name()) +} + +// scanAll scans all rows into a destination, which must be a slice of any +// type. If the destination slice type is a Struct, then StructScan will be +// used on each row. If the destination is some other kind of base type, then +// each row must only have one column which can scan into that type. This +// allows you to do something like: +// +// rows, _ := db.Query("select id from people;") +// var ids []int +// scanAll(rows, &ids, false) +// +// and ids will be a list of the id results. I realize that this is a desirable +// interface to expose to users, but for now it will only be exposed via changes +// to `Get` and `Select`. The reason that this has been implemented like this is +// this is the only way to not duplicate reflect work in the new API while +// maintaining backwards compatibility. +func scanAll(rows rowsi, dest interface{}, structOnly bool) error { + var v, vp reflect.Value + + value := reflect.ValueOf(dest) + + // json.Unmarshal returns errors for these + if value.Kind() != reflect.Ptr { + return errors.New("must pass a pointer, not a value, to StructScan destination") + } + if value.IsNil() { + return errors.New("nil pointer passed to StructScan destination") + } + direct := reflect.Indirect(value) + + slice, err := baseType(value.Type(), reflect.Slice) + if err != nil { + return err + } + + isPtr := slice.Elem().Kind() == reflect.Ptr + base := reflectx.Deref(slice.Elem()) + scannable := isScannable(base) + + if structOnly && scannable { + return structOnlyError(base) + } + + columns, err := rows.Columns() + if err != nil { + return err + } + + // if it's a base type make sure it only has 1 column; if not return an error + if scannable && len(columns) > 1 { + return fmt.Errorf("non-struct dest type %s with >1 columns (%d)", base.Kind(), len(columns)) + } + + if !scannable { + var values []interface{} + var m *reflectx.Mapper + + switch rows.(type) { + case *Rows: + m = rows.(*Rows).Mapper + default: + m = mapper() + } + + fields := m.TraversalsByName(base, columns) + // if we are not unsafe and are missing fields, return an error + if f, err := missingFields(fields); err != nil && !isUnsafe(rows) { + return fmt.Errorf("missing destination name %s", columns[f]) + } + values = make([]interface{}, len(columns)) + + for rows.Next() { + // create a new struct type (which returns PtrTo) and indirect it + vp = reflect.New(base) + v = reflect.Indirect(vp) + + err = fieldsByTraversal(v, fields, values, true) + + // scan into the struct field pointers and append to our results + err = rows.Scan(values...) + if err != nil { + return err + } + + if isPtr { + direct.Set(reflect.Append(direct, vp)) + } else { + direct.Set(reflect.Append(direct, v)) + } + } + } else { + for rows.Next() { + vp = reflect.New(base) + err = rows.Scan(vp.Interface()) + // append + if isPtr { + direct.Set(reflect.Append(direct, vp)) + } else { + direct.Set(reflect.Append(direct, reflect.Indirect(vp))) + } + } + } + + return rows.Err() +} + +// FIXME: StructScan was the very first bit of API in sqlx, and now unfortunately +// it doesn't really feel like it's named properly. There is an incongruency +// between this and the way that StructScan (which might better be ScanStruct +// anyway) works on a rows object. + +// StructScan all rows from an sql.Rows or an sqlx.Rows into the dest slice. +// StructScan will scan in the entire rows result, so if you need do not want to +// allocate structs for the entire result, use Queryx and see sqlx.Rows.StructScan. +// If rows is sqlx.Rows, it will use its mapper, otherwise it will use the default. +func StructScan(rows rowsi, dest interface{}) error { + return scanAll(rows, dest, true) + +} + +// reflect helpers + +func baseType(t reflect.Type, expected reflect.Kind) (reflect.Type, error) { + t = reflectx.Deref(t) + if t.Kind() != expected { + return nil, fmt.Errorf("expected %s but got %s", expected, t.Kind()) + } + return t, nil +} + +// fieldsByName fills a values interface with fields from the passed value based +// on the traversals in int. If ptrs is true, return addresses instead of values. +// We write this instead of using FieldsByName to save allocations and map lookups +// when iterating over many rows. Empty traversals will get an interface pointer. +// Because of the necessity of requesting ptrs or values, it's considered a bit too +// specialized for inclusion in reflectx itself. +func fieldsByTraversal(v reflect.Value, traversals [][]int, values []interface{}, ptrs bool) error { + v = reflect.Indirect(v) + if v.Kind() != reflect.Struct { + return errors.New("argument not a struct") + } + + for i, traversal := range traversals { + if len(traversal) == 0 { + values[i] = new(interface{}) + continue + } + f := reflectx.FieldByIndexes(v, traversal) + if ptrs { + values[i] = f.Addr().Interface() + } else { + values[i] = f.Interface() + } + } + return nil +} + +func missingFields(transversals [][]int) (field int, err error) { + for i, t := range transversals { + if len(t) == 0 { + return i, errors.New("missing field") + } + } + return 0, nil +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go new file mode 100644 index 0000000..bc13485 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go @@ -0,0 +1,1311 @@ +// The following environment variables, if set, will be used: +// +// * SQLX_SQLITE_DSN +// * SQLX_POSTGRES_DSN +// * SQLX_MYSQL_DSN +// +// Set any of these variables to 'skip' to skip them. Note that for MySQL, +// the string '?parseTime=True' will be appended to the DSN if it's not there +// already. +// +package sqlx + +import ( + "database/sql" + "fmt" + "log" + "os" + "strings" + "testing" + "time" + + _ "github.com/go-sql-driver/mysql" + "github.com/jmoiron/sqlx/reflectx" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" +) + +/* compile time checks that Db, Tx, Stmt (qStmt) implement expected interfaces */ +var _, _ Ext = &DB{}, &Tx{} +var _, _ ColScanner = &Row{}, &Rows{} +var _ Queryer = &qStmt{} +var _ Execer = &qStmt{} + +var TestPostgres = true +var TestSqlite = true +var TestMysql = true + +var sldb *DB +var pgdb *DB +var mysqldb *DB +var active = []*DB{} + +func init() { + ConnectAll() +} + +func ConnectAll() { + var err error + + pgdsn := os.Getenv("SQLX_POSTGRES_DSN") + mydsn := os.Getenv("SQLX_MYSQL_DSN") + sqdsn := os.Getenv("SQLX_SQLITE_DSN") + + TestPostgres = pgdsn != "skip" + TestMysql = mydsn != "skip" + TestSqlite = sqdsn != "skip" + + if TestPostgres { + pgdb, err = Connect("postgres", pgdsn) + if err != nil { + fmt.Printf("Disabling PG tests:\n %v\n", err) + TestPostgres = false + } + } else { + fmt.Println("Disabling Postgres tests.") + } + + if TestMysql { + mysqldb, err = Connect("mysql", mydsn) + if err != nil { + fmt.Printf("Disabling MySQL tests:\n %v", err) + TestMysql = false + } + } else { + fmt.Println("Disabling MySQL tests.") + } + + if TestSqlite { + sldb, err = Connect("sqlite3", sqdsn) + if err != nil { + fmt.Printf("Disabling SQLite:\n %v", err) + TestSqlite = false + } + } else { + fmt.Println("Disabling SQLite tests.") + } +} + +type Schema struct { + create string + drop string +} + +func (s Schema) Postgres() (string, string) { + return s.create, s.drop +} + +func (s Schema) MySQL() (string, string) { + return strings.Replace(s.create, `"`, "`", -1), s.drop +} + +func (s Schema) Sqlite3() (string, string) { + return strings.Replace(s.create, `now()`, `CURRENT_TIMESTAMP`, -1), s.drop +} + +var defaultSchema = Schema{ + create: ` +CREATE TABLE person ( + first_name text, + last_name text, + email text, + added_at timestamp default now() +); + +CREATE TABLE place ( + country text, + city text NULL, + telcode integer +); + +CREATE TABLE capplace ( + "COUNTRY" text, + "CITY" text NULL, + "TELCODE" integer +); + +CREATE TABLE nullperson ( + first_name text NULL, + last_name text NULL, + email text NULL +); +`, + drop: ` +drop table person; +drop table place; +drop table capplace; +drop table nullperson; +`, +} + +type Person struct { + FirstName string `db:"first_name"` + LastName string `db:"last_name"` + Email string + AddedAt time.Time `db:"added_at"` +} + +type Person2 struct { + FirstName sql.NullString `db:"first_name"` + LastName sql.NullString `db:"last_name"` + Email sql.NullString +} + +type Place struct { + Country string + City sql.NullString + TelCode int +} + +type PlacePtr struct { + Country string + City *string + TelCode int +} + +type PersonPlace struct { + Person + Place +} + +type PersonPlacePtr struct { + *Person + *Place +} + +type EmbedConflict struct { + FirstName string `db:"first_name"` + Person +} + +type Loop1 struct { + Person +} + +type Loop2 struct { + Loop1 +} + +type Loop3 struct { + Loop2 +} + +type SliceMember struct { + Country string + City sql.NullString + TelCode int + People []Person `db:"-"` + Addresses []Place `db:"-"` +} + +// Note that because of field map caching, we need a new type here +// if we've used Place already soemwhere in sqlx +type CPlace Place + +func MultiExec(e Execer, query string) { + stmts := strings.Split(query, ";\n") + if len(strings.Trim(stmts[len(stmts)-1], " \n\t\r")) == 0 { + stmts = stmts[:len(stmts)-1] + } + for _, s := range stmts { + _, err := e.Exec(s) + if err != nil { + fmt.Println(err, s) + } + } +} + +func RunWithSchema(schema Schema, t *testing.T, test func(db *DB, t *testing.T)) { + runner := func(db *DB, t *testing.T, create, drop string) { + defer func() { + MultiExec(db, drop) + }() + + MultiExec(db, create) + test(db, t) + } + + if TestPostgres { + create, drop := schema.Postgres() + runner(pgdb, t, create, drop) + } + if TestSqlite { + create, drop := schema.Sqlite3() + runner(sldb, t, create, drop) + } + if TestMysql { + create, drop := schema.MySQL() + runner(mysqldb, t, create, drop) + } +} + +func loadDefaultFixture(db *DB, t *testing.T) { + tx := db.MustBegin() + tx.MustExec(tx.Rebind("INSERT INTO person (first_name, last_name, email) VALUES (?, ?, ?)"), "Jason", "Moiron", "jmoiron@jmoiron.net") + tx.MustExec(tx.Rebind("INSERT INTO person (first_name, last_name, email) VALUES (?, ?, ?)"), "John", "Doe", "johndoeDNE@gmail.net") + tx.MustExec(tx.Rebind("INSERT INTO place (country, city, telcode) VALUES (?, ?, ?)"), "United States", "New York", "1") + tx.MustExec(tx.Rebind("INSERT INTO place (country, telcode) VALUES (?, ?)"), "Hong Kong", "852") + tx.MustExec(tx.Rebind("INSERT INTO place (country, telcode) VALUES (?, ?)"), "Singapore", "65") + if db.DriverName() == "mysql" { + tx.MustExec(tx.Rebind("INSERT INTO capplace (`COUNTRY`, `TELCODE`) VALUES (?, ?)"), "Sarf Efrica", "27") + } else { + tx.MustExec(tx.Rebind("INSERT INTO capplace (\"COUNTRY\", \"TELCODE\") VALUES (?, ?)"), "Sarf Efrica", "27") + } + tx.Commit() +} + +// Test a new backwards compatible feature, that missing scan destinations +// will silently scan into sql.RawText rather than failing/panicing +func TestMissingNames(t *testing.T) { + RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T) { + loadDefaultFixture(db, t) + type PersonPlus struct { + FirstName string `db:"first_name"` + LastName string `db:"last_name"` + Email string + //AddedAt time.Time `db:"added_at"` + } + + // test Select first + pps := []PersonPlus{} + // pps lacks added_at destination + err := db.Select(&pps, "SELECT * FROM person") + if err == nil { + t.Error("Expected missing name from Select to fail, but it did not.") + } + + // test Get + pp := PersonPlus{} + err = db.Get(&pp, "SELECT * FROM person LIMIT 1") + if err == nil { + t.Error("Expected missing name Get to fail, but it did not.") + } + + // test naked StructScan + pps = []PersonPlus{} + rows, err := db.Query("SELECT * FROM person LIMIT 1") + if err != nil { + t.Fatal(err) + } + rows.Next() + err = StructScan(rows, &pps) + if err == nil { + t.Error("Expected missing name in StructScan to fail, but it did not.") + } + rows.Close() + + // now try various things with unsafe set. + db = db.Unsafe() + pps = []PersonPlus{} + err = db.Select(&pps, "SELECT * FROM person") + if err != nil { + t.Error(err) + } + + // test Get + pp = PersonPlus{} + err = db.Get(&pp, "SELECT * FROM person LIMIT 1") + if err != nil { + t.Error(err) + } + + // test naked StructScan + pps = []PersonPlus{} + rowsx, err := db.Queryx("SELECT * FROM person LIMIT 1") + if err != nil { + t.Fatal(err) + } + rowsx.Next() + err = StructScan(rowsx, &pps) + if err != nil { + t.Error(err) + } + rowsx.Close() + + }) +} + +func TestEmbeddedStructs(t *testing.T) { + RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T) { + loadDefaultFixture(db, t) + peopleAndPlaces := []PersonPlace{} + err := db.Select( + &peopleAndPlaces, + `SELECT person.*, place.* FROM + person natural join place`) + if err != nil { + t.Fatal(err) + } + for _, pp := range peopleAndPlaces { + if len(pp.Person.FirstName) == 0 { + t.Errorf("Expected non zero lengthed first name.") + } + if len(pp.Place.Country) == 0 { + t.Errorf("Expected non zero lengthed country.") + } + } + + // test embedded structs with StructScan + rows, err := db.Queryx( + `SELECT person.*, place.* FROM + person natural join place`) + if err != nil { + t.Error(err) + } + + perp := PersonPlace{} + rows.Next() + err = rows.StructScan(&perp) + if err != nil { + t.Error(err) + } + + if len(perp.Person.FirstName) == 0 { + t.Errorf("Expected non zero lengthed first name.") + } + if len(perp.Place.Country) == 0 { + t.Errorf("Expected non zero lengthed country.") + } + + rows.Close() + + // test the same for embedded pointer structs + peopleAndPlacesPtrs := []PersonPlacePtr{} + err = db.Select( + &peopleAndPlacesPtrs, + `SELECT person.*, place.* FROM + person natural join place`) + if err != nil { + t.Fatal(err) + } + for _, pp := range peopleAndPlacesPtrs { + if len(pp.Person.FirstName) == 0 { + t.Errorf("Expected non zero lengthed first name.") + } + if len(pp.Place.Country) == 0 { + t.Errorf("Expected non zero lengthed country.") + } + } + + // test "deep nesting" + l3s := []Loop3{} + err = db.Select(&l3s, `select * from person`) + if err != nil { + t.Fatal(err) + } + for _, l3 := range l3s { + if len(l3.Loop2.Loop1.Person.FirstName) == 0 { + t.Errorf("Expected non zero lengthed first name.") + } + } + + // test "embed conflicts" + ec := []EmbedConflict{} + err = db.Select(&ec, `select * from person`) + // I'm torn between erroring here or having some kind of working behavior + // in order to allow for more flexibility in destination structs + if err != nil { + t.Errorf("Was not expecting an error on embed conflicts.") + } + }) +} + +func TestSelectSliceMapTime(t *testing.T) { + RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T) { + loadDefaultFixture(db, t) + rows, err := db.Queryx("SELECT * FROM person") + if err != nil { + t.Fatal(err) + } + for rows.Next() { + _, err := rows.SliceScan() + if err != nil { + t.Error(err) + } + } + + rows, err = db.Queryx("SELECT * FROM person") + if err != nil { + t.Fatal(err) + } + for rows.Next() { + m := map[string]interface{}{} + err := rows.MapScan(m) + if err != nil { + t.Error(err) + } + } + + }) +} + +func TestNilReceiver(t *testing.T) { + RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T) { + loadDefaultFixture(db, t) + var p *Person + err := db.Get(p, "SELECT * FROM person LIMIT 1") + if err == nil { + t.Error("Expected error when getting into nil struct ptr.") + } + var pp *[]Person + err = db.Select(pp, "SELECT * FROM person") + if err == nil { + t.Error("Expected an error when selecting into nil slice ptr.") + } + }) +} + +func TestNamedQuery(t *testing.T) { + var schema = Schema{ + create: ` + CREATE TABLE person ( + first_name text NULL, + last_name text NULL, + email text NULL + ); + CREATE TABLE jsperson ( + "FIRST" text NULL, + last_name text NULL, + "EMAIL" text NULL + );`, + drop: ` + drop table person; + drop table jsperson; + `, + } + + RunWithSchema(schema, t, func(db *DB, t *testing.T) { + type Person struct { + FirstName sql.NullString `db:"first_name"` + LastName sql.NullString `db:"last_name"` + Email sql.NullString + } + + p := Person{ + FirstName: sql.NullString{"ben", true}, + LastName: sql.NullString{"doe", true}, + Email: sql.NullString{"ben@doe.com", true}, + } + + q1 := `INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)` + _, err := db.NamedExec(q1, p) + if err != nil { + log.Fatal(err) + } + + p2 := &Person{} + rows, err := db.NamedQuery("SELECT * FROM person WHERE first_name=:first_name", p) + if err != nil { + log.Fatal(err) + } + for rows.Next() { + err = rows.StructScan(p2) + if err != nil { + t.Error(err) + } + if p2.FirstName.String != "ben" { + t.Error("Expected first name of `ben`, got " + p2.FirstName.String) + } + if p2.LastName.String != "doe" { + t.Error("Expected first name of `doe`, got " + p2.LastName.String) + } + } + + // these are tests for #73; they verify that named queries work if you've + // changed the db mapper. This code checks both NamedQuery "ad-hoc" style + // queries and NamedStmt queries, which use different code paths internally. + old := *db.Mapper + + type JsonPerson struct { + FirstName sql.NullString `json:"FIRST"` + LastName sql.NullString `json:"last_name"` + Email sql.NullString + } + + jp := JsonPerson{ + FirstName: sql.NullString{"ben", true}, + LastName: sql.NullString{"smith", true}, + Email: sql.NullString{"ben@smith.com", true}, + } + + db.Mapper = reflectx.NewMapperFunc("json", strings.ToUpper) + + // prepare queries for case sensitivity to test our ToUpper function. + // postgres and sqlite accept "", but mysql uses ``; since Go's multi-line + // strings are `` we use "" by default and swap out for MySQL + pdb := func(s string, db *DB) string { + if db.DriverName() == "mysql" { + return strings.Replace(s, `"`, "`", -1) + } + return s + } + + q1 = `INSERT INTO jsperson ("FIRST", last_name, "EMAIL") VALUES (:FIRST, :last_name, :EMAIL)` + _, err = db.NamedExec(pdb(q1, db), jp) + if err != nil { + t.Fatal(err, db.DriverName()) + } + + // Checks that a person pulled out of the db matches the one we put in + check := func(t *testing.T, rows *Rows) { + jp = JsonPerson{} + for rows.Next() { + err = rows.StructScan(&jp) + if err != nil { + t.Error(err) + } + if jp.FirstName.String != "ben" { + t.Errorf("Expected first name of `ben`, got `%s` (%s) ", jp.FirstName.String, db.DriverName()) + } + if jp.LastName.String != "smith" { + t.Errorf("Expected LastName of `smith`, got `%s` (%s)", jp.LastName.String, db.DriverName()) + } + if jp.Email.String != "ben@smith.com" { + t.Errorf("Expected first name of `doe`, got `%s` (%s)", jp.Email.String, db.DriverName()) + } + } + } + + ns, err := db.PrepareNamed(pdb(` + SELECT * FROM jsperson + WHERE + "FIRST"=:FIRST AND + last_name=:last_name AND + "EMAIL"=:EMAIL + `, db)) + + if err != nil { + t.Fatal(err) + } + rows, err = ns.Queryx(jp) + if err != nil { + t.Fatal(err) + } + + check(t, rows) + + // Check exactly the same thing, but with db.NamedQuery, which does not go + // through the PrepareNamed/NamedStmt path. + rows, err = db.NamedQuery(pdb(` + SELECT * FROM jsperson + WHERE + "FIRST"=:FIRST AND + last_name=:last_name AND + "EMAIL"=:EMAIL + `, db), jp) + if err != nil { + t.Fatal(err) + } + + check(t, rows) + + db.Mapper = &old + + }) +} + +func TestNilInserts(t *testing.T) { + var schema = Schema{ + create: ` + CREATE TABLE tt ( + id integer, + value text NULL DEFAULT NULL + );`, + drop: "drop table tt;", + } + + RunWithSchema(schema, t, func(db *DB, t *testing.T) { + type TT struct { + Id int + Value *string + } + var v, v2 TT + r := db.Rebind + + db.MustExec(r(`INSERT INTO tt (id) VALUES (1)`)) + db.Get(&v, r(`SELECT * FROM tt`)) + if v.Id != 1 { + t.Errorf("Expecting id of 1, got %v", v.Id) + } + if v.Value != nil { + t.Errorf("Expecting NULL to map to nil, got %s", v.Value) + } + + v.Id = 2 + // NOTE: this incidentally uncovered a bug which was that named queries with + // pointer destinations would not work if the passed value here was not addressable, + // as reflectx.FieldByIndexes attempts to allocate nil pointer receivers for + // writing. This was fixed by creating & using the reflectx.FieldByIndexesReadOnly + // function. This next line is important as it provides the only coverage for this. + db.NamedExec(`INSERT INTO tt (id, value) VALUES (:id, :value)`, v) + + db.Get(&v2, r(`SELECT * FROM tt WHERE id=2`)) + if v.Id != v2.Id { + t.Errorf("%v != %v", v.Id, v2.Id) + } + if v2.Value != nil { + t.Errorf("Expecting NULL to map to nil, got %s", v.Value) + } + }) +} + +func TestScanError(t *testing.T) { + var schema = Schema{ + create: ` + CREATE TABLE kv ( + k text, + v integer + );`, + drop: `drop table kv;`, + } + + RunWithSchema(schema, t, func(db *DB, t *testing.T) { + type WrongTypes struct { + K int + V string + } + _, err := db.Exec(db.Rebind("INSERT INTO kv (k, v) VALUES (?, ?)"), "hi", 1) + if err != nil { + t.Error(err) + } + + rows, err := db.Queryx("SELECT * FROM kv") + if err != nil { + t.Error(err) + } + for rows.Next() { + var wt WrongTypes + err := rows.StructScan(&wt) + if err == nil { + t.Errorf("%s: Scanning wrong types into keys should have errored.", db.DriverName()) + } + } + }) +} + +// FIXME: this function is kinda big but it slows things down to be constantly +// loading and reloading the schema.. + +func TestUsage(t *testing.T) { + RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T) { + loadDefaultFixture(db, t) + slicemembers := []SliceMember{} + err := db.Select(&slicemembers, "SELECT * FROM place ORDER BY telcode ASC") + if err != nil { + t.Fatal(err) + } + + people := []Person{} + + err = db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC") + if err != nil { + t.Fatal(err) + } + + jason, john := people[0], people[1] + if jason.FirstName != "Jason" { + t.Errorf("Expecting FirstName of Jason, got %s", jason.FirstName) + } + if jason.LastName != "Moiron" { + t.Errorf("Expecting LastName of Moiron, got %s", jason.LastName) + } + if jason.Email != "jmoiron@jmoiron.net" { + t.Errorf("Expecting Email of jmoiron@jmoiron.net, got %s", jason.Email) + } + if john.FirstName != "John" || john.LastName != "Doe" || john.Email != "johndoeDNE@gmail.net" { + t.Errorf("John Doe's person record not what expected: Got %v\n", john) + } + + jason = Person{} + err = db.Get(&jason, db.Rebind("SELECT * FROM person WHERE first_name=?"), "Jason") + + if err != nil { + t.Fatal(err) + } + if jason.FirstName != "Jason" { + t.Errorf("Expecting to get back Jason, but got %v\n", jason.FirstName) + } + + err = db.Get(&jason, db.Rebind("SELECT * FROM person WHERE first_name=?"), "Foobar") + if err == nil { + t.Errorf("Expecting an error, got nil\n") + } + if err != sql.ErrNoRows { + t.Errorf("Expected sql.ErrNoRows, got %v\n", err) + } + + // The following tests check statement reuse, which was actually a problem + // due to copying being done when creating Stmt's which was eventually removed + stmt1, err := db.Preparex(db.Rebind("SELECT * FROM person WHERE first_name=?")) + if err != nil { + t.Fatal(err) + } + jason = Person{} + + row := stmt1.QueryRowx("DoesNotExist") + row.Scan(&jason) + row = stmt1.QueryRowx("DoesNotExist") + row.Scan(&jason) + + err = stmt1.Get(&jason, "DoesNotExist User") + if err == nil { + t.Error("Expected an error") + } + err = stmt1.Get(&jason, "DoesNotExist User 2") + + stmt2, err := db.Preparex(db.Rebind("SELECT * FROM person WHERE first_name=?")) + if err != nil { + t.Fatal(err) + } + jason = Person{} + tx, err := db.Beginx() + if err != nil { + t.Fatal(err) + } + tstmt2 := tx.Stmtx(stmt2) + row2 := tstmt2.QueryRowx("Jason") + err = row2.StructScan(&jason) + if err != nil { + t.Error(err) + } + tx.Commit() + + places := []*Place{} + err = db.Select(&places, "SELECT telcode FROM place ORDER BY telcode ASC") + usa, singsing, honkers := places[0], places[1], places[2] + + if usa.TelCode != 1 || honkers.TelCode != 852 || singsing.TelCode != 65 { + t.Errorf("Expected integer telcodes to work, got %#v", places) + } + + placesptr := []PlacePtr{} + err = db.Select(&placesptr, "SELECT * FROM place ORDER BY telcode ASC") + if err != nil { + t.Error(err) + } + //fmt.Printf("%#v\n%#v\n%#v\n", placesptr[0], placesptr[1], placesptr[2]) + + // if you have null fields and use SELECT *, you must use sql.Null* in your struct + // this test also verifies that you can use either a []Struct{} or a []*Struct{} + places2 := []Place{} + err = db.Select(&places2, "SELECT * FROM place ORDER BY telcode ASC") + usa, singsing, honkers = &places2[0], &places2[1], &places2[2] + + // this should return a type error that &p is not a pointer to a struct slice + p := Place{} + err = db.Select(&p, "SELECT * FROM place ORDER BY telcode ASC") + if err == nil { + t.Errorf("Expected an error, argument to select should be a pointer to a struct slice") + } + + // this should be an error + pl := []Place{} + err = db.Select(pl, "SELECT * FROM place ORDER BY telcode ASC") + if err == nil { + t.Errorf("Expected an error, argument to select should be a pointer to a struct slice, not a slice.") + } + + if usa.TelCode != 1 || honkers.TelCode != 852 || singsing.TelCode != 65 { + t.Errorf("Expected integer telcodes to work, got %#v", places) + } + + stmt, err := db.Preparex(db.Rebind("SELECT country, telcode FROM place WHERE telcode > ? ORDER BY telcode ASC")) + if err != nil { + t.Error(err) + } + + places = []*Place{} + err = stmt.Select(&places, 10) + if len(places) != 2 { + t.Error("Expected 2 places, got 0.") + } + if err != nil { + t.Fatal(err) + } + singsing, honkers = places[0], places[1] + if singsing.TelCode != 65 || honkers.TelCode != 852 { + t.Errorf("Expected the right telcodes, got %#v", places) + } + + rows, err := db.Queryx("SELECT * FROM place") + if err != nil { + t.Fatal(err) + } + place := Place{} + for rows.Next() { + err = rows.StructScan(&place) + if err != nil { + t.Fatal(err) + } + } + + rows, err = db.Queryx("SELECT * FROM place") + if err != nil { + t.Fatal(err) + } + m := map[string]interface{}{} + for rows.Next() { + err = rows.MapScan(m) + if err != nil { + t.Fatal(err) + } + _, ok := m["country"] + if !ok { + t.Errorf("Expected key `country` in map but could not find it (%#v)\n", m) + } + } + + rows, err = db.Queryx("SELECT * FROM place") + if err != nil { + t.Fatal(err) + } + for rows.Next() { + s, err := rows.SliceScan() + if err != nil { + t.Error(err) + } + if len(s) != 3 { + t.Errorf("Expected 3 columns in result, got %d\n", len(s)) + } + } + + // test advanced querying + // test that NamedExec works with a map as well as a struct + _, err = db.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first, :last, :email)", map[string]interface{}{ + "first": "Bin", + "last": "Smuth", + "email": "bensmith@allblacks.nz", + }) + if err != nil { + t.Fatal(err) + } + + // ensure that if the named param happens right at the end it still works + // ensure that NamedQuery works with a map[string]interface{} + rows, err = db.NamedQuery("SELECT * FROM person WHERE first_name=:first", map[string]interface{}{"first": "Bin"}) + if err != nil { + t.Fatal(err) + } + + ben := &Person{} + for rows.Next() { + err = rows.StructScan(ben) + if err != nil { + t.Fatal(err) + } + if ben.FirstName != "Bin" { + t.Fatal("Expected first name of `Bin`, got " + ben.FirstName) + } + if ben.LastName != "Smuth" { + t.Fatal("Expected first name of `Smuth`, got " + ben.LastName) + } + } + + ben.FirstName = "Ben" + ben.LastName = "Smith" + ben.Email = "binsmuth@allblacks.nz" + + // Insert via a named query using the struct + _, err = db.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", ben) + + if err != nil { + t.Fatal(err) + } + + rows, err = db.NamedQuery("SELECT * FROM person WHERE first_name=:first_name", ben) + if err != nil { + t.Fatal(err) + } + for rows.Next() { + err = rows.StructScan(ben) + if err != nil { + t.Fatal(err) + } + if ben.FirstName != "Ben" { + t.Fatal("Expected first name of `Ben`, got " + ben.FirstName) + } + if ben.LastName != "Smith" { + t.Fatal("Expected first name of `Smith`, got " + ben.LastName) + } + } + // ensure that Get does not panic on emppty result set + person := &Person{} + err = db.Get(person, "SELECT * FROM person WHERE first_name=$1", "does-not-exist") + if err == nil { + t.Fatal("Should have got an error for Get on non-existant row.") + } + + // lets test prepared statements some more + + stmt, err = db.Preparex(db.Rebind("SELECT * FROM person WHERE first_name=?")) + if err != nil { + t.Fatal(err) + } + rows, err = stmt.Queryx("Ben") + if err != nil { + t.Fatal(err) + } + for rows.Next() { + err = rows.StructScan(ben) + if err != nil { + t.Fatal(err) + } + if ben.FirstName != "Ben" { + t.Fatal("Expected first name of `Ben`, got " + ben.FirstName) + } + if ben.LastName != "Smith" { + t.Fatal("Expected first name of `Smith`, got " + ben.LastName) + } + } + + john = Person{} + stmt, err = db.Preparex(db.Rebind("SELECT * FROM person WHERE first_name=?")) + if err != nil { + t.Error(err) + } + err = stmt.Get(&john, "John") + if err != nil { + t.Error(err) + } + + // test name mapping + // THIS USED TO WORK BUT WILL NO LONGER WORK. + db.MapperFunc(strings.ToUpper) + rsa := CPlace{} + err = db.Get(&rsa, "SELECT * FROM capplace;") + if err != nil { + t.Error(err, "in db:", db.DriverName()) + } + db.MapperFunc(strings.ToLower) + + // create a copy and change the mapper, then verify the copy behaves + // differently from the original. + dbCopy := NewDb(db.DB, db.DriverName()) + dbCopy.MapperFunc(strings.ToUpper) + err = dbCopy.Get(&rsa, "SELECT * FROM capplace;") + if err != nil { + fmt.Println(db.DriverName()) + t.Error(err) + } + + err = db.Get(&rsa, "SELECT * FROM cappplace;") + if err == nil { + t.Error("Expected no error, got ", err) + } + + // test base type slices + var sdest []string + rows, err = db.Queryx("SELECT email FROM person ORDER BY email ASC;") + if err != nil { + t.Error(err) + } + err = scanAll(rows, &sdest, false) + if err != nil { + t.Error(err) + } + + // test Get with base types + var count int + err = db.Get(&count, "SELECT count(*) FROM person;") + if err != nil { + t.Error(err) + } + if count != len(sdest) { + t.Errorf("Expected %d == %d (count(*) vs len(SELECT ..)", count, len(sdest)) + } + + // test Get and Select with time.Time, #84 + var addedAt time.Time + err = db.Get(&addedAt, "SELECT added_at FROM person LIMIT 1;") + if err != nil { + t.Error(err) + } + + var addedAts []time.Time + err = db.Select(&addedAts, "SELECT added_at FROM person;") + if err != nil { + t.Error(err) + } + + // test it on a double pointer + var pcount *int + err = db.Get(&pcount, "SELECT count(*) FROM person;") + if err != nil { + t.Error(err) + } + if *pcount != count { + t.Error("expected %d = %d", *pcount, count) + } + + // test Select... + sdest = []string{} + err = db.Select(&sdest, "SELECT first_name FROM person ORDER BY first_name ASC;") + if err != nil { + t.Error(err) + } + expected := []string{"Ben", "Bin", "Jason", "John"} + for i, got := range sdest { + if got != expected[i] { + t.Errorf("Expected %d result to be %s, but got %s", i, expected[i], got) + } + } + + var nsdest []sql.NullString + err = db.Select(&nsdest, "SELECT city FROM place ORDER BY city ASC") + if err != nil { + t.Error(err) + } + for _, val := range nsdest { + if val.Valid && val.String != "New York" { + t.Errorf("expected single valid result to be `New York`, but got %s", val.String) + } + } + }) +} + +type Product struct { + ProductID int +} + +// tests that sqlx will not panic when the wrong driver is passed because +// of an automatic nil dereference in sqlx.Open(), which was fixed. +func TestDoNotPanicOnConnect(t *testing.T) { + _, err := Connect("bogus", "hehe") + if err == nil { + t.Errorf("Should return error when using bogus driverName") + } +} +func TestRebind(t *testing.T) { + q1 := `INSERT INTO foo (a, b, c, d, e, f, g, h, i) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` + q2 := `INSERT INTO foo (a, b, c) VALUES (?, ?, "foo"), ("Hi", ?, ?)` + + s1 := Rebind(DOLLAR, q1) + s2 := Rebind(DOLLAR, q2) + + if s1 != `INSERT INTO foo (a, b, c, d, e, f, g, h, i) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` { + t.Errorf("q1 failed") + } + + if s2 != `INSERT INTO foo (a, b, c) VALUES ($1, $2, "foo"), ("Hi", $3, $4)` { + t.Errorf("q2 failed") + } +} + +func TestBindMap(t *testing.T) { + // Test that it works.. + q1 := `INSERT INTO foo (a, b, c, d) VALUES (:name, :age, :first, :last)` + am := map[string]interface{}{ + "name": "Jason Moiron", + "age": 30, + "first": "Jason", + "last": "Moiron", + } + + bq, args, _ := bindMap(QUESTION, q1, am) + expect := `INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?)` + if bq != expect { + t.Errorf("Interpolation of query failed: got `%v`, expected `%v`\n", bq, expect) + } + + if args[0].(string) != "Jason Moiron" { + t.Errorf("Expected `Jason Moiron`, got %v\n", args[0]) + } + + if args[1].(int) != 30 { + t.Errorf("Expected 30, got %v\n", args[1]) + } + + if args[2].(string) != "Jason" { + t.Errorf("Expected Jason, got %v\n", args[2]) + } + + if args[3].(string) != "Moiron" { + t.Errorf("Expected Moiron, got %v\n", args[3]) + } +} + +func TestBindStruct(t *testing.T) { + var err error + + q1 := `INSERT INTO foo (a, b, c, d) VALUES (:name, :age, :first, :last)` + + type tt struct { + Name string + Age int + First string + Last string + } + + type tt2 struct { + Field1 string `db:"field_1"` + Field2 string `db:"field_2"` + } + + type tt3 struct { + tt2 + Name string + } + + am := tt{"Jason Moiron", 30, "Jason", "Moiron"} + + bq, args, _ := bindStruct(QUESTION, q1, am, mapper()) + expect := `INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?)` + if bq != expect { + t.Errorf("Interpolation of query failed: got `%v`, expected `%v`\n", bq, expect) + } + + if args[0].(string) != "Jason Moiron" { + t.Errorf("Expected `Jason Moiron`, got %v\n", args[0]) + } + + if args[1].(int) != 30 { + t.Errorf("Expected 30, got %v\n", args[1]) + } + + if args[2].(string) != "Jason" { + t.Errorf("Expected Jason, got %v\n", args[2]) + } + + if args[3].(string) != "Moiron" { + t.Errorf("Expected Moiron, got %v\n", args[3]) + } + + am2 := tt2{"Hello", "World"} + bq, args, _ = bindStruct(QUESTION, "INSERT INTO foo (a, b) VALUES (:field_2, :field_1)", am2, mapper()) + expect = `INSERT INTO foo (a, b) VALUES (?, ?)` + if bq != expect { + t.Errorf("Interpolation of query failed: got `%v`, expected `%v`\n", bq, expect) + } + + if args[0].(string) != "World" { + t.Errorf("Expected 'World', got %s\n", args[0].(string)) + } + if args[1].(string) != "Hello" { + t.Errorf("Expected 'Hello', got %s\n", args[1].(string)) + } + + am3 := tt3{Name: "Hello!"} + am3.Field1 = "Hello" + am3.Field2 = "World" + + bq, args, err = bindStruct(QUESTION, "INSERT INTO foo (a, b, c) VALUES (:name, :field_1, :field_2)", am3, mapper()) + + if err != nil { + t.Fatal(err) + } + + expect = `INSERT INTO foo (a, b, c) VALUES (?, ?, ?)` + if bq != expect { + t.Errorf("Interpolation of query failed: got `%v`, expected `%v`\n", bq, expect) + } + + if args[0].(string) != "Hello!" { + t.Errorf("Expected 'Hello!', got %s\n", args[0].(string)) + } + if args[1].(string) != "Hello" { + t.Errorf("Expected 'Hello', got %s\n", args[1].(string)) + } + if args[2].(string) != "World" { + t.Errorf("Expected 'World', got %s\n", args[0].(string)) + } +} + +func TestEmbeddedLiterals(t *testing.T) { + var schema = Schema{ + create: ` + CREATE TABLE x ( + k text + );`, + drop: `drop table x;`, + } + + RunWithSchema(schema, t, func(db *DB, t *testing.T) { + type t1 struct { + K *string + } + type t2 struct { + Inline struct { + F string + } + K *string + } + + db.MustExec(db.Rebind("INSERT INTO x (k) VALUES (?), (?), (?);"), "one", "two", "three") + + target := t1{} + err := db.Get(&target, db.Rebind("SELECT * FROM x WHERE k=?"), "one") + if err != nil { + t.Error(err) + } + if *target.K != "one" { + t.Error("Expected target.K to be `one`, got ", target.K) + } + + target2 := t2{} + err = db.Get(&target2, db.Rebind("SELECT * FROM x WHERE k=?"), "one") + if err != nil { + t.Error(err) + } + if *target2.K != "one" { + t.Errorf("Expected target2.K to be `one`, got `%v`", target2.K) + } + + }) +} + +func BenchmarkBindStruct(b *testing.B) { + b.StopTimer() + q1 := `INSERT INTO foo (a, b, c, d) VALUES (:name, :age, :first, :last)` + type t struct { + Name string + Age int + First string + Last string + } + am := t{"Jason Moiron", 30, "Jason", "Moiron"} + b.StartTimer() + for i := 0; i < b.N; i++ { + bindStruct(DOLLAR, q1, am, mapper()) + //bindMap(QUESTION, q1, am) + } +} + +func BenchmarkBindMap(b *testing.B) { + b.StopTimer() + q1 := `INSERT INTO foo (a, b, c, d) VALUES (:name, :age, :first, :last)` + am := map[string]interface{}{ + "name": "Jason Moiron", + "age": 30, + "first": "Jason", + "last": "Moiron", + } + b.StartTimer() + for i := 0; i < b.N; i++ { + bindMap(DOLLAR, q1, am) + //bindMap(QUESTION, q1, am) + } +} + +func BenchmarkRebind(b *testing.B) { + b.StopTimer() + q1 := `INSERT INTO foo (a, b, c, d, e, f, g, h, i) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)` + q2 := `INSERT INTO foo (a, b, c) VALUES (?, ?, "foo"), ("Hi", ?, ?)` + b.StartTimer() + + for i := 0; i < b.N; i++ { + Rebind(DOLLAR, q1) + Rebind(DOLLAR, q2) + } +} + +func BenchmarkRebindBuffer(b *testing.B) { + b.StopTimer() + q1 := `INSERT INTO foo (a, b, c, d, e, f, g, h, i) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)` + q2 := `INSERT INTO foo (a, b, c) VALUES (?, ?, "foo"), ("Hi", ?, ?)` + b.StartTimer() + + for i := 0; i < b.N; i++ { + rebindBuff(DOLLAR, q1) + rebindBuff(DOLLAR, q2) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/README.md new file mode 100644 index 0000000..713abe5 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/README.md @@ -0,0 +1,5 @@ +# types + +The types package provides some useful types which implement the `sql.Scanner` +and `driver.Valuer` interfaces, suitable for use as scan and value targets with +database/sql. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types.go new file mode 100644 index 0000000..f1700b6 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types.go @@ -0,0 +1,95 @@ +package types + +import ( + "bytes" + "compress/gzip" + "database/sql/driver" + "encoding/json" + "errors" + + "io/ioutil" +) + +type GzippedText []byte + +func (g GzippedText) Value() (driver.Value, error) { + b := make([]byte, 0, len(g)) + buf := bytes.NewBuffer(b) + w := gzip.NewWriter(buf) + w.Write(g) + w.Close() + return buf.Bytes(), nil + +} + +func (g *GzippedText) Scan(src interface{}) error { + var source []byte + switch src.(type) { + case string: + source = []byte(src.(string)) + case []byte: + source = src.([]byte) + default: + return errors.New("Incompatible type for GzippedText") + } + reader, err := gzip.NewReader(bytes.NewReader(source)) + defer reader.Close() + b, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + *g = GzippedText(b) + return nil +} + +// JsonText is a json.RawMessage, which is a []byte underneath. +// Value() validates the json format in the source, and returns an error if +// the json is not valid. Scan does no validation. JsonText additionally +// implements `Unmarshal`, which unmarshals the json within to an interface{} +type JsonText json.RawMessage + +// Returns the *j as the JSON encoding of j. +func (j *JsonText) MarshalJSON() ([]byte, error) { + return *j, nil +} + +// UnmarshalJSON sets *j to a copy of data +func (j *JsonText) UnmarshalJSON(data []byte) error { + if j == nil { + return errors.New("JsonText: UnmarshalJSON on nil pointer") + } + *j = append((*j)[0:0], data...) + return nil + +} + +// Value returns j as a value. This does a validating unmarshal into another +// RawMessage. If j is invalid json, it returns an error. +func (j JsonText) Value() (driver.Value, error) { + var m json.RawMessage + var err = j.Unmarshal(&m) + if err != nil { + return []byte{}, err + } + return []byte(j), nil +} + +// Scan stores the src in *j. No validation is done. +func (j *JsonText) Scan(src interface{}) error { + var source []byte + switch src.(type) { + case string: + source = []byte(src.(string)) + case []byte: + source = src.([]byte) + default: + return errors.New("Incompatible type for JsonText") + } + *j = JsonText(append((*j)[0:0], source...)) + return nil +} + +// Unmarshal unmarshal's the json in j to v, as in json.Unmarshal. +func (j *JsonText) Unmarshal(v interface{}) error { + return json.Unmarshal([]byte(*j), v) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types_test.go new file mode 100644 index 0000000..e5c9e1a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/jmoiron/sqlx/types/types_test.go @@ -0,0 +1,42 @@ +package types + +import "testing" + +func TestGzipText(t *testing.T) { + g := GzippedText("Hello, world") + v, err := g.Value() + if err != nil { + t.Errorf("Was not expecting an error") + } + err = (&g).Scan(v) + if err != nil { + t.Errorf("Was not expecting an error") + } + if string(g) != "Hello, world" { + t.Errorf("Was expecting the string we sent in (Hello World), got %s", string(g)) + } +} + +func TestJsonText(t *testing.T) { + j := JsonText(`{"foo": 1, "bar": 2}`) + v, err := j.Value() + if err != nil { + t.Errorf("Was not expecting an error") + } + err = (&j).Scan(v) + if err != nil { + t.Errorf("Was not expecting an error") + } + m := map[string]interface{}{} + j.Unmarshal(&m) + + if m["foo"].(float64) != 1 || m["bar"].(float64) != 2 { + t.Errorf("Expected valid json but got some garbage instead? %#v", m) + } + + j = JsonText(`{"foo": 1, invalid, false}`) + v, err = j.Value() + if err == nil { + t.Errorf("Was expecting invalid json to fail!") + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/.gitignore b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/LICENSE b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/LICENSE new file mode 100644 index 0000000..ade9307 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/LICENSE @@ -0,0 +1,191 @@ +All files in this repository are licensed as follows. If you contribute +to this repository, it is assumed that you license your contribution +under the same license unless you state otherwise. + +All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file. + +This software is licensed under the LGPLv3, included below. + +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. + +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/Makefile b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/Makefile new file mode 100644 index 0000000..ab7c2e6 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/Makefile @@ -0,0 +1,11 @@ +default: check + +check: + go test && go test -compiler gccgo + +docs: + godoc2md github.com/juju/errors > README.md + sed -i 's|\[godoc-link-here\]|[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors)|' README.md + + +.PHONY: default check docs diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/README.md new file mode 100644 index 0000000..ee24891 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/README.md @@ -0,0 +1,536 @@ + +# errors + import "github.com/juju/errors" + +[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors) + +The juju/errors provides an easy way to annotate errors without losing the +orginal error context. + +The exported `New` and `Errorf` functions are designed to replace the +`errors.New` and `fmt.Errorf` functions respectively. The same underlying +error is there, but the package also records the location at which the error +was created. + +A primary use case for this library is to add extra context any time an +error is returned from a function. + + + if err := SomeFunc(); err != nil { + return err + } + +This instead becomes: + + + if err := SomeFunc(); err != nil { + return errors.Trace(err) + } + +which just records the file and line number of the Trace call, or + + + if err := SomeFunc(); err != nil { + return errors.Annotate(err, "more context") + } + +which also adds an annotation to the error. + +When you want to check to see if an error is of a particular type, a helper +function is normally exported by the package that returned the error, like the +`os` package does. The underlying cause of the error is available using the +`Cause` function. + + + os.IsNotExist(errors.Cause(err)) + +The result of the `Error()` call on an annotated error is the annotations joined +with colons, then the result of the `Error()` method for the underlying error +that was the cause. + + + err := errors.Errorf("original") + err = errors.Annotatef(err, "context") + err = errors.Annotatef(err, "more context") + err.Error() -> "more context: context: original" + +Obviously recording the file, line and functions is not very useful if you +cannot get them back out again. + + + errors.ErrorStack(err) + +will return something like: + + + first error + github.com/juju/errors/annotation_test.go:193: + github.com/juju/errors/annotation_test.go:194: annotation + github.com/juju/errors/annotation_test.go:195: + github.com/juju/errors/annotation_test.go:196: more context + github.com/juju/errors/annotation_test.go:197: + +The first error was generated by an external system, so there was no location +associated. The second, fourth, and last lines were generated with Trace calls, +and the other two through Annotate. + +Sometimes when responding to an error you want to return a more specific error +for the situation. + + + if err := FindField(field); err != nil { + return errors.Wrap(err, errors.NotFoundf(field)) + } + +This returns an error where the complete error stack is still available, and +`errors.Cause()` will return the `NotFound` error. + + + + + + +## func AlreadyExistsf +``` go +func AlreadyExistsf(format string, args ...interface{}) error +``` +AlreadyExistsf returns an error which satisfies IsAlreadyExists(). + + +## func Annotate +``` go +func Annotate(other error, message string) error +``` +Annotate is used to add extra context to an existing error. The location of +the Annotate call is recorded with the annotations. The file, line and +function are also recorded. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Annotate(err, "failed to frombulate") + } + + +## func Annotatef +``` go +func Annotatef(other error, format string, args ...interface{}) error +``` +Annotatef is used to add extra context to an existing error. The location of +the Annotate call is recorded with the annotations. The file, line and +function are also recorded. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Annotatef(err, "failed to frombulate the %s", arg) + } + + +## func Cause +``` go +func Cause(err error) error +``` +Cause returns the cause of the given error. This will be either the +original error, or the result of a Wrap or Mask call. + +Cause is the usual way to diagnose errors that may have been wrapped by +the other errors functions. + + +## func DeferredAnnotatef +``` go +func DeferredAnnotatef(err *error, format string, args ...interface{}) +``` +DeferredAnnotatef annotates the given error (when it is not nil) with the given +format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef +does nothing. This method is used in a defer statement in order to annotate any +resulting error with the same message. + +For example: + + + defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg) + + +## func Details +``` go +func Details(err error) string +``` +Details returns information about the stack of errors wrapped by err, in +the format: + + + [{filename:99: error one} {otherfile:55: cause of error one}] + +This is a terse alternative to ErrorStack as it returns a single line. + + +## func ErrorStack +``` go +func ErrorStack(err error) string +``` +ErrorStack returns a string representation of the annotated error. If the +error passed as the parameter is not an annotated error, the result is +simply the result of the Error() method on that error. + +If the error is an annotated error, a multi-line string is returned where +each line represents one entry in the annotation stack. The full filename +from the call stack is used in the output. + + + first error + github.com/juju/errors/annotation_test.go:193: + github.com/juju/errors/annotation_test.go:194: annotation + github.com/juju/errors/annotation_test.go:195: + github.com/juju/errors/annotation_test.go:196: more context + github.com/juju/errors/annotation_test.go:197: + + +## func Errorf +``` go +func Errorf(format string, args ...interface{}) error +``` +Errorf creates a new annotated error and records the location that the +error is created. This should be a drop in replacement for fmt.Errorf. + +For example: + + + return errors.Errorf("validation failed: %s", message) + + +## func IsAlreadyExists +``` go +func IsAlreadyExists(err error) bool +``` +IsAlreadyExists reports whether the error was created with +AlreadyExistsf() or NewAlreadyExists(). + + +## func IsNotFound +``` go +func IsNotFound(err error) bool +``` +IsNotFound reports whether err was created with NotFoundf() or +NewNotFound(). + + +## func IsNotImplemented +``` go +func IsNotImplemented(err error) bool +``` +IsNotImplemented reports whether err was created with +NotImplementedf() or NewNotImplemented(). + + +## func IsNotSupported +``` go +func IsNotSupported(err error) bool +``` +IsNotSupported reports whether the error was created with +NotSupportedf() or NewNotSupported(). + + +## func IsNotValid +``` go +func IsNotValid(err error) bool +``` +IsNotValid reports whether the error was created with NotValidf() or +NewNotValid(). + + +## func IsUnauthorized +``` go +func IsUnauthorized(err error) bool +``` +IsUnauthorized reports whether err was created with Unauthorizedf() or +NewUnauthorized(). + + +## func Mask +``` go +func Mask(other error) error +``` +Mask hides the underlying error type, and records the location of the masking. + + +## func Maskf +``` go +func Maskf(other error, format string, args ...interface{}) error +``` +Mask masks the given error with the given format string and arguments (like +fmt.Sprintf), returning a new error that maintains the error stack, but +hides the underlying error type. The error string still contains the full +annotations. If you want to hide the annotations, call Wrap. + + +## func New +``` go +func New(message string) error +``` +New is a drop in replacement for the standard libary errors module that records +the location that the error is created. + +For example: + + + return errors.New("validation failed") + + +## func NewAlreadyExists +``` go +func NewAlreadyExists(err error, msg string) error +``` +NewAlreadyExists returns an error which wraps err and satisfies +IsAlreadyExists(). + + +## func NewNotFound +``` go +func NewNotFound(err error, msg string) error +``` +NewNotFound returns an error which wraps err that satisfies +IsNotFound(). + + +## func NewNotImplemented +``` go +func NewNotImplemented(err error, msg string) error +``` +NewNotImplemented returns an error which wraps err and satisfies +IsNotImplemented(). + + +## func NewNotSupported +``` go +func NewNotSupported(err error, msg string) error +``` +NewNotSupported returns an error which wraps err and satisfies +IsNotSupported(). + + +## func NewNotValid +``` go +func NewNotValid(err error, msg string) error +``` +NewNotValid returns an error which wraps err and satisfies IsNotValid(). + + +## func NewUnauthorized +``` go +func NewUnauthorized(err error, msg string) error +``` +NewUnauthorized returns an error which wraps err and satisfies +IsUnauthorized(). + + +## func NotFoundf +``` go +func NotFoundf(format string, args ...interface{}) error +``` +NotFoundf returns an error which satisfies IsNotFound(). + + +## func NotImplementedf +``` go +func NotImplementedf(format string, args ...interface{}) error +``` +NotImplementedf returns an error which satisfies IsNotImplemented(). + + +## func NotSupportedf +``` go +func NotSupportedf(format string, args ...interface{}) error +``` +NotSupportedf returns an error which satisfies IsNotSupported(). + + +## func NotValidf +``` go +func NotValidf(format string, args ...interface{}) error +``` +NotValidf returns an error which satisfies IsNotValid(). + + +## func Trace +``` go +func Trace(other error) error +``` +Trace adds the location of the Trace call to the stack. The Cause of the +resulting error is the same as the error parameter. If the other error is +nil, the result will be nil. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Trace(err) + } + + +## func Unauthorizedf +``` go +func Unauthorizedf(format string, args ...interface{}) error +``` +Unauthorizedf returns an error which satisfies IsUnauthorized(). + + +## func Wrap +``` go +func Wrap(other, newDescriptive error) error +``` +Wrap changes the Cause of the error. The location of the Wrap call is also +stored in the error stack. + +For example: + + + if err := SomeFunc(); err != nil { + newErr := &packageError{"more context", private_value} + return errors.Wrap(err, newErr) + } + + +## func Wrapf +``` go +func Wrapf(other, newDescriptive error, format string, args ...interface{}) error +``` +Wrapf changes the Cause of the error, and adds an annotation. The location +of the Wrap call is also stored in the error stack. + +For example: + + + if err := SomeFunc(); err != nil { + return errors.Wrapf(err, simpleErrorType, "invalid value %q", value) + } + + + +## type Err +``` go +type Err struct { + // contains filtered or unexported fields +} +``` +Err holds a description of an error along with information about +where the error was created. + +It may be embedded in custom error types to add extra information that +this errors package can understand. + + + + + + + + + +### func NewErr +``` go +func NewErr(format string, args ...interface{}) Err +``` +NewErr is used to return an Err for the purpose of embedding in other +structures. The location is not specified, and needs to be set with a call +to SetLocation. + +For example: + + + type FooError struct { + errors.Err + code int + } + + func NewFooError(code int) error { + err := &FooError{errors.NewErr("foo"), code} + err.SetLocation(1) + return err + } + + + + +### func (\*Err) Cause +``` go +func (e *Err) Cause() error +``` +The Cause of an error is the most recent error in the error stack that +meets one of these criteria: the original error that was raised; the new +error that was passed into the Wrap function; the most recently masked +error; or nil if the error itself is considered the Cause. Normally this +method is not invoked directly, but instead through the Cause stand alone +function. + + + +### func (\*Err) Error +``` go +func (e *Err) Error() string +``` +Error implements error.Error. + + + +### func (\*Err) Location +``` go +func (e *Err) Location() (filename string, line int) +``` +Location is the file and line of where the error was most recently +created or annotated. + + + +### func (\*Err) Message +``` go +func (e *Err) Message() string +``` +Message returns the message stored with the most recent location. This is +the empty string if the most recent call was Trace, or the message stored +with Annotate or Mask. + + + +### func (\*Err) SetLocation +``` go +func (e *Err) SetLocation(callDepth int) +``` +SetLocation records the source location of the error at callDepth stack +frames above the call. + + + +### func (\*Err) StackTrace +``` go +func (e *Err) StackTrace() []string +``` +StackTrace returns one string for each location recorded in the stack of +errors. The first value is the originating error, with a line for each +other annotation or tracing of the error. + + + +### func (\*Err) Underlying +``` go +func (e *Err) Underlying() error +``` +Underlying returns the previous error in the error stack, if any. A client +should not ever really call this method. It is used to build the error +stack and should not be introspected by client calls. Or more +specifically, clients should not depend on anything but the `Cause` of an +error. + + + + + + + + + +- - - +Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) \ No newline at end of file diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/doc.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/doc.go new file mode 100644 index 0000000..35b119a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/doc.go @@ -0,0 +1,81 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +/* +[godoc-link-here] + +The juju/errors provides an easy way to annotate errors without losing the +orginal error context. + +The exported `New` and `Errorf` functions are designed to replace the +`errors.New` and `fmt.Errorf` functions respectively. The same underlying +error is there, but the package also records the location at which the error +was created. + +A primary use case for this library is to add extra context any time an +error is returned from a function. + + if err := SomeFunc(); err != nil { + return err + } + +This instead becomes: + + if err := SomeFunc(); err != nil { + return errors.Trace(err) + } + +which just records the file and line number of the Trace call, or + + if err := SomeFunc(); err != nil { + return errors.Annotate(err, "more context") + } + +which also adds an annotation to the error. + +When you want to check to see if an error is of a particular type, a helper +function is normally exported by the package that returned the error, like the +`os` package does. The underlying cause of the error is available using the +`Cause` function. + + os.IsNotExist(errors.Cause(err)) + +The result of the `Error()` call on an annotated error is the annotations joined +with colons, then the result of the `Error()` method for the underlying error +that was the cause. + + err := errors.Errorf("original") + err = errors.Annotatef(err, "context") + err = errors.Annotatef(err, "more context") + err.Error() -> "more context: context: original" + +Obviously recording the file, line and functions is not very useful if you +cannot get them back out again. + + errors.ErrorStack(err) + +will return something like: + + first error + github.com/juju/errors/annotation_test.go:193: + github.com/juju/errors/annotation_test.go:194: annotation + github.com/juju/errors/annotation_test.go:195: + github.com/juju/errors/annotation_test.go:196: more context + github.com/juju/errors/annotation_test.go:197: + +The first error was generated by an external system, so there was no location +associated. The second, fourth, and last lines were generated with Trace calls, +and the other two through Annotate. + +Sometimes when responding to an error you want to return a more specific error +for the situation. + + if err := FindField(field); err != nil { + return errors.Wrap(err, errors.NotFoundf(field)) + } + +This returns an error where the complete error stack is still available, and +`errors.Cause()` will return the `NotFound` error. + +*/ +package errors diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error.go new file mode 100644 index 0000000..4799acb --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error.go @@ -0,0 +1,122 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "fmt" + "reflect" + "runtime" +) + +// Err holds a description of an error along with information about +// where the error was created. +// +// It may be embedded in custom error types to add extra information that +// this errors package can understand. +type Err struct { + // message holds an annotation of the error. + message string + + // cause holds the cause of the error as returned + // by the Cause method. + cause error + + // previous holds the previous error in the error stack, if any. + previous error + + // file and line hold the source code location where the error was + // created. + file string + line int +} + +// NewErr is used to return an Err for the purpose of embedding in other +// structures. The location is not specified, and needs to be set with a call +// to SetLocation. +// +// For example: +// type FooError struct { +// errors.Err +// code int +// } +// +// func NewFooError(code int) error { +// err := &FooError{errors.NewErr("foo"), code} +// err.SetLocation(1) +// return err +// } +func NewErr(format string, args ...interface{}) Err { + return Err{ + message: fmt.Sprintf(format, args...), + } +} + +// Location is the file and line of where the error was most recently +// created or annotated. +func (e *Err) Location() (filename string, line int) { + return e.file, e.line +} + +// Underlying returns the previous error in the error stack, if any. A client +// should not ever really call this method. It is used to build the error +// stack and should not be introspected by client calls. Or more +// specifically, clients should not depend on anything but the `Cause` of an +// error. +func (e *Err) Underlying() error { + return e.previous +} + +// The Cause of an error is the most recent error in the error stack that +// meets one of these criteria: the original error that was raised; the new +// error that was passed into the Wrap function; the most recently masked +// error; or nil if the error itself is considered the Cause. Normally this +// method is not invoked directly, but instead through the Cause stand alone +// function. +func (e *Err) Cause() error { + return e.cause +} + +// Message returns the message stored with the most recent location. This is +// the empty string if the most recent call was Trace, or the message stored +// with Annotate or Mask. +func (e *Err) Message() string { + return e.message +} + +// Error implements error.Error. +func (e *Err) Error() string { + // We want to walk up the stack of errors showing the annotations + // as long as the cause is the same. + err := e.previous + if !sameError(Cause(err), e.cause) && e.cause != nil { + err = e.cause + } + switch { + case err == nil: + return e.message + case e.message == "": + return err.Error() + } + return fmt.Sprintf("%s: %v", e.message, err) +} + +// SetLocation records the source location of the error at callDepth stack +// frames above the call. +func (e *Err) SetLocation(callDepth int) { + _, file, line, _ := runtime.Caller(callDepth + 1) + e.file = trimGoPath(file) + e.line = line +} + +// StackTrace returns one string for each location recorded in the stack of +// errors. The first value is the originating error, with a line for each +// other annotation or tracing of the error. +func (e *Err) StackTrace() []string { + return errorStack(e) +} + +// Ideally we'd have a way to check identity, but deep equals will do. +func sameError(e1, e2 error) bool { + return reflect.DeepEqual(e1, e2) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error_test.go new file mode 100644 index 0000000..ac1d2b4 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/error_test.go @@ -0,0 +1,161 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + "runtime" + + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +type errorsSuite struct{} + +var _ = gc.Suite(&errorsSuite{}) + +var someErr = errors.New("some error") //err varSomeErr + +func (*errorsSuite) TestErrorString(c *gc.C) { + for i, test := range []struct { + message string + generator func() error + expected string + }{ + { + message: "uncomparable errors", + generator: func() error { + err := errors.Annotatef(newNonComparableError("uncomparable"), "annotation") + return errors.Annotatef(err, "another") + }, + expected: "another: annotation: uncomparable", + }, { + message: "Errorf", + generator: func() error { + return errors.Errorf("first error") + }, + expected: "first error", + }, { + message: "annotated error", + generator: func() error { + err := errors.Errorf("first error") + return errors.Annotatef(err, "annotation") + }, + expected: "annotation: first error", + }, { + message: "test annotation format", + generator: func() error { + err := errors.Errorf("first %s", "error") + return errors.Annotatef(err, "%s", "annotation") + }, + expected: "annotation: first error", + }, { + message: "wrapped error", + generator: func() error { + err := newError("first error") + return errors.Wrap(err, newError("detailed error")) + }, + expected: "detailed error", + }, { + message: "wrapped annotated error", + generator: func() error { + err := errors.Errorf("first error") + err = errors.Annotatef(err, "annotated") + return errors.Wrap(err, fmt.Errorf("detailed error")) + }, + expected: "detailed error", + }, { + message: "annotated wrapped error", + generator: func() error { + err := errors.Errorf("first error") + err = errors.Wrap(err, fmt.Errorf("detailed error")) + return errors.Annotatef(err, "annotated") + }, + expected: "annotated: detailed error", + }, { + message: "traced, and annotated", + generator: func() error { + err := errors.New("first error") + err = errors.Trace(err) + err = errors.Annotate(err, "some context") + err = errors.Trace(err) + err = errors.Annotate(err, "more context") + return errors.Trace(err) + }, + expected: "more context: some context: first error", + }, { + message: "traced, and annotated, masked and annotated", + generator: func() error { + err := errors.New("first error") + err = errors.Trace(err) + err = errors.Annotate(err, "some context") + err = errors.Maskf(err, "masked") + err = errors.Annotate(err, "more context") + return errors.Trace(err) + }, + expected: "more context: masked: some context: first error", + }, + } { + c.Logf("%v: %s", i, test.message) + err := test.generator() + ok := c.Check(err.Error(), gc.Equals, test.expected) + if !ok { + c.Logf("%#v", test.generator()) + } + } +} + +type embed struct { + errors.Err +} + +func newEmbed(format string, args ...interface{}) *embed { + err := &embed{errors.NewErr(format, args...)} + err.SetLocation(1) + return err +} + +func (*errorsSuite) TestNewErr(c *gc.C) { + if runtime.Compiler == "gccgo" { + c.Skip("gccgo can't determine the location") + } + err := newEmbed("testing %d", 42) //err embedErr + c.Assert(err.Error(), gc.Equals, "testing 42") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["embedErr"].String()) +} + +var _ error = (*embed)(nil) + +// This is an uncomparable error type, as it is a struct that supports the +// error interface (as opposed to a pointer type). +type error_ struct { + info string + slice []string +} + +// Create a non-comparable error +func newNonComparableError(message string) error { + return error_{info: message} +} + +func (e error_) Error() string { + return e.info +} + +func newError(message string) error { + return testError{message} +} + +// The testError is a value type error for ease of seeing results +// when the test fails. +type testError struct { + message string +} + +func (e testError) Error() string { + return e.message +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes.go new file mode 100644 index 0000000..10b3b19 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes.go @@ -0,0 +1,284 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "fmt" +) + +// wrap is a helper to construct an *wrapper. +func wrap(err error, format, suffix string, args ...interface{}) Err { + newErr := Err{ + message: fmt.Sprintf(format+suffix, args...), + previous: err, + } + newErr.SetLocation(2) + return newErr +} + +// notFound represents an error when something has not been found. +type notFound struct { + Err +} + +// NotFoundf returns an error which satisfies IsNotFound(). +func NotFoundf(format string, args ...interface{}) error { + return ¬Found{wrap(nil, format, " not found", args...)} +} + +// NewNotFound returns an error which wraps err that satisfies +// IsNotFound(). +func NewNotFound(err error, msg string) error { + return ¬Found{wrap(err, msg, "")} +} + +// IsNotFound reports whether err was created with NotFoundf() or +// NewNotFound(). +func IsNotFound(err error) bool { + err = Cause(err) + _, ok := err.(*notFound) + return ok +} + +// userNotFound represents an error when an inexistent user is looked up. +type userNotFound struct { + Err +} + +// UserNotFoundf returns an error which satisfies IsUserNotFound(). +func UserNotFoundf(format string, args ...interface{}) error { + return &userNotFound{wrap(nil, format, " user not found", args...)} +} + +// NewUserNotFound returns an error which wraps err and satisfies +// IsUserNotFound(). +func NewUserNotFound(err error, msg string) error { + return &userNotFound{wrap(err, msg, "")} +} + +// IsUserNotFound reports whether err was created with UserNotFoundf() or +// NewUserNotFound(). +func IsUserNotFound(err error) bool { + err = Cause(err) + _, ok := err.(*userNotFound) + return ok +} + +// unauthorized represents an error when an operation is unauthorized. +type unauthorized struct { + Err +} + +// Unauthorizedf returns an error which satisfies IsUnauthorized(). +func Unauthorizedf(format string, args ...interface{}) error { + return &unauthorized{wrap(nil, format, "", args...)} +} + +// NewUnauthorized returns an error which wraps err and satisfies +// IsUnauthorized(). +func NewUnauthorized(err error, msg string) error { + return &unauthorized{wrap(err, msg, "")} +} + +// IsUnauthorized reports whether err was created with Unauthorizedf() or +// NewUnauthorized(). +func IsUnauthorized(err error) bool { + err = Cause(err) + _, ok := err.(*unauthorized) + return ok +} + +// notImplemented represents an error when something is not +// implemented. +type notImplemented struct { + Err +} + +// NotImplementedf returns an error which satisfies IsNotImplemented(). +func NotImplementedf(format string, args ...interface{}) error { + return ¬Implemented{wrap(nil, format, " not implemented", args...)} +} + +// NewNotImplemented returns an error which wraps err and satisfies +// IsNotImplemented(). +func NewNotImplemented(err error, msg string) error { + return ¬Implemented{wrap(err, msg, "")} +} + +// IsNotImplemented reports whether err was created with +// NotImplementedf() or NewNotImplemented(). +func IsNotImplemented(err error) bool { + err = Cause(err) + _, ok := err.(*notImplemented) + return ok +} + +// alreadyExists represents and error when something already exists. +type alreadyExists struct { + Err +} + +// AlreadyExistsf returns an error which satisfies IsAlreadyExists(). +func AlreadyExistsf(format string, args ...interface{}) error { + return &alreadyExists{wrap(nil, format, " already exists", args...)} +} + +// NewAlreadyExists returns an error which wraps err and satisfies +// IsAlreadyExists(). +func NewAlreadyExists(err error, msg string) error { + return &alreadyExists{wrap(err, msg, "")} +} + +// IsAlreadyExists reports whether the error was created with +// AlreadyExistsf() or NewAlreadyExists(). +func IsAlreadyExists(err error) bool { + err = Cause(err) + _, ok := err.(*alreadyExists) + return ok +} + +// notSupported represents an error when something is not supported. +type notSupported struct { + Err +} + +// NotSupportedf returns an error which satisfies IsNotSupported(). +func NotSupportedf(format string, args ...interface{}) error { + return ¬Supported{wrap(nil, format, " not supported", args...)} +} + +// NewNotSupported returns an error which wraps err and satisfies +// IsNotSupported(). +func NewNotSupported(err error, msg string) error { + return ¬Supported{wrap(err, msg, "")} +} + +// IsNotSupported reports whether the error was created with +// NotSupportedf() or NewNotSupported(). +func IsNotSupported(err error) bool { + err = Cause(err) + _, ok := err.(*notSupported) + return ok +} + +// notValid represents an error when something is not valid. +type notValid struct { + Err +} + +// NotValidf returns an error which satisfies IsNotValid(). +func NotValidf(format string, args ...interface{}) error { + return ¬Valid{wrap(nil, format, " not valid", args...)} +} + +// NewNotValid returns an error which wraps err and satisfies IsNotValid(). +func NewNotValid(err error, msg string) error { + return ¬Valid{wrap(err, msg, "")} +} + +// IsNotValid reports whether the error was created with NotValidf() or +// NewNotValid(). +func IsNotValid(err error) bool { + err = Cause(err) + _, ok := err.(*notValid) + return ok +} + +// notProvisioned represents an error when something is not yet provisioned. +type notProvisioned struct { + Err +} + +// NotProvisionedf returns an error which satisfies IsNotProvisioned(). +func NotProvisionedf(format string, args ...interface{}) error { + return ¬Provisioned{wrap(nil, format, " not provisioned", args...)} +} + +// NewNotProvisioned returns an error which wraps err that satisfies +// IsNotProvisioned(). +func NewNotProvisioned(err error, msg string) error { + return ¬Provisioned{wrap(err, msg, "")} +} + +// IsNotProvisioned reports whether err was created with NotProvisionedf() or +// NewNotProvisioned(). +func IsNotProvisioned(err error) bool { + err = Cause(err) + _, ok := err.(*notProvisioned) + return ok +} + +// notAssigned represents an error when something is not yet assigned to +// something else. +type notAssigned struct { + Err +} + +// NotAssignedf returns an error which satisfies IsNotAssigned(). +func NotAssignedf(format string, args ...interface{}) error { + return ¬Assigned{wrap(nil, format, " not assigned", args...)} +} + +// NewNotAssigned returns an error which wraps err that satisfies +// IsNotAssigned(). +func NewNotAssigned(err error, msg string) error { + return ¬Assigned{wrap(err, msg, "")} +} + +// IsNotAssigned reports whether err was created with NotAssignedf() or +// NewNotAssigned(). +func IsNotAssigned(err error) bool { + err = Cause(err) + _, ok := err.(*notAssigned) + return ok +} + +// badRequest represents an error when a request has bad parameters. +type badRequest struct { + Err +} + +// BadRequestf returns an error which satisfies IsBadRequest(). +func BadRequestf(format string, args ...interface{}) error { + return &badRequest{wrap(nil, format, "", args...)} +} + +// NewBadRequest returns an error which wraps err that satisfies +// IsBadRequest(). +func NewBadRequest(err error, msg string) error { + return &badRequest{wrap(err, msg, "")} +} + +// IsBadRequest reports whether err was created with BadRequestf() or +// NewBadRequest(). +func IsBadRequest(err error) bool { + err = Cause(err) + _, ok := err.(*badRequest) + return ok +} + +// methodNotAllowed represents an error when an HTTP request +// is made with an inappropriate method. +type methodNotAllowed struct { + Err +} + +// MethodNotAllowedf returns an error which satisfies IsMethodNotAllowed(). +func MethodNotAllowedf(format string, args ...interface{}) error { + return &methodNotAllowed{wrap(nil, format, "", args...)} +} + +// NewMethodNotAllowed returns an error which wraps err that satisfies +// IsMethodNotAllowed(). +func NewMethodNotAllowed(err error, msg string) error { + return &methodNotAllowed{wrap(err, msg, "")} +} + +// IsMethodNotAllowed reports whether err was created with MethodNotAllowedf() or +// NewMethodNotAllowed(). +func IsMethodNotAllowed(err error) bool { + err = Cause(err) + _, ok := err.(*methodNotAllowed) + return ok +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes_test.go new file mode 100644 index 0000000..7cc2da9 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/errortypes_test.go @@ -0,0 +1,173 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + stderrors "errors" + "fmt" + "reflect" + "runtime" + + "github.com/juju/errors" + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" +) + +// errorInfo holds information about a single error type: a satisfier +// function, wrapping and variable arguments constructors and message +// suffix. +type errorInfo struct { + satisfier func(error) bool + argsConstructor func(string, ...interface{}) error + wrapConstructor func(error, string) error + suffix string +} + +// allErrors holds information for all defined errors. When adding new +// errors, add them here as well to include them in tests. +var allErrors = []*errorInfo{ + &errorInfo{errors.IsNotFound, errors.NotFoundf, errors.NewNotFound, " not found"}, + &errorInfo{errors.IsUserNotFound, errors.UserNotFoundf, errors.NewUserNotFound, " user not found"}, + &errorInfo{errors.IsUnauthorized, errors.Unauthorizedf, errors.NewUnauthorized, ""}, + &errorInfo{errors.IsNotImplemented, errors.NotImplementedf, errors.NewNotImplemented, " not implemented"}, + &errorInfo{errors.IsAlreadyExists, errors.AlreadyExistsf, errors.NewAlreadyExists, " already exists"}, + &errorInfo{errors.IsNotSupported, errors.NotSupportedf, errors.NewNotSupported, " not supported"}, + &errorInfo{errors.IsNotValid, errors.NotValidf, errors.NewNotValid, " not valid"}, + &errorInfo{errors.IsNotProvisioned, errors.NotProvisionedf, errors.NewNotProvisioned, " not provisioned"}, + &errorInfo{errors.IsNotAssigned, errors.NotAssignedf, errors.NewNotAssigned, " not assigned"}, + &errorInfo{errors.IsMethodNotAllowed, errors.MethodNotAllowedf, errors.NewMethodNotAllowed, ""}, + &errorInfo{errors.IsBadRequest, errors.BadRequestf, errors.NewBadRequest, ""}, +} + +type errorTypeSuite struct{} + +var _ = gc.Suite(&errorTypeSuite{}) + +func (t *errorInfo) satisfierName() string { + value := reflect.ValueOf(t.satisfier) + f := runtime.FuncForPC(value.Pointer()) + return f.Name() +} + +func (t *errorInfo) equal(t0 *errorInfo) bool { + if t0 == nil { + return false + } + return t.satisfierName() == t0.satisfierName() +} + +type errorTest struct { + err error + message string + errInfo *errorInfo +} + +func deferredAnnotatef(err error, format string, args ...interface{}) error { + errors.DeferredAnnotatef(&err, format, args...) + return err +} + +func mustSatisfy(c *gc.C, err error, errInfo *errorInfo) { + if errInfo != nil { + msg := fmt.Sprintf("%#v must satisfy %v", err, errInfo.satisfierName()) + c.Check(err, jc.Satisfies, errInfo.satisfier, gc.Commentf(msg)) + } +} + +func mustNotSatisfy(c *gc.C, err error, errInfo *errorInfo) { + if errInfo != nil { + msg := fmt.Sprintf("%#v must not satisfy %v", err, errInfo.satisfierName()) + c.Check(err, gc.Not(jc.Satisfies), errInfo.satisfier, gc.Commentf(msg)) + } +} + +func checkErrorMatches(c *gc.C, err error, message string, errInfo *errorInfo) { + if message == "" { + c.Check(err, gc.IsNil) + c.Check(errInfo, gc.IsNil) + } else { + c.Check(err, gc.ErrorMatches, message) + } +} + +func runErrorTests(c *gc.C, errorTests []errorTest, checkMustSatisfy bool) { + for i, t := range errorTests { + c.Logf("test %d: %T: %v", i, t.err, t.err) + checkErrorMatches(c, t.err, t.message, t.errInfo) + if checkMustSatisfy { + mustSatisfy(c, t.err, t.errInfo) + } + + // Check all other satisfiers to make sure none match. + for _, otherErrInfo := range allErrors { + if checkMustSatisfy && otherErrInfo.equal(t.errInfo) { + continue + } + mustNotSatisfy(c, t.err, otherErrInfo) + } + } +} + +func (*errorTypeSuite) TestDeferredAnnotatef(c *gc.C) { + // Ensure DeferredAnnotatef annotates the errors. + errorTests := []errorTest{} + for _, errInfo := range allErrors { + errorTests = append(errorTests, []errorTest{{ + deferredAnnotatef(nil, "comment"), + "", + nil, + }, { + deferredAnnotatef(stderrors.New("blast"), "comment"), + "comment: blast", + nil, + }, { + deferredAnnotatef(errInfo.argsConstructor("foo %d", 42), "comment %d", 69), + "comment 69: foo 42" + errInfo.suffix, + errInfo, + }, { + deferredAnnotatef(errInfo.argsConstructor(""), "comment"), + "comment: " + errInfo.suffix, + errInfo, + }, { + deferredAnnotatef(errInfo.wrapConstructor(stderrors.New("pow!"), "woo"), "comment"), + "comment: woo: pow!", + errInfo, + }}...) + } + + runErrorTests(c, errorTests, true) +} + +func (*errorTypeSuite) TestAllErrors(c *gc.C) { + errorTests := []errorTest{} + for _, errInfo := range allErrors { + errorTests = append(errorTests, []errorTest{{ + nil, + "", + nil, + }, { + errInfo.argsConstructor("foo %d", 42), + "foo 42" + errInfo.suffix, + errInfo, + }, { + errInfo.argsConstructor(""), + errInfo.suffix, + errInfo, + }, { + errInfo.wrapConstructor(stderrors.New("pow!"), "prefix"), + "prefix: pow!", + errInfo, + }, { + errInfo.wrapConstructor(stderrors.New("pow!"), ""), + "pow!", + errInfo, + }, { + errInfo.wrapConstructor(nil, "prefix"), + "prefix", + errInfo, + }}...) + } + + runErrorTests(c, errorTests, true) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/example_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/example_test.go new file mode 100644 index 0000000..2a79cf4 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/example_test.go @@ -0,0 +1,23 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + + "github.com/juju/errors" +) + +func ExampleTrace() { + var err1 error = fmt.Errorf("something wicked this way comes") + var err2 error = nil + + // Tracing a non nil error will return an error + fmt.Println(errors.Trace(err1)) + // Tracing nil will return nil + fmt.Println(errors.Trace(err2)) + + // Output: something wicked this way comes + // +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/export_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/export_test.go new file mode 100644 index 0000000..db57ec8 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/export_test.go @@ -0,0 +1,12 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +// Since variables are declared before the init block, in order to get the goPath +// we need to return it rather than just reference it. +func GoPath() string { + return goPath +} + +var TrimGoPath = trimGoPath diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions.go new file mode 100644 index 0000000..994208d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions.go @@ -0,0 +1,330 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "fmt" + "strings" +) + +// New is a drop in replacement for the standard libary errors module that records +// the location that the error is created. +// +// For example: +// return errors.New("validation failed") +// +func New(message string) error { + err := &Err{message: message} + err.SetLocation(1) + return err +} + +// Errorf creates a new annotated error and records the location that the +// error is created. This should be a drop in replacement for fmt.Errorf. +// +// For example: +// return errors.Errorf("validation failed: %s", message) +// +func Errorf(format string, args ...interface{}) error { + err := &Err{message: fmt.Sprintf(format, args...)} + err.SetLocation(1) + return err +} + +// Trace adds the location of the Trace call to the stack. The Cause of the +// resulting error is the same as the error parameter. If the other error is +// nil, the result will be nil. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Trace(err) +// } +// +func Trace(other error) error { + if other == nil { + return nil + } + err := &Err{previous: other, cause: Cause(other)} + err.SetLocation(1) + return err +} + +// Annotate is used to add extra context to an existing error. The location of +// the Annotate call is recorded with the annotations. The file, line and +// function are also recorded. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Annotate(err, "failed to frombulate") +// } +// +func Annotate(other error, message string) error { + if other == nil { + return nil + } + err := &Err{ + previous: other, + cause: Cause(other), + message: message, + } + err.SetLocation(1) + return err +} + +// Annotatef is used to add extra context to an existing error. The location of +// the Annotate call is recorded with the annotations. The file, line and +// function are also recorded. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Annotatef(err, "failed to frombulate the %s", arg) +// } +// +func Annotatef(other error, format string, args ...interface{}) error { + if other == nil { + return nil + } + err := &Err{ + previous: other, + cause: Cause(other), + message: fmt.Sprintf(format, args...), + } + err.SetLocation(1) + return err +} + +// DeferredAnnotatef annotates the given error (when it is not nil) with the given +// format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef +// does nothing. This method is used in a defer statement in order to annotate any +// resulting error with the same message. +// +// For example: +// +// defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg) +// +func DeferredAnnotatef(err *error, format string, args ...interface{}) { + if *err == nil { + return + } + newErr := &Err{ + message: fmt.Sprintf(format, args...), + cause: Cause(*err), + previous: *err, + } + newErr.SetLocation(1) + *err = newErr +} + +// Wrap changes the Cause of the error. The location of the Wrap call is also +// stored in the error stack. +// +// For example: +// if err := SomeFunc(); err != nil { +// newErr := &packageError{"more context", private_value} +// return errors.Wrap(err, newErr) +// } +// +func Wrap(other, newDescriptive error) error { + err := &Err{ + previous: other, + cause: newDescriptive, + } + err.SetLocation(1) + return err +} + +// Wrapf changes the Cause of the error, and adds an annotation. The location +// of the Wrap call is also stored in the error stack. +// +// For example: +// if err := SomeFunc(); err != nil { +// return errors.Wrapf(err, simpleErrorType, "invalid value %q", value) +// } +// +func Wrapf(other, newDescriptive error, format string, args ...interface{}) error { + err := &Err{ + message: fmt.Sprintf(format, args...), + previous: other, + cause: newDescriptive, + } + err.SetLocation(1) + return err +} + +// Mask masks the given error with the given format string and arguments (like +// fmt.Sprintf), returning a new error that maintains the error stack, but +// hides the underlying error type. The error string still contains the full +// annotations. If you want to hide the annotations, call Wrap. +func Maskf(other error, format string, args ...interface{}) error { + if other == nil { + return nil + } + err := &Err{ + message: fmt.Sprintf(format, args...), + previous: other, + } + err.SetLocation(1) + return err +} + +// Mask hides the underlying error type, and records the location of the masking. +func Mask(other error) error { + if other == nil { + return nil + } + err := &Err{ + previous: other, + } + err.SetLocation(1) + return err +} + +// Cause returns the cause of the given error. This will be either the +// original error, or the result of a Wrap or Mask call. +// +// Cause is the usual way to diagnose errors that may have been wrapped by +// the other errors functions. +func Cause(err error) error { + var diag error + if err, ok := err.(causer); ok { + diag = err.Cause() + } + if diag != nil { + return diag + } + return err +} + +type causer interface { + Cause() error +} + +type wrapper interface { + // Message returns the top level error message, + // not including the message from the Previous + // error. + Message() string + + // Underlying returns the Previous error, or nil + // if there is none. + Underlying() error +} + +type locationer interface { + Location() (string, int) +} + +var ( + _ wrapper = (*Err)(nil) + _ locationer = (*Err)(nil) + _ causer = (*Err)(nil) +) + +// Details returns information about the stack of errors wrapped by err, in +// the format: +// +// [{filename:99: error one} {otherfile:55: cause of error one}] +// +// This is a terse alternative to ErrorStack as it returns a single line. +func Details(err error) string { + if err == nil { + return "[]" + } + var s []byte + s = append(s, '[') + for { + s = append(s, '{') + if err, ok := err.(locationer); ok { + file, line := err.Location() + if file != "" { + s = append(s, fmt.Sprintf("%s:%d", file, line)...) + s = append(s, ": "...) + } + } + if cerr, ok := err.(wrapper); ok { + s = append(s, cerr.Message()...) + err = cerr.Underlying() + } else { + s = append(s, err.Error()...) + err = nil + } + s = append(s, '}') + if err == nil { + break + } + s = append(s, ' ') + } + s = append(s, ']') + return string(s) +} + +// ErrorStack returns a string representation of the annotated error. If the +// error passed as the parameter is not an annotated error, the result is +// simply the result of the Error() method on that error. +// +// If the error is an annotated error, a multi-line string is returned where +// each line represents one entry in the annotation stack. The full filename +// from the call stack is used in the output. +// +// first error +// github.com/juju/errors/annotation_test.go:193: +// github.com/juju/errors/annotation_test.go:194: annotation +// github.com/juju/errors/annotation_test.go:195: +// github.com/juju/errors/annotation_test.go:196: more context +// github.com/juju/errors/annotation_test.go:197: +func ErrorStack(err error) string { + return strings.Join(errorStack(err), "\n") +} + +func errorStack(err error) []string { + if err == nil { + return nil + } + + // We want the first error first + var lines []string + for { + var buff []byte + if err, ok := err.(locationer); ok { + file, line := err.Location() + // Strip off the leading GOPATH/src path elements. + file = trimGoPath(file) + if file != "" { + buff = append(buff, fmt.Sprintf("%s:%d", file, line)...) + buff = append(buff, ": "...) + } + } + if cerr, ok := err.(wrapper); ok { + message := cerr.Message() + buff = append(buff, message...) + // If there is a cause for this error, and it is different to the cause + // of the underlying error, then output the error string in the stack trace. + var cause error + if err1, ok := err.(causer); ok { + cause = err1.Cause() + } + err = cerr.Underlying() + if cause != nil && !sameError(Cause(err), cause) { + if message != "" { + buff = append(buff, ": "...) + } + buff = append(buff, cause.Error()...) + } + } else { + buff = append(buff, err.Error()...) + err = nil + } + lines = append(lines, string(buff)) + if err == nil { + break + } + } + // reverse the lines to get the original error, which was at the end of + // the list, back to the start. + var result []string + for i := len(lines); i > 0; i-- { + result = append(result, lines[i-1]) + } + return result +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions_test.go new file mode 100644 index 0000000..7b1e43b --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/functions_test.go @@ -0,0 +1,305 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +type functionSuite struct { +} + +var _ = gc.Suite(&functionSuite{}) + +func (*functionSuite) TestNew(c *gc.C) { + err := errors.New("testing") //err newTest + c.Assert(err.Error(), gc.Equals, "testing") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["newTest"].String()) +} + +func (*functionSuite) TestErrorf(c *gc.C) { + err := errors.Errorf("testing %d", 42) //err errorfTest + c.Assert(err.Error(), gc.Equals, "testing 42") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["errorfTest"].String()) +} + +func (*functionSuite) TestTrace(c *gc.C) { + first := errors.New("first") + err := errors.Trace(first) //err traceTest + c.Assert(err.Error(), gc.Equals, "first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["traceTest"].String()) + + c.Assert(errors.Trace(nil), gc.IsNil) +} + +func (*functionSuite) TestAnnotate(c *gc.C) { + first := errors.New("first") + err := errors.Annotate(first, "annotation") //err annotateTest + c.Assert(err.Error(), gc.Equals, "annotation: first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["annotateTest"].String()) + + c.Assert(errors.Annotate(nil, "annotate"), gc.IsNil) +} + +func (*functionSuite) TestAnnotatef(c *gc.C) { + first := errors.New("first") + err := errors.Annotatef(first, "annotation %d", 2) //err annotatefTest + c.Assert(err.Error(), gc.Equals, "annotation 2: first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["annotatefTest"].String()) + + c.Assert(errors.Annotatef(nil, "annotate"), gc.IsNil) +} + +func (*functionSuite) TestDeferredAnnotatef(c *gc.C) { + // NOTE: this test fails with gccgo + if runtime.Compiler == "gccgo" { + c.Skip("gccgo can't determine the location") + } + first := errors.New("first") + test := func() (err error) { + defer errors.DeferredAnnotatef(&err, "deferred %s", "annotate") + return first + } //err deferredAnnotate + err := test() + c.Assert(err.Error(), gc.Equals, "deferred annotate: first") + c.Assert(errors.Cause(err), gc.Equals, first) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["deferredAnnotate"].String()) + + err = nil + errors.DeferredAnnotatef(&err, "deferred %s", "annotate") + c.Assert(err, gc.IsNil) +} + +func (*functionSuite) TestWrap(c *gc.C) { + first := errors.New("first") //err wrapFirst + detailed := errors.New("detailed") + err := errors.Wrap(first, detailed) //err wrapTest + c.Assert(err.Error(), gc.Equals, "detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapFirst"].String()) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapTest"].String()) +} + +func (*functionSuite) TestWrapOfNil(c *gc.C) { + detailed := errors.New("detailed") + err := errors.Wrap(nil, detailed) //err nilWrapTest + c.Assert(err.Error(), gc.Equals, "detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["nilWrapTest"].String()) +} + +func (*functionSuite) TestWrapf(c *gc.C) { + first := errors.New("first") //err wrapfFirst + detailed := errors.New("detailed") + err := errors.Wrapf(first, detailed, "value %d", 42) //err wrapfTest + c.Assert(err.Error(), gc.Equals, "value 42: detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapfFirst"].String()) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapfTest"].String()) +} + +func (*functionSuite) TestWrapfOfNil(c *gc.C) { + detailed := errors.New("detailed") + err := errors.Wrapf(nil, detailed, "value %d", 42) //err nilWrapfTest + c.Assert(err.Error(), gc.Equals, "value 42: detailed") + c.Assert(errors.Cause(err), gc.Equals, detailed) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["nilWrapfTest"].String()) +} + +func (*functionSuite) TestMask(c *gc.C) { + first := errors.New("first") + err := errors.Mask(first) //err maskTest + c.Assert(err.Error(), gc.Equals, "first") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["maskTest"].String()) + + c.Assert(errors.Mask(nil), gc.IsNil) +} + +func (*functionSuite) TestMaskf(c *gc.C) { + first := errors.New("first") + err := errors.Maskf(first, "masked %d", 42) //err maskfTest + c.Assert(err.Error(), gc.Equals, "masked 42: first") + c.Assert(errors.Cause(err), gc.Equals, err) + c.Assert(errors.Details(err), jc.Contains, tagToLocation["maskfTest"].String()) + + c.Assert(errors.Maskf(nil, "mask"), gc.IsNil) +} + +func (*functionSuite) TestCause(c *gc.C) { + c.Assert(errors.Cause(nil), gc.IsNil) + c.Assert(errors.Cause(someErr), gc.Equals, someErr) + + fmtErr := fmt.Errorf("simple") + c.Assert(errors.Cause(fmtErr), gc.Equals, fmtErr) + + err := errors.Wrap(someErr, fmtErr) + c.Assert(errors.Cause(err), gc.Equals, fmtErr) + + err = errors.Annotate(err, "annotated") + c.Assert(errors.Cause(err), gc.Equals, fmtErr) + + err = errors.Maskf(err, "maksed") + c.Assert(errors.Cause(err), gc.Equals, err) + + // Look for a file that we know isn't there. + dir := c.MkDir() + _, err = os.Stat(filepath.Join(dir, "not-there")) + c.Assert(os.IsNotExist(err), jc.IsTrue) + + err = errors.Annotatef(err, "wrap it") + // Now the error itself isn't a 'IsNotExist'. + c.Assert(os.IsNotExist(err), jc.IsFalse) + // However if we use the Check method, it is. + c.Assert(os.IsNotExist(errors.Cause(err)), jc.IsTrue) +} + +func (s *functionSuite) TestDetails(c *gc.C) { + if runtime.Compiler == "gccgo" { + c.Skip("gccgo can't determine the location") + } + c.Assert(errors.Details(nil), gc.Equals, "[]") + + otherErr := fmt.Errorf("other") + checkDetails(c, otherErr, "[{other}]") + + err0 := newEmbed("foo") //err TestStack#0 + checkDetails(c, err0, "[{$TestStack#0$: foo}]") + + err1 := errors.Annotate(err0, "bar") //err TestStack#1 + checkDetails(c, err1, "[{$TestStack#1$: bar} {$TestStack#0$: foo}]") + + err2 := errors.Trace(err1) //err TestStack#2 + checkDetails(c, err2, "[{$TestStack#2$: } {$TestStack#1$: bar} {$TestStack#0$: foo}]") +} + +type tracer interface { + StackTrace() []string +} + +func (*functionSuite) TestErrorStack(c *gc.C) { + for i, test := range []struct { + message string + generator func() error + expected string + tracer bool + }{ + { + message: "nil", + generator: func() error { + return nil + }, + }, { + message: "raw error", + generator: func() error { + return fmt.Errorf("raw") + }, + expected: "raw", + }, { + message: "single error stack", + generator: func() error { + return errors.New("first error") //err single + }, + expected: "$single$: first error", + tracer: true, + }, { + message: "annotated error", + generator: func() error { + err := errors.New("first error") //err annotated-0 + return errors.Annotate(err, "annotation") //err annotated-1 + }, + expected: "" + + "$annotated-0$: first error\n" + + "$annotated-1$: annotation", + tracer: true, + }, { + message: "wrapped error", + generator: func() error { + err := errors.New("first error") //err wrapped-0 + return errors.Wrap(err, newError("detailed error")) //err wrapped-1 + }, + expected: "" + + "$wrapped-0$: first error\n" + + "$wrapped-1$: detailed error", + tracer: true, + }, { + message: "annotated wrapped error", + generator: func() error { + err := errors.Errorf("first error") //err ann-wrap-0 + err = errors.Wrap(err, fmt.Errorf("detailed error")) //err ann-wrap-1 + return errors.Annotatef(err, "annotated") //err ann-wrap-2 + }, + expected: "" + + "$ann-wrap-0$: first error\n" + + "$ann-wrap-1$: detailed error\n" + + "$ann-wrap-2$: annotated", + tracer: true, + }, { + message: "traced, and annotated", + generator: func() error { + err := errors.New("first error") //err stack-0 + err = errors.Trace(err) //err stack-1 + err = errors.Annotate(err, "some context") //err stack-2 + err = errors.Trace(err) //err stack-3 + err = errors.Annotate(err, "more context") //err stack-4 + return errors.Trace(err) //err stack-5 + }, + expected: "" + + "$stack-0$: first error\n" + + "$stack-1$: \n" + + "$stack-2$: some context\n" + + "$stack-3$: \n" + + "$stack-4$: more context\n" + + "$stack-5$: ", + tracer: true, + }, { + message: "uncomparable, wrapped with a value error", + generator: func() error { + err := newNonComparableError("first error") //err mixed-0 + err = errors.Trace(err) //err mixed-1 + err = errors.Wrap(err, newError("value error")) //err mixed-2 + err = errors.Maskf(err, "masked") //err mixed-3 + err = errors.Annotate(err, "more context") //err mixed-4 + return errors.Trace(err) //err mixed-5 + }, + expected: "" + + "first error\n" + + "$mixed-1$: \n" + + "$mixed-2$: value error\n" + + "$mixed-3$: masked\n" + + "$mixed-4$: more context\n" + + "$mixed-5$: ", + tracer: true, + }, + } { + c.Logf("%v: %s", i, test.message) + err := test.generator() + expected := replaceLocations(test.expected) + stack := errors.ErrorStack(err) + ok := c.Check(stack, gc.Equals, expected) + if !ok { + c.Logf("%#v", err) + } + tracer, ok := err.(tracer) + c.Check(ok, gc.Equals, test.tracer) + if ok { + stackTrace := tracer.StackTrace() + c.Check(stackTrace, gc.DeepEquals, strings.Split(stack, "\n")) + } + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/package_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/package_test.go new file mode 100644 index 0000000..5bbb8f0 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/package_test.go @@ -0,0 +1,95 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "fmt" + "io/ioutil" + "strings" + "testing" + + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +func Test(t *testing.T) { + gc.TestingT(t) +} + +func checkDetails(c *gc.C, err error, details string) { + c.Assert(err, gc.NotNil) + expectedDetails := replaceLocations(details) + c.Assert(errors.Details(err), gc.Equals, expectedDetails) +} + +func checkErr(c *gc.C, err, cause error, msg string, details string) { + c.Assert(err, gc.NotNil) + c.Assert(err.Error(), gc.Equals, msg) + c.Assert(errors.Cause(err), gc.Equals, cause) + expectedDetails := replaceLocations(details) + c.Assert(errors.Details(err), gc.Equals, expectedDetails) +} + +func replaceLocations(line string) string { + result := "" + for { + i := strings.Index(line, "$") + if i == -1 { + break + } + result += line[0:i] + line = line[i+1:] + i = strings.Index(line, "$") + if i == -1 { + panic("no second $") + } + result += location(line[0:i]).String() + line = line[i+1:] + } + result += line + return result +} + +func location(tag string) Location { + loc, ok := tagToLocation[tag] + if !ok { + panic(fmt.Sprintf("tag %q not found", tag)) + } + return loc +} + +type Location struct { + file string + line int +} + +func (loc Location) String() string { + return fmt.Sprintf("%s:%d", loc.file, loc.line) +} + +var tagToLocation = make(map[string]Location) + +func setLocationsForErrorTags(filename string) { + data, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + filename = "github.com/juju/errors/" + filename + lines := strings.Split(string(data), "\n") + for i, line := range lines { + if j := strings.Index(line, "//err "); j >= 0 { + tag := line[j+len("//err "):] + if _, found := tagToLocation[tag]; found { + panic(fmt.Sprintf("tag %q already processed previously", tag)) + } + tagToLocation[tag] = Location{file: filename, line: i + 1} + } + } +} + +func init() { + setLocationsForErrorTags("error_test.go") + setLocationsForErrorTags("functions_test.go") +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path.go new file mode 100644 index 0000000..3ec517c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path.go @@ -0,0 +1,35 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors + +import ( + "runtime" + "strings" +) + +// prefixSize is used internally to trim the user specific path from the +// front of the returned filenames from the runtime call stack. +var prefixSize int + +// goPath is the deduced path based on the location of this file as compiled. +var goPath string + +func init() { + _, file, _, ok := runtime.Caller(0) + if ok { + // We know that the end of the file should be: + // github.com/juju/errors/path.go + size := len(file) + suffix := len("github.com/juju/errors/path.go") + goPath = file[:size-suffix] + prefixSize = len(goPath) + } +} + +func trimGoPath(filename string) string { + if strings.HasPrefix(filename, goPath) { + return filename[prefixSize:] + } + return filename +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path_test.go new file mode 100644 index 0000000..ef4f34f --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/juju/errors/path_test.go @@ -0,0 +1,29 @@ +// Copyright 2013, 2014 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package errors_test + +import ( + "path" + + gc "gopkg.in/check.v1" + + "github.com/juju/errors" +) + +type pathSuite struct{} + +var _ = gc.Suite(&pathSuite{}) + +func (*pathSuite) TestGoPathSet(c *gc.C) { + c.Assert(errors.GoPath(), gc.Not(gc.Equals), "") +} + +func (*pathSuite) TestTrimGoPath(c *gc.C) { + relativeImport := "github.com/foo/bar/baz.go" + filename := path.Join(errors.GoPath(), relativeImport) + c.Assert(errors.TrimGoPath(filename), gc.Equals, relativeImport) + + absoluteImport := "/usr/share/foo/bar/baz.go" + c.Assert(errors.TrimGoPath(absoluteImport), gc.Equals, absoluteImport) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/.travis.yml b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/.travis.yml new file mode 100644 index 0000000..56ab36c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - 1.0 + - 1.1 + - 1.2 + - 1.3 +notifications: + email: false diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/LICENSE b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/LICENSE new file mode 100644 index 0000000..e085b25 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2013-2014 by Maxim Bublis + +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. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/README.md new file mode 100644 index 0000000..a863495 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/README.md @@ -0,0 +1,66 @@ +# UUID package for Go language + +[![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid) +[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](http://godoc.org/github.com/satori/go.uuid) + +This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. + +With 100% test coverage and benchmarks out of box. + +Supported versions: +* Version 1, based on timestamp and MAC address (RFC 4122) +* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) +* Version 3, based on MD5 hashing (RFC 4122) +* Version 4, based on random numbers (RFC 4122) +* Version 5, based on SHA-1 hashing (RFC 4122) + +## Installation + +Use the `go` command: + + $ go get github.com/satori/go.uuid + +## Requirements + +UUID package requires any stable version of Go Programming Language. + +It is tested against following versions of Go: 1.0, 1.1, 1.2 + +## Example + +```go +package main + +import ( + "fmt" + "github.com/satori/go.uuid" +) + +func main() { + // Creating UUID Version 4 + u1 := uuid.NewV4() + fmt.Printf("UUIDv4: %s\n", u1) + + // Parsing UUID from string input + u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + if err != nil { + fmt.Printf("Something gone wrong: %s", err) + } + fmt.Printf("Successfully parsed: %s", u2) +} +``` + +## Documentation + +[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. + +## Links +* [RFC 4122](http://tools.ietf.org/html/rfc4122) +* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) + +## Copyright + +Copyright (C) 2013-2014 by Maxim Bublis . + +UUID package released under MIT License. +See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/benchmarks_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/benchmarks_test.go new file mode 100644 index 0000000..6a8bf7e --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/benchmarks_test.go @@ -0,0 +1,114 @@ +// Copyright (C) 2013-2014 by Maxim Bublis +// +// 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 uuid + +import ( + "testing" +) + +func BenchmarkFromBytes(b *testing.B) { + bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + for i := 0; i < b.N; i++ { + FromBytes(bytes) + } +} + +func BenchmarkFromString(b *testing.B) { + s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + for i := 0; i < b.N; i++ { + FromString(s) + } +} + +func BenchmarkFromStringUrn(b *testing.B) { + s := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" + for i := 0; i < b.N; i++ { + FromString(s) + } +} + +func BenchmarkFromStringWithBrackets(b *testing.B) { + s := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" + for i := 0; i < b.N; i++ { + FromString(s) + } +} + +func BenchmarkNewV1(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV1() + } +} + +func BenchmarkNewV2(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV2(DomainPerson) + } +} + +func BenchmarkNewV3(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV3(NamespaceDNS, "www.example.com") + } +} + +func BenchmarkNewV4(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV4() + } +} + +func BenchmarkNewV5(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV5(NamespaceDNS, "www.example.com") + } +} + +func BenchmarkMarshalBinary(b *testing.B) { + u := NewV4() + for i := 0; i < b.N; i++ { + u.MarshalBinary() + } +} + +func BenchmarkMarshalText(b *testing.B) { + u := NewV4() + for i := 0; i < b.N; i++ { + u.MarshalText() + } +} + +func BenchmarkUnmarshalBinary(b *testing.B) { + bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + u := UUID{} + for i := 0; i < b.N; i++ { + u.UnmarshalBinary(bytes) + } +} + +func BenchmarkUnmarshalText(b *testing.B) { + bytes := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + u := UUID{} + for i := 0; i < b.N; i++ { + u.UnmarshalText(bytes) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid.go new file mode 100644 index 0000000..44a235f --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid.go @@ -0,0 +1,361 @@ +// Copyright (C) 2013-2014 by Maxim Bublis +// +// 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 uuid provides implementation of Universally Unique Identifier (UUID). +// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and +// version 2 (as specified in DCE 1.1). +package uuid + +import ( + "bytes" + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "encoding/binary" + "encoding/hex" + "fmt" + "hash" + "net" + "os" + "sync" + "time" +) + +// UUID layout variants. +const ( + VariantNCS = iota + VariantRFC4122 + VariantMicrosoft + VariantFuture +) + +// UUID DCE domains. +const ( + DomainPerson = iota + DomainGroup + DomainOrg +) + +// Difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). +const epochStart = 122192928000000000 + +// UUID v1/v2 storage. +var ( + storageMutex sync.Mutex + clockSequence uint16 + lastTime uint64 + hardwareAddr [6]byte + posixUID = uint32(os.Getuid()) + posixGID = uint32(os.Getgid()) +) + +// String parse helpers. +var ( + urnPrefix = []byte("urn:uuid:") + byteGroups = []int{8, 4, 4, 4, 12} +) + +// Epoch calculation function +var epochFunc func() uint64 + +// Initialize storage +func init() { + buf := make([]byte, 2) + rand.Read(buf) + clockSequence = binary.BigEndian.Uint16(buf) + + // Initialize hardwareAddr randomly in case + // of real network interfaces absence + rand.Read(hardwareAddr[:]) + + // Set multicast bit as recommended in RFC 4122 + hardwareAddr[0] |= 0x01 + + interfaces, err := net.Interfaces() + if err == nil { + for _, iface := range interfaces { + if len(iface.HardwareAddr) >= 6 { + copy(hardwareAddr[:], iface.HardwareAddr) + break + } + } + } + epochFunc = unixTimeFunc +} + +// Returns difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and current time. +// This is default epoch calculation function. +func unixTimeFunc() uint64 { + return epochStart + uint64(time.Now().UnixNano()/100) +} + +// UUID representation compliant with specification +// described in RFC 4122. +type UUID [16]byte + +// Predefined namespace UUIDs. +var ( + NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8") + NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8") + NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8") +) + +// And returns result of binary AND of two UUIDs. +func And(u1 UUID, u2 UUID) UUID { + u := UUID{} + for i := 0; i < 16; i++ { + u[i] = u1[i] & u2[i] + } + return u +} + +// Or returns result of binary OR of two UUIDs. +func Or(u1 UUID, u2 UUID) UUID { + u := UUID{} + for i := 0; i < 16; i++ { + u[i] = u1[i] | u2[i] + } + return u +} + +// Equal returns true if u1 and u2 equals, otherwise returns false. +func Equal(u1 UUID, u2 UUID) bool { + return bytes.Equal(u1[:], u2[:]) +} + +// Version returns algorithm version used to generate UUID. +func (u UUID) Version() uint { + return uint(u[6] >> 4) +} + +// Variant returns UUID layout variant. +func (u UUID) Variant() uint { + switch { + case (u[8] & 0x80) == 0x00: + return VariantNCS + case (u[8]&0xc0)|0x80 == 0x80: + return VariantRFC4122 + case (u[8]&0xe0)|0xc0 == 0xc0: + return VariantMicrosoft + } + return VariantFuture +} + +// Bytes returns bytes slice representation of UUID. +func (u UUID) Bytes() []byte { + return u[:] +} + +// Returns canonical string representation of UUID: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (u UUID) String() string { + return fmt.Sprintf("%x-%x-%x-%x-%x", + u[:4], u[4:6], u[6:8], u[8:10], u[10:]) +} + +// SetVersion sets version bits. +func (u *UUID) SetVersion(v byte) { + u[6] = (u[6] & 0x0f) | (v << 4) +} + +// SetVariant sets variant bits as described in RFC 4122. +func (u *UUID) SetVariant() { + u[8] = (u[8] & 0xbf) | 0x80 +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (u UUID) MarshalText() (text []byte, err error) { + text = []byte(u.String()) + return +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +func (u *UUID) UnmarshalText(text []byte) (err error) { + if len(text) < 32 { + err = fmt.Errorf("uuid: invalid UUID string: %s", text) + return + } + + if bytes.Equal(text[:9], urnPrefix) { + text = text[9:] + } else if text[0] == '{' { + text = text[1:] + } + + b := u[:] + + for _, byteGroup := range byteGroups { + if text[0] == '-' { + text = text[1:] + } + + _, err = hex.Decode(b[:byteGroup/2], text[:byteGroup]) + + if err != nil { + return + } + + text = text[byteGroup:] + b = b[byteGroup/2:] + } + + return +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (u UUID) MarshalBinary() (data []byte, err error) { + data = u.Bytes() + return +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It will return error if the slice isn't 16 bytes long. +func (u *UUID) UnmarshalBinary(data []byte) (err error) { + if len(data) != 16 { + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) + return + } + copy(u[:], data) + + return +} + +// FromBytes returns UUID converted from raw byte slice input. +// It will return error if the slice isn't 16 bytes long. +func FromBytes(input []byte) (u UUID, err error) { + err = u.UnmarshalBinary(input) + return +} + +// FromString returns UUID parsed from string input. +// Input is expected in a form accepted by UnmarshalText. +func FromString(input string) (u UUID, err error) { + err = u.UnmarshalText([]byte(input)) + return +} + +// Returns UUID v1/v2 storage state. +// Returns epoch timestamp and clock sequence. +func getStorage() (uint64, uint16) { + storageMutex.Lock() + defer storageMutex.Unlock() + + timeNow := epochFunc() + // Clock changed backwards since last UUID generation. + // Should increase clock sequence. + if timeNow <= lastTime { + clockSequence++ + } + lastTime = timeNow + + return timeNow, clockSequence +} + +// NewV1 returns UUID based on current timestamp and MAC address. +func NewV1() UUID { + u := UUID{} + + timeNow, clockSeq := getStorage() + + binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + + copy(u[10:], hardwareAddr[:]) + + u.SetVersion(1) + u.SetVariant() + + return u +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func NewV2(domain byte) UUID { + u := UUID{} + + switch domain { + case DomainPerson: + binary.BigEndian.PutUint32(u[0:], posixUID) + case DomainGroup: + binary.BigEndian.PutUint32(u[0:], posixGID) + } + + timeNow, clockSeq := getStorage() + + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + u[9] = domain + + copy(u[10:], hardwareAddr[:]) + + u.SetVersion(2) + u.SetVariant() + + return u +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func NewV3(ns UUID, name string) UUID { + u := newFromHash(md5.New(), ns, name) + u.SetVersion(3) + u.SetVariant() + + return u +} + +// NewV4 returns random generated UUID. +func NewV4() UUID { + u := UUID{} + rand.Read(u[:]) + u.SetVersion(4) + u.SetVariant() + + return u +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func NewV5(ns UUID, name string) UUID { + u := newFromHash(sha1.New(), ns, name) + u.SetVersion(5) + u.SetVariant() + + return u +} + +// Returns UUID based on hashing of namespace UUID and name. +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid_test.go new file mode 100644 index 0000000..0bc85b2 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/satori/go.uuid/uuid_test.go @@ -0,0 +1,399 @@ +// Copyright (C) 2013 by Maxim Bublis +// +// 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 uuid + +import ( + "bytes" + "testing" +) + +func TestBytes(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + bytes1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + if !bytes.Equal(u.Bytes(), bytes1) { + t.Errorf("Incorrect bytes representation for UUID: %s", u) + } +} + +func TestString(t *testing.T) { + if NamespaceDNS.String() != "6ba7b810-9dad-11d1-80b4-00c04fd430c8" { + t.Errorf("Incorrect string representation for UUID: %s", NamespaceDNS.String()) + } +} + +func TestEqual(t *testing.T) { + if !Equal(NamespaceDNS, NamespaceDNS) { + t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceDNS) + } + + if Equal(NamespaceDNS, NamespaceURL) { + t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceURL) + } +} + +func TestOr(t *testing.T) { + u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff} + u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00} + + u := UUID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + + if !Equal(u, Or(u1, u2)) { + t.Errorf("Incorrect bitwise OR result %s", Or(u1, u2)) + } +} + +func TestAnd(t *testing.T) { + u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff} + u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00} + + u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if !Equal(u, And(u1, u2)) { + t.Errorf("Incorrect bitwise AND result %s", And(u1, u2)) + } +} + +func TestVersion(t *testing.T) { + u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u.Version() != 1 { + t.Errorf("Incorrect version for UUID: %d", u.Version()) + } +} + +func TestSetVersion(t *testing.T) { + u := UUID{} + u.SetVersion(4) + + if u.Version() != 4 { + t.Errorf("Incorrect version for UUID after u.setVersion(4): %d", u.Version()) + } +} + +func TestVariant(t *testing.T) { + u1 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u1.Variant() != VariantNCS { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantNCS, u1.Variant()) + } + + u2 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u2.Variant() != VariantRFC4122 { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantRFC4122, u2.Variant()) + } + + u3 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u3.Variant() != VariantMicrosoft { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantMicrosoft, u3.Variant()) + } + + u4 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u4.Variant() != VariantFuture { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantFuture, u4.Variant()) + } +} + +func TestSetVariant(t *testing.T) { + u := new(UUID) + u.SetVariant() + + if u.Variant() != VariantRFC4122 { + t.Errorf("Incorrect variant for UUID after u.setVariant(): %d", u.Variant()) + } +} + +func TestFromBytes(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + u1, err := FromBytes(b1) + if err != nil { + t.Errorf("Error parsing UUID from bytes: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte{} + + _, err = FromBytes(b2) + if err == nil { + t.Errorf("Should return error parsing from empty byte slice, got %s", err) + } +} + +func TestMarshalBinary(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + b2, err := u.MarshalBinary() + if err != nil { + t.Errorf("Error marshaling UUID: %s", err) + } + + if !bytes.Equal(b1, b2) { + t.Errorf("Marshaled UUID should be %s, got %s", b1, b2) + } +} + +func TestUnmarshalBinary(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + u1 := UUID{} + err := u1.UnmarshalBinary(b1) + if err != nil { + t.Errorf("Error unmarshaling UUID: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte{} + u2 := UUID{} + + err = u2.UnmarshalBinary(b2) + if err == nil { + t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err) + } +} + +func TestFromString(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + s2 := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" + s3 := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" + + _, err := FromString("") + if err == nil { + t.Errorf("Should return error trying to parse empty string, got %s", err) + } + + u1, err := FromString(s1) + if err != nil { + t.Errorf("Error parsing UUID from string: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + u2, err := FromString(s2) + if err != nil { + t.Errorf("Error parsing UUID from string: %s", err) + } + + if !Equal(u, u2) { + t.Errorf("UUIDs should be equal: %s and %s", u, u2) + } + + u3, err := FromString(s3) + if err != nil { + t.Errorf("Error parsing UUID from string: %s", err) + } + + if !Equal(u, u3) { + t.Errorf("UUIDs should be equal: %s and %s", u, u3) + } +} + +func TestMarshalText(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + + b2, err := u.MarshalText() + if err != nil { + t.Errorf("Error marshaling UUID: %s", err) + } + + if !bytes.Equal(b1, b2) { + t.Errorf("Marshaled UUID should be %s, got %s", b1, b2) + } +} + +func TestUnmarshalText(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + + u1 := UUID{} + err := u1.UnmarshalText(b1) + if err != nil { + t.Errorf("Error unmarshaling UUID: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte("") + u2 := UUID{} + + err = u2.UnmarshalText(b2) + if err == nil { + t.Errorf("Should return error trying to unmarshal from empty string") + } +} + +func TestNewV1(t *testing.T) { + u := NewV1() + + if u.Version() != 1 { + t.Errorf("UUIDv1 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv1 generated with incorrect variant: %d", u.Variant()) + } + + u1 := NewV1() + u2 := NewV1() + + if Equal(u1, u2) { + t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u1, u2) + } + + oldFunc := epochFunc + epochFunc = func() uint64 { return 0 } + + u3 := NewV1() + u4 := NewV1() + + if Equal(u3, u4) { + t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u3, u4) + } + + epochFunc = oldFunc +} + +func TestNewV2(t *testing.T) { + u1 := NewV2(DomainPerson) + + if u1.Version() != 2 { + t.Errorf("UUIDv2 generated with incorrect version: %d", u1.Version()) + } + + if u1.Variant() != VariantRFC4122 { + t.Errorf("UUIDv2 generated with incorrect variant: %d", u1.Variant()) + } + + u2 := NewV2(DomainGroup) + + if u2.Version() != 2 { + t.Errorf("UUIDv2 generated with incorrect version: %d", u2.Version()) + } + + if u2.Variant() != VariantRFC4122 { + t.Errorf("UUIDv2 generated with incorrect variant: %d", u2.Variant()) + } +} + +func TestNewV3(t *testing.T) { + u := NewV3(NamespaceDNS, "www.example.com") + + if u.Version() != 3 { + t.Errorf("UUIDv3 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv3 generated with incorrect variant: %d", u.Variant()) + } + + if u.String() != "5df41881-3aed-3515-88a7-2f4a814cf09e" { + t.Errorf("UUIDv3 generated incorrectly: %s", u.String()) + } + + u = NewV3(NamespaceDNS, "python.org") + + if u.String() != "6fa459ea-ee8a-3ca4-894e-db77e160355e" { + t.Errorf("UUIDv3 generated incorrectly: %s", u.String()) + } + + u1 := NewV3(NamespaceDNS, "golang.org") + u2 := NewV3(NamespaceDNS, "golang.org") + if !Equal(u1, u2) { + t.Errorf("UUIDv3 generated different UUIDs for same namespace and name: %s and %s", u1, u2) + } + + u3 := NewV3(NamespaceDNS, "example.com") + if Equal(u1, u3) { + t.Errorf("UUIDv3 generated same UUIDs for different names in same namespace: %s and %s", u1, u2) + } + + u4 := NewV3(NamespaceURL, "golang.org") + if Equal(u1, u4) { + t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4) + } +} + +func TestNewV4(t *testing.T) { + u := NewV4() + + if u.Version() != 4 { + t.Errorf("UUIDv4 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv4 generated with incorrect variant: %d", u.Variant()) + } +} + +func TestNewV5(t *testing.T) { + u := NewV5(NamespaceDNS, "www.example.com") + + if u.Version() != 5 { + t.Errorf("UUIDv5 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv5 generated with incorrect variant: %d", u.Variant()) + } + + u = NewV5(NamespaceDNS, "python.org") + + if u.String() != "886313e1-3b8a-5372-9b90-0c9aee199e5d" { + t.Errorf("UUIDv5 generated incorrectly: %s", u.String()) + } + + u1 := NewV5(NamespaceDNS, "golang.org") + u2 := NewV5(NamespaceDNS, "golang.org") + if !Equal(u1, u2) { + t.Errorf("UUIDv5 generated different UUIDs for same namespace and name: %s and %s", u1, u2) + } + + u3 := NewV5(NamespaceDNS, "example.com") + if Equal(u1, u3) { + t.Errorf("UUIDv5 generated same UUIDs for different names in same namespace: %s and %s", u1, u2) + } + + u4 := NewV5(NamespaceURL, "golang.org") + if Equal(u1, u4) { + t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack.go new file mode 100644 index 0000000..74ee83c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack.go @@ -0,0 +1,27 @@ +package hack + +import ( + "reflect" + "unsafe" +) + +// no copy to change slice to string +// use your own risk +func String(b []byte) (s string) { + pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) + pstring.Data = pbytes.Data + pstring.Len = pbytes.Len + return +} + +// no copy to change string to slice +// use your own risk +func Slice(s string) (b []byte) { + pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) + pbytes.Data = pstring.Data + pbytes.Len = pstring.Len + pbytes.Cap = pstring.Len + return +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack_test.go new file mode 100644 index 0000000..7b11b0b --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/hack/hack_test.go @@ -0,0 +1,36 @@ +package hack + +import ( + "bytes" + "testing" +) + +func TestString(t *testing.T) { + b := []byte("hello world") + a := String(b) + + if a != "hello world" { + t.Fatal(a) + } + + b[0] = 'a' + + if a != "aello world" { + t.Fatal(a) + } + + b = append(b, "abc"...) + if a != "aello world" { + t.Fatal(a) + } +} + +func TestByte(t *testing.T) { + a := "hello world" + + b := Slice(a) + + if !bytes.Equal(b, []byte("hello world")) { + t.Fatal(string(b)) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/ioutil.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/ioutil.go new file mode 100644 index 0000000..c99c987 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/ioutil.go @@ -0,0 +1,39 @@ +// Copyright 2012, Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ioutil2 + +import ( + "io" + "io/ioutil" + "os" + "path" +) + +// Write file to temp and atomically move when everything else succeeds. +func WriteFileAtomic(filename string, data []byte, perm os.FileMode) error { + dir, name := path.Split(filename) + f, err := ioutil.TempFile(dir, name) + if err != nil { + return err + } + n, err := f.Write(data) + f.Close() + if err == nil && n < len(data) { + err = io.ErrShortWrite + } else { + err = os.Chmod(f.Name(), perm) + } + if err != nil { + os.Remove(f.Name()) + return err + } + return os.Rename(f.Name(), filename) +} + +// Check file exists or not +func FileExists(name string) bool { + _, err := os.Stat(name) + return !os.IsNotExist(err) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter.go new file mode 100644 index 0000000..c02ab0d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter.go @@ -0,0 +1,69 @@ +package ioutil2 + +import ( + "errors" + "io" +) + +var ErrExceedLimit = errors.New("write exceed limit") + +func NewSectionWriter(w io.WriterAt, off int64, n int64) *SectionWriter { + return &SectionWriter{w, off, off, off + n} +} + +type SectionWriter struct { + w io.WriterAt + base int64 + off int64 + limit int64 +} + +func (s *SectionWriter) Write(p []byte) (n int, err error) { + if s.off >= s.limit { + return 0, ErrExceedLimit + } + + if max := s.limit - s.off; int64(len(p)) > max { + return 0, ErrExceedLimit + } + + n, err = s.w.WriteAt(p, s.off) + s.off += int64(n) + return +} + +var errWhence = errors.New("Seek: invalid whence") +var errOffset = errors.New("Seek: invalid offset") + +func (s *SectionWriter) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, errWhence + case 0: + offset += s.base + case 1: + offset += s.off + case 2: + offset += s.limit + } + if offset < s.base { + return 0, errOffset + } + s.off = offset + return offset - s.base, nil +} + +func (s *SectionWriter) WriteAt(p []byte, off int64) (n int, err error) { + if off < 0 || off >= s.limit-s.base { + return 0, errOffset + } + off += s.base + if max := s.limit - off; int64(len(p)) > max { + return 0, ErrExceedLimit + } + + return s.w.WriteAt(p, off) +} + +// Size returns the size of the section in bytes. +func (s *SectionWriter) Size() int64 { return s.limit - s.base } diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter_test.go new file mode 100644 index 0000000..35bf6d7 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/ioutil2/sectionwriter_test.go @@ -0,0 +1,56 @@ +package ioutil2 + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestSectionWriter(t *testing.T) { + f, err := ioutil.TempFile(".", "test_") + if err != nil { + t.Fatal(err) + } + + defer func() { + n := f.Name() + f.Close() + os.Remove(n) + }() + + f.Truncate(3) + + rw := NewSectionWriter(f, 0, 1) + + _, err = rw.Write([]byte{'1'}) + if err != nil { + t.Fatal(err) + } + + _, err = rw.Write([]byte{'1'}) + if err == nil { + t.Fatal("must err") + } + + rw = NewSectionWriter(f, 1, 2) + + _, err = rw.Write([]byte{'2', '3', '4'}) + if err == nil { + t.Fatal("must err") + } + + _, err = rw.Write([]byte{'2', '3'}) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 3) + _, err = f.ReadAt(buf, 0) + if err != nil { + t.Fatal(err) + } + + if string(buf) != "123" { + t.Fatal(string(buf)) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/doc.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/doc.go new file mode 100644 index 0000000..81a60ee --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/doc.go @@ -0,0 +1,21 @@ +// log package supplies more advanced features than go orign log package. +// +// It supports log different level: trace, debug, info, warn, error, fatal. +// +// It also supports different log handlers which you can log to stdout, file, socket, etc... +// +// Use +// +// import "github.com/siddontang/go/log" +// +// //log with different level +// log.Info("hello world") +// log.Error("hello world") +// +// //create a logger with specified handler +// h := NewStreamHandler(os.Stdout) +// l := log.NewDefault(h) +// l.Info("hello world") +// l.Infof("%s %d", "hello", 123) +// +package log diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/filehandler.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/filehandler.go new file mode 100644 index 0000000..783b652 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/filehandler.go @@ -0,0 +1,200 @@ +package log + +import ( + "fmt" + "os" + "path" + "time" +) + +//FileHandler writes log to a file. +type FileHandler struct { + fd *os.File +} + +func NewFileHandler(fileName string, flag int) (*FileHandler, error) { + dir := path.Dir(fileName) + os.Mkdir(dir, 0777) + + f, err := os.OpenFile(fileName, flag, 0) + if err != nil { + return nil, err + } + + h := new(FileHandler) + + h.fd = f + + return h, nil +} + +func (h *FileHandler) Write(b []byte) (n int, err error) { + return h.fd.Write(b) +} + +func (h *FileHandler) Close() error { + return h.fd.Close() +} + +//RotatingFileHandler writes log a file, if file size exceeds maxBytes, +//it will backup current file and open a new one. +// +//max backup file number is set by backupCount, it will delete oldest if backups too many. +type RotatingFileHandler struct { + fd *os.File + + fileName string + maxBytes int + backupCount int +} + +func NewRotatingFileHandler(fileName string, maxBytes int, backupCount int) (*RotatingFileHandler, error) { + dir := path.Dir(fileName) + os.Mkdir(dir, 0777) + + h := new(RotatingFileHandler) + + if maxBytes <= 0 { + return nil, fmt.Errorf("invalid max bytes") + } + + h.fileName = fileName + h.maxBytes = maxBytes + h.backupCount = backupCount + + var err error + h.fd, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + return nil, err + } + + return h, nil +} + +func (h *RotatingFileHandler) Write(p []byte) (n int, err error) { + h.doRollover() + return h.fd.Write(p) +} + +func (h *RotatingFileHandler) Close() error { + if h.fd != nil { + return h.fd.Close() + } + return nil +} + +func (h *RotatingFileHandler) doRollover() { + f, err := h.fd.Stat() + if err != nil { + return + } + + if h.maxBytes <= 0 { + return + } else if f.Size() < int64(h.maxBytes) { + return + } + + if h.backupCount > 0 { + h.fd.Close() + + for i := h.backupCount - 1; i > 0; i-- { + sfn := fmt.Sprintf("%s.%d", h.fileName, i) + dfn := fmt.Sprintf("%s.%d", h.fileName, i+1) + + os.Rename(sfn, dfn) + } + + dfn := fmt.Sprintf("%s.1", h.fileName) + os.Rename(h.fileName, dfn) + + h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + } +} + +//TimeRotatingFileHandler writes log to a file, +//it will backup current and open a new one, with a period time you sepecified. +// +//refer: http://docs.python.org/2/library/logging.handlers.html. +//same like python TimedRotatingFileHandler. +type TimeRotatingFileHandler struct { + fd *os.File + + baseName string + interval int64 + suffix string + rolloverAt int64 +} + +const ( + WhenSecond = iota + WhenMinute + WhenHour + WhenDay +) + +func NewTimeRotatingFileHandler(baseName string, when int8, interval int) (*TimeRotatingFileHandler, error) { + dir := path.Dir(baseName) + os.Mkdir(dir, 0777) + + h := new(TimeRotatingFileHandler) + + h.baseName = baseName + + switch when { + case WhenSecond: + h.interval = 1 + h.suffix = "2006-01-02_15-04-05" + case WhenMinute: + h.interval = 60 + h.suffix = "2006-01-02_15-04" + case WhenHour: + h.interval = 3600 + h.suffix = "2006-01-02_15" + case WhenDay: + h.interval = 3600 * 24 + h.suffix = "2006-01-02" + default: + return nil, fmt.Errorf("invalid when_rotate: %d", when) + } + + h.interval = h.interval * int64(interval) + + var err error + h.fd, err = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + return nil, err + } + + fInfo, _ := h.fd.Stat() + h.rolloverAt = fInfo.ModTime().Unix() + h.interval + + return h, nil +} + +func (h *TimeRotatingFileHandler) doRollover() { + //refer http://hg.python.org/cpython/file/2.7/Lib/logging/handlers.py + now := time.Now() + + if h.rolloverAt <= now.Unix() { + fName := h.baseName + now.Format(h.suffix) + h.fd.Close() + e := os.Rename(h.baseName, fName) + if e != nil { + panic(e) + } + + h.fd, _ = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + + h.rolloverAt = time.Now().Unix() + h.interval + } +} + +func (h *TimeRotatingFileHandler) Write(b []byte) (n int, err error) { + h.doRollover() + return h.fd.Write(b) +} + +func (h *TimeRotatingFileHandler) Close() error { + return h.fd.Close() +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/handler.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/handler.go new file mode 100644 index 0000000..352e30c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/handler.go @@ -0,0 +1,49 @@ +package log + +import ( + "io" +) + +//Handler writes logs to somewhere +type Handler interface { + Write(p []byte) (n int, err error) + Close() error +} + +//StreamHandler writes logs to a specified io Writer, maybe stdout, stderr, etc... +type StreamHandler struct { + w io.Writer +} + +func NewStreamHandler(w io.Writer) (*StreamHandler, error) { + h := new(StreamHandler) + + h.w = w + + return h, nil +} + +func (h *StreamHandler) Write(b []byte) (n int, err error) { + return h.w.Write(b) +} + +func (h *StreamHandler) Close() error { + return nil +} + + +//NullHandler does nothing, it discards anything. +type NullHandler struct { +} + +func NewNullHandler() (*NullHandler, error) { + return new(NullHandler), nil +} + +func (h *NullHandler) Write(b []byte) (n int, err error) { + return len(b), nil +} + +func (h *NullHandler) Close() { + +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log.go new file mode 100644 index 0000000..74cd76a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log.go @@ -0,0 +1,341 @@ +package log + +import ( + "fmt" + "os" + "runtime" + "strconv" + "sync" + "sync/atomic" + "time" +) + +//log level, from low to high, more high means more serious +const ( + LevelTrace = iota + LevelDebug + LevelInfo + LevelWarn + LevelError + LevelFatal +) + +const ( + Ltime = 1 << iota //time format "2006/01/02 15:04:05" + Lfile //file.go:123 + Llevel //[Trace|Debug|Info...] +) + +var LevelName [6]string = [6]string{"Trace", "Debug", "Info", "Warn", "Error", "Fatal"} + +const TimeFormat = "2006/01/02 15:04:05" + +const maxBufPoolSize = 16 + +type atomicInt32 int32 + +func (i *atomicInt32) Set(n int) { + atomic.StoreInt32((*int32)(i), int32(n)) +} + +func (i *atomicInt32) Get() int { + return int(atomic.LoadInt32((*int32)(i))) +} + +type Logger struct { + level atomicInt32 + flag int + + hMutex sync.Mutex + handler Handler + + quit chan struct{} + msg chan []byte + + bufMutex sync.Mutex + bufs [][]byte + + wg sync.WaitGroup + + closed atomicInt32 +} + +//new a logger with specified handler and flag +func New(handler Handler, flag int) *Logger { + var l = new(Logger) + + l.level.Set(LevelInfo) + l.handler = handler + + l.flag = flag + + l.quit = make(chan struct{}) + l.closed.Set(0) + + l.msg = make(chan []byte, 1024) + + l.bufs = make([][]byte, 0, 16) + + l.wg.Add(1) + go l.run() + + return l +} + +//new a default logger with specified handler and flag: Ltime|Lfile|Llevel +func NewDefault(handler Handler) *Logger { + return New(handler, Ltime|Lfile|Llevel) +} + +func newStdHandler() *StreamHandler { + h, _ := NewStreamHandler(os.Stdout) + return h +} + +var std = NewDefault(newStdHandler()) + +func (l *Logger) run() { + defer l.wg.Done() + for { + select { + case msg := <-l.msg: + l.hMutex.Lock() + l.handler.Write(msg) + l.hMutex.Unlock() + l.putBuf(msg) + case <-l.quit: + //we must log all msg + if len(l.msg) == 0 { + return + } + } + } +} + +func (l *Logger) popBuf() []byte { + l.bufMutex.Lock() + var buf []byte + if len(l.bufs) == 0 { + buf = make([]byte, 0, 1024) + } else { + buf = l.bufs[len(l.bufs)-1] + l.bufs = l.bufs[0 : len(l.bufs)-1] + } + l.bufMutex.Unlock() + + return buf +} + +func (l *Logger) putBuf(buf []byte) { + l.bufMutex.Lock() + if len(l.bufs) < maxBufPoolSize { + buf = buf[0:0] + l.bufs = append(l.bufs, buf) + } + l.bufMutex.Unlock() +} + +func (l *Logger) Close() { + if l.closed.Get() == 1 { + return + } + l.closed.Set(1) + + close(l.quit) + + l.wg.Wait() + + l.quit = nil + + l.handler.Close() +} + +//set log level, any log level less than it will not log +func (l *Logger) SetLevel(level int) { + l.level.Set(level) +} + +func (l *Logger) SetHandler(h Handler) { + if l.closed.Get() == 1 { + return + } + + l.hMutex.Lock() + if l.handler != nil { + l.handler.Close() + } + l.handler = h + l.hMutex.Unlock() +} + +func (l *Logger) Output(callDepth int, level int, s string) { + if l.closed.Get() == 1 { + // closed + return + } + + if l.level.Get() > level { + // higher level can be logged + return + } + + buf := l.popBuf() + + if l.flag&Ltime > 0 { + now := time.Now().Format(TimeFormat) + buf = append(buf, '[') + buf = append(buf, now...) + buf = append(buf, "] "...) + } + + if l.flag&Lfile > 0 { + _, file, line, ok := runtime.Caller(callDepth) + if !ok { + file = "???" + line = 0 + } else { + for i := len(file) - 1; i > 0; i-- { + if file[i] == '/' { + file = file[i+1:] + break + } + } + } + + buf = append(buf, file...) + buf = append(buf, ':') + + buf = strconv.AppendInt(buf, int64(line), 10) + buf = append(buf, ' ') + } + + if l.flag&Llevel > 0 { + buf = append(buf, '[') + buf = append(buf, LevelName[level]...) + buf = append(buf, "] "...) + } + + buf = append(buf, s...) + + if s[len(s)-1] != '\n' { + buf = append(buf, '\n') + } + + l.msg <- buf +} + +//log with Trace level +func (l *Logger) Trace(v ...interface{}) { + l.Output(2, LevelTrace, fmt.Sprint(v...)) +} + +//log with Debug level +func (l *Logger) Debug(v ...interface{}) { + l.Output(2, LevelDebug, fmt.Sprint(v...)) +} + +//log with info level +func (l *Logger) Info(v ...interface{}) { + l.Output(2, LevelInfo, fmt.Sprint(v...)) +} + +//log with warn level +func (l *Logger) Warn(v ...interface{}) { + l.Output(2, LevelWarn, fmt.Sprint(v...)) +} + +//log with error level +func (l *Logger) Error(v ...interface{}) { + l.Output(2, LevelError, fmt.Sprint(v...)) +} + +//log with fatal level +func (l *Logger) Fatal(v ...interface{}) { + l.Output(2, LevelFatal, fmt.Sprint(v...)) +} + +//log with Trace level +func (l *Logger) Tracef(format string, v ...interface{}) { + l.Output(2, LevelTrace, fmt.Sprintf(format, v...)) +} + +//log with Debug level +func (l *Logger) Debugf(format string, v ...interface{}) { + l.Output(2, LevelDebug, fmt.Sprintf(format, v...)) +} + +//log with info level +func (l *Logger) Infof(format string, v ...interface{}) { + l.Output(2, LevelInfo, fmt.Sprintf(format, v...)) +} + +//log with warn level +func (l *Logger) Warnf(format string, v ...interface{}) { + l.Output(2, LevelWarn, fmt.Sprintf(format, v...)) +} + +//log with error level +func (l *Logger) Errorf(format string, v ...interface{}) { + l.Output(2, LevelError, fmt.Sprintf(format, v...)) +} + +//log with fatal level +func (l *Logger) Fatalf(format string, v ...interface{}) { + l.Output(2, LevelFatal, fmt.Sprintf(format, v...)) +} + +func SetLevel(level int) { + std.SetLevel(level) +} + +func SetHandler(h Handler) { + std.SetHandler(h) +} + +func Trace(v ...interface{}) { + std.Output(2, LevelTrace, fmt.Sprint(v...)) +} + +func Debug(v ...interface{}) { + std.Output(2, LevelDebug, fmt.Sprint(v...)) +} + +func Info(v ...interface{}) { + std.Output(2, LevelInfo, fmt.Sprint(v...)) +} + +func Warn(v ...interface{}) { + std.Output(2, LevelWarn, fmt.Sprint(v...)) +} + +func Error(v ...interface{}) { + std.Output(2, LevelError, fmt.Sprint(v...)) +} + +func Fatal(v ...interface{}) { + std.Output(2, LevelFatal, fmt.Sprint(v...)) +} + +func Tracef(format string, v ...interface{}) { + std.Output(2, LevelTrace, fmt.Sprintf(format, v...)) +} + +func Debugf(format string, v ...interface{}) { + std.Output(2, LevelDebug, fmt.Sprintf(format, v...)) +} + +func Infof(format string, v ...interface{}) { + std.Output(2, LevelInfo, fmt.Sprintf(format, v...)) +} + +func Warnf(format string, v ...interface{}) { + std.Output(2, LevelWarn, fmt.Sprintf(format, v...)) +} + +func Errorf(format string, v ...interface{}) { + std.Output(2, LevelError, fmt.Sprintf(format, v...)) +} + +func Fatalf(format string, v ...interface{}) { + std.Output(2, LevelFatal, fmt.Sprintf(format, v...)) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log_test.go new file mode 100644 index 0000000..2e29b31 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/log_test.go @@ -0,0 +1,63 @@ +package log + +import ( + "os" + "testing" +) + +func TestStdStreamLog(t *testing.T) { + h, _ := NewStreamHandler(os.Stdout) + s := NewDefault(h) + s.Info("hello world") + + s.Close() + + s.Info("can not log") + + Info("hello world") + + SetHandler(os.Stderr) + + Infof("%s %d", "Hello", 123) + + SetLevel(LevelError) + + Infof("%s %d", "Hello", 123) + Fatalf("%s %d", "Hello", 123) +} + +func TestRotatingFileLog(t *testing.T) { + path := "./test_log" + os.RemoveAll(path) + + os.Mkdir(path, 0777) + fileName := path + "/test" + + h, err := NewRotatingFileHandler(fileName, 10, 2) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 10) + + h.Write(buf) + + h.Write(buf) + + if _, err := os.Stat(fileName + ".1"); err != nil { + t.Fatal(err) + } + + if _, err := os.Stat(fileName + ".2"); err == nil { + t.Fatal(err) + } + + h.Write(buf) + if _, err := os.Stat(fileName + ".2"); err != nil { + t.Fatal(err) + } + + h.Close() + + os.RemoveAll(path) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/sockethandler.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/sockethandler.go new file mode 100644 index 0000000..ad81ccd --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/log/sockethandler.go @@ -0,0 +1,65 @@ +package log + +import ( + "encoding/binary" + "net" + "time" +) + +//SocketHandler writes log to a connectionl. +//Network protocol is simple: log length + log | log length + log. log length is uint32, bigendian. +//you must implement your own log server, maybe you can use logd instead simply. +type SocketHandler struct { + c net.Conn + protocol string + addr string +} + +func NewSocketHandler(protocol string, addr string) (*SocketHandler, error) { + s := new(SocketHandler) + + s.protocol = protocol + s.addr = addr + + return s, nil +} + +func (h *SocketHandler) Write(p []byte) (n int, err error) { + if err = h.connect(); err != nil { + return + } + + buf := make([]byte, len(p)+4) + + binary.BigEndian.PutUint32(buf, uint32(len(p))) + + copy(buf[4:], p) + + n, err = h.c.Write(buf) + if err != nil { + h.c.Close() + h.c = nil + } + return +} + +func (h *SocketHandler) Close() error { + if h.c != nil { + h.c.Close() + } + return nil +} + +func (h *SocketHandler) connect() error { + if h.c != nil { + return nil + } + + var err error + h.c, err = net.DialTimeout(h.protocol, h.addr, 20*time.Second) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic.go new file mode 100644 index 0000000..424a974 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic.go @@ -0,0 +1,146 @@ +// Copyright 2013, Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sync2 + +import ( + "sync" + "sync/atomic" + "time" +) + +type AtomicInt32 int32 + +func (i *AtomicInt32) Add(n int32) int32 { + return atomic.AddInt32((*int32)(i), n) +} + +func (i *AtomicInt32) Set(n int32) { + atomic.StoreInt32((*int32)(i), n) +} + +func (i *AtomicInt32) Get() int32 { + return atomic.LoadInt32((*int32)(i)) +} + +func (i *AtomicInt32) CompareAndSwap(oldval, newval int32) (swapped bool) { + return atomic.CompareAndSwapInt32((*int32)(i), oldval, newval) +} + +type AtomicUint32 uint32 + +func (i *AtomicUint32) Add(n uint32) uint32 { + return atomic.AddUint32((*uint32)(i), n) +} + +func (i *AtomicUint32) Set(n uint32) { + atomic.StoreUint32((*uint32)(i), n) +} + +func (i *AtomicUint32) Get() uint32 { + return atomic.LoadUint32((*uint32)(i)) +} + +func (i *AtomicUint32) CompareAndSwap(oldval, newval uint32) (swapped bool) { + return atomic.CompareAndSwapUint32((*uint32)(i), oldval, newval) +} + +type AtomicInt64 int64 + +func (i *AtomicInt64) Add(n int64) int64 { + return atomic.AddInt64((*int64)(i), n) +} + +func (i *AtomicInt64) Set(n int64) { + atomic.StoreInt64((*int64)(i), n) +} + +func (i *AtomicInt64) Get() int64 { + return atomic.LoadInt64((*int64)(i)) +} + +func (i *AtomicInt64) CompareAndSwap(oldval, newval int64) (swapped bool) { + return atomic.CompareAndSwapInt64((*int64)(i), oldval, newval) +} + +type AtomicUint64 uint64 + +func (i *AtomicUint64) Add(n uint64) uint64 { + return atomic.AddUint64((*uint64)(i), n) +} + +func (i *AtomicUint64) Set(n uint64) { + atomic.StoreUint64((*uint64)(i), n) +} + +func (i *AtomicUint64) Get() uint64 { + return atomic.LoadUint64((*uint64)(i)) +} + +func (i *AtomicUint64) CompareAndSwap(oldval, newval uint64) (swapped bool) { + return atomic.CompareAndSwapUint64((*uint64)(i), oldval, newval) +} + +type AtomicDuration int64 + +func (d *AtomicDuration) Add(duration time.Duration) time.Duration { + return time.Duration(atomic.AddInt64((*int64)(d), int64(duration))) +} + +func (d *AtomicDuration) Set(duration time.Duration) { + atomic.StoreInt64((*int64)(d), int64(duration)) +} + +func (d *AtomicDuration) Get() time.Duration { + return time.Duration(atomic.LoadInt64((*int64)(d))) +} + +func (d *AtomicDuration) CompareAndSwap(oldval, newval time.Duration) (swapped bool) { + return atomic.CompareAndSwapInt64((*int64)(d), int64(oldval), int64(newval)) +} + +// AtomicString gives you atomic-style APIs for string, but +// it's only a convenience wrapper that uses a mutex. So, it's +// not as efficient as the rest of the atomic types. +type AtomicString struct { + mu sync.Mutex + str string +} + +func (s *AtomicString) Set(str string) { + s.mu.Lock() + s.str = str + s.mu.Unlock() +} + +func (s *AtomicString) Get() string { + s.mu.Lock() + str := s.str + s.mu.Unlock() + return str +} + +func (s *AtomicString) CompareAndSwap(oldval, newval string) (swqpped bool) { + s.mu.Lock() + defer s.mu.Unlock() + if s.str == oldval { + s.str = newval + return true + } + return false +} + +type AtomicBool int32 + +func (b *AtomicBool) Set(v bool) { + if v { + atomic.StoreInt32((*int32)(b), 1) + } else { + atomic.StoreInt32((*int32)(b), 0) + } +} + +func (b *AtomicBool) Get() bool { + return atomic.LoadInt32((*int32)(b)) == 1 +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic_test.go new file mode 100644 index 0000000..040397f --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/atomic_test.go @@ -0,0 +1,51 @@ +// Copyright 2013, Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sync2 + +import ( + "testing" +) + +func TestAtomicString(t *testing.T) { + var s AtomicString + if s.Get() != "" { + t.Errorf("want empty, got %s", s.Get()) + } + s.Set("a") + if s.Get() != "a" { + t.Errorf("want a, got %s", s.Get()) + } + if s.CompareAndSwap("b", "c") { + t.Errorf("want false, got true") + } + if s.Get() != "a" { + t.Errorf("want a, got %s", s.Get()) + } + if !s.CompareAndSwap("a", "c") { + t.Errorf("want true, got false") + } + if s.Get() != "c" { + t.Errorf("want c, got %s", s.Get()) + } +} + +func TestAtomicBool(t *testing.T) { + var b AtomicBool + if b.Get() != false { + t.Fatal("must false") + } + + b.Set(true) + + if b.Get() != true { + t.Fatal("must true") + } + + b.Set(false) + + if b.Get() != false { + t.Fatal("must false") + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore.go new file mode 100644 index 0000000..d310da7 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore.go @@ -0,0 +1,65 @@ +package sync2 + +import ( + "sync" + "sync/atomic" + "time" +) + +func NewSemaphore(initialCount int) *Semaphore { + res := &Semaphore{ + counter: int64(initialCount), + } + res.cond.L = &res.lock + return res +} + +type Semaphore struct { + lock sync.Mutex + cond sync.Cond + counter int64 +} + +func (s *Semaphore) Release() { + s.lock.Lock() + s.counter += 1 + if s.counter >= 0 { + s.cond.Signal() + } + s.lock.Unlock() +} + +func (s *Semaphore) Acquire() { + s.lock.Lock() + for s.counter < 1 { + s.cond.Wait() + } + s.counter -= 1 + s.lock.Unlock() +} + +func (s *Semaphore) AcquireTimeout(timeout time.Duration) bool { + done := make(chan bool, 1) + // Gate used to communicate between the threads and decide what the result + // is. If the main thread decides, we have timed out, otherwise we succeed. + decided := new(int32) + go func() { + s.Acquire() + if atomic.SwapInt32(decided, 1) == 0 { + done <- true + } else { + // If we already decided the result, and this thread did not win + s.Release() + } + }() + select { + case <-done: + return true + case <-time.NewTimer(timeout).C: + if atomic.SwapInt32(decided, 1) == 1 { + // The other thread already decided the result + return true + } + return false + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore_test.go new file mode 100644 index 0000000..8c48694 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/github.com/siddontang/go/sync2/semaphore_test.go @@ -0,0 +1,41 @@ +// Copyright 2012, Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sync2 + +import ( + "testing" + "time" +) + +func TestSemaNoTimeout(t *testing.T) { + s := NewSemaphore(1) + s.Acquire() + released := false + go func() { + time.Sleep(10 * time.Millisecond) + released = true + s.Release() + }() + s.Acquire() + if !released { + t.Errorf("want true, got false") + } +} + +func TestSemaTimeout(t *testing.T) { + s := NewSemaphore(1) + s.Acquire() + go func() { + time.Sleep(10 * time.Millisecond) + s.Release() + }() + if ok := s.AcquireTimeout(5 * time.Millisecond); ok { + t.Errorf("want false, got true") + } + time.Sleep(10 * time.Millisecond) + if ok := s.AcquireTimeout(5 * time.Millisecond); !ok { + t.Errorf("want true, got false") + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/.gitignore b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/.gitignore new file mode 100644 index 0000000..191a536 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/.gitignore @@ -0,0 +1,4 @@ +_* +*.swp +*.[568] +[568].out diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/LICENSE b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/LICENSE new file mode 100644 index 0000000..545cf2d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/LICENSE @@ -0,0 +1,25 @@ +Gocheck - A rich testing framework for Go + +Copyright (c) 2010-2013 Gustavo Niemeyer + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/README.md b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/README.md new file mode 100644 index 0000000..0ca9e57 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/README.md @@ -0,0 +1,20 @@ +Instructions +============ + +Install the package with: + + go get gopkg.in/check.v1 + +Import it with: + + import "gopkg.in/check.v1" + +and use _check_ as the package name inside the code. + +For more details, visit the project page: + +* http://labix.org/gocheck + +and the API documentation: + +* https://gopkg.in/check.v1 diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/TODO b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/TODO new file mode 100644 index 0000000..3349827 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/TODO @@ -0,0 +1,2 @@ +- Assert(slice, Contains, item) +- Parallel test support diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark.go new file mode 100644 index 0000000..48cb8c8 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark.go @@ -0,0 +1,163 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package check + +import ( + "fmt" + "runtime" + "time" +) + +var memStats runtime.MemStats + +// testingB is a type passed to Benchmark functions to manage benchmark +// timing and to specify the number of iterations to run. +type timer struct { + start time.Time // Time test or benchmark started + duration time.Duration + N int + bytes int64 + timerOn bool + benchTime time.Duration + // The initial states of memStats.Mallocs and memStats.TotalAlloc. + startAllocs uint64 + startBytes uint64 + // The net total of this test after being run. + netAllocs uint64 + netBytes uint64 +} + +// StartTimer starts timing a test. This function is called automatically +// before a benchmark starts, but it can also used to resume timing after +// a call to StopTimer. +func (c *C) StartTimer() { + if !c.timerOn { + c.start = time.Now() + c.timerOn = true + + runtime.ReadMemStats(&memStats) + c.startAllocs = memStats.Mallocs + c.startBytes = memStats.TotalAlloc + } +} + +// StopTimer stops timing a test. This can be used to pause the timer +// while performing complex initialization that you don't +// want to measure. +func (c *C) StopTimer() { + if c.timerOn { + c.duration += time.Now().Sub(c.start) + c.timerOn = false + runtime.ReadMemStats(&memStats) + c.netAllocs += memStats.Mallocs - c.startAllocs + c.netBytes += memStats.TotalAlloc - c.startBytes + } +} + +// ResetTimer sets the elapsed benchmark time to zero. +// It does not affect whether the timer is running. +func (c *C) ResetTimer() { + if c.timerOn { + c.start = time.Now() + runtime.ReadMemStats(&memStats) + c.startAllocs = memStats.Mallocs + c.startBytes = memStats.TotalAlloc + } + c.duration = 0 + c.netAllocs = 0 + c.netBytes = 0 +} + +// SetBytes informs the number of bytes that the benchmark processes +// on each iteration. If this is called in a benchmark it will also +// report MB/s. +func (c *C) SetBytes(n int64) { + c.bytes = n +} + +func (c *C) nsPerOp() int64 { + if c.N <= 0 { + return 0 + } + return c.duration.Nanoseconds() / int64(c.N) +} + +func (c *C) mbPerSec() float64 { + if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 { + return 0 + } + return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds() +} + +func (c *C) timerString() string { + if c.N <= 0 { + return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9) + } + mbs := c.mbPerSec() + mb := "" + if mbs != 0 { + mb = fmt.Sprintf("\t%7.2f MB/s", mbs) + } + nsop := c.nsPerOp() + ns := fmt.Sprintf("%10d ns/op", nsop) + if c.N > 0 && nsop < 100 { + // The format specifiers here make sure that + // the ones digits line up for all three possible formats. + if nsop < 10 { + ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) + } else { + ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) + } + } + memStats := "" + if c.benchMem { + allocedBytes := fmt.Sprintf("%8d B/op", int64(c.netBytes)/int64(c.N)) + allocs := fmt.Sprintf("%8d allocs/op", int64(c.netAllocs)/int64(c.N)) + memStats = fmt.Sprintf("\t%s\t%s", allocedBytes, allocs) + } + return fmt.Sprintf("%8d\t%s%s%s", c.N, ns, mb, memStats) +} + +func min(x, y int) int { + if x > y { + return y + } + return x +} + +func max(x, y int) int { + if x < y { + return y + } + return x +} + +// roundDown10 rounds a number down to the nearest power of 10. +func roundDown10(n int) int { + var tens = 0 + // tens = floor(log_10(n)) + for n > 10 { + n = n / 10 + tens++ + } + // result = 10^tens + result := 1 + for i := 0; i < tens; i++ { + result *= 10 + } + return result +} + +// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. +func roundUp(n int) int { + base := roundDown10(n) + if n < (2 * base) { + return 2 * base + } + if n < (5 * base) { + return 5 * base + } + return 10 * base +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go new file mode 100644 index 0000000..4dd827c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go @@ -0,0 +1,91 @@ +// These tests verify the test running logic. + +package check_test + +import ( + "time" + . "gopkg.in/check.v1" +) + +var benchmarkS = Suite(&BenchmarkS{}) + +type BenchmarkS struct{} + +func (s *BenchmarkS) TestCountSuite(c *C) { + suitesRun += 1 +} + +func (s *BenchmarkS) TestBasicTestTiming(c *C) { + helper := FixtureHelper{sleepOn: "Test1", sleep: 1000000 * time.Nanosecond} + output := String{} + runConf := RunConf{Output: &output, Verbose: true} + Run(&helper, &runConf) + + expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t0\\.001s\n" + + "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t0\\.000s\n" + c.Assert(output.value, Matches, expected) +} + +func (s *BenchmarkS) TestStreamTestTiming(c *C) { + helper := FixtureHelper{sleepOn: "SetUpSuite", sleep: 1000000 * time.Nanosecond} + output := String{} + runConf := RunConf{Output: &output, Stream: true} + Run(&helper, &runConf) + + expected := "(?s).*\nPASS: check_test\\.go:[0-9]+: FixtureHelper\\.SetUpSuite\t *0\\.001s\n.*" + c.Assert(output.value, Matches, expected) +} + +func (s *BenchmarkS) TestBenchmark(c *C) { + helper := FixtureHelper{sleep: 100000} + output := String{} + runConf := RunConf{ + Output: &output, + Benchmark: true, + BenchmarkTime: 10000000, + Filter: "Benchmark1", + } + Run(&helper, &runConf) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Benchmark1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "SetUpTest") + c.Check(helper.calls[5], Equals, "Benchmark1") + c.Check(helper.calls[6], Equals, "TearDownTest") + // ... and more. + + expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark1\t *100\t *[12][0-9]{5} ns/op\n" + c.Assert(output.value, Matches, expected) +} + +func (s *BenchmarkS) TestBenchmarkBytes(c *C) { + helper := FixtureHelper{sleep: 100000} + output := String{} + runConf := RunConf{ + Output: &output, + Benchmark: true, + BenchmarkTime: 10000000, + Filter: "Benchmark2", + } + Run(&helper, &runConf) + + expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark2\t *100\t *[12][0-9]{5} ns/op\t *[4-9]\\.[0-9]{2} MB/s\n" + c.Assert(output.value, Matches, expected) +} + +func (s *BenchmarkS) TestBenchmarkMem(c *C) { + helper := FixtureHelper{sleep: 100000} + output := String{} + runConf := RunConf{ + Output: &output, + Benchmark: true, + BenchmarkMem: true, + BenchmarkTime: 10000000, + Filter: "Benchmark3", + } + Run(&helper, &runConf) + + expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark3\t *100\t *[12][0-9]{5} ns/op\t *[0-9]+ B/op\t *[1-9] allocs/op\n" + c.Assert(output.value, Matches, expected) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/bootstrap_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/bootstrap_test.go new file mode 100644 index 0000000..e55f327 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/bootstrap_test.go @@ -0,0 +1,82 @@ +// These initial tests are for bootstrapping. They verify that we can +// basically use the testing infrastructure itself to check if the test +// system is working. +// +// These tests use will break down the test runner badly in case of +// errors because if they simply fail, we can't be sure the developer +// will ever see anything (because failing means the failing system +// somehow isn't working! :-) +// +// Do not assume *any* internal functionality works as expected besides +// what's actually tested here. + +package check_test + +import ( + "fmt" + "gopkg.in/check.v1" + "strings" +) + +type BootstrapS struct{} + +var boostrapS = check.Suite(&BootstrapS{}) + +func (s *BootstrapS) TestCountSuite(c *check.C) { + suitesRun += 1 +} + +func (s *BootstrapS) TestFailedAndFail(c *check.C) { + if c.Failed() { + critical("c.Failed() must be false first!") + } + c.Fail() + if !c.Failed() { + critical("c.Fail() didn't put the test in a failed state!") + } + c.Succeed() +} + +func (s *BootstrapS) TestFailedAndSucceed(c *check.C) { + c.Fail() + c.Succeed() + if c.Failed() { + critical("c.Succeed() didn't put the test back in a non-failed state") + } +} + +func (s *BootstrapS) TestLogAndGetTestLog(c *check.C) { + c.Log("Hello there!") + log := c.GetTestLog() + if log != "Hello there!\n" { + critical(fmt.Sprintf("Log() or GetTestLog() is not working! Got: %#v", log)) + } +} + +func (s *BootstrapS) TestLogfAndGetTestLog(c *check.C) { + c.Logf("Hello %v", "there!") + log := c.GetTestLog() + if log != "Hello there!\n" { + critical(fmt.Sprintf("Logf() or GetTestLog() is not working! Got: %#v", log)) + } +} + +func (s *BootstrapS) TestRunShowsErrors(c *check.C) { + output := String{} + check.Run(&FailHelper{}, &check.RunConf{Output: &output}) + if strings.Index(output.value, "Expected failure!") == -1 { + critical(fmt.Sprintf("RunWithWriter() output did not contain the "+ + "expected failure! Got: %#v", + output.value)) + } +} + +func (s *BootstrapS) TestRunDoesntShowSuccesses(c *check.C) { + output := String{} + check.Run(&SuccessHelper{}, &check.RunConf{Output: &output}) + if strings.Index(output.value, "Expected success!") != -1 { + critical(fmt.Sprintf("RunWithWriter() output contained a successful "+ + "test! Got: %#v", + output.value)) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check.go new file mode 100644 index 0000000..ca8c0f9 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check.go @@ -0,0 +1,945 @@ +// Package check is a rich testing extension for Go's testing package. +// +// For details about the project, see: +// +// http://labix.org/gocheck +// +package check + +import ( + "bytes" + "errors" + "fmt" + "io" + "math/rand" + "os" + "path" + "path/filepath" + "reflect" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "time" +) + +// ----------------------------------------------------------------------- +// Internal type which deals with suite method calling. + +const ( + fixtureKd = iota + testKd +) + +type funcKind int + +const ( + succeededSt = iota + failedSt + skippedSt + panickedSt + fixturePanickedSt + missedSt +) + +type funcStatus int + +// A method value can't reach its own Method structure. +type methodType struct { + reflect.Value + Info reflect.Method +} + +func newMethod(receiver reflect.Value, i int) *methodType { + return &methodType{receiver.Method(i), receiver.Type().Method(i)} +} + +func (method *methodType) PC() uintptr { + return method.Info.Func.Pointer() +} + +func (method *methodType) suiteName() string { + t := method.Info.Type.In(0) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t.Name() +} + +func (method *methodType) String() string { + return method.suiteName() + "." + method.Info.Name +} + +func (method *methodType) matches(re *regexp.Regexp) bool { + return (re.MatchString(method.Info.Name) || + re.MatchString(method.suiteName()) || + re.MatchString(method.String())) +} + +type C struct { + method *methodType + kind funcKind + testName string + status funcStatus + logb *logger + logw io.Writer + done chan *C + reason string + mustFail bool + tempDir *tempDir + benchMem bool + startTime time.Time + timer +} + +func (c *C) stopNow() { + runtime.Goexit() +} + +// logger is a concurrency safe byte.Buffer +type logger struct { + sync.Mutex + writer bytes.Buffer +} + +func (l *logger) Write(buf []byte) (int, error) { + l.Lock() + defer l.Unlock() + return l.writer.Write(buf) +} + +func (l *logger) WriteTo(w io.Writer) (int64, error) { + l.Lock() + defer l.Unlock() + return l.writer.WriteTo(w) +} + +func (l *logger) String() string { + l.Lock() + defer l.Unlock() + return l.writer.String() +} + +// ----------------------------------------------------------------------- +// Handling of temporary files and directories. + +type tempDir struct { + sync.Mutex + path string + counter int +} + +func (td *tempDir) newPath() string { + td.Lock() + defer td.Unlock() + if td.path == "" { + var err error + for i := 0; i != 100; i++ { + path := fmt.Sprintf("%s%ccheck-%d", os.TempDir(), os.PathSeparator, rand.Int()) + if err = os.Mkdir(path, 0700); err == nil { + td.path = path + break + } + } + if td.path == "" { + panic("Couldn't create temporary directory: " + err.Error()) + } + } + result := filepath.Join(td.path, strconv.Itoa(td.counter)) + td.counter += 1 + return result +} + +func (td *tempDir) removeAll() { + td.Lock() + defer td.Unlock() + if td.path != "" { + err := os.RemoveAll(td.path) + if err != nil { + fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error()) + } + } +} + +// Create a new temporary directory which is automatically removed after +// the suite finishes running. +func (c *C) MkDir() string { + path := c.tempDir.newPath() + if err := os.Mkdir(path, 0700); err != nil { + panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error())) + } + return path +} + +// ----------------------------------------------------------------------- +// Low-level logging functions. + +func (c *C) log(args ...interface{}) { + c.writeLog([]byte(fmt.Sprint(args...) + "\n")) +} + +func (c *C) logf(format string, args ...interface{}) { + c.writeLog([]byte(fmt.Sprintf(format+"\n", args...))) +} + +func (c *C) logNewLine() { + c.writeLog([]byte{'\n'}) +} + +func (c *C) writeLog(buf []byte) { + c.logb.Write(buf) + if c.logw != nil { + c.logw.Write(buf) + } +} + +func hasStringOrError(x interface{}) (ok bool) { + _, ok = x.(fmt.Stringer) + if ok { + return + } + _, ok = x.(error) + return +} + +func (c *C) logValue(label string, value interface{}) { + if label == "" { + if hasStringOrError(value) { + c.logf("... %#v (%q)", value, value) + } else { + c.logf("... %#v", value) + } + } else if value == nil { + c.logf("... %s = nil", label) + } else { + if hasStringOrError(value) { + fv := fmt.Sprintf("%#v", value) + qv := fmt.Sprintf("%q", value) + if fv != qv { + c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv) + return + } + } + if s, ok := value.(string); ok && isMultiLine(s) { + c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value)) + c.logMultiLine(s) + } else { + c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value) + } + } +} + +func (c *C) logMultiLine(s string) { + b := make([]byte, 0, len(s)*2) + i := 0 + n := len(s) + for i < n { + j := i + 1 + for j < n && s[j-1] != '\n' { + j++ + } + b = append(b, "... "...) + b = strconv.AppendQuote(b, s[i:j]) + if j < n { + b = append(b, " +"...) + } + b = append(b, '\n') + i = j + } + c.writeLog(b) +} + +func isMultiLine(s string) bool { + for i := 0; i+1 < len(s); i++ { + if s[i] == '\n' { + return true + } + } + return false +} + +func (c *C) logString(issue string) { + c.log("... ", issue) +} + +func (c *C) logCaller(skip int) { + // This is a bit heavier than it ought to be. + skip += 1 // Our own frame. + pc, callerFile, callerLine, ok := runtime.Caller(skip) + if !ok { + return + } + var testFile string + var testLine int + testFunc := runtime.FuncForPC(c.method.PC()) + if runtime.FuncForPC(pc) != testFunc { + for { + skip += 1 + if pc, file, line, ok := runtime.Caller(skip); ok { + // Note that the test line may be different on + // distinct calls for the same test. Showing + // the "internal" line is helpful when debugging. + if runtime.FuncForPC(pc) == testFunc { + testFile, testLine = file, line + break + } + } else { + break + } + } + } + if testFile != "" && (testFile != callerFile || testLine != callerLine) { + c.logCode(testFile, testLine) + } + c.logCode(callerFile, callerLine) +} + +func (c *C) logCode(path string, line int) { + c.logf("%s:%d:", nicePath(path), line) + code, err := printLine(path, line) + if code == "" { + code = "..." // XXX Open the file and take the raw line. + if err != nil { + code += err.Error() + } + } + c.log(indent(code, " ")) +} + +var valueGo = filepath.Join("reflect", "value.go") +var asmGo = filepath.Join("runtime", "asm_") + +func (c *C) logPanic(skip int, value interface{}) { + skip++ // Our own frame. + initialSkip := skip + for ; ; skip++ { + if pc, file, line, ok := runtime.Caller(skip); ok { + if skip == initialSkip { + c.logf("... Panic: %s (PC=0x%X)\n", value, pc) + } + name := niceFuncName(pc) + path := nicePath(file) + if strings.Contains(path, "/gopkg.in/check.v") { + continue + } + if name == "Value.call" && strings.HasSuffix(path, valueGo) { + continue + } + if name == "call16" && strings.Contains(path, asmGo) { + continue + } + c.logf("%s:%d\n in %s", nicePath(file), line, name) + } else { + break + } + } +} + +func (c *C) logSoftPanic(issue string) { + c.log("... Panic: ", issue) +} + +func (c *C) logArgPanic(method *methodType, expectedType string) { + c.logf("... Panic: %s argument should be %s", + niceFuncName(method.PC()), expectedType) +} + +// ----------------------------------------------------------------------- +// Some simple formatting helpers. + +var initWD, initWDErr = os.Getwd() + +func init() { + if initWDErr == nil { + initWD = strings.Replace(initWD, "\\", "/", -1) + "/" + } +} + +func nicePath(path string) string { + if initWDErr == nil { + if strings.HasPrefix(path, initWD) { + return path[len(initWD):] + } + } + return path +} + +func niceFuncPath(pc uintptr) string { + function := runtime.FuncForPC(pc) + if function != nil { + filename, line := function.FileLine(pc) + return fmt.Sprintf("%s:%d", nicePath(filename), line) + } + return "" +} + +func niceFuncName(pc uintptr) string { + function := runtime.FuncForPC(pc) + if function != nil { + name := path.Base(function.Name()) + if i := strings.Index(name, "."); i > 0 { + name = name[i+1:] + } + if strings.HasPrefix(name, "(*") { + if i := strings.Index(name, ")"); i > 0 { + name = name[2:i] + name[i+1:] + } + } + if i := strings.LastIndex(name, ".*"); i != -1 { + name = name[:i] + "." + name[i+2:] + } + if i := strings.LastIndex(name, "·"); i != -1 { + name = name[:i] + "." + name[i+2:] + } + return name + } + return "" +} + +// ----------------------------------------------------------------------- +// Result tracker to aggregate call results. + +type Result struct { + Succeeded int + Failed int + Skipped int + Panicked int + FixturePanicked int + ExpectedFailures int + Missed int // Not even tried to run, related to a panic in the fixture. + RunError error // Houston, we've got a problem. + WorkDir string // If KeepWorkDir is true +} + +type resultTracker struct { + result Result + _lastWasProblem bool + _waiting int + _missed int + _expectChan chan *C + _doneChan chan *C + _stopChan chan bool +} + +func newResultTracker() *resultTracker { + return &resultTracker{_expectChan: make(chan *C), // Synchronous + _doneChan: make(chan *C, 32), // Asynchronous + _stopChan: make(chan bool)} // Synchronous +} + +func (tracker *resultTracker) start() { + go tracker._loopRoutine() +} + +func (tracker *resultTracker) waitAndStop() { + <-tracker._stopChan +} + +func (tracker *resultTracker) expectCall(c *C) { + tracker._expectChan <- c +} + +func (tracker *resultTracker) callDone(c *C) { + tracker._doneChan <- c +} + +func (tracker *resultTracker) _loopRoutine() { + for { + var c *C + if tracker._waiting > 0 { + // Calls still running. Can't stop. + select { + // XXX Reindent this (not now to make diff clear) + case c = <-tracker._expectChan: + tracker._waiting += 1 + case c = <-tracker._doneChan: + tracker._waiting -= 1 + switch c.status { + case succeededSt: + if c.kind == testKd { + if c.mustFail { + tracker.result.ExpectedFailures++ + } else { + tracker.result.Succeeded++ + } + } + case failedSt: + tracker.result.Failed++ + case panickedSt: + if c.kind == fixtureKd { + tracker.result.FixturePanicked++ + } else { + tracker.result.Panicked++ + } + case fixturePanickedSt: + // Track it as missed, since the panic + // was on the fixture, not on the test. + tracker.result.Missed++ + case missedSt: + tracker.result.Missed++ + case skippedSt: + if c.kind == testKd { + tracker.result.Skipped++ + } + } + } + } else { + // No calls. Can stop, but no done calls here. + select { + case tracker._stopChan <- true: + return + case c = <-tracker._expectChan: + tracker._waiting += 1 + case c = <-tracker._doneChan: + panic("Tracker got an unexpected done call.") + } + } + } +} + +// ----------------------------------------------------------------------- +// The underlying suite runner. + +type suiteRunner struct { + suite interface{} + setUpSuite, tearDownSuite *methodType + setUpTest, tearDownTest *methodType + tests []*methodType + tracker *resultTracker + tempDir *tempDir + keepDir bool + output *outputWriter + reportedProblemLast bool + benchTime time.Duration + benchMem bool +} + +type RunConf struct { + Output io.Writer + Stream bool + Verbose bool + Filter string + Benchmark bool + BenchmarkTime time.Duration // Defaults to 1 second + BenchmarkMem bool + KeepWorkDir bool +} + +// Create a new suiteRunner able to run all methods in the given suite. +func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner { + var conf RunConf + if runConf != nil { + conf = *runConf + } + if conf.Output == nil { + conf.Output = os.Stdout + } + if conf.Benchmark { + conf.Verbose = true + } + + suiteType := reflect.TypeOf(suite) + suiteNumMethods := suiteType.NumMethod() + suiteValue := reflect.ValueOf(suite) + + runner := &suiteRunner{ + suite: suite, + output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose), + tracker: newResultTracker(), + benchTime: conf.BenchmarkTime, + benchMem: conf.BenchmarkMem, + tempDir: &tempDir{}, + keepDir: conf.KeepWorkDir, + tests: make([]*methodType, 0, suiteNumMethods), + } + if runner.benchTime == 0 { + runner.benchTime = 1 * time.Second + } + + var filterRegexp *regexp.Regexp + if conf.Filter != "" { + if regexp, err := regexp.Compile(conf.Filter); err != nil { + msg := "Bad filter expression: " + err.Error() + runner.tracker.result.RunError = errors.New(msg) + return runner + } else { + filterRegexp = regexp + } + } + + for i := 0; i != suiteNumMethods; i++ { + method := newMethod(suiteValue, i) + switch method.Info.Name { + case "SetUpSuite": + runner.setUpSuite = method + case "TearDownSuite": + runner.tearDownSuite = method + case "SetUpTest": + runner.setUpTest = method + case "TearDownTest": + runner.tearDownTest = method + default: + prefix := "Test" + if conf.Benchmark { + prefix = "Benchmark" + } + if !strings.HasPrefix(method.Info.Name, prefix) { + continue + } + if filterRegexp == nil || method.matches(filterRegexp) { + runner.tests = append(runner.tests, method) + } + } + } + return runner +} + +// Run all methods in the given suite. +func (runner *suiteRunner) run() *Result { + if runner.tracker.result.RunError == nil && len(runner.tests) > 0 { + runner.tracker.start() + if runner.checkFixtureArgs() { + c := runner.runFixture(runner.setUpSuite, "", nil) + if c == nil || c.status == succeededSt { + for i := 0; i != len(runner.tests); i++ { + c := runner.runTest(runner.tests[i]) + if c.status == fixturePanickedSt { + runner.skipTests(missedSt, runner.tests[i+1:]) + break + } + } + } else if c != nil && c.status == skippedSt { + runner.skipTests(skippedSt, runner.tests) + } else { + runner.skipTests(missedSt, runner.tests) + } + runner.runFixture(runner.tearDownSuite, "", nil) + } else { + runner.skipTests(missedSt, runner.tests) + } + runner.tracker.waitAndStop() + if runner.keepDir { + runner.tracker.result.WorkDir = runner.tempDir.path + } else { + runner.tempDir.removeAll() + } + } + return &runner.tracker.result +} + +// Create a call object with the given suite method, and fork a +// goroutine with the provided dispatcher for running it. +func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { + var logw io.Writer + if runner.output.Stream { + logw = runner.output + } + if logb == nil { + logb = new(logger) + } + c := &C{ + method: method, + kind: kind, + testName: testName, + logb: logb, + logw: logw, + tempDir: runner.tempDir, + done: make(chan *C, 1), + timer: timer{benchTime: runner.benchTime}, + startTime: time.Now(), + benchMem: runner.benchMem, + } + runner.tracker.expectCall(c) + go (func() { + runner.reportCallStarted(c) + defer runner.callDone(c) + dispatcher(c) + })() + return c +} + +// Same as forkCall(), but wait for call to finish before returning. +func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { + c := runner.forkCall(method, kind, testName, logb, dispatcher) + <-c.done + return c +} + +// Handle a finished call. If there were any panics, update the call status +// accordingly. Then, mark the call as done and report to the tracker. +func (runner *suiteRunner) callDone(c *C) { + value := recover() + if value != nil { + switch v := value.(type) { + case *fixturePanic: + if v.status == skippedSt { + c.status = skippedSt + } else { + c.logSoftPanic("Fixture has panicked (see related PANIC)") + c.status = fixturePanickedSt + } + default: + c.logPanic(1, value) + c.status = panickedSt + } + } + if c.mustFail { + switch c.status { + case failedSt: + c.status = succeededSt + case succeededSt: + c.status = failedSt + c.logString("Error: Test succeeded, but was expected to fail") + c.logString("Reason: " + c.reason) + } + } + + runner.reportCallDone(c) + c.done <- c +} + +// Runs a fixture call synchronously. The fixture will still be run in a +// goroutine like all suite methods, but this method will not return +// while the fixture goroutine is not done, because the fixture must be +// run in a desired order. +func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger) *C { + if method != nil { + c := runner.runFunc(method, fixtureKd, testName, logb, func(c *C) { + c.ResetTimer() + c.StartTimer() + defer c.StopTimer() + c.method.Call([]reflect.Value{reflect.ValueOf(c)}) + }) + return c + } + return nil +} + +// Run the fixture method with runFixture(), but panic with a fixturePanic{} +// in case the fixture method panics. This makes it easier to track the +// fixture panic together with other call panics within forkTest(). +func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool) *C { + if skipped != nil && *skipped { + return nil + } + c := runner.runFixture(method, testName, logb) + if c != nil && c.status != succeededSt { + if skipped != nil { + *skipped = c.status == skippedSt + } + panic(&fixturePanic{c.status, method}) + } + return c +} + +type fixturePanic struct { + status funcStatus + method *methodType +} + +// Run the suite test method, together with the test-specific fixture, +// asynchronously. +func (runner *suiteRunner) forkTest(method *methodType) *C { + testName := method.String() + return runner.forkCall(method, testKd, testName, nil, func(c *C) { + var skipped bool + defer runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped) + defer c.StopTimer() + benchN := 1 + for { + runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped) + mt := c.method.Type() + if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) { + // Rather than a plain panic, provide a more helpful message when + // the argument type is incorrect. + c.status = panickedSt + c.logArgPanic(c.method, "*check.C") + return + } + if strings.HasPrefix(c.method.Info.Name, "Test") { + c.ResetTimer() + c.StartTimer() + c.method.Call([]reflect.Value{reflect.ValueOf(c)}) + return + } + if !strings.HasPrefix(c.method.Info.Name, "Benchmark") { + panic("unexpected method prefix: " + c.method.Info.Name) + } + + runtime.GC() + c.N = benchN + c.ResetTimer() + c.StartTimer() + c.method.Call([]reflect.Value{reflect.ValueOf(c)}) + c.StopTimer() + if c.status != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 { + return + } + perOpN := int(1e9) + if c.nsPerOp() != 0 { + perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp()) + } + + // Logic taken from the stock testing package: + // - Run more iterations than we think we'll need for a second (1.5x). + // - Don't grow too fast in case we had timing errors previously. + // - Be sure to run at least one more than last time. + benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1) + benchN = roundUp(benchN) + + skipped = true // Don't run the deferred one if this panics. + runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil) + skipped = false + } + }) +} + +// Same as forkTest(), but wait for the test to finish before returning. +func (runner *suiteRunner) runTest(method *methodType) *C { + c := runner.forkTest(method) + <-c.done + return c +} + +// Helper to mark tests as skipped or missed. A bit heavy for what +// it does, but it enables homogeneous handling of tracking, including +// nice verbose output. +func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) { + for _, method := range methods { + runner.runFunc(method, testKd, "", nil, func(c *C) { + c.status = status + }) + } +} + +// Verify if the fixture arguments are *check.C. In case of errors, +// log the error as a panic in the fixture method call, and return false. +func (runner *suiteRunner) checkFixtureArgs() bool { + succeeded := true + argType := reflect.TypeOf(&C{}) + for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest} { + if method != nil { + mt := method.Type() + if mt.NumIn() != 1 || mt.In(0) != argType { + succeeded = false + runner.runFunc(method, fixtureKd, "", nil, func(c *C) { + c.logArgPanic(method, "*check.C") + c.status = panickedSt + }) + } + } + } + return succeeded +} + +func (runner *suiteRunner) reportCallStarted(c *C) { + runner.output.WriteCallStarted("START", c) +} + +func (runner *suiteRunner) reportCallDone(c *C) { + runner.tracker.callDone(c) + switch c.status { + case succeededSt: + if c.mustFail { + runner.output.WriteCallSuccess("FAIL EXPECTED", c) + } else { + runner.output.WriteCallSuccess("PASS", c) + } + case skippedSt: + runner.output.WriteCallSuccess("SKIP", c) + case failedSt: + runner.output.WriteCallProblem("FAIL", c) + case panickedSt: + runner.output.WriteCallProblem("PANIC", c) + case fixturePanickedSt: + // That's a testKd call reporting that its fixture + // has panicked. The fixture call which caused the + // panic itself was tracked above. We'll report to + // aid debugging. + runner.output.WriteCallProblem("PANIC", c) + case missedSt: + runner.output.WriteCallSuccess("MISS", c) + } +} + +// ----------------------------------------------------------------------- +// Output writer manages atomic output writing according to settings. + +type outputWriter struct { + m sync.Mutex + writer io.Writer + wroteCallProblemLast bool + Stream bool + Verbose bool +} + +func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter { + return &outputWriter{writer: writer, Stream: stream, Verbose: verbose} +} + +func (ow *outputWriter) Write(content []byte) (n int, err error) { + ow.m.Lock() + n, err = ow.writer.Write(content) + ow.m.Unlock() + return +} + +func (ow *outputWriter) WriteCallStarted(label string, c *C) { + if ow.Stream { + header := renderCallHeader(label, c, "", "\n") + ow.m.Lock() + ow.writer.Write([]byte(header)) + ow.m.Unlock() + } +} + +func (ow *outputWriter) WriteCallProblem(label string, c *C) { + var prefix string + if !ow.Stream { + prefix = "\n-----------------------------------" + + "-----------------------------------\n" + } + header := renderCallHeader(label, c, prefix, "\n\n") + ow.m.Lock() + ow.wroteCallProblemLast = true + ow.writer.Write([]byte(header)) + if !ow.Stream { + c.logb.WriteTo(ow.writer) + } + ow.m.Unlock() +} + +func (ow *outputWriter) WriteCallSuccess(label string, c *C) { + if ow.Stream || (ow.Verbose && c.kind == testKd) { + // TODO Use a buffer here. + var suffix string + if c.reason != "" { + suffix = " (" + c.reason + ")" + } + if c.status == succeededSt { + suffix += "\t" + c.timerString() + } + suffix += "\n" + if ow.Stream { + suffix += "\n" + } + header := renderCallHeader(label, c, "", suffix) + ow.m.Lock() + // Resist temptation of using line as prefix above due to race. + if !ow.Stream && ow.wroteCallProblemLast { + header = "\n-----------------------------------" + + "-----------------------------------\n" + + header + } + ow.wroteCallProblemLast = false + ow.writer.Write([]byte(header)) + ow.m.Unlock() + } +} + +func renderCallHeader(label string, c *C, prefix, suffix string) string { + pc := c.method.PC() + return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc), + niceFuncName(pc), suffix) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check_test.go new file mode 100644 index 0000000..871b325 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/check_test.go @@ -0,0 +1,207 @@ +// This file contains just a few generic helpers which are used by the +// other test files. + +package check_test + +import ( + "flag" + "fmt" + "os" + "regexp" + "runtime" + "testing" + "time" + + "gopkg.in/check.v1" +) + +// We count the number of suites run at least to get a vague hint that the +// test suite is behaving as it should. Otherwise a bug introduced at the +// very core of the system could go unperceived. +const suitesRunExpected = 8 + +var suitesRun int = 0 + +func Test(t *testing.T) { + check.TestingT(t) + if suitesRun != suitesRunExpected && flag.Lookup("check.f").Value.String() == "" { + critical(fmt.Sprintf("Expected %d suites to run rather than %d", + suitesRunExpected, suitesRun)) + } +} + +// ----------------------------------------------------------------------- +// Helper functions. + +// Break down badly. This is used in test cases which can't yet assume +// that the fundamental bits are working. +func critical(error string) { + fmt.Fprintln(os.Stderr, "CRITICAL: "+error) + os.Exit(1) +} + +// Return the file line where it's called. +func getMyLine() int { + if _, _, line, ok := runtime.Caller(1); ok { + return line + } + return -1 +} + +// ----------------------------------------------------------------------- +// Helper type implementing a basic io.Writer for testing output. + +// Type implementing the io.Writer interface for analyzing output. +type String struct { + value string +} + +// The only function required by the io.Writer interface. Will append +// written data to the String.value string. +func (s *String) Write(p []byte) (n int, err error) { + s.value += string(p) + return len(p), nil +} + +// Trivial wrapper to test errors happening on a different file +// than the test itself. +func checkEqualWrapper(c *check.C, obtained, expected interface{}) (result bool, line int) { + return c.Check(obtained, check.Equals, expected), getMyLine() +} + +// ----------------------------------------------------------------------- +// Helper suite for testing basic fail behavior. + +type FailHelper struct { + testLine int +} + +func (s *FailHelper) TestLogAndFail(c *check.C) { + s.testLine = getMyLine() - 1 + c.Log("Expected failure!") + c.Fail() +} + +// ----------------------------------------------------------------------- +// Helper suite for testing basic success behavior. + +type SuccessHelper struct{} + +func (s *SuccessHelper) TestLogAndSucceed(c *check.C) { + c.Log("Expected success!") +} + +// ----------------------------------------------------------------------- +// Helper suite for testing ordering and behavior of fixture. + +type FixtureHelper struct { + calls []string + panicOn string + skip bool + skipOnN int + sleepOn string + sleep time.Duration + bytes int64 +} + +func (s *FixtureHelper) trace(name string, c *check.C) { + s.calls = append(s.calls, name) + if name == s.panicOn { + panic(name) + } + if s.sleep > 0 && s.sleepOn == name { + time.Sleep(s.sleep) + } + if s.skip && s.skipOnN == len(s.calls)-1 { + c.Skip("skipOnN == n") + } +} + +func (s *FixtureHelper) SetUpSuite(c *check.C) { + s.trace("SetUpSuite", c) +} + +func (s *FixtureHelper) TearDownSuite(c *check.C) { + s.trace("TearDownSuite", c) +} + +func (s *FixtureHelper) SetUpTest(c *check.C) { + s.trace("SetUpTest", c) +} + +func (s *FixtureHelper) TearDownTest(c *check.C) { + s.trace("TearDownTest", c) +} + +func (s *FixtureHelper) Test1(c *check.C) { + s.trace("Test1", c) +} + +func (s *FixtureHelper) Test2(c *check.C) { + s.trace("Test2", c) +} + +func (s *FixtureHelper) Benchmark1(c *check.C) { + s.trace("Benchmark1", c) + for i := 0; i < c.N; i++ { + time.Sleep(s.sleep) + } +} + +func (s *FixtureHelper) Benchmark2(c *check.C) { + s.trace("Benchmark2", c) + c.SetBytes(1024) + for i := 0; i < c.N; i++ { + time.Sleep(s.sleep) + } +} + +func (s *FixtureHelper) Benchmark3(c *check.C) { + var x []int64 + s.trace("Benchmark3", c) + for i := 0; i < c.N; i++ { + time.Sleep(s.sleep) + x = make([]int64, 5) + _ = x + } +} + +// ----------------------------------------------------------------------- +// Helper which checks the state of the test and ensures that it matches +// the given expectations. Depends on c.Errorf() working, so shouldn't +// be used to test this one function. + +type expectedState struct { + name string + result interface{} + failed bool + log string +} + +// Verify the state of the test. Note that since this also verifies if +// the test is supposed to be in a failed state, no other checks should +// be done in addition to what is being tested. +func checkState(c *check.C, result interface{}, expected *expectedState) { + failed := c.Failed() + c.Succeed() + log := c.GetTestLog() + matched, matchError := regexp.MatchString("^"+expected.log+"$", log) + if matchError != nil { + c.Errorf("Error in matching expression used in testing %s", + expected.name) + } else if !matched { + c.Errorf("%s logged:\n----------\n%s----------\n\nExpected:\n----------\n%s\n----------", + expected.name, log, expected.log) + } + if result != expected.result { + c.Errorf("%s returned %#v rather than %#v", + expected.name, result, expected.result) + } + if failed != expected.failed { + if failed { + c.Errorf("%s has failed when it shouldn't", expected.name) + } else { + c.Errorf("%s has not failed when it should", expected.name) + } + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers.go new file mode 100644 index 0000000..bac3387 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers.go @@ -0,0 +1,458 @@ +package check + +import ( + "fmt" + "reflect" + "regexp" +) + +// ----------------------------------------------------------------------- +// CommentInterface and Commentf helper, to attach extra information to checks. + +type comment struct { + format string + args []interface{} +} + +// Commentf returns an infomational value to use with Assert or Check calls. +// If the checker test fails, the provided arguments will be passed to +// fmt.Sprintf, and will be presented next to the logged failure. +// +// For example: +// +// c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i)) +// +// Note that if the comment is constant, a better option is to +// simply use a normal comment right above or next to the line, as +// it will also get printed with any errors: +// +// c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123) +// +func Commentf(format string, args ...interface{}) CommentInterface { + return &comment{format, args} +} + +// CommentInterface must be implemented by types that attach extra +// information to failed checks. See the Commentf function for details. +type CommentInterface interface { + CheckCommentString() string +} + +func (c *comment) CheckCommentString() string { + return fmt.Sprintf(c.format, c.args...) +} + +// ----------------------------------------------------------------------- +// The Checker interface. + +// The Checker interface must be provided by checkers used with +// the Assert and Check verification methods. +type Checker interface { + Info() *CheckerInfo + Check(params []interface{}, names []string) (result bool, error string) +} + +// See the Checker interface. +type CheckerInfo struct { + Name string + Params []string +} + +func (info *CheckerInfo) Info() *CheckerInfo { + return info +} + +// ----------------------------------------------------------------------- +// Not checker logic inverter. + +// The Not checker inverts the logic of the provided checker. The +// resulting checker will succeed where the original one failed, and +// vice-versa. +// +// For example: +// +// c.Assert(a, Not(Equals), b) +// +func Not(checker Checker) Checker { + return ¬Checker{checker} +} + +type notChecker struct { + sub Checker +} + +func (checker *notChecker) Info() *CheckerInfo { + info := *checker.sub.Info() + info.Name = "Not(" + info.Name + ")" + return &info +} + +func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) { + result, error = checker.sub.Check(params, names) + result = !result + return +} + +// ----------------------------------------------------------------------- +// IsNil checker. + +type isNilChecker struct { + *CheckerInfo +} + +// The IsNil checker tests whether the obtained value is nil. +// +// For example: +// +// c.Assert(err, IsNil) +// +var IsNil Checker = &isNilChecker{ + &CheckerInfo{Name: "IsNil", Params: []string{"value"}}, +} + +func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) { + return isNil(params[0]), "" +} + +func isNil(obtained interface{}) (result bool) { + if obtained == nil { + result = true + } else { + switch v := reflect.ValueOf(obtained); v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + } + } + return +} + +// ----------------------------------------------------------------------- +// NotNil checker. Alias for Not(IsNil), since it's so common. + +type notNilChecker struct { + *CheckerInfo +} + +// The NotNil checker verifies that the obtained value is not nil. +// +// For example: +// +// c.Assert(iface, NotNil) +// +// This is an alias for Not(IsNil), made available since it's a +// fairly common check. +// +var NotNil Checker = ¬NilChecker{ + &CheckerInfo{Name: "NotNil", Params: []string{"value"}}, +} + +func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) { + return !isNil(params[0]), "" +} + +// ----------------------------------------------------------------------- +// Equals checker. + +type equalsChecker struct { + *CheckerInfo +} + +// The Equals checker verifies that the obtained value is equal to +// the expected value, according to usual Go semantics for ==. +// +// For example: +// +// c.Assert(value, Equals, 42) +// +var Equals Checker = &equalsChecker{ + &CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}}, +} + +func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) { + defer func() { + if v := recover(); v != nil { + result = false + error = fmt.Sprint(v) + } + }() + return params[0] == params[1], "" +} + +// ----------------------------------------------------------------------- +// DeepEquals checker. + +type deepEqualsChecker struct { + *CheckerInfo +} + +// The DeepEquals checker verifies that the obtained value is deep-equal to +// the expected value. The check will work correctly even when facing +// slices, interfaces, and values of different types (which always fail +// the test). +// +// For example: +// +// c.Assert(value, DeepEquals, 42) +// c.Assert(array, DeepEquals, []string{"hi", "there"}) +// +var DeepEquals Checker = &deepEqualsChecker{ + &CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, +} + +func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { + return reflect.DeepEqual(params[0], params[1]), "" +} + +// ----------------------------------------------------------------------- +// HasLen checker. + +type hasLenChecker struct { + *CheckerInfo +} + +// The HasLen checker verifies that the obtained value has the +// provided length. In many cases this is superior to using Equals +// in conjuction with the len function because in case the check +// fails the value itself will be printed, instead of its length, +// providing more details for figuring the problem. +// +// For example: +// +// c.Assert(list, HasLen, 5) +// +var HasLen Checker = &hasLenChecker{ + &CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}}, +} + +func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) { + n, ok := params[1].(int) + if !ok { + return false, "n must be an int" + } + value := reflect.ValueOf(params[0]) + switch value.Kind() { + case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String: + default: + return false, "obtained value type has no length" + } + return value.Len() == n, "" +} + +// ----------------------------------------------------------------------- +// ErrorMatches checker. + +type errorMatchesChecker struct { + *CheckerInfo +} + +// The ErrorMatches checker verifies that the error value +// is non nil and matches the regular expression provided. +// +// For example: +// +// c.Assert(err, ErrorMatches, "perm.*denied") +// +var ErrorMatches Checker = errorMatchesChecker{ + &CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}}, +} + +func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) { + if params[0] == nil { + return false, "Error value is nil" + } + err, ok := params[0].(error) + if !ok { + return false, "Value is not an error" + } + params[0] = err.Error() + names[0] = "error" + return matches(params[0], params[1]) +} + +// ----------------------------------------------------------------------- +// Matches checker. + +type matchesChecker struct { + *CheckerInfo +} + +// The Matches checker verifies that the string provided as the obtained +// value (or the string resulting from obtained.String()) matches the +// regular expression provided. +// +// For example: +// +// c.Assert(err, Matches, "perm.*denied") +// +var Matches Checker = &matchesChecker{ + &CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}}, +} + +func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) { + return matches(params[0], params[1]) +} + +func matches(value, regex interface{}) (result bool, error string) { + reStr, ok := regex.(string) + if !ok { + return false, "Regex must be a string" + } + valueStr, valueIsStr := value.(string) + if !valueIsStr { + if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr { + valueStr, valueIsStr = valueWithStr.String(), true + } + } + if valueIsStr { + matches, err := regexp.MatchString("^"+reStr+"$", valueStr) + if err != nil { + return false, "Can't compile regex: " + err.Error() + } + return matches, "" + } + return false, "Obtained value is not a string and has no .String()" +} + +// ----------------------------------------------------------------------- +// Panics checker. + +type panicsChecker struct { + *CheckerInfo +} + +// The Panics checker verifies that calling the provided zero-argument +// function will cause a panic which is deep-equal to the provided value. +// +// For example: +// +// c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}). +// +// +var Panics Checker = &panicsChecker{ + &CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}}, +} + +func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) { + f := reflect.ValueOf(params[0]) + if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { + return false, "Function must take zero arguments" + } + defer func() { + // If the function has not panicked, then don't do the check. + if error != "" { + return + } + params[0] = recover() + names[0] = "panic" + result = reflect.DeepEqual(params[0], params[1]) + }() + f.Call(nil) + return false, "Function has not panicked" +} + +type panicMatchesChecker struct { + *CheckerInfo +} + +// The PanicMatches checker verifies that calling the provided zero-argument +// function will cause a panic with an error value matching +// the regular expression provided. +// +// For example: +// +// c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`). +// +// +var PanicMatches Checker = &panicMatchesChecker{ + &CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}}, +} + +func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) { + f := reflect.ValueOf(params[0]) + if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { + return false, "Function must take zero arguments" + } + defer func() { + // If the function has not panicked, then don't do the check. + if errmsg != "" { + return + } + obtained := recover() + names[0] = "panic" + if e, ok := obtained.(error); ok { + params[0] = e.Error() + } else if _, ok := obtained.(string); ok { + params[0] = obtained + } else { + errmsg = "Panic value is not a string or an error" + return + } + result, errmsg = matches(params[0], params[1]) + }() + f.Call(nil) + return false, "Function has not panicked" +} + +// ----------------------------------------------------------------------- +// FitsTypeOf checker. + +type fitsTypeChecker struct { + *CheckerInfo +} + +// The FitsTypeOf checker verifies that the obtained value is +// assignable to a variable with the same type as the provided +// sample value. +// +// For example: +// +// c.Assert(value, FitsTypeOf, int64(0)) +// c.Assert(value, FitsTypeOf, os.Error(nil)) +// +var FitsTypeOf Checker = &fitsTypeChecker{ + &CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}}, +} + +func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) { + obtained := reflect.ValueOf(params[0]) + sample := reflect.ValueOf(params[1]) + if !obtained.IsValid() { + return false, "" + } + if !sample.IsValid() { + return false, "Invalid sample value" + } + return obtained.Type().AssignableTo(sample.Type()), "" +} + +// ----------------------------------------------------------------------- +// Implements checker. + +type implementsChecker struct { + *CheckerInfo +} + +// The Implements checker verifies that the obtained value +// implements the interface specified via a pointer to an interface +// variable. +// +// For example: +// +// var e os.Error +// c.Assert(err, Implements, &e) +// +var Implements Checker = &implementsChecker{ + &CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}}, +} + +func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) { + obtained := reflect.ValueOf(params[0]) + ifaceptr := reflect.ValueOf(params[1]) + if !obtained.IsValid() { + return false, "" + } + if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface { + return false, "ifaceptr should be a pointer to an interface variable" + } + return obtained.Type().Implements(ifaceptr.Elem().Type()), "" +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers_test.go new file mode 100644 index 0000000..5c69747 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/checkers_test.go @@ -0,0 +1,272 @@ +package check_test + +import ( + "errors" + "gopkg.in/check.v1" + "reflect" + "runtime" +) + +type CheckersS struct{} + +var _ = check.Suite(&CheckersS{}) + +func testInfo(c *check.C, checker check.Checker, name string, paramNames []string) { + info := checker.Info() + if info.Name != name { + c.Fatalf("Got name %s, expected %s", info.Name, name) + } + if !reflect.DeepEqual(info.Params, paramNames) { + c.Fatalf("Got param names %#v, expected %#v", info.Params, paramNames) + } +} + +func testCheck(c *check.C, checker check.Checker, result bool, error string, params ...interface{}) ([]interface{}, []string) { + info := checker.Info() + if len(params) != len(info.Params) { + c.Fatalf("unexpected param count in test; expected %d got %d", len(info.Params), len(params)) + } + names := append([]string{}, info.Params...) + result_, error_ := checker.Check(params, names) + if result_ != result || error_ != error { + c.Fatalf("%s.Check(%#v) returned (%#v, %#v) rather than (%#v, %#v)", + info.Name, params, result_, error_, result, error) + } + return params, names +} + +func (s *CheckersS) TestComment(c *check.C) { + bug := check.Commentf("a %d bc", 42) + comment := bug.CheckCommentString() + if comment != "a 42 bc" { + c.Fatalf("Commentf returned %#v", comment) + } +} + +func (s *CheckersS) TestIsNil(c *check.C) { + testInfo(c, check.IsNil, "IsNil", []string{"value"}) + + testCheck(c, check.IsNil, true, "", nil) + testCheck(c, check.IsNil, false, "", "a") + + testCheck(c, check.IsNil, true, "", (chan int)(nil)) + testCheck(c, check.IsNil, false, "", make(chan int)) + testCheck(c, check.IsNil, true, "", (error)(nil)) + testCheck(c, check.IsNil, false, "", errors.New("")) + testCheck(c, check.IsNil, true, "", ([]int)(nil)) + testCheck(c, check.IsNil, false, "", make([]int, 1)) + testCheck(c, check.IsNil, false, "", int(0)) +} + +func (s *CheckersS) TestNotNil(c *check.C) { + testInfo(c, check.NotNil, "NotNil", []string{"value"}) + + testCheck(c, check.NotNil, false, "", nil) + testCheck(c, check.NotNil, true, "", "a") + + testCheck(c, check.NotNil, false, "", (chan int)(nil)) + testCheck(c, check.NotNil, true, "", make(chan int)) + testCheck(c, check.NotNil, false, "", (error)(nil)) + testCheck(c, check.NotNil, true, "", errors.New("")) + testCheck(c, check.NotNil, false, "", ([]int)(nil)) + testCheck(c, check.NotNil, true, "", make([]int, 1)) +} + +func (s *CheckersS) TestNot(c *check.C) { + testInfo(c, check.Not(check.IsNil), "Not(IsNil)", []string{"value"}) + + testCheck(c, check.Not(check.IsNil), false, "", nil) + testCheck(c, check.Not(check.IsNil), true, "", "a") +} + +type simpleStruct struct { + i int +} + +func (s *CheckersS) TestEquals(c *check.C) { + testInfo(c, check.Equals, "Equals", []string{"obtained", "expected"}) + + // The simplest. + testCheck(c, check.Equals, true, "", 42, 42) + testCheck(c, check.Equals, false, "", 42, 43) + + // Different native types. + testCheck(c, check.Equals, false, "", int32(42), int64(42)) + + // With nil. + testCheck(c, check.Equals, false, "", 42, nil) + + // Slices + testCheck(c, check.Equals, false, "runtime error: comparing uncomparable type []uint8", []byte{1, 2}, []byte{1, 2}) + + // Struct values + testCheck(c, check.Equals, true, "", simpleStruct{1}, simpleStruct{1}) + testCheck(c, check.Equals, false, "", simpleStruct{1}, simpleStruct{2}) + + // Struct pointers + testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{1}) + testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{2}) +} + +func (s *CheckersS) TestDeepEquals(c *check.C) { + testInfo(c, check.DeepEquals, "DeepEquals", []string{"obtained", "expected"}) + + // The simplest. + testCheck(c, check.DeepEquals, true, "", 42, 42) + testCheck(c, check.DeepEquals, false, "", 42, 43) + + // Different native types. + testCheck(c, check.DeepEquals, false, "", int32(42), int64(42)) + + // With nil. + testCheck(c, check.DeepEquals, false, "", 42, nil) + + // Slices + testCheck(c, check.DeepEquals, true, "", []byte{1, 2}, []byte{1, 2}) + testCheck(c, check.DeepEquals, false, "", []byte{1, 2}, []byte{1, 3}) + + // Struct values + testCheck(c, check.DeepEquals, true, "", simpleStruct{1}, simpleStruct{1}) + testCheck(c, check.DeepEquals, false, "", simpleStruct{1}, simpleStruct{2}) + + // Struct pointers + testCheck(c, check.DeepEquals, true, "", &simpleStruct{1}, &simpleStruct{1}) + testCheck(c, check.DeepEquals, false, "", &simpleStruct{1}, &simpleStruct{2}) +} + +func (s *CheckersS) TestHasLen(c *check.C) { + testInfo(c, check.HasLen, "HasLen", []string{"obtained", "n"}) + + testCheck(c, check.HasLen, true, "", "abcd", 4) + testCheck(c, check.HasLen, true, "", []int{1, 2}, 2) + testCheck(c, check.HasLen, false, "", []int{1, 2}, 3) + + testCheck(c, check.HasLen, false, "n must be an int", []int{1, 2}, "2") + testCheck(c, check.HasLen, false, "obtained value type has no length", nil, 2) +} + +func (s *CheckersS) TestErrorMatches(c *check.C) { + testInfo(c, check.ErrorMatches, "ErrorMatches", []string{"value", "regex"}) + + testCheck(c, check.ErrorMatches, false, "Error value is nil", nil, "some error") + testCheck(c, check.ErrorMatches, false, "Value is not an error", 1, "some error") + testCheck(c, check.ErrorMatches, true, "", errors.New("some error"), "some error") + testCheck(c, check.ErrorMatches, true, "", errors.New("some error"), "so.*or") + + // Verify params mutation + params, names := testCheck(c, check.ErrorMatches, false, "", errors.New("some error"), "other error") + c.Assert(params[0], check.Equals, "some error") + c.Assert(names[0], check.Equals, "error") +} + +func (s *CheckersS) TestMatches(c *check.C) { + testInfo(c, check.Matches, "Matches", []string{"value", "regex"}) + + // Simple matching + testCheck(c, check.Matches, true, "", "abc", "abc") + testCheck(c, check.Matches, true, "", "abc", "a.c") + + // Must match fully + testCheck(c, check.Matches, false, "", "abc", "ab") + testCheck(c, check.Matches, false, "", "abc", "bc") + + // String()-enabled values accepted + testCheck(c, check.Matches, true, "", reflect.ValueOf("abc"), "a.c") + testCheck(c, check.Matches, false, "", reflect.ValueOf("abc"), "a.d") + + // Some error conditions. + testCheck(c, check.Matches, false, "Obtained value is not a string and has no .String()", 1, "a.c") + testCheck(c, check.Matches, false, "Can't compile regex: error parsing regexp: missing closing ]: `[c$`", "abc", "a[c") +} + +func (s *CheckersS) TestPanics(c *check.C) { + testInfo(c, check.Panics, "Panics", []string{"function", "expected"}) + + // Some errors. + testCheck(c, check.Panics, false, "Function has not panicked", func() bool { return false }, "BOOM") + testCheck(c, check.Panics, false, "Function must take zero arguments", 1, "BOOM") + + // Plain strings. + testCheck(c, check.Panics, true, "", func() { panic("BOOM") }, "BOOM") + testCheck(c, check.Panics, false, "", func() { panic("KABOOM") }, "BOOM") + testCheck(c, check.Panics, true, "", func() bool { panic("BOOM") }, "BOOM") + + // Error values. + testCheck(c, check.Panics, true, "", func() { panic(errors.New("BOOM")) }, errors.New("BOOM")) + testCheck(c, check.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM")) + + type deep struct{ i int } + // Deep value + testCheck(c, check.Panics, true, "", func() { panic(&deep{99}) }, &deep{99}) + + // Verify params/names mutation + params, names := testCheck(c, check.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM")) + c.Assert(params[0], check.ErrorMatches, "KABOOM") + c.Assert(names[0], check.Equals, "panic") + + // Verify a nil panic + testCheck(c, check.Panics, true, "", func() { panic(nil) }, nil) + testCheck(c, check.Panics, false, "", func() { panic(nil) }, "NOPE") +} + +func (s *CheckersS) TestPanicMatches(c *check.C) { + testInfo(c, check.PanicMatches, "PanicMatches", []string{"function", "expected"}) + + // Error matching. + testCheck(c, check.PanicMatches, true, "", func() { panic(errors.New("BOOM")) }, "BO.M") + testCheck(c, check.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BO.M") + + // Some errors. + testCheck(c, check.PanicMatches, false, "Function has not panicked", func() bool { return false }, "BOOM") + testCheck(c, check.PanicMatches, false, "Function must take zero arguments", 1, "BOOM") + + // Plain strings. + testCheck(c, check.PanicMatches, true, "", func() { panic("BOOM") }, "BO.M") + testCheck(c, check.PanicMatches, false, "", func() { panic("KABOOM") }, "BOOM") + testCheck(c, check.PanicMatches, true, "", func() bool { panic("BOOM") }, "BO.M") + + // Verify params/names mutation + params, names := testCheck(c, check.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BOOM") + c.Assert(params[0], check.Equals, "KABOOM") + c.Assert(names[0], check.Equals, "panic") + + // Verify a nil panic + testCheck(c, check.PanicMatches, false, "Panic value is not a string or an error", func() { panic(nil) }, "") +} + +func (s *CheckersS) TestFitsTypeOf(c *check.C) { + testInfo(c, check.FitsTypeOf, "FitsTypeOf", []string{"obtained", "sample"}) + + // Basic types + testCheck(c, check.FitsTypeOf, true, "", 1, 0) + testCheck(c, check.FitsTypeOf, false, "", 1, int64(0)) + + // Aliases + testCheck(c, check.FitsTypeOf, false, "", 1, errors.New("")) + testCheck(c, check.FitsTypeOf, false, "", "error", errors.New("")) + testCheck(c, check.FitsTypeOf, true, "", errors.New("error"), errors.New("")) + + // Structures + testCheck(c, check.FitsTypeOf, false, "", 1, simpleStruct{}) + testCheck(c, check.FitsTypeOf, false, "", simpleStruct{42}, &simpleStruct{}) + testCheck(c, check.FitsTypeOf, true, "", simpleStruct{42}, simpleStruct{}) + testCheck(c, check.FitsTypeOf, true, "", &simpleStruct{42}, &simpleStruct{}) + + // Some bad values + testCheck(c, check.FitsTypeOf, false, "Invalid sample value", 1, interface{}(nil)) + testCheck(c, check.FitsTypeOf, false, "", interface{}(nil), 0) +} + +func (s *CheckersS) TestImplements(c *check.C) { + testInfo(c, check.Implements, "Implements", []string{"obtained", "ifaceptr"}) + + var e error + var re runtime.Error + testCheck(c, check.Implements, true, "", errors.New(""), &e) + testCheck(c, check.Implements, false, "", errors.New(""), &re) + + // Some bad values + testCheck(c, check.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, errors.New("")) + testCheck(c, check.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, interface{}(nil)) + testCheck(c, check.Implements, false, "", interface{}(nil), &e) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/export_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/export_test.go new file mode 100644 index 0000000..0e6cfe0 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/export_test.go @@ -0,0 +1,9 @@ +package check + +func PrintLine(filename string, line int) (string, error) { + return printLine(filename, line) +} + +func Indent(s, with string) string { + return indent(s, with) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/fixture_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/fixture_test.go new file mode 100644 index 0000000..2bff9e1 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/fixture_test.go @@ -0,0 +1,484 @@ +// Tests for the behavior of the test fixture system. + +package check_test + +import ( + . "gopkg.in/check.v1" +) + +// ----------------------------------------------------------------------- +// Fixture test suite. + +type FixtureS struct{} + +var fixtureS = Suite(&FixtureS{}) + +func (s *FixtureS) TestCountSuite(c *C) { + suitesRun += 1 +} + +// ----------------------------------------------------------------------- +// Basic fixture ordering verification. + +func (s *FixtureS) TestOrder(c *C) { + helper := FixtureHelper{} + Run(&helper, nil) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "SetUpTest") + c.Check(helper.calls[5], Equals, "Test2") + c.Check(helper.calls[6], Equals, "TearDownTest") + c.Check(helper.calls[7], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 8) +} + +// ----------------------------------------------------------------------- +// Check the behavior when panics occur within tests and fixtures. + +func (s *FixtureS) TestPanicOnTest(c *C) { + helper := FixtureHelper{panicOn: "Test1"} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "SetUpTest") + c.Check(helper.calls[5], Equals, "Test2") + c.Check(helper.calls[6], Equals, "TearDownTest") + c.Check(helper.calls[7], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 8) + + expected := "^\n-+\n" + + "PANIC: check_test\\.go:[0-9]+: FixtureHelper.Test1\n\n" + + "\\.\\.\\. Panic: Test1 \\(PC=[xA-F0-9]+\\)\n\n" + + ".+:[0-9]+\n" + + " in (go)?panic\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.trace\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.Test1\n" + + "(.|\n)*$" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnSetUpTest(c *C) { + helper := FixtureHelper{panicOn: "SetUpTest"} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "TearDownTest") + c.Check(helper.calls[3], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 4) + + expected := "^\n-+\n" + + "PANIC: check_test\\.go:[0-9]+: " + + "FixtureHelper\\.SetUpTest\n\n" + + "\\.\\.\\. Panic: SetUpTest \\(PC=[xA-F0-9]+\\)\n\n" + + ".+:[0-9]+\n" + + " in (go)?panic\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.trace\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.SetUpTest\n" + + "(.|\n)*" + + "\n-+\n" + + "PANIC: check_test\\.go:[0-9]+: " + + "FixtureHelper\\.Test1\n\n" + + "\\.\\.\\. Panic: Fixture has panicked " + + "\\(see related PANIC\\)\n$" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnTearDownTest(c *C) { + helper := FixtureHelper{panicOn: "TearDownTest"} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 5) + + expected := "^\n-+\n" + + "PANIC: check_test\\.go:[0-9]+: " + + "FixtureHelper.TearDownTest\n\n" + + "\\.\\.\\. Panic: TearDownTest \\(PC=[xA-F0-9]+\\)\n\n" + + ".+:[0-9]+\n" + + " in (go)?panic\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.trace\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.TearDownTest\n" + + "(.|\n)*" + + "\n-+\n" + + "PANIC: check_test\\.go:[0-9]+: " + + "FixtureHelper\\.Test1\n\n" + + "\\.\\.\\. Panic: Fixture has panicked " + + "\\(see related PANIC\\)\n$" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnSetUpSuite(c *C) { + helper := FixtureHelper{panicOn: "SetUpSuite"} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 2) + + expected := "^\n-+\n" + + "PANIC: check_test\\.go:[0-9]+: " + + "FixtureHelper.SetUpSuite\n\n" + + "\\.\\.\\. Panic: SetUpSuite \\(PC=[xA-F0-9]+\\)\n\n" + + ".+:[0-9]+\n" + + " in (go)?panic\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.trace\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.SetUpSuite\n" + + "(.|\n)*$" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnTearDownSuite(c *C) { + helper := FixtureHelper{panicOn: "TearDownSuite"} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "SetUpTest") + c.Check(helper.calls[5], Equals, "Test2") + c.Check(helper.calls[6], Equals, "TearDownTest") + c.Check(helper.calls[7], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 8) + + expected := "^\n-+\n" + + "PANIC: check_test\\.go:[0-9]+: " + + "FixtureHelper.TearDownSuite\n\n" + + "\\.\\.\\. Panic: TearDownSuite \\(PC=[xA-F0-9]+\\)\n\n" + + ".+:[0-9]+\n" + + " in (go)?panic\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.trace\n" + + ".*check_test.go:[0-9]+\n" + + " in FixtureHelper.TearDownSuite\n" + + "(.|\n)*$" + + c.Check(output.value, Matches, expected) +} + +// ----------------------------------------------------------------------- +// A wrong argument on a test or fixture will produce a nice error. + +func (s *FixtureS) TestPanicOnWrongTestArg(c *C) { + helper := WrongTestArgHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "TearDownTest") + c.Check(helper.calls[3], Equals, "SetUpTest") + c.Check(helper.calls[4], Equals, "Test2") + c.Check(helper.calls[5], Equals, "TearDownTest") + c.Check(helper.calls[6], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 7) + + expected := "^\n-+\n" + + "PANIC: fixture_test\\.go:[0-9]+: " + + "WrongTestArgHelper\\.Test1\n\n" + + "\\.\\.\\. Panic: WrongTestArgHelper\\.Test1 argument " + + "should be \\*check\\.C\n" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnWrongSetUpTestArg(c *C) { + helper := WrongSetUpTestArgHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(len(helper.calls), Equals, 0) + + expected := + "^\n-+\n" + + "PANIC: fixture_test\\.go:[0-9]+: " + + "WrongSetUpTestArgHelper\\.SetUpTest\n\n" + + "\\.\\.\\. Panic: WrongSetUpTestArgHelper\\.SetUpTest argument " + + "should be \\*check\\.C\n" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnWrongSetUpSuiteArg(c *C) { + helper := WrongSetUpSuiteArgHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(len(helper.calls), Equals, 0) + + expected := + "^\n-+\n" + + "PANIC: fixture_test\\.go:[0-9]+: " + + "WrongSetUpSuiteArgHelper\\.SetUpSuite\n\n" + + "\\.\\.\\. Panic: WrongSetUpSuiteArgHelper\\.SetUpSuite argument " + + "should be \\*check\\.C\n" + + c.Check(output.value, Matches, expected) +} + +// ----------------------------------------------------------------------- +// Nice errors also when tests or fixture have wrong arg count. + +func (s *FixtureS) TestPanicOnWrongTestArgCount(c *C) { + helper := WrongTestArgCountHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "TearDownTest") + c.Check(helper.calls[3], Equals, "SetUpTest") + c.Check(helper.calls[4], Equals, "Test2") + c.Check(helper.calls[5], Equals, "TearDownTest") + c.Check(helper.calls[6], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 7) + + expected := "^\n-+\n" + + "PANIC: fixture_test\\.go:[0-9]+: " + + "WrongTestArgCountHelper\\.Test1\n\n" + + "\\.\\.\\. Panic: WrongTestArgCountHelper\\.Test1 argument " + + "should be \\*check\\.C\n" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnWrongSetUpTestArgCount(c *C) { + helper := WrongSetUpTestArgCountHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(len(helper.calls), Equals, 0) + + expected := + "^\n-+\n" + + "PANIC: fixture_test\\.go:[0-9]+: " + + "WrongSetUpTestArgCountHelper\\.SetUpTest\n\n" + + "\\.\\.\\. Panic: WrongSetUpTestArgCountHelper\\.SetUpTest argument " + + "should be \\*check\\.C\n" + + c.Check(output.value, Matches, expected) +} + +func (s *FixtureS) TestPanicOnWrongSetUpSuiteArgCount(c *C) { + helper := WrongSetUpSuiteArgCountHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(len(helper.calls), Equals, 0) + + expected := + "^\n-+\n" + + "PANIC: fixture_test\\.go:[0-9]+: " + + "WrongSetUpSuiteArgCountHelper\\.SetUpSuite\n\n" + + "\\.\\.\\. Panic: WrongSetUpSuiteArgCountHelper" + + "\\.SetUpSuite argument should be \\*check\\.C\n" + + c.Check(output.value, Matches, expected) +} + +// ----------------------------------------------------------------------- +// Helper test suites with wrong function arguments. + +type WrongTestArgHelper struct { + FixtureHelper +} + +func (s *WrongTestArgHelper) Test1(t int) { +} + +type WrongSetUpTestArgHelper struct { + FixtureHelper +} + +func (s *WrongSetUpTestArgHelper) SetUpTest(t int) { +} + +type WrongSetUpSuiteArgHelper struct { + FixtureHelper +} + +func (s *WrongSetUpSuiteArgHelper) SetUpSuite(t int) { +} + +type WrongTestArgCountHelper struct { + FixtureHelper +} + +func (s *WrongTestArgCountHelper) Test1(c *C, i int) { +} + +type WrongSetUpTestArgCountHelper struct { + FixtureHelper +} + +func (s *WrongSetUpTestArgCountHelper) SetUpTest(c *C, i int) { +} + +type WrongSetUpSuiteArgCountHelper struct { + FixtureHelper +} + +func (s *WrongSetUpSuiteArgCountHelper) SetUpSuite(c *C, i int) { +} + +// ----------------------------------------------------------------------- +// Ensure fixture doesn't run without tests. + +type NoTestsHelper struct { + hasRun bool +} + +func (s *NoTestsHelper) SetUpSuite(c *C) { + s.hasRun = true +} + +func (s *NoTestsHelper) TearDownSuite(c *C) { + s.hasRun = true +} + +func (s *FixtureS) TestFixtureDoesntRunWithoutTests(c *C) { + helper := NoTestsHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Check(helper.hasRun, Equals, false) +} + +// ----------------------------------------------------------------------- +// Verify that checks and assertions work correctly inside the fixture. + +type FixtureCheckHelper struct { + fail string + completed bool +} + +func (s *FixtureCheckHelper) SetUpSuite(c *C) { + switch s.fail { + case "SetUpSuiteAssert": + c.Assert(false, Equals, true) + case "SetUpSuiteCheck": + c.Check(false, Equals, true) + } + s.completed = true +} + +func (s *FixtureCheckHelper) SetUpTest(c *C) { + switch s.fail { + case "SetUpTestAssert": + c.Assert(false, Equals, true) + case "SetUpTestCheck": + c.Check(false, Equals, true) + } + s.completed = true +} + +func (s *FixtureCheckHelper) Test(c *C) { + // Do nothing. +} + +func (s *FixtureS) TestSetUpSuiteCheck(c *C) { + helper := FixtureCheckHelper{fail: "SetUpSuiteCheck"} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Assert(output.value, Matches, + "\n---+\n"+ + "FAIL: fixture_test\\.go:[0-9]+: "+ + "FixtureCheckHelper\\.SetUpSuite\n\n"+ + "fixture_test\\.go:[0-9]+:\n"+ + " c\\.Check\\(false, Equals, true\\)\n"+ + "\\.+ obtained bool = false\n"+ + "\\.+ expected bool = true\n\n") + c.Assert(helper.completed, Equals, true) +} + +func (s *FixtureS) TestSetUpSuiteAssert(c *C) { + helper := FixtureCheckHelper{fail: "SetUpSuiteAssert"} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Assert(output.value, Matches, + "\n---+\n"+ + "FAIL: fixture_test\\.go:[0-9]+: "+ + "FixtureCheckHelper\\.SetUpSuite\n\n"+ + "fixture_test\\.go:[0-9]+:\n"+ + " c\\.Assert\\(false, Equals, true\\)\n"+ + "\\.+ obtained bool = false\n"+ + "\\.+ expected bool = true\n\n") + c.Assert(helper.completed, Equals, false) +} + +// ----------------------------------------------------------------------- +// Verify that logging within SetUpTest() persists within the test log itself. + +type FixtureLogHelper struct { + c *C +} + +func (s *FixtureLogHelper) SetUpTest(c *C) { + s.c = c + c.Log("1") +} + +func (s *FixtureLogHelper) Test(c *C) { + c.Log("2") + s.c.Log("3") + c.Log("4") + c.Fail() +} + +func (s *FixtureLogHelper) TearDownTest(c *C) { + s.c.Log("5") +} + +func (s *FixtureS) TestFixtureLogging(c *C) { + helper := FixtureLogHelper{} + output := String{} + Run(&helper, &RunConf{Output: &output}) + c.Assert(output.value, Matches, + "\n---+\n"+ + "FAIL: fixture_test\\.go:[0-9]+: "+ + "FixtureLogHelper\\.Test\n\n"+ + "1\n2\n3\n4\n5\n") +} + +// ----------------------------------------------------------------------- +// Skip() within fixture methods. + +func (s *FixtureS) TestSkipSuite(c *C) { + helper := FixtureHelper{skip: true, skipOnN: 0} + output := String{} + result := Run(&helper, &RunConf{Output: &output}) + c.Assert(output.value, Equals, "") + c.Assert(helper.calls[0], Equals, "SetUpSuite") + c.Assert(helper.calls[1], Equals, "TearDownSuite") + c.Assert(len(helper.calls), Equals, 2) + c.Assert(result.Skipped, Equals, 2) +} + +func (s *FixtureS) TestSkipTest(c *C) { + helper := FixtureHelper{skip: true, skipOnN: 1} + output := String{} + result := Run(&helper, &RunConf{Output: &output}) + c.Assert(helper.calls[0], Equals, "SetUpSuite") + c.Assert(helper.calls[1], Equals, "SetUpTest") + c.Assert(helper.calls[2], Equals, "SetUpTest") + c.Assert(helper.calls[3], Equals, "Test2") + c.Assert(helper.calls[4], Equals, "TearDownTest") + c.Assert(helper.calls[5], Equals, "TearDownSuite") + c.Assert(len(helper.calls), Equals, 6) + c.Assert(result.Skipped, Equals, 1) +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/foundation_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/foundation_test.go new file mode 100644 index 0000000..8ecf791 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/foundation_test.go @@ -0,0 +1,335 @@ +// These tests check that the foundations of gocheck are working properly. +// They already assume that fundamental failing is working already, though, +// since this was tested in bootstrap_test.go. Even then, some care may +// still have to be taken when using external functions, since they should +// of course not rely on functionality tested here. + +package check_test + +import ( + "fmt" + "gopkg.in/check.v1" + "log" + "os" + "regexp" + "strings" +) + +// ----------------------------------------------------------------------- +// Foundation test suite. + +type FoundationS struct{} + +var foundationS = check.Suite(&FoundationS{}) + +func (s *FoundationS) TestCountSuite(c *check.C) { + suitesRun += 1 +} + +func (s *FoundationS) TestErrorf(c *check.C) { + // Do not use checkState() here. It depends on Errorf() working. + expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+ + " c.Errorf(\"Error %%v!\", \"message\")\n"+ + "... Error: Error message!\n\n", + getMyLine()+1) + c.Errorf("Error %v!", "message") + failed := c.Failed() + c.Succeed() + if log := c.GetTestLog(); log != expectedLog { + c.Logf("Errorf() logged %#v rather than %#v", log, expectedLog) + c.Fail() + } + if !failed { + c.Logf("Errorf() didn't put the test in a failed state") + c.Fail() + } +} + +func (s *FoundationS) TestError(c *check.C) { + expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+ + " c\\.Error\\(\"Error \", \"message!\"\\)\n"+ + "\\.\\.\\. Error: Error message!\n\n", + getMyLine()+1) + c.Error("Error ", "message!") + checkState(c, nil, + &expectedState{ + name: "Error(`Error `, `message!`)", + failed: true, + log: expectedLog, + }) +} + +func (s *FoundationS) TestFailNow(c *check.C) { + defer (func() { + if !c.Failed() { + c.Error("FailNow() didn't fail the test") + } else { + c.Succeed() + if c.GetTestLog() != "" { + c.Error("Something got logged:\n" + c.GetTestLog()) + } + } + })() + + c.FailNow() + c.Log("FailNow() didn't stop the test") +} + +func (s *FoundationS) TestSucceedNow(c *check.C) { + defer (func() { + if c.Failed() { + c.Error("SucceedNow() didn't succeed the test") + } + if c.GetTestLog() != "" { + c.Error("Something got logged:\n" + c.GetTestLog()) + } + })() + + c.Fail() + c.SucceedNow() + c.Log("SucceedNow() didn't stop the test") +} + +func (s *FoundationS) TestFailureHeader(c *check.C) { + output := String{} + failHelper := FailHelper{} + check.Run(&failHelper, &check.RunConf{Output: &output}) + header := fmt.Sprintf(""+ + "\n-----------------------------------"+ + "-----------------------------------\n"+ + "FAIL: check_test.go:%d: FailHelper.TestLogAndFail\n", + failHelper.testLine) + if strings.Index(output.value, header) == -1 { + c.Errorf(""+ + "Failure didn't print a proper header.\n"+ + "... Got:\n%s... Expected something with:\n%s", + output.value, header) + } +} + +func (s *FoundationS) TestFatal(c *check.C) { + var line int + defer (func() { + if !c.Failed() { + c.Error("Fatal() didn't fail the test") + } else { + c.Succeed() + expected := fmt.Sprintf("foundation_test.go:%d:\n"+ + " c.Fatal(\"Die \", \"now!\")\n"+ + "... Error: Die now!\n\n", + line) + if c.GetTestLog() != expected { + c.Error("Incorrect log:", c.GetTestLog()) + } + } + })() + + line = getMyLine() + 1 + c.Fatal("Die ", "now!") + c.Log("Fatal() didn't stop the test") +} + +func (s *FoundationS) TestFatalf(c *check.C) { + var line int + defer (func() { + if !c.Failed() { + c.Error("Fatalf() didn't fail the test") + } else { + c.Succeed() + expected := fmt.Sprintf("foundation_test.go:%d:\n"+ + " c.Fatalf(\"Die %%s!\", \"now\")\n"+ + "... Error: Die now!\n\n", + line) + if c.GetTestLog() != expected { + c.Error("Incorrect log:", c.GetTestLog()) + } + } + })() + + line = getMyLine() + 1 + c.Fatalf("Die %s!", "now") + c.Log("Fatalf() didn't stop the test") +} + +func (s *FoundationS) TestCallerLoggingInsideTest(c *check.C) { + log := fmt.Sprintf(""+ + "foundation_test.go:%d:\n"+ + " result := c.Check\\(10, check.Equals, 20\\)\n"+ + "\\.\\.\\. obtained int = 10\n"+ + "\\.\\.\\. expected int = 20\n\n", + getMyLine()+1) + result := c.Check(10, check.Equals, 20) + checkState(c, result, + &expectedState{ + name: "Check(10, Equals, 20)", + result: false, + failed: true, + log: log, + }) +} + +func (s *FoundationS) TestCallerLoggingInDifferentFile(c *check.C) { + result, line := checkEqualWrapper(c, 10, 20) + testLine := getMyLine() - 1 + log := fmt.Sprintf(""+ + "foundation_test.go:%d:\n"+ + " result, line := checkEqualWrapper\\(c, 10, 20\\)\n"+ + "check_test.go:%d:\n"+ + " return c.Check\\(obtained, check.Equals, expected\\), getMyLine\\(\\)\n"+ + "\\.\\.\\. obtained int = 10\n"+ + "\\.\\.\\. expected int = 20\n\n", + testLine, line) + checkState(c, result, + &expectedState{ + name: "Check(10, Equals, 20)", + result: false, + failed: true, + log: log, + }) +} + +// ----------------------------------------------------------------------- +// ExpectFailure() inverts the logic of failure. + +type ExpectFailureSucceedHelper struct{} + +func (s *ExpectFailureSucceedHelper) TestSucceed(c *check.C) { + c.ExpectFailure("It booms!") + c.Error("Boom!") +} + +type ExpectFailureFailHelper struct{} + +func (s *ExpectFailureFailHelper) TestFail(c *check.C) { + c.ExpectFailure("Bug #XYZ") +} + +func (s *FoundationS) TestExpectFailureFail(c *check.C) { + helper := ExpectFailureFailHelper{} + output := String{} + result := check.Run(&helper, &check.RunConf{Output: &output}) + + expected := "" + + "^\n-+\n" + + "FAIL: foundation_test\\.go:[0-9]+:" + + " ExpectFailureFailHelper\\.TestFail\n\n" + + "\\.\\.\\. Error: Test succeeded, but was expected to fail\n" + + "\\.\\.\\. Reason: Bug #XYZ\n$" + + matched, err := regexp.MatchString(expected, output.value) + if err != nil { + c.Error("Bad expression: ", expected) + } else if !matched { + c.Error("ExpectFailure() didn't log properly:\n", output.value) + } + + c.Assert(result.ExpectedFailures, check.Equals, 0) +} + +func (s *FoundationS) TestExpectFailureSucceed(c *check.C) { + helper := ExpectFailureSucceedHelper{} + output := String{} + result := check.Run(&helper, &check.RunConf{Output: &output}) + + c.Assert(output.value, check.Equals, "") + c.Assert(result.ExpectedFailures, check.Equals, 1) +} + +func (s *FoundationS) TestExpectFailureSucceedVerbose(c *check.C) { + helper := ExpectFailureSucceedHelper{} + output := String{} + result := check.Run(&helper, &check.RunConf{Output: &output, Verbose: true}) + + expected := "" + + "FAIL EXPECTED: foundation_test\\.go:[0-9]+:" + + " ExpectFailureSucceedHelper\\.TestSucceed \\(It booms!\\)\t *[.0-9]+s\n" + + matched, err := regexp.MatchString(expected, output.value) + if err != nil { + c.Error("Bad expression: ", expected) + } else if !matched { + c.Error("ExpectFailure() didn't log properly:\n", output.value) + } + + c.Assert(result.ExpectedFailures, check.Equals, 1) +} + +// ----------------------------------------------------------------------- +// Skip() allows stopping a test without positive/negative results. + +type SkipTestHelper struct{} + +func (s *SkipTestHelper) TestFail(c *check.C) { + c.Skip("Wrong platform or whatever") + c.Error("Boom!") +} + +func (s *FoundationS) TestSkip(c *check.C) { + helper := SkipTestHelper{} + output := String{} + check.Run(&helper, &check.RunConf{Output: &output}) + + if output.value != "" { + c.Error("Skip() logged something:\n", output.value) + } +} + +func (s *FoundationS) TestSkipVerbose(c *check.C) { + helper := SkipTestHelper{} + output := String{} + check.Run(&helper, &check.RunConf{Output: &output, Verbose: true}) + + expected := "SKIP: foundation_test\\.go:[0-9]+: SkipTestHelper\\.TestFail" + + " \\(Wrong platform or whatever\\)" + matched, err := regexp.MatchString(expected, output.value) + if err != nil { + c.Error("Bad expression: ", expected) + } else if !matched { + c.Error("Skip() didn't log properly:\n", output.value) + } +} + +// ----------------------------------------------------------------------- +// Check minimum *log.Logger interface provided by *check.C. + +type minLogger interface { + Output(calldepth int, s string) error +} + +func (s *BootstrapS) TestMinLogger(c *check.C) { + var logger minLogger + logger = log.New(os.Stderr, "", 0) + logger = c + logger.Output(0, "Hello there") + expected := `\[LOG\] [0-9]+:[0-9][0-9]\.[0-9][0-9][0-9] +Hello there\n` + output := c.GetTestLog() + c.Assert(output, check.Matches, expected) +} + +// ----------------------------------------------------------------------- +// Ensure that suites with embedded types are working fine, including the +// the workaround for issue 906. + +type EmbeddedInternalS struct { + called bool +} + +type EmbeddedS struct { + EmbeddedInternalS +} + +var embeddedS = check.Suite(&EmbeddedS{}) + +func (s *EmbeddedS) TestCountSuite(c *check.C) { + suitesRun += 1 +} + +func (s *EmbeddedInternalS) TestMethod(c *check.C) { + c.Error("TestMethod() of the embedded type was called!?") +} + +func (s *EmbeddedS) TestMethod(c *check.C) { + // http://code.google.com/p/go/issues/detail?id=906 + c.Check(s.called, check.Equals, false) // Go issue 906 is affecting the runner? + s.called = true +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers.go new file mode 100644 index 0000000..4b6c26d --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers.go @@ -0,0 +1,231 @@ +package check + +import ( + "fmt" + "strings" + "time" +) + +// TestName returns the current test name in the form "SuiteName.TestName" +func (c *C) TestName() string { + return c.testName +} + +// ----------------------------------------------------------------------- +// Basic succeeding/failing logic. + +// Failed returns whether the currently running test has already failed. +func (c *C) Failed() bool { + return c.status == failedSt +} + +// Fail marks the currently running test as failed. +// +// Something ought to have been previously logged so the developer can tell +// what went wrong. The higher level helper functions will fail the test +// and do the logging properly. +func (c *C) Fail() { + c.status = failedSt +} + +// FailNow marks the currently running test as failed and stops running it. +// Something ought to have been previously logged so the developer can tell +// what went wrong. The higher level helper functions will fail the test +// and do the logging properly. +func (c *C) FailNow() { + c.Fail() + c.stopNow() +} + +// Succeed marks the currently running test as succeeded, undoing any +// previous failures. +func (c *C) Succeed() { + c.status = succeededSt +} + +// SucceedNow marks the currently running test as succeeded, undoing any +// previous failures, and stops running the test. +func (c *C) SucceedNow() { + c.Succeed() + c.stopNow() +} + +// ExpectFailure informs that the running test is knowingly broken for +// the provided reason. If the test does not fail, an error will be reported +// to raise attention to this fact. This method is useful to temporarily +// disable tests which cover well known problems until a better time to +// fix the problem is found, without forgetting about the fact that a +// failure still exists. +func (c *C) ExpectFailure(reason string) { + if reason == "" { + panic("Missing reason why the test is expected to fail") + } + c.mustFail = true + c.reason = reason +} + +// Skip skips the running test for the provided reason. If run from within +// SetUpTest, the individual test being set up will be skipped, and if run +// from within SetUpSuite, the whole suite is skipped. +func (c *C) Skip(reason string) { + if reason == "" { + panic("Missing reason why the test is being skipped") + } + c.reason = reason + c.status = skippedSt + c.stopNow() +} + +// ----------------------------------------------------------------------- +// Basic logging. + +// GetTestLog returns the current test error output. +func (c *C) GetTestLog() string { + return c.logb.String() +} + +// Log logs some information into the test error output. +// The provided arguments are assembled together into a string with fmt.Sprint. +func (c *C) Log(args ...interface{}) { + c.log(args...) +} + +// Log logs some information into the test error output. +// The provided arguments are assembled together into a string with fmt.Sprintf. +func (c *C) Logf(format string, args ...interface{}) { + c.logf(format, args...) +} + +// Output enables *C to be used as a logger in functions that require only +// the minimum interface of *log.Logger. +func (c *C) Output(calldepth int, s string) error { + d := time.Now().Sub(c.startTime) + msec := d / time.Millisecond + sec := d / time.Second + min := d / time.Minute + + c.Logf("[LOG] %d:%02d.%03d %s", min, sec%60, msec%1000, s) + return nil +} + +// Error logs an error into the test error output and marks the test as failed. +// The provided arguments are assembled together into a string with fmt.Sprint. +func (c *C) Error(args ...interface{}) { + c.logCaller(1) + c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) + c.logNewLine() + c.Fail() +} + +// Errorf logs an error into the test error output and marks the test as failed. +// The provided arguments are assembled together into a string with fmt.Sprintf. +func (c *C) Errorf(format string, args ...interface{}) { + c.logCaller(1) + c.logString(fmt.Sprintf("Error: "+format, args...)) + c.logNewLine() + c.Fail() +} + +// Fatal logs an error into the test error output, marks the test as failed, and +// stops the test execution. The provided arguments are assembled together into +// a string with fmt.Sprint. +func (c *C) Fatal(args ...interface{}) { + c.logCaller(1) + c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) + c.logNewLine() + c.FailNow() +} + +// Fatlaf logs an error into the test error output, marks the test as failed, and +// stops the test execution. The provided arguments are assembled together into +// a string with fmt.Sprintf. +func (c *C) Fatalf(format string, args ...interface{}) { + c.logCaller(1) + c.logString(fmt.Sprint("Error: ", fmt.Sprintf(format, args...))) + c.logNewLine() + c.FailNow() +} + +// ----------------------------------------------------------------------- +// Generic checks and assertions based on checkers. + +// Check verifies if the first value matches the expected value according +// to the provided checker. If they do not match, an error is logged, the +// test is marked as failed, and the test execution continues. +// +// Some checkers may not need the expected argument (e.g. IsNil). +// +// Extra arguments provided to the function are logged next to the reported +// problem when the matching fails. +func (c *C) Check(obtained interface{}, checker Checker, args ...interface{}) bool { + return c.internalCheck("Check", obtained, checker, args...) +} + +// Assert ensures that the first value matches the expected value according +// to the provided checker. If they do not match, an error is logged, the +// test is marked as failed, and the test execution stops. +// +// Some checkers may not need the expected argument (e.g. IsNil). +// +// Extra arguments provided to the function are logged next to the reported +// problem when the matching fails. +func (c *C) Assert(obtained interface{}, checker Checker, args ...interface{}) { + if !c.internalCheck("Assert", obtained, checker, args...) { + c.stopNow() + } +} + +func (c *C) internalCheck(funcName string, obtained interface{}, checker Checker, args ...interface{}) bool { + if checker == nil { + c.logCaller(2) + c.logString(fmt.Sprintf("%s(obtained, nil!?, ...):", funcName)) + c.logString("Oops.. you've provided a nil checker!") + c.logNewLine() + c.Fail() + return false + } + + // If the last argument is a bug info, extract it out. + var comment CommentInterface + if len(args) > 0 { + if c, ok := args[len(args)-1].(CommentInterface); ok { + comment = c + args = args[:len(args)-1] + } + } + + params := append([]interface{}{obtained}, args...) + info := checker.Info() + + if len(params) != len(info.Params) { + names := append([]string{info.Params[0], info.Name}, info.Params[1:]...) + c.logCaller(2) + c.logString(fmt.Sprintf("%s(%s):", funcName, strings.Join(names, ", "))) + c.logString(fmt.Sprintf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(names), len(params)+1)) + c.logNewLine() + c.Fail() + return false + } + + // Copy since it may be mutated by Check. + names := append([]string{}, info.Params...) + + // Do the actual check. + result, error := checker.Check(params, names) + if !result || error != "" { + c.logCaller(2) + for i := 0; i != len(params); i++ { + c.logValue(names[i], params[i]) + } + if comment != nil { + c.logString(comment.CheckCommentString()) + } + if error != "" { + c.logString(error) + } + c.logNewLine() + c.Fail() + return false + } + return true +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers_test.go new file mode 100644 index 0000000..4baa656 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/helpers_test.go @@ -0,0 +1,519 @@ +// These tests verify the inner workings of the helper methods associated +// with check.T. + +package check_test + +import ( + "gopkg.in/check.v1" + "os" + "reflect" + "runtime" + "sync" +) + +var helpersS = check.Suite(&HelpersS{}) + +type HelpersS struct{} + +func (s *HelpersS) TestCountSuite(c *check.C) { + suitesRun += 1 +} + +// ----------------------------------------------------------------------- +// Fake checker and bug info to verify the behavior of Assert() and Check(). + +type MyChecker struct { + info *check.CheckerInfo + params []interface{} + names []string + result bool + error string +} + +func (checker *MyChecker) Info() *check.CheckerInfo { + if checker.info == nil { + return &check.CheckerInfo{Name: "MyChecker", Params: []string{"myobtained", "myexpected"}} + } + return checker.info +} + +func (checker *MyChecker) Check(params []interface{}, names []string) (bool, string) { + rparams := checker.params + rnames := checker.names + checker.params = append([]interface{}{}, params...) + checker.names = append([]string{}, names...) + if rparams != nil { + copy(params, rparams) + } + if rnames != nil { + copy(names, rnames) + } + return checker.result, checker.error +} + +type myCommentType string + +func (c myCommentType) CheckCommentString() string { + return string(c) +} + +func myComment(s string) myCommentType { + return myCommentType(s) +} + +// ----------------------------------------------------------------------- +// Ensure a real checker actually works fine. + +func (s *HelpersS) TestCheckerInterface(c *check.C) { + testHelperSuccess(c, "Check(1, Equals, 1)", true, func() interface{} { + return c.Check(1, check.Equals, 1) + }) +} + +// ----------------------------------------------------------------------- +// Tests for Check(), mostly the same as for Assert() following these. + +func (s *HelpersS) TestCheckSucceedWithExpected(c *check.C) { + checker := &MyChecker{result: true} + testHelperSuccess(c, "Check(1, checker, 2)", true, func() interface{} { + return c.Check(1, checker, 2) + }) + if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) { + c.Fatalf("Bad params for check: %#v", checker.params) + } +} + +func (s *HelpersS) TestCheckSucceedWithoutExpected(c *check.C) { + checker := &MyChecker{result: true, info: &check.CheckerInfo{Params: []string{"myvalue"}}} + testHelperSuccess(c, "Check(1, checker)", true, func() interface{} { + return c.Check(1, checker) + }) + if !reflect.DeepEqual(checker.params, []interface{}{1}) { + c.Fatalf("Bad params for check: %#v", checker.params) + } +} + +func (s *HelpersS) TestCheckFailWithExpected(c *check.C) { + checker := &MyChecker{result: false} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker, 2\\)\n" + + "\\.+ myobtained int = 1\n" + + "\\.+ myexpected int = 2\n\n" + testHelperFailure(c, "Check(1, checker, 2)", false, false, log, + func() interface{} { + return c.Check(1, checker, 2) + }) +} + +func (s *HelpersS) TestCheckFailWithExpectedAndComment(c *check.C) { + checker := &MyChecker{result: false} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + + "\\.+ myobtained int = 1\n" + + "\\.+ myexpected int = 2\n" + + "\\.+ Hello world!\n\n" + testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, + func() interface{} { + return c.Check(1, checker, 2, myComment("Hello world!")) + }) +} + +func (s *HelpersS) TestCheckFailWithExpectedAndStaticComment(c *check.C) { + checker := &MyChecker{result: false} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " // Nice leading comment\\.\n" + + " return c\\.Check\\(1, checker, 2\\) // Hello there\n" + + "\\.+ myobtained int = 1\n" + + "\\.+ myexpected int = 2\n\n" + testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, + func() interface{} { + // Nice leading comment. + return c.Check(1, checker, 2) // Hello there + }) +} + +func (s *HelpersS) TestCheckFailWithoutExpected(c *check.C) { + checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker\\)\n" + + "\\.+ myvalue int = 1\n\n" + testHelperFailure(c, "Check(1, checker)", false, false, log, + func() interface{} { + return c.Check(1, checker) + }) +} + +func (s *HelpersS) TestCheckFailWithoutExpectedAndMessage(c *check.C) { + checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + + "\\.+ myvalue int = 1\n" + + "\\.+ Hello world!\n\n" + testHelperFailure(c, "Check(1, checker, msg)", false, false, log, + func() interface{} { + return c.Check(1, checker, myComment("Hello world!")) + }) +} + +func (s *HelpersS) TestCheckWithMissingExpected(c *check.C) { + checker := &MyChecker{result: true} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker\\)\n" + + "\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + + "\\.+ Wrong number of parameters for MyChecker: " + + "want 3, got 2\n\n" + testHelperFailure(c, "Check(1, checker, !?)", false, false, log, + func() interface{} { + return c.Check(1, checker) + }) +} + +func (s *HelpersS) TestCheckWithTooManyExpected(c *check.C) { + checker := &MyChecker{result: true} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker, 2, 3\\)\n" + + "\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + + "\\.+ Wrong number of parameters for MyChecker: " + + "want 3, got 4\n\n" + testHelperFailure(c, "Check(1, checker, 2, 3)", false, false, log, + func() interface{} { + return c.Check(1, checker, 2, 3) + }) +} + +func (s *HelpersS) TestCheckWithError(c *check.C) { + checker := &MyChecker{result: false, error: "Some not so cool data provided!"} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker, 2\\)\n" + + "\\.+ myobtained int = 1\n" + + "\\.+ myexpected int = 2\n" + + "\\.+ Some not so cool data provided!\n\n" + testHelperFailure(c, "Check(1, checker, 2)", false, false, log, + func() interface{} { + return c.Check(1, checker, 2) + }) +} + +func (s *HelpersS) TestCheckWithNilChecker(c *check.C) { + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, nil\\)\n" + + "\\.+ Check\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + + "\\.+ Oops\\.\\. you've provided a nil checker!\n\n" + testHelperFailure(c, "Check(obtained, nil)", false, false, log, + func() interface{} { + return c.Check(1, nil) + }) +} + +func (s *HelpersS) TestCheckWithParamsAndNamesMutation(c *check.C) { + checker := &MyChecker{result: false, params: []interface{}{3, 4}, names: []string{"newobtained", "newexpected"}} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " return c\\.Check\\(1, checker, 2\\)\n" + + "\\.+ newobtained int = 3\n" + + "\\.+ newexpected int = 4\n\n" + testHelperFailure(c, "Check(1, checker, 2) with mutation", false, false, log, + func() interface{} { + return c.Check(1, checker, 2) + }) +} + +// ----------------------------------------------------------------------- +// Tests for Assert(), mostly the same as for Check() above. + +func (s *HelpersS) TestAssertSucceedWithExpected(c *check.C) { + checker := &MyChecker{result: true} + testHelperSuccess(c, "Assert(1, checker, 2)", nil, func() interface{} { + c.Assert(1, checker, 2) + return nil + }) + if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) { + c.Fatalf("Bad params for check: %#v", checker.params) + } +} + +func (s *HelpersS) TestAssertSucceedWithoutExpected(c *check.C) { + checker := &MyChecker{result: true, info: &check.CheckerInfo{Params: []string{"myvalue"}}} + testHelperSuccess(c, "Assert(1, checker)", nil, func() interface{} { + c.Assert(1, checker) + return nil + }) + if !reflect.DeepEqual(checker.params, []interface{}{1}) { + c.Fatalf("Bad params for check: %#v", checker.params) + } +} + +func (s *HelpersS) TestAssertFailWithExpected(c *check.C) { + checker := &MyChecker{result: false} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " c\\.Assert\\(1, checker, 2\\)\n" + + "\\.+ myobtained int = 1\n" + + "\\.+ myexpected int = 2\n\n" + testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, + func() interface{} { + c.Assert(1, checker, 2) + return nil + }) +} + +func (s *HelpersS) TestAssertFailWithExpectedAndMessage(c *check.C) { + checker := &MyChecker{result: false} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " c\\.Assert\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + + "\\.+ myobtained int = 1\n" + + "\\.+ myexpected int = 2\n" + + "\\.+ Hello world!\n\n" + testHelperFailure(c, "Assert(1, checker, 2, msg)", nil, true, log, + func() interface{} { + c.Assert(1, checker, 2, myComment("Hello world!")) + return nil + }) +} + +func (s *HelpersS) TestAssertFailWithoutExpected(c *check.C) { + checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " c\\.Assert\\(1, checker\\)\n" + + "\\.+ myvalue int = 1\n\n" + testHelperFailure(c, "Assert(1, checker)", nil, true, log, + func() interface{} { + c.Assert(1, checker) + return nil + }) +} + +func (s *HelpersS) TestAssertFailWithoutExpectedAndMessage(c *check.C) { + checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " c\\.Assert\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + + "\\.+ myvalue int = 1\n" + + "\\.+ Hello world!\n\n" + testHelperFailure(c, "Assert(1, checker, msg)", nil, true, log, + func() interface{} { + c.Assert(1, checker, myComment("Hello world!")) + return nil + }) +} + +func (s *HelpersS) TestAssertWithMissingExpected(c *check.C) { + checker := &MyChecker{result: true} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " c\\.Assert\\(1, checker\\)\n" + + "\\.+ Assert\\(myobtained, MyChecker, myexpected\\):\n" + + "\\.+ Wrong number of parameters for MyChecker: " + + "want 3, got 2\n\n" + testHelperFailure(c, "Assert(1, checker, !?)", nil, true, log, + func() interface{} { + c.Assert(1, checker) + return nil + }) +} + +func (s *HelpersS) TestAssertWithError(c *check.C) { + checker := &MyChecker{result: false, error: "Some not so cool data provided!"} + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " c\\.Assert\\(1, checker, 2\\)\n" + + "\\.+ myobtained int = 1\n" + + "\\.+ myexpected int = 2\n" + + "\\.+ Some not so cool data provided!\n\n" + testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, + func() interface{} { + c.Assert(1, checker, 2) + return nil + }) +} + +func (s *HelpersS) TestAssertWithNilChecker(c *check.C) { + log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + + " c\\.Assert\\(1, nil\\)\n" + + "\\.+ Assert\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + + "\\.+ Oops\\.\\. you've provided a nil checker!\n\n" + testHelperFailure(c, "Assert(obtained, nil)", nil, true, log, + func() interface{} { + c.Assert(1, nil) + return nil + }) +} + +// ----------------------------------------------------------------------- +// Ensure that values logged work properly in some interesting cases. + +func (s *HelpersS) TestValueLoggingWithArrays(c *check.C) { + checker := &MyChecker{result: false} + log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + + " return c\\.Check\\(\\[\\]byte{1, 2}, checker, \\[\\]byte{1, 3}\\)\n" + + "\\.+ myobtained \\[\\]uint8 = \\[\\]byte{0x1, 0x2}\n" + + "\\.+ myexpected \\[\\]uint8 = \\[\\]byte{0x1, 0x3}\n\n" + testHelperFailure(c, "Check([]byte{1}, chk, []byte{3})", false, false, log, + func() interface{} { + return c.Check([]byte{1, 2}, checker, []byte{1, 3}) + }) +} + +func (s *HelpersS) TestValueLoggingWithMultiLine(c *check.C) { + checker := &MyChecker{result: false} + log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + + " return c\\.Check\\(\"a\\\\nb\\\\n\", checker, \"a\\\\nb\\\\nc\"\\)\n" + + "\\.+ myobtained string = \"\" \\+\n" + + "\\.+ \"a\\\\n\" \\+\n" + + "\\.+ \"b\\\\n\"\n" + + "\\.+ myexpected string = \"\" \\+\n" + + "\\.+ \"a\\\\n\" \\+\n" + + "\\.+ \"b\\\\n\" \\+\n" + + "\\.+ \"c\"\n\n" + testHelperFailure(c, `Check("a\nb\n", chk, "a\nb\nc")`, false, false, log, + func() interface{} { + return c.Check("a\nb\n", checker, "a\nb\nc") + }) +} + +func (s *HelpersS) TestValueLoggingWithMultiLineException(c *check.C) { + // If the newline is at the end of the string, don't log as multi-line. + checker := &MyChecker{result: false} + log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + + " return c\\.Check\\(\"a b\\\\n\", checker, \"a\\\\nb\"\\)\n" + + "\\.+ myobtained string = \"a b\\\\n\"\n" + + "\\.+ myexpected string = \"\" \\+\n" + + "\\.+ \"a\\\\n\" \\+\n" + + "\\.+ \"b\"\n\n" + testHelperFailure(c, `Check("a b\n", chk, "a\nb")`, false, false, log, + func() interface{} { + return c.Check("a b\n", checker, "a\nb") + }) +} + +// ----------------------------------------------------------------------- +// MakeDir() tests. + +type MkDirHelper struct { + path1 string + path2 string + isDir1 bool + isDir2 bool + isDir3 bool + isDir4 bool +} + +func (s *MkDirHelper) SetUpSuite(c *check.C) { + s.path1 = c.MkDir() + s.isDir1 = isDir(s.path1) +} + +func (s *MkDirHelper) Test(c *check.C) { + s.path2 = c.MkDir() + s.isDir2 = isDir(s.path2) +} + +func (s *MkDirHelper) TearDownSuite(c *check.C) { + s.isDir3 = isDir(s.path1) + s.isDir4 = isDir(s.path2) +} + +func (s *HelpersS) TestMkDir(c *check.C) { + helper := MkDirHelper{} + output := String{} + check.Run(&helper, &check.RunConf{Output: &output}) + c.Assert(output.value, check.Equals, "") + c.Check(helper.isDir1, check.Equals, true) + c.Check(helper.isDir2, check.Equals, true) + c.Check(helper.isDir3, check.Equals, true) + c.Check(helper.isDir4, check.Equals, true) + c.Check(helper.path1, check.Not(check.Equals), + helper.path2) + c.Check(isDir(helper.path1), check.Equals, false) + c.Check(isDir(helper.path2), check.Equals, false) +} + +func isDir(path string) bool { + if stat, err := os.Stat(path); err == nil { + return stat.IsDir() + } + return false +} + +// Concurrent logging should not corrupt the underling buffer. +// Use go test -race to detect the race in this test. +func (s *HelpersS) TestConcurrentLogging(c *check.C) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU())) + var start, stop sync.WaitGroup + start.Add(1) + for i, n := 0, runtime.NumCPU()*2; i < n; i++ { + stop.Add(1) + go func(i int) { + start.Wait() + for j := 0; j < 30; j++ { + c.Logf("Worker %d: line %d", i, j) + } + stop.Done() + }(i) + } + start.Done() + stop.Wait() +} + +// ----------------------------------------------------------------------- +// Test the TestName function + +type TestNameHelper struct { + name1 string + name2 string + name3 string + name4 string + name5 string +} + +func (s *TestNameHelper) SetUpSuite(c *check.C) { s.name1 = c.TestName() } +func (s *TestNameHelper) SetUpTest(c *check.C) { s.name2 = c.TestName() } +func (s *TestNameHelper) Test(c *check.C) { s.name3 = c.TestName() } +func (s *TestNameHelper) TearDownTest(c *check.C) { s.name4 = c.TestName() } +func (s *TestNameHelper) TearDownSuite(c *check.C) { s.name5 = c.TestName() } + +func (s *HelpersS) TestTestName(c *check.C) { + helper := TestNameHelper{} + output := String{} + check.Run(&helper, &check.RunConf{Output: &output}) + c.Check(helper.name1, check.Equals, "") + c.Check(helper.name2, check.Equals, "TestNameHelper.Test") + c.Check(helper.name3, check.Equals, "TestNameHelper.Test") + c.Check(helper.name4, check.Equals, "TestNameHelper.Test") + c.Check(helper.name5, check.Equals, "") +} + +// ----------------------------------------------------------------------- +// A couple of helper functions to test helper functions. :-) + +func testHelperSuccess(c *check.C, name string, expectedResult interface{}, closure func() interface{}) { + var result interface{} + defer (func() { + if err := recover(); err != nil { + panic(err) + } + checkState(c, result, + &expectedState{ + name: name, + result: expectedResult, + failed: false, + log: "", + }) + })() + result = closure() +} + +func testHelperFailure(c *check.C, name string, expectedResult interface{}, shouldStop bool, log string, closure func() interface{}) { + var result interface{} + defer (func() { + if err := recover(); err != nil { + panic(err) + } + checkState(c, result, + &expectedState{ + name: name, + result: expectedResult, + failed: true, + log: log, + }) + })() + result = closure() + if shouldStop { + c.Logf("%s didn't stop when it should", name) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer.go new file mode 100644 index 0000000..e0f7557 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer.go @@ -0,0 +1,168 @@ +package check + +import ( + "bytes" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "os" +) + +func indent(s, with string) (r string) { + eol := true + for i := 0; i != len(s); i++ { + c := s[i] + switch { + case eol && c == '\n' || c == '\r': + case c == '\n' || c == '\r': + eol = true + case eol: + eol = false + s = s[:i] + with + s[i:] + i += len(with) + } + } + return s +} + +func printLine(filename string, line int) (string, error) { + fset := token.NewFileSet() + file, err := os.Open(filename) + if err != nil { + return "", err + } + fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments) + if err != nil { + return "", err + } + config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4} + lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config} + ast.Walk(lp, fnode) + result := lp.output.Bytes() + // Comments leave \n at the end. + n := len(result) + for n > 0 && result[n-1] == '\n' { + n-- + } + return string(result[:n]), nil +} + +type linePrinter struct { + config *printer.Config + fset *token.FileSet + fnode *ast.File + line int + output bytes.Buffer + stmt ast.Stmt +} + +func (lp *linePrinter) emit() bool { + if lp.stmt != nil { + lp.trim(lp.stmt) + lp.printWithComments(lp.stmt) + lp.stmt = nil + return true + } + return false +} + +func (lp *linePrinter) printWithComments(n ast.Node) { + nfirst := lp.fset.Position(n.Pos()).Line + nlast := lp.fset.Position(n.End()).Line + for _, g := range lp.fnode.Comments { + cfirst := lp.fset.Position(g.Pos()).Line + clast := lp.fset.Position(g.End()).Line + if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column { + for _, c := range g.List { + lp.output.WriteString(c.Text) + lp.output.WriteByte('\n') + } + } + if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash { + // The printer will not include the comment if it starts past + // the node itself. Trick it into printing by overlapping the + // slash with the end of the statement. + g.List[0].Slash = n.End() - 1 + } + } + node := &printer.CommentedNode{n, lp.fnode.Comments} + lp.config.Fprint(&lp.output, lp.fset, node) +} + +func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) { + if n == nil { + if lp.output.Len() == 0 { + lp.emit() + } + return nil + } + first := lp.fset.Position(n.Pos()).Line + last := lp.fset.Position(n.End()).Line + if first <= lp.line && last >= lp.line { + // Print the innermost statement containing the line. + if stmt, ok := n.(ast.Stmt); ok { + if _, ok := n.(*ast.BlockStmt); !ok { + lp.stmt = stmt + } + } + if first == lp.line && lp.emit() { + return nil + } + return lp + } + return nil +} + +func (lp *linePrinter) trim(n ast.Node) bool { + stmt, ok := n.(ast.Stmt) + if !ok { + return true + } + line := lp.fset.Position(n.Pos()).Line + if line != lp.line { + return false + } + switch stmt := stmt.(type) { + case *ast.IfStmt: + stmt.Body = lp.trimBlock(stmt.Body) + case *ast.SwitchStmt: + stmt.Body = lp.trimBlock(stmt.Body) + case *ast.TypeSwitchStmt: + stmt.Body = lp.trimBlock(stmt.Body) + case *ast.CaseClause: + stmt.Body = lp.trimList(stmt.Body) + case *ast.CommClause: + stmt.Body = lp.trimList(stmt.Body) + case *ast.BlockStmt: + stmt.List = lp.trimList(stmt.List) + } + return true +} + +func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt { + if !lp.trim(stmt) { + return lp.emptyBlock(stmt) + } + stmt.Rbrace = stmt.Lbrace + return stmt +} + +func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt { + for i := 0; i != len(stmts); i++ { + if !lp.trim(stmts[i]) { + stmts[i] = lp.emptyStmt(stmts[i]) + break + } + } + return stmts +} + +func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt { + return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}} +} + +func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt { + p := n.Pos() + return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p} +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go new file mode 100644 index 0000000..538b2d5 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go @@ -0,0 +1,104 @@ +package check_test + +import ( + . "gopkg.in/check.v1" +) + +var _ = Suite(&PrinterS{}) + +type PrinterS struct{} + +func (s *PrinterS) TestCountSuite(c *C) { + suitesRun += 1 +} + +var printTestFuncLine int + +func init() { + printTestFuncLine = getMyLine() + 3 +} + +func printTestFunc() { + println(1) // Comment1 + if 2 == 2 { // Comment2 + println(3) // Comment3 + } + switch 5 { + case 6: println(6) // Comment6 + println(7) + } + switch interface{}(9).(type) {// Comment9 + case int: println(10) + println(11) + } + select { + case <-(chan bool)(nil): println(14) + println(15) + default: println(16) + println(17) + } + println(19, + 20) + _ = func() { println(21) + println(22) + } + println(24, func() { + println(25) + }) + // Leading comment + // with multiple lines. + println(29) // Comment29 +} + +var printLineTests = []struct { + line int + output string +}{ + {1, "println(1) // Comment1"}, + {2, "if 2 == 2 { // Comment2\n ...\n}"}, + {3, "println(3) // Comment3"}, + {5, "switch 5 {\n...\n}"}, + {6, "case 6:\n println(6) // Comment6\n ..."}, + {7, "println(7)"}, + {9, "switch interface{}(9).(type) { // Comment9\n...\n}"}, + {10, "case int:\n println(10)\n ..."}, + {14, "case <-(chan bool)(nil):\n println(14)\n ..."}, + {15, "println(15)"}, + {16, "default:\n println(16)\n ..."}, + {17, "println(17)"}, + {19, "println(19,\n 20)"}, + {20, "println(19,\n 20)"}, + {21, "_ = func() {\n println(21)\n println(22)\n}"}, + {22, "println(22)"}, + {24, "println(24, func() {\n println(25)\n})"}, + {25, "println(25)"}, + {26, "println(24, func() {\n println(25)\n})"}, + {29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"}, +} + +func (s *PrinterS) TestPrintLine(c *C) { + for _, test := range printLineTests { + output, err := PrintLine("printer_test.go", printTestFuncLine+test.line) + c.Assert(err, IsNil) + c.Assert(output, Equals, test.output) + } +} + +var indentTests = []struct { + in, out string +}{ + {"", ""}, + {"\n", "\n"}, + {"a", ">>>a"}, + {"a\n", ">>>a\n"}, + {"a\nb", ">>>a\n>>>b"}, + {" ", ">>> "}, +} + +func (s *PrinterS) TestIndent(c *C) { + for _, test := range indentTests { + out := Indent(test.in, ">>>") + c.Assert(out, Equals, test.out) + } + +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run.go new file mode 100644 index 0000000..da8fd79 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run.go @@ -0,0 +1,175 @@ +package check + +import ( + "bufio" + "flag" + "fmt" + "os" + "testing" + "time" +) + +// ----------------------------------------------------------------------- +// Test suite registry. + +var allSuites []interface{} + +// Suite registers the given value as a test suite to be run. Any methods +// starting with the Test prefix in the given value will be considered as +// a test method. +func Suite(suite interface{}) interface{} { + allSuites = append(allSuites, suite) + return suite +} + +// ----------------------------------------------------------------------- +// Public running interface. + +var ( + oldFilterFlag = flag.String("gocheck.f", "", "Regular expression selecting which tests and/or suites to run") + oldVerboseFlag = flag.Bool("gocheck.v", false, "Verbose mode") + oldStreamFlag = flag.Bool("gocheck.vv", false, "Super verbose mode (disables output caching)") + oldBenchFlag = flag.Bool("gocheck.b", false, "Run benchmarks") + oldBenchTime = flag.Duration("gocheck.btime", 1*time.Second, "approximate run time for each benchmark") + oldListFlag = flag.Bool("gocheck.list", false, "List the names of all tests that will be run") + oldWorkFlag = flag.Bool("gocheck.work", false, "Display and do not remove the test working directory") + + newFilterFlag = flag.String("check.f", "", "Regular expression selecting which tests and/or suites to run") + newVerboseFlag = flag.Bool("check.v", false, "Verbose mode") + newStreamFlag = flag.Bool("check.vv", false, "Super verbose mode (disables output caching)") + newBenchFlag = flag.Bool("check.b", false, "Run benchmarks") + newBenchTime = flag.Duration("check.btime", 1*time.Second, "approximate run time for each benchmark") + newBenchMem = flag.Bool("check.bmem", false, "Report memory benchmarks") + newListFlag = flag.Bool("check.list", false, "List the names of all tests that will be run") + newWorkFlag = flag.Bool("check.work", false, "Display and do not remove the test working directory") +) + +// TestingT runs all test suites registered with the Suite function, +// printing results to stdout, and reporting any failures back to +// the "testing" package. +func TestingT(testingT *testing.T) { + benchTime := *newBenchTime + if benchTime == 1*time.Second { + benchTime = *oldBenchTime + } + conf := &RunConf{ + Filter: *oldFilterFlag + *newFilterFlag, + Verbose: *oldVerboseFlag || *newVerboseFlag, + Stream: *oldStreamFlag || *newStreamFlag, + Benchmark: *oldBenchFlag || *newBenchFlag, + BenchmarkTime: benchTime, + BenchmarkMem: *newBenchMem, + KeepWorkDir: *oldWorkFlag || *newWorkFlag, + } + if *oldListFlag || *newListFlag { + w := bufio.NewWriter(os.Stdout) + for _, name := range ListAll(conf) { + fmt.Fprintln(w, name) + } + w.Flush() + return + } + result := RunAll(conf) + println(result.String()) + if !result.Passed() { + testingT.Fail() + } +} + +// RunAll runs all test suites registered with the Suite function, using the +// provided run configuration. +func RunAll(runConf *RunConf) *Result { + result := Result{} + for _, suite := range allSuites { + result.Add(Run(suite, runConf)) + } + return &result +} + +// Run runs the provided test suite using the provided run configuration. +func Run(suite interface{}, runConf *RunConf) *Result { + runner := newSuiteRunner(suite, runConf) + return runner.run() +} + +// ListAll returns the names of all the test functions registered with the +// Suite function that will be run with the provided run configuration. +func ListAll(runConf *RunConf) []string { + var names []string + for _, suite := range allSuites { + names = append(names, List(suite, runConf)...) + } + return names +} + +// List returns the names of the test functions in the given +// suite that will be run with the provided run configuration. +func List(suite interface{}, runConf *RunConf) []string { + var names []string + runner := newSuiteRunner(suite, runConf) + for _, t := range runner.tests { + names = append(names, t.String()) + } + return names +} + +// ----------------------------------------------------------------------- +// Result methods. + +func (r *Result) Add(other *Result) { + r.Succeeded += other.Succeeded + r.Skipped += other.Skipped + r.Failed += other.Failed + r.Panicked += other.Panicked + r.FixturePanicked += other.FixturePanicked + r.ExpectedFailures += other.ExpectedFailures + r.Missed += other.Missed + if r.WorkDir != "" && other.WorkDir != "" { + r.WorkDir += ":" + other.WorkDir + } else if other.WorkDir != "" { + r.WorkDir = other.WorkDir + } +} + +func (r *Result) Passed() bool { + return (r.Failed == 0 && r.Panicked == 0 && + r.FixturePanicked == 0 && r.Missed == 0 && + r.RunError == nil) +} + +func (r *Result) String() string { + if r.RunError != nil { + return "ERROR: " + r.RunError.Error() + } + + var value string + if r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 && + r.Missed == 0 { + value = "OK: " + } else { + value = "OOPS: " + } + value += fmt.Sprintf("%d passed", r.Succeeded) + if r.Skipped != 0 { + value += fmt.Sprintf(", %d skipped", r.Skipped) + } + if r.ExpectedFailures != 0 { + value += fmt.Sprintf(", %d expected failures", r.ExpectedFailures) + } + if r.Failed != 0 { + value += fmt.Sprintf(", %d FAILED", r.Failed) + } + if r.Panicked != 0 { + value += fmt.Sprintf(", %d PANICKED", r.Panicked) + } + if r.FixturePanicked != 0 { + value += fmt.Sprintf(", %d FIXTURE-PANICKED", r.FixturePanicked) + } + if r.Missed != 0 { + value += fmt.Sprintf(", %d MISSED", r.Missed) + } + if r.WorkDir != "" { + value += "\nWORK=" + r.WorkDir + } + return value +} diff --git a/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go new file mode 100644 index 0000000..f41fffc --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/Godeps/_workspace/src/gopkg.in/check.v1/run_test.go @@ -0,0 +1,419 @@ +// These tests verify the test running logic. + +package check_test + +import ( + "errors" + . "gopkg.in/check.v1" + "os" + "sync" +) + +var runnerS = Suite(&RunS{}) + +type RunS struct{} + +func (s *RunS) TestCountSuite(c *C) { + suitesRun += 1 +} + +// ----------------------------------------------------------------------- +// Tests ensuring result counting works properly. + +func (s *RunS) TestSuccess(c *C) { + output := String{} + result := Run(&SuccessHelper{}, &RunConf{Output: &output}) + c.Check(result.Succeeded, Equals, 1) + c.Check(result.Failed, Equals, 0) + c.Check(result.Skipped, Equals, 0) + c.Check(result.Panicked, Equals, 0) + c.Check(result.FixturePanicked, Equals, 0) + c.Check(result.Missed, Equals, 0) + c.Check(result.RunError, IsNil) +} + +func (s *RunS) TestFailure(c *C) { + output := String{} + result := Run(&FailHelper{}, &RunConf{Output: &output}) + c.Check(result.Succeeded, Equals, 0) + c.Check(result.Failed, Equals, 1) + c.Check(result.Skipped, Equals, 0) + c.Check(result.Panicked, Equals, 0) + c.Check(result.FixturePanicked, Equals, 0) + c.Check(result.Missed, Equals, 0) + c.Check(result.RunError, IsNil) +} + +func (s *RunS) TestFixture(c *C) { + output := String{} + result := Run(&FixtureHelper{}, &RunConf{Output: &output}) + c.Check(result.Succeeded, Equals, 2) + c.Check(result.Failed, Equals, 0) + c.Check(result.Skipped, Equals, 0) + c.Check(result.Panicked, Equals, 0) + c.Check(result.FixturePanicked, Equals, 0) + c.Check(result.Missed, Equals, 0) + c.Check(result.RunError, IsNil) +} + +func (s *RunS) TestPanicOnTest(c *C) { + output := String{} + helper := &FixtureHelper{panicOn: "Test1"} + result := Run(helper, &RunConf{Output: &output}) + c.Check(result.Succeeded, Equals, 1) + c.Check(result.Failed, Equals, 0) + c.Check(result.Skipped, Equals, 0) + c.Check(result.Panicked, Equals, 1) + c.Check(result.FixturePanicked, Equals, 0) + c.Check(result.Missed, Equals, 0) + c.Check(result.RunError, IsNil) +} + +func (s *RunS) TestPanicOnSetUpTest(c *C) { + output := String{} + helper := &FixtureHelper{panicOn: "SetUpTest"} + result := Run(helper, &RunConf{Output: &output}) + c.Check(result.Succeeded, Equals, 0) + c.Check(result.Failed, Equals, 0) + c.Check(result.Skipped, Equals, 0) + c.Check(result.Panicked, Equals, 0) + c.Check(result.FixturePanicked, Equals, 1) + c.Check(result.Missed, Equals, 2) + c.Check(result.RunError, IsNil) +} + +func (s *RunS) TestPanicOnSetUpSuite(c *C) { + output := String{} + helper := &FixtureHelper{panicOn: "SetUpSuite"} + result := Run(helper, &RunConf{Output: &output}) + c.Check(result.Succeeded, Equals, 0) + c.Check(result.Failed, Equals, 0) + c.Check(result.Skipped, Equals, 0) + c.Check(result.Panicked, Equals, 0) + c.Check(result.FixturePanicked, Equals, 1) + c.Check(result.Missed, Equals, 2) + c.Check(result.RunError, IsNil) +} + +// ----------------------------------------------------------------------- +// Check result aggregation. + +func (s *RunS) TestAdd(c *C) { + result := &Result{ + Succeeded: 1, + Skipped: 2, + Failed: 3, + Panicked: 4, + FixturePanicked: 5, + Missed: 6, + ExpectedFailures: 7, + } + result.Add(&Result{ + Succeeded: 10, + Skipped: 20, + Failed: 30, + Panicked: 40, + FixturePanicked: 50, + Missed: 60, + ExpectedFailures: 70, + }) + c.Check(result.Succeeded, Equals, 11) + c.Check(result.Skipped, Equals, 22) + c.Check(result.Failed, Equals, 33) + c.Check(result.Panicked, Equals, 44) + c.Check(result.FixturePanicked, Equals, 55) + c.Check(result.Missed, Equals, 66) + c.Check(result.ExpectedFailures, Equals, 77) + c.Check(result.RunError, IsNil) +} + +// ----------------------------------------------------------------------- +// Check the Passed() method. + +func (s *RunS) TestPassed(c *C) { + c.Assert((&Result{}).Passed(), Equals, true) + c.Assert((&Result{Succeeded: 1}).Passed(), Equals, true) + c.Assert((&Result{Skipped: 1}).Passed(), Equals, true) + c.Assert((&Result{Failed: 1}).Passed(), Equals, false) + c.Assert((&Result{Panicked: 1}).Passed(), Equals, false) + c.Assert((&Result{FixturePanicked: 1}).Passed(), Equals, false) + c.Assert((&Result{Missed: 1}).Passed(), Equals, false) + c.Assert((&Result{RunError: errors.New("!")}).Passed(), Equals, false) +} + +// ----------------------------------------------------------------------- +// Check that result printing is working correctly. + +func (s *RunS) TestPrintSuccess(c *C) { + result := &Result{Succeeded: 5} + c.Check(result.String(), Equals, "OK: 5 passed") +} + +func (s *RunS) TestPrintFailure(c *C) { + result := &Result{Failed: 5} + c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FAILED") +} + +func (s *RunS) TestPrintSkipped(c *C) { + result := &Result{Skipped: 5} + c.Check(result.String(), Equals, "OK: 0 passed, 5 skipped") +} + +func (s *RunS) TestPrintExpectedFailures(c *C) { + result := &Result{ExpectedFailures: 5} + c.Check(result.String(), Equals, "OK: 0 passed, 5 expected failures") +} + +func (s *RunS) TestPrintPanicked(c *C) { + result := &Result{Panicked: 5} + c.Check(result.String(), Equals, "OOPS: 0 passed, 5 PANICKED") +} + +func (s *RunS) TestPrintFixturePanicked(c *C) { + result := &Result{FixturePanicked: 5} + c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FIXTURE-PANICKED") +} + +func (s *RunS) TestPrintMissed(c *C) { + result := &Result{Missed: 5} + c.Check(result.String(), Equals, "OOPS: 0 passed, 5 MISSED") +} + +func (s *RunS) TestPrintAll(c *C) { + result := &Result{Succeeded: 1, Skipped: 2, ExpectedFailures: 3, + Panicked: 4, FixturePanicked: 5, Missed: 6} + c.Check(result.String(), Equals, + "OOPS: 1 passed, 2 skipped, 3 expected failures, 4 PANICKED, "+ + "5 FIXTURE-PANICKED, 6 MISSED") +} + +func (s *RunS) TestPrintRunError(c *C) { + result := &Result{Succeeded: 1, Failed: 1, + RunError: errors.New("Kaboom!")} + c.Check(result.String(), Equals, "ERROR: Kaboom!") +} + +// ----------------------------------------------------------------------- +// Verify that the method pattern flag works correctly. + +func (s *RunS) TestFilterTestName(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Filter: "Test[91]"} + Run(&helper, &runConf) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 5) +} + +func (s *RunS) TestFilterTestNameWithAll(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Filter: ".*"} + Run(&helper, &runConf) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "SetUpTest") + c.Check(helper.calls[5], Equals, "Test2") + c.Check(helper.calls[6], Equals, "TearDownTest") + c.Check(helper.calls[7], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 8) +} + +func (s *RunS) TestFilterSuiteName(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Filter: "FixtureHelper"} + Run(&helper, &runConf) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test1") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "SetUpTest") + c.Check(helper.calls[5], Equals, "Test2") + c.Check(helper.calls[6], Equals, "TearDownTest") + c.Check(helper.calls[7], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 8) +} + +func (s *RunS) TestFilterSuiteNameAndTestName(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Filter: "FixtureHelper\\.Test2"} + Run(&helper, &runConf) + c.Check(helper.calls[0], Equals, "SetUpSuite") + c.Check(helper.calls[1], Equals, "SetUpTest") + c.Check(helper.calls[2], Equals, "Test2") + c.Check(helper.calls[3], Equals, "TearDownTest") + c.Check(helper.calls[4], Equals, "TearDownSuite") + c.Check(len(helper.calls), Equals, 5) +} + +func (s *RunS) TestFilterAllOut(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Filter: "NotFound"} + Run(&helper, &runConf) + c.Check(len(helper.calls), Equals, 0) +} + +func (s *RunS) TestRequirePartialMatch(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Filter: "est"} + Run(&helper, &runConf) + c.Check(len(helper.calls), Equals, 8) +} + +func (s *RunS) TestFilterError(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Filter: "]["} + result := Run(&helper, &runConf) + c.Check(result.String(), Equals, + "ERROR: Bad filter expression: error parsing regexp: missing closing ]: `[`") + c.Check(len(helper.calls), Equals, 0) +} + +// ----------------------------------------------------------------------- +// Verify that List works correctly. + +func (s *RunS) TestListFiltered(c *C) { + names := List(&FixtureHelper{}, &RunConf{Filter: "1"}) + c.Assert(names, DeepEquals, []string{ + "FixtureHelper.Test1", + }) +} + +func (s *RunS) TestList(c *C) { + names := List(&FixtureHelper{}, &RunConf{}) + c.Assert(names, DeepEquals, []string{ + "FixtureHelper.Test1", + "FixtureHelper.Test2", + }) +} + +// ----------------------------------------------------------------------- +// Verify that verbose mode prints tests which pass as well. + +func (s *RunS) TestVerboseMode(c *C) { + helper := FixtureHelper{} + output := String{} + runConf := RunConf{Output: &output, Verbose: true} + Run(&helper, &runConf) + + expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t *[.0-9]+s\n" + + "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" + + c.Assert(output.value, Matches, expected) +} + +func (s *RunS) TestVerboseModeWithFailBeforePass(c *C) { + helper := FixtureHelper{panicOn: "Test1"} + output := String{} + runConf := RunConf{Output: &output, Verbose: true} + Run(&helper, &runConf) + + expected := "(?s).*PANIC.*\n-+\n" + // Should have an extra line. + "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" + + c.Assert(output.value, Matches, expected) +} + +// ----------------------------------------------------------------------- +// Verify the stream output mode. In this mode there's no output caching. + +type StreamHelper struct { + l2 sync.Mutex + l3 sync.Mutex +} + +func (s *StreamHelper) SetUpSuite(c *C) { + c.Log("0") +} + +func (s *StreamHelper) Test1(c *C) { + c.Log("1") + s.l2.Lock() + s.l3.Lock() + go func() { + s.l2.Lock() // Wait for "2". + c.Log("3") + s.l3.Unlock() + }() +} + +func (s *StreamHelper) Test2(c *C) { + c.Log("2") + s.l2.Unlock() + s.l3.Lock() // Wait for "3". + c.Fail() + c.Log("4") +} + +func (s *RunS) TestStreamMode(c *C) { + helper := &StreamHelper{} + output := String{} + runConf := RunConf{Output: &output, Stream: true} + Run(helper, &runConf) + + expected := "START: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\n0\n" + + "PASS: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\t *[.0-9]+s\n\n" + + "START: run_test\\.go:[0-9]+: StreamHelper\\.Test1\n1\n" + + "PASS: run_test\\.go:[0-9]+: StreamHelper\\.Test1\t *[.0-9]+s\n\n" + + "START: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n2\n3\n4\n" + + "FAIL: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n\n" + + c.Assert(output.value, Matches, expected) +} + +type StreamMissHelper struct{} + +func (s *StreamMissHelper) SetUpSuite(c *C) { + c.Log("0") + c.Fail() +} + +func (s *StreamMissHelper) Test1(c *C) { + c.Log("1") +} + +func (s *RunS) TestStreamModeWithMiss(c *C) { + helper := &StreamMissHelper{} + output := String{} + runConf := RunConf{Output: &output, Stream: true} + Run(helper, &runConf) + + expected := "START: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n0\n" + + "FAIL: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n\n" + + "START: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n" + + "MISS: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n\n" + + c.Assert(output.value, Matches, expected) +} + +// ----------------------------------------------------------------------- +// Verify that that the keep work dir request indeed does so. + +type WorkDirSuite struct {} + +func (s *WorkDirSuite) Test(c *C) { + c.MkDir() +} + +func (s *RunS) TestKeepWorkDir(c *C) { + output := String{} + runConf := RunConf{Output: &output, Verbose: true, KeepWorkDir: true} + result := Run(&WorkDirSuite{}, &runConf) + + c.Assert(result.String(), Matches, ".*\nWORK=" + result.WorkDir) + + stat, err := os.Stat(result.WorkDir) + c.Assert(err, IsNil) + c.Assert(stat.IsDir(), Equals, true) +} diff --git a/vendor/github.com/siddontang/go-mysql/client/auth.go b/vendor/github.com/siddontang/go-mysql/client/auth.go new file mode 100644 index 0000000..217ed05 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/client/auth.go @@ -0,0 +1,139 @@ +package client + +import ( + "bytes" + "encoding/binary" + + "github.com/juju/errors" + . "github.com/siddontang/go-mysql/mysql" +) + +func (c *Conn) readInitialHandshake() error { + data, err := c.ReadPacket() + if err != nil { + return errors.Trace(err) + } + + if data[0] == ERR_HEADER { + return errors.New("read initial handshake error") + } + + if data[0] < MinProtocolVersion { + return errors.Errorf("invalid protocol version %d, must >= 10", data[0]) + } + + //skip mysql version + //mysql version end with 0x00 + pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + + //connection id length is 4 + c.connectionID = uint32(binary.LittleEndian.Uint32(data[pos : pos+4])) + pos += 4 + + c.salt = []byte{} + c.salt = append(c.salt, data[pos:pos+8]...) + + //skip filter + pos += 8 + 1 + + //capability lower 2 bytes + c.capability = uint32(binary.LittleEndian.Uint16(data[pos : pos+2])) + + pos += 2 + + if len(data) > pos { + //skip server charset + //c.charset = data[pos] + pos += 1 + + c.status = binary.LittleEndian.Uint16(data[pos : pos+2]) + pos += 2 + + c.capability = uint32(binary.LittleEndian.Uint16(data[pos:pos+2]))<<16 | c.capability + + pos += 2 + + //skip auth data len or [00] + //skip reserved (all [00]) + pos += 10 + 1 + + // The documentation is ambiguous about the length. + // The official Python library uses the fixed length 12 + // mysql-proxy also use 12 + // which is not documented but seems to work. + c.salt = append(c.salt, data[pos:pos+12]...) + } + + return nil +} + +func (c *Conn) writeAuthHandshake() error { + // Adjust client capability flags based on server support + capability := CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | + CLIENT_LONG_PASSWORD | CLIENT_TRANSACTIONS | CLIENT_LONG_FLAG + + capability &= c.capability + + //packet length + //capbility 4 + //max-packet size 4 + //charset 1 + //reserved all[0] 23 + length := 4 + 4 + 1 + 23 + + //username + length += len(c.user) + 1 + + //we only support secure connection + auth := CalcPassword(c.salt, []byte(c.password)) + + length += 1 + len(auth) + + if len(c.db) > 0 { + capability |= CLIENT_CONNECT_WITH_DB + + length += len(c.db) + 1 + } + + c.capability = capability + + data := make([]byte, length+4) + + //capability [32 bit] + data[4] = byte(capability) + data[5] = byte(capability >> 8) + data[6] = byte(capability >> 16) + data[7] = byte(capability >> 24) + + //MaxPacketSize [32 bit] (none) + //data[8] = 0x00 + //data[9] = 0x00 + //data[10] = 0x00 + //data[11] = 0x00 + + //Charset [1 byte] + //use default collation id 33 here, is utf-8 + data[12] = byte(DEFAULT_COLLATION_ID) + + //Filler [23 bytes] (all 0x00) + pos := 13 + 23 + + //User [null terminated string] + if len(c.user) > 0 { + pos += copy(data[pos:], c.user) + } + //data[pos] = 0x00 + pos++ + + // auth [length encoded integer] + data[pos] = byte(len(auth)) + pos += 1 + copy(data[pos+1:], auth) + + // db [null terminated string] + if len(c.db) > 0 { + pos += copy(data[pos:], c.db) + //data[pos] = 0x00 + } + + return c.WritePacket(data) +} diff --git a/vendor/github.com/siddontang/go-mysql/client/client_test.go b/vendor/github.com/siddontang/go-mysql/client/client_test.go new file mode 100644 index 0000000..dbf5a70 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/client/client_test.go @@ -0,0 +1,331 @@ +package client + +import ( + "flag" + "fmt" + "testing" + + . "gopkg.in/check.v1" + + "github.com/siddontang/go-mysql/mysql" +) + +var testAddr = flag.String("addr", "127.0.0.1:3306", "MySQL server address") +var testUser = flag.String("user", "root", "MySQL user") +var testPassword = flag.String("pass", "", "MySQL password") +var testDB = flag.String("db", "test", "MySQL test database") + +func Test(t *testing.T) { + TestingT(t) +} + +type clientTestSuite struct { + c *Conn +} + +var _ = Suite(&clientTestSuite{}) + +func (s *clientTestSuite) SetUpSuite(c *C) { + var err error + s.c, err = Connect(*testAddr, *testUser, *testPassword, *testDB) + if err != nil { + c.Fatal(err) + } + + s.testConn_CreateTable(c) + s.testStmt_CreateTable(c) +} + +func (s *clientTestSuite) TearDownSuite(c *C) { + if s.c == nil { + return + } + + s.testConn_DropTable(c) + s.testStmt_DropTable(c) + + if s.c != nil { + s.c.Close() + } +} + +func (s *clientTestSuite) testConn_DropTable(c *C) { + _, err := s.c.Execute("drop table if exists mixer_test_conn") + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) testConn_CreateTable(c *C) { + str := `CREATE TABLE IF NOT EXISTS mixer_test_conn ( + id BIGINT(64) UNSIGNED NOT NULL, + str VARCHAR(256), + f DOUBLE, + e enum("test1", "test2"), + u tinyint unsigned, + i tinyint, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8` + + _, err := s.c.Execute(str) + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) TestConn_Ping(c *C) { + err := s.c.Ping() + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) TestConn_Insert(c *C) { + str := `insert into mixer_test_conn (id, str, f, e) values(1, "a", 3.14, "test1")` + + pkg, err := s.c.Execute(str) + c.Assert(err, IsNil) + c.Assert(pkg.AffectedRows, Equals, uint64(1)) +} + +func (s *clientTestSuite) TestConn_Select(c *C) { + str := `select str, f, e from mixer_test_conn where id = 1` + + result, err := s.c.Execute(str) + c.Assert(err, IsNil) + c.Assert(result.Fields, HasLen, 3) + c.Assert(result.Values, HasLen, 1) + + ss, _ := result.GetString(0, 0) + c.Assert(ss, Equals, "a") + + f, _ := result.GetFloat(0, 1) + c.Assert(f, Equals, float64(3.14)) + + e, _ := result.GetString(0, 2) + c.Assert(e, Equals, "test1") + + ss, _ = result.GetStringByName(0, "str") + c.Assert(ss, Equals, "a") + + f, _ = result.GetFloatByName(0, "f") + c.Assert(f, Equals, float64(3.14)) + + e, _ = result.GetStringByName(0, "e") + c.Assert(e, Equals, "test1") +} + +func (s *clientTestSuite) TestConn_Escape(c *C) { + e := `""''\abc` + str := fmt.Sprintf(`insert into mixer_test_conn (id, str) values(5, "%s")`, + mysql.Escape(e)) + + _, err := s.c.Execute(str) + c.Assert(err, IsNil) + + str = `select str from mixer_test_conn where id = ?` + + r, err := s.c.Execute(str, 5) + c.Assert(err, IsNil) + + ss, _ := r.GetString(0, 0) + c.Assert(ss, Equals, e) +} + +func (s *clientTestSuite) TestConn_SetCharset(c *C) { + err := s.c.SetCharset("gb2312") + c.Assert(err, IsNil) + + err = s.c.SetCharset("utf8") + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) testStmt_DropTable(c *C) { + str := `drop table if exists mixer_test_stmt` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + + defer stmt.Close() + + _, err = stmt.Execute() + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) testStmt_CreateTable(c *C) { + str := `CREATE TABLE IF NOT EXISTS mixer_test_stmt ( + id BIGINT(64) UNSIGNED NOT NULL, + str VARCHAR(256), + f DOUBLE, + e enum("test1", "test2"), + u tinyint unsigned, + i tinyint, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + + defer stmt.Close() + + _, err = stmt.Execute() + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) TestStmt_Delete(c *C) { + str := `delete from mixer_test_stmt` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + + defer stmt.Close() + + _, err = stmt.Execute() + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) TestStmt_Insert(c *C) { + str := `insert into mixer_test_stmt (id, str, f, e, u, i) values (?, ?, ?, ?, ?, ?)` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + + defer stmt.Close() + + r, err := stmt.Execute(1, "a", 3.14, "test1", 255, -127) + c.Assert(err, IsNil) + + c.Assert(r.AffectedRows, Equals, uint64(1)) +} + +func (s *clientTestSuite) TestStmt_Select(c *C) { + str := `select str, f, e from mixer_test_stmt where id = ?` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + + defer stmt.Close() + + result, err := stmt.Execute(1) + c.Assert(err, IsNil) + c.Assert(result.Values, HasLen, 1) + c.Assert(result.Fields, HasLen, 3) + + ss, _ := result.GetString(0, 0) + c.Assert(ss, Equals, "a") + + f, _ := result.GetFloat(0, 1) + c.Assert(f, Equals, float64(3.14)) + + e, _ := result.GetString(0, 2) + c.Assert(e, Equals, "test1") + + ss, _ = result.GetStringByName(0, "str") + c.Assert(ss, Equals, "a") + + f, _ = result.GetFloatByName(0, "f") + c.Assert(f, Equals, float64(3.14)) + + e, _ = result.GetStringByName(0, "e") + c.Assert(e, Equals, "test1") + +} + +func (s *clientTestSuite) TestStmt_NULL(c *C) { + str := `insert into mixer_test_stmt (id, str, f, e) values (?, ?, ?, ?)` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + + defer stmt.Close() + + result, err := stmt.Execute(2, nil, 3.14, nil) + c.Assert(err, IsNil) + + c.Assert(result.AffectedRows, Equals, uint64(1)) + + stmt.Close() + + str = `select * from mixer_test_stmt where id = ?` + stmt, err = s.c.Prepare(str) + defer stmt.Close() + + c.Assert(err, IsNil) + + result, err = stmt.Execute(2) + b, err := result.IsNullByName(0, "id") + c.Assert(err, IsNil) + c.Assert(b, Equals, false) + + b, err = result.IsNullByName(0, "str") + c.Assert(err, IsNil) + c.Assert(b, Equals, true) + + b, err = result.IsNullByName(0, "f") + c.Assert(err, IsNil) + c.Assert(b, Equals, false) + + b, err = result.IsNullByName(0, "e") + c.Assert(err, IsNil) + c.Assert(b, Equals, true) +} + +func (s *clientTestSuite) TestStmt_Unsigned(c *C) { + str := `insert into mixer_test_stmt (id, u) values (?, ?)` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + defer stmt.Close() + + result, err := stmt.Execute(3, uint8(255)) + c.Assert(err, IsNil) + c.Assert(result.AffectedRows, Equals, uint64(1)) + + str = `select u from mixer_test_stmt where id = ?` + + stmt, err = s.c.Prepare(str) + c.Assert(err, IsNil) + defer stmt.Close() + + result, err = stmt.Execute(3) + c.Assert(err, IsNil) + + u, err := result.GetUint(0, 0) + c.Assert(err, IsNil) + c.Assert(u, Equals, uint64(255)) +} + +func (s *clientTestSuite) TestStmt_Signed(c *C) { + str := `insert into mixer_test_stmt (id, i) values (?, ?)` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + defer stmt.Close() + + _, err = stmt.Execute(4, 127) + c.Assert(err, IsNil) + + _, err = stmt.Execute(uint64(18446744073709551516), int8(-128)) + c.Assert(err, IsNil) +} + +func (s *clientTestSuite) TestStmt_Trans(c *C) { + _, err := s.c.Execute(`insert into mixer_test_stmt (id, str) values (1002, "abc")`) + c.Assert(err, IsNil) + + err = s.c.Begin() + c.Assert(err, IsNil) + + str := `select str from mixer_test_stmt where id = ?` + + stmt, err := s.c.Prepare(str) + c.Assert(err, IsNil) + + defer stmt.Close() + + _, err = stmt.Execute(1002) + c.Assert(err, IsNil) + + err = s.c.Commit() + c.Assert(err, IsNil) + + r, err := stmt.Execute(1002) + c.Assert(err, IsNil) + + str, _ = r.GetString(0, 0) + c.Assert(str, Equals, `abc`) +} diff --git a/vendor/github.com/siddontang/go-mysql/client/conn.go b/vendor/github.com/siddontang/go-mysql/client/conn.go new file mode 100644 index 0000000..da37648 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/client/conn.go @@ -0,0 +1,246 @@ +package client + +import ( + "fmt" + "net" + "strings" + "time" + + "github.com/juju/errors" + . "github.com/siddontang/go-mysql/mysql" + "github.com/siddontang/go-mysql/packet" +) + +type Conn struct { + *packet.Conn + + user string + password string + db string + + capability uint32 + + status uint16 + + charset string + + salt []byte + + connectionID uint32 +} + +func getNetProto(addr string) string { + proto := "tcp" + if strings.Contains(addr, "/") { + proto = "unix" + } + return proto +} + +// Connect to a MySQL server, addr can be ip:port, or a unix socket domain like /var/sock. +func Connect(addr string, user string, password string, dbName string) (*Conn, error) { + proto := getNetProto(addr) + + c := new(Conn) + + var err error + conn, err := net.DialTimeout(proto, addr, 10*time.Second) + if err != nil { + return nil, errors.Trace(err) + } + + c.Conn = packet.NewConn(conn) + c.user = user + c.password = password + c.db = dbName + + //use default charset here, utf-8 + c.charset = DEFAULT_CHARSET + + if err = c.handshake(); err != nil { + return nil, errors.Trace(err) + } + + return c, nil +} + +func (c *Conn) handshake() error { + var err error + if err = c.readInitialHandshake(); err != nil { + c.Close() + return errors.Trace(err) + } + + if err := c.writeAuthHandshake(); err != nil { + c.Close() + + return errors.Trace(err) + } + + if _, err := c.readOK(); err != nil { + c.Close() + return errors.Trace(err) + } + + return nil +} + +func (c *Conn) Close() error { + return c.Conn.Close() +} + +func (c *Conn) Ping() error { + if err := c.writeCommand(COM_PING); err != nil { + return errors.Trace(err) + } + + if _, err := c.readOK(); err != nil { + return errors.Trace(err) + } + + return nil +} + +func (c *Conn) UseDB(dbName string) error { + if c.db == dbName { + return nil + } + + if err := c.writeCommandStr(COM_INIT_DB, dbName); err != nil { + return errors.Trace(err) + } + + if _, err := c.readOK(); err != nil { + return errors.Trace(err) + } + + c.db = dbName + return nil +} + +func (c *Conn) GetDB() string { + return c.db +} + +func (c *Conn) Execute(command string, args ...interface{}) (*Result, error) { + if len(args) == 0 { + return c.exec(command) + } else { + if s, err := c.Prepare(command); err != nil { + return nil, errors.Trace(err) + } else { + var r *Result + r, err = s.Execute(args...) + s.Close() + return r, err + } + } +} + +func (c *Conn) Begin() error { + _, err := c.exec("BEGIN") + return errors.Trace(err) +} + +func (c *Conn) Commit() error { + _, err := c.exec("COMMIT") + return errors.Trace(err) +} + +func (c *Conn) Rollback() error { + _, err := c.exec("ROLLBACK") + return errors.Trace(err) +} + +func (c *Conn) SetCharset(charset string) error { + if c.charset == charset { + return nil + } + + if _, err := c.exec(fmt.Sprintf("SET NAMES %s", charset)); err != nil { + return errors.Trace(err) + } else { + c.charset = charset + return nil + } +} + +func (c *Conn) FieldList(table string, wildcard string) ([]*Field, error) { + if err := c.writeCommandStrStr(COM_FIELD_LIST, table, wildcard); err != nil { + return nil, errors.Trace(err) + } + + data, err := c.ReadPacket() + if err != nil { + return nil, errors.Trace(err) + } + + fs := make([]*Field, 0, 4) + var f *Field + if data[0] == ERR_HEADER { + return nil, c.handleErrorPacket(data) + } else { + for { + if data, err = c.ReadPacket(); err != nil { + return nil, errors.Trace(err) + } + + // EOF Packet + if c.isEOFPacket(data) { + return fs, nil + } + + if f, err = FieldData(data).Parse(); err != nil { + return nil, errors.Trace(err) + } + fs = append(fs, f) + } + } + return nil, fmt.Errorf("field list error") +} + +func (c *Conn) SetAutoCommit() error { + if !c.IsAutoCommit() { + if _, err := c.exec("SET AUTOCOMMIT = 1"); err != nil { + return errors.Trace(err) + } + } + return nil +} + +func (c *Conn) IsAutoCommit() bool { + return c.status&SERVER_STATUS_AUTOCOMMIT > 0 +} + +func (c *Conn) IsInTransaction() bool { + return c.status&SERVER_STATUS_IN_TRANS > 0 +} + +func (c *Conn) GetCharset() string { + return c.charset +} + +func (c *Conn) GetConnectionID() uint32 { + return c.connectionID +} + +func (c *Conn) HandleOKPacket(data []byte) *Result { + r, _ := c.handleOKPacket(data) + return r +} + +func (c *Conn) HandleErrorPacket(data []byte) error { + return c.handleErrorPacket(data) +} + +func (c *Conn) ReadOKPacket() (*Result, error) { + return c.readOK() +} + +func (c *Conn) exec(query string) (*Result, error) { + if err := c.writeCommandStr(COM_QUERY, query); err != nil { + return nil, errors.Trace(err) + } + + return c.readResult(false) +} diff --git a/vendor/github.com/siddontang/go-mysql/client/req.go b/vendor/github.com/siddontang/go-mysql/client/req.go new file mode 100644 index 0000000..dde03e7 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/client/req.go @@ -0,0 +1,72 @@ +package client + +func (c *Conn) writeCommand(command byte) error { + c.ResetSequence() + + return c.WritePacket([]byte{ + 0x01, //1 bytes long + 0x00, + 0x00, + 0x00, //sequence + command, + }) +} + +func (c *Conn) writeCommandBuf(command byte, arg []byte) error { + c.ResetSequence() + + length := len(arg) + 1 + + data := make([]byte, length+4) + + data[4] = command + + copy(data[5:], arg) + + return c.WritePacket(data) +} + +func (c *Conn) writeCommandStr(command byte, arg string) error { + c.ResetSequence() + + length := len(arg) + 1 + + data := make([]byte, length+4) + + data[4] = command + + copy(data[5:], arg) + + return c.WritePacket(data) +} + +func (c *Conn) writeCommandUint32(command byte, arg uint32) error { + c.ResetSequence() + + return c.WritePacket([]byte{ + 0x05, //5 bytes long + 0x00, + 0x00, + 0x00, //sequence + + command, + + byte(arg), + byte(arg >> 8), + byte(arg >> 16), + byte(arg >> 24), + }) +} + +func (c *Conn) writeCommandStrStr(command byte, arg1 string, arg2 string) error { + c.ResetSequence() + + data := make([]byte, 4, 6+len(arg1)+len(arg2)) + + data = append(data, command) + data = append(data, arg1...) + data = append(data, 0) + data = append(data, arg2...) + + return c.WritePacket(data) +} diff --git a/vendor/github.com/siddontang/go-mysql/client/resp.go b/vendor/github.com/siddontang/go-mysql/client/resp.go new file mode 100644 index 0000000..4e5f855 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/client/resp.go @@ -0,0 +1,219 @@ +package client + +import ( + "encoding/binary" + + "github.com/juju/errors" + . "github.com/siddontang/go-mysql/mysql" + "github.com/siddontang/go/hack" +) + +func (c *Conn) readUntilEOF() (err error) { + var data []byte + + for { + data, err = c.ReadPacket() + + if err != nil { + return + } + + // EOF Packet + if c.isEOFPacket(data) { + return + } + } + return +} + +func (c *Conn) isEOFPacket(data []byte) bool { + return data[0] == EOF_HEADER && len(data) <= 5 +} + +func (c *Conn) handleOKPacket(data []byte) (*Result, error) { + var n int + var pos int = 1 + + r := new(Result) + + r.AffectedRows, _, n = LengthEncodedInt(data[pos:]) + pos += n + r.InsertId, _, n = LengthEncodedInt(data[pos:]) + pos += n + + if c.capability&CLIENT_PROTOCOL_41 > 0 { + r.Status = binary.LittleEndian.Uint16(data[pos:]) + c.status = r.Status + pos += 2 + + //todo:strict_mode, check warnings as error + //Warnings := binary.LittleEndian.Uint16(data[pos:]) + //pos += 2 + } else if c.capability&CLIENT_TRANSACTIONS > 0 { + r.Status = binary.LittleEndian.Uint16(data[pos:]) + c.status = r.Status + pos += 2 + } + + //new ok package will check CLIENT_SESSION_TRACK too, but I don't support it now. + + //skip info + return r, nil +} + +func (c *Conn) handleErrorPacket(data []byte) error { + e := new(MyError) + + var pos int = 1 + + e.Code = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + if c.capability&CLIENT_PROTOCOL_41 > 0 { + //skip '#' + pos++ + e.State = hack.String(data[pos : pos+5]) + pos += 5 + } + + e.Message = hack.String(data[pos:]) + + return e +} + +func (c *Conn) readOK() (*Result, error) { + data, err := c.ReadPacket() + if err != nil { + return nil, errors.Trace(err) + } + + if data[0] == OK_HEADER { + return c.handleOKPacket(data) + } else if data[0] == ERR_HEADER { + return nil, c.handleErrorPacket(data) + } else { + return nil, errors.New("invalid ok packet") + } +} + +func (c *Conn) readResult(binary bool) (*Result, error) { + data, err := c.ReadPacket() + if err != nil { + return nil, errors.Trace(err) + } + + if data[0] == OK_HEADER { + return c.handleOKPacket(data) + } else if data[0] == ERR_HEADER { + return nil, c.handleErrorPacket(data) + } else if data[0] == LocalInFile_HEADER { + return nil, ErrMalformPacket + } + + return c.readResultset(data, binary) +} + +func (c *Conn) readResultset(data []byte, binary bool) (*Result, error) { + result := &Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + + Resultset: &Resultset{}, + } + + // column count + count, _, n := LengthEncodedInt(data) + + if n-len(data) != 0 { + return nil, ErrMalformPacket + } + + result.Fields = make([]*Field, count) + result.FieldNames = make(map[string]int, count) + + if err := c.readResultColumns(result); err != nil { + return nil, errors.Trace(err) + } + + if err := c.readResultRows(result, binary); err != nil { + return nil, errors.Trace(err) + } + + return result, nil +} + +func (c *Conn) readResultColumns(result *Result) (err error) { + var i int = 0 + var data []byte + + for { + data, err = c.ReadPacket() + 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 + } + + if i != len(result.Fields) { + err = ErrMalformPacket + } + + return + } + + result.Fields[i], err = FieldData(data).Parse() + if err != nil { + return + } + + result.FieldNames[hack.String(result.Fields[i].Name)] = i + + i++ + } +} + +func (c *Conn) readResultRows(result *Result, isBinary bool) (err error) { + var data []byte + + for { + data, err = c.ReadPacket() + + 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 + } + + result.RowDatas = append(result.RowDatas, data) + } + + result.Values = make([][]interface{}, len(result.RowDatas)) + + for i := range result.Values { + result.Values[i], err = result.RowDatas[i].Parse(result.Fields, isBinary) + + if err != nil { + return errors.Trace(err) + } + } + + return nil +} diff --git a/vendor/github.com/siddontang/go-mysql/client/stmt.go b/vendor/github.com/siddontang/go-mysql/client/stmt.go new file mode 100644 index 0000000..fd2dfd7 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/client/stmt.go @@ -0,0 +1,216 @@ +package client + +import ( + "encoding/binary" + "fmt" + "math" + + "github.com/juju/errors" + . "github.com/siddontang/go-mysql/mysql" +) + +type Stmt struct { + conn *Conn + id uint32 + query string + + params int + columns int +} + +func (s *Stmt) ParamNum() int { + return s.params +} + +func (s *Stmt) ColumnNum() int { + return s.columns +} + +func (s *Stmt) Execute(args ...interface{}) (*Result, error) { + if err := s.write(args...); err != nil { + return nil, errors.Trace(err) + } + + return s.conn.readResult(true) +} + +func (s *Stmt) Close() error { + if err := s.conn.writeCommandUint32(COM_STMT_CLOSE, s.id); err != nil { + return errors.Trace(err) + } + + return nil +} + +func (s *Stmt) write(args ...interface{}) error { + paramsNum := s.params + + if len(args) != paramsNum { + return fmt.Errorf("argument mismatch, need %d but got %d", s.params, len(args)) + } + + paramTypes := make([]byte, paramsNum<<1) + paramValues := make([][]byte, paramsNum) + + //NULL-bitmap, length: (num-params+7) + nullBitmap := make([]byte, (paramsNum+7)>>3) + + var length int = int(1 + 4 + 1 + 4 + ((paramsNum + 7) >> 3) + 1 + (paramsNum << 1)) + + var newParamBoundFlag byte = 0 + + for i := range args { + if args[i] == nil { + nullBitmap[i/8] |= (1 << (uint(i) % 8)) + paramTypes[i<<1] = MYSQL_TYPE_NULL + continue + } + + newParamBoundFlag = 1 + + switch v := args[i].(type) { + case int8: + paramTypes[i<<1] = MYSQL_TYPE_TINY + paramValues[i] = []byte{byte(v)} + case int16: + paramTypes[i<<1] = MYSQL_TYPE_SHORT + paramValues[i] = Uint16ToBytes(uint16(v)) + case int32: + paramTypes[i<<1] = MYSQL_TYPE_LONG + paramValues[i] = Uint32ToBytes(uint32(v)) + case int: + paramTypes[i<<1] = MYSQL_TYPE_LONGLONG + paramValues[i] = Uint64ToBytes(uint64(v)) + case int64: + paramTypes[i<<1] = MYSQL_TYPE_LONGLONG + paramValues[i] = Uint64ToBytes(uint64(v)) + case uint8: + paramTypes[i<<1] = MYSQL_TYPE_TINY + paramTypes[(i<<1)+1] = 0x80 + paramValues[i] = []byte{v} + case uint16: + paramTypes[i<<1] = MYSQL_TYPE_SHORT + paramTypes[(i<<1)+1] = 0x80 + paramValues[i] = Uint16ToBytes(uint16(v)) + case uint32: + paramTypes[i<<1] = MYSQL_TYPE_LONG + paramTypes[(i<<1)+1] = 0x80 + paramValues[i] = Uint32ToBytes(uint32(v)) + case uint: + paramTypes[i<<1] = MYSQL_TYPE_LONGLONG + paramTypes[(i<<1)+1] = 0x80 + paramValues[i] = Uint64ToBytes(uint64(v)) + case uint64: + paramTypes[i<<1] = MYSQL_TYPE_LONGLONG + paramTypes[(i<<1)+1] = 0x80 + paramValues[i] = Uint64ToBytes(uint64(v)) + case bool: + paramTypes[i<<1] = MYSQL_TYPE_TINY + if v { + paramValues[i] = []byte{1} + } else { + paramValues[i] = []byte{0} + + } + case float32: + paramTypes[i<<1] = MYSQL_TYPE_FLOAT + paramValues[i] = Uint32ToBytes(math.Float32bits(v)) + case float64: + paramTypes[i<<1] = MYSQL_TYPE_DOUBLE + paramValues[i] = Uint64ToBytes(math.Float64bits(v)) + case string: + paramTypes[i<<1] = MYSQL_TYPE_STRING + paramValues[i] = append(PutLengthEncodedInt(uint64(len(v))), v...) + case []byte: + paramTypes[i<<1] = MYSQL_TYPE_STRING + paramValues[i] = append(PutLengthEncodedInt(uint64(len(v))), v...) + default: + return fmt.Errorf("invalid argument type %T", args[i]) + } + + length += len(paramValues[i]) + } + + data := make([]byte, 4, 4+length) + + data = append(data, COM_STMT_EXECUTE) + data = append(data, byte(s.id), byte(s.id>>8), byte(s.id>>16), byte(s.id>>24)) + + //flag: CURSOR_TYPE_NO_CURSOR + data = append(data, 0x00) + + //iteration-count, always 1 + data = append(data, 1, 0, 0, 0) + + if s.params > 0 { + data = append(data, nullBitmap...) + + //new-params-bound-flag + data = append(data, newParamBoundFlag) + + if newParamBoundFlag == 1 { + //type of each parameter, length: num-params * 2 + data = append(data, paramTypes...) + + //value of each parameter + for _, v := range paramValues { + data = append(data, v...) + } + } + } + + s.conn.ResetSequence() + + return s.conn.WritePacket(data) +} + +func (c *Conn) Prepare(query string) (*Stmt, error) { + if err := c.writeCommandStr(COM_STMT_PREPARE, query); err != nil { + return nil, errors.Trace(err) + } + + data, err := c.ReadPacket() + if err != nil { + return nil, errors.Trace(err) + } + + if data[0] == ERR_HEADER { + return nil, c.handleErrorPacket(data) + } else if data[0] != OK_HEADER { + return nil, ErrMalformPacket + } + + s := new(Stmt) + s.conn = c + + pos := 1 + + //for statement id + s.id = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + //number columns + s.columns = int(binary.LittleEndian.Uint16(data[pos:])) + pos += 2 + + //number params + s.params = int(binary.LittleEndian.Uint16(data[pos:])) + pos += 2 + + //warnings + //warnings = binary.LittleEndian.Uint16(data[pos:]) + + if s.params > 0 { + if err := s.conn.readUntilEOF(); err != nil { + return nil, errors.Trace(err) + } + } + + if s.columns > 0 { + if err := s.conn.readUntilEOF(); err != nil { + return nil, errors.Trace(err) + } + } + + return s, nil +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/const.go b/vendor/github.com/siddontang/go-mysql/mysql/const.go new file mode 100644 index 0000000..cd15642 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/const.go @@ -0,0 +1,163 @@ +package mysql + +const ( + MinProtocolVersion byte = 10 + MaxPayloadLen int = 1<<24 - 1 + TimeFormat string = "2006-01-02 15:04:05" +) + +var ( + // maybe you can change for your specified name + ServerVersion string = "go-mysql-0.1" +) + +const ( + OK_HEADER byte = 0x00 + ERR_HEADER byte = 0xff + EOF_HEADER byte = 0xfe + LocalInFile_HEADER byte = 0xfb +) + +const ( + SERVER_STATUS_IN_TRANS uint16 = 0x0001 + SERVER_STATUS_AUTOCOMMIT uint16 = 0x0002 + SERVER_MORE_RESULTS_EXISTS uint16 = 0x0008 + SERVER_STATUS_NO_GOOD_INDEX_USED uint16 = 0x0010 + SERVER_STATUS_NO_INDEX_USED uint16 = 0x0020 + SERVER_STATUS_CURSOR_EXISTS uint16 = 0x0040 + SERVER_STATUS_LAST_ROW_SEND uint16 = 0x0080 + SERVER_STATUS_DB_DROPPED uint16 = 0x0100 + SERVER_STATUS_NO_BACKSLASH_ESCAPED uint16 = 0x0200 + SERVER_STATUS_METADATA_CHANGED uint16 = 0x0400 + SERVER_QUERY_WAS_SLOW uint16 = 0x0800 + SERVER_PS_OUT_PARAMS uint16 = 0x1000 +) + +const ( + COM_SLEEP byte = iota + COM_QUIT + COM_INIT_DB + COM_QUERY + COM_FIELD_LIST + COM_CREATE_DB + COM_DROP_DB + COM_REFRESH + COM_SHUTDOWN + COM_STATISTICS + COM_PROCESS_INFO + COM_CONNECT + COM_PROCESS_KILL + COM_DEBUG + COM_PING + COM_TIME + COM_DELAYED_INSERT + COM_CHANGE_USER + COM_BINLOG_DUMP + COM_TABLE_DUMP + COM_CONNECT_OUT + COM_REGISTER_SLAVE + COM_STMT_PREPARE + COM_STMT_EXECUTE + COM_STMT_SEND_LONG_DATA + COM_STMT_CLOSE + COM_STMT_RESET + COM_SET_OPTION + COM_STMT_FETCH + COM_DAEMON + COM_BINLOG_DUMP_GTID + COM_RESET_CONNECTION +) + +const ( + CLIENT_LONG_PASSWORD uint32 = 1 << iota + CLIENT_FOUND_ROWS + CLIENT_LONG_FLAG + CLIENT_CONNECT_WITH_DB + CLIENT_NO_SCHEMA + CLIENT_COMPRESS + CLIENT_ODBC + CLIENT_LOCAL_FILES + CLIENT_IGNORE_SPACE + CLIENT_PROTOCOL_41 + CLIENT_INTERACTIVE + CLIENT_SSL + CLIENT_IGNORE_SIGPIPE + CLIENT_TRANSACTIONS + CLIENT_RESERVED + CLIENT_SECURE_CONNECTION + CLIENT_MULTI_STATEMENTS + CLIENT_MULTI_RESULTS + CLIENT_PS_MULTI_RESULTS + CLIENT_PLUGIN_AUTH + CLIENT_CONNECT_ATTRS + CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA +) + +const ( + MYSQL_TYPE_DECIMAL byte = iota + MYSQL_TYPE_TINY + MYSQL_TYPE_SHORT + MYSQL_TYPE_LONG + MYSQL_TYPE_FLOAT + MYSQL_TYPE_DOUBLE + MYSQL_TYPE_NULL + MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_LONGLONG + MYSQL_TYPE_INT24 + MYSQL_TYPE_DATE + MYSQL_TYPE_TIME + MYSQL_TYPE_DATETIME + MYSQL_TYPE_YEAR + MYSQL_TYPE_NEWDATE + MYSQL_TYPE_VARCHAR + MYSQL_TYPE_BIT + + //mysql 5.6 + MYSQL_TYPE_TIMESTAMP2 + MYSQL_TYPE_DATETIME2 + MYSQL_TYPE_TIME2 +) + +const ( + MYSQL_TYPE_NEWDECIMAL byte = iota + 0xf6 + MYSQL_TYPE_ENUM + MYSQL_TYPE_SET + MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_MEDIUM_BLOB + MYSQL_TYPE_LONG_BLOB + MYSQL_TYPE_BLOB + MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_STRING + MYSQL_TYPE_GEOMETRY +) + +const ( + NOT_NULL_FLAG = 1 + PRI_KEY_FLAG = 2 + UNIQUE_KEY_FLAG = 4 + BLOB_FLAG = 16 + UNSIGNED_FLAG = 32 + ZEROFILL_FLAG = 64 + BINARY_FLAG = 128 + ENUM_FLAG = 256 + AUTO_INCREMENT_FLAG = 512 + TIMESTAMP_FLAG = 1024 + SET_FLAG = 2048 + NUM_FLAG = 32768 + PART_KEY_FLAG = 16384 + GROUP_FLAG = 32768 + UNIQUE_FLAG = 65536 +) + +const ( + AUTH_NAME = "mysql_native_password" + DEFAULT_CHARSET = "utf8" + DEFAULT_COLLATION_ID uint8 = 33 + DEFAULT_COLLATION_NAME string = "utf8_general_ci" +) + +// Like vitess, use flavor for different MySQL versions, +const ( + MySQLFlavor = "mysql" + MariaDBFlavor = "mariadb" +) diff --git a/vendor/github.com/siddontang/go-mysql/mysql/errcode.go b/vendor/github.com/siddontang/go-mysql/mysql/errcode.go new file mode 100644 index 0000000..8acff1a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/errcode.go @@ -0,0 +1,870 @@ +package mysql + +const ( + ER_ERROR_FIRST uint16 = 1000 + ER_HASHCHK = 1000 + ER_NISAMCHK = 1001 + ER_NO = 1002 + ER_YES = 1003 + ER_CANT_CREATE_FILE = 1004 + ER_CANT_CREATE_TABLE = 1005 + ER_CANT_CREATE_DB = 1006 + ER_DB_CREATE_EXISTS = 1007 + ER_DB_DROP_EXISTS = 1008 + ER_DB_DROP_DELETE = 1009 + ER_DB_DROP_RMDIR = 1010 + ER_CANT_DELETE_FILE = 1011 + ER_CANT_FIND_SYSTEM_REC = 1012 + ER_CANT_GET_STAT = 1013 + ER_CANT_GET_WD = 1014 + ER_CANT_LOCK = 1015 + ER_CANT_OPEN_FILE = 1016 + ER_FILE_NOT_FOUND = 1017 + ER_CANT_READ_DIR = 1018 + ER_CANT_SET_WD = 1019 + ER_CHECKREAD = 1020 + ER_DISK_FULL = 1021 + ER_DUP_KEY = 1022 + ER_ERROR_ON_CLOSE = 1023 + ER_ERROR_ON_READ = 1024 + ER_ERROR_ON_RENAME = 1025 + ER_ERROR_ON_WRITE = 1026 + ER_FILE_USED = 1027 + ER_FILSORT_ABORT = 1028 + ER_FORM_NOT_FOUND = 1029 + ER_GET_ERRNO = 1030 + ER_ILLEGAL_HA = 1031 + ER_KEY_NOT_FOUND = 1032 + ER_NOT_FORM_FILE = 1033 + ER_NOT_KEYFILE = 1034 + ER_OLD_KEYFILE = 1035 + ER_OPEN_AS_READONLY = 1036 + ER_OUTOFMEMORY = 1037 + ER_OUT_OF_SORTMEMORY = 1038 + ER_UNEXPECTED_EOF = 1039 + ER_CON_COUNT_ERROR = 1040 + ER_OUT_OF_RESOURCES = 1041 + ER_BAD_HOST_ERROR = 1042 + ER_HANDSHAKE_ERROR = 1043 + ER_DBACCESS_DENIED_ERROR = 1044 + ER_ACCESS_DENIED_ERROR = 1045 + ER_NO_DB_ERROR = 1046 + ER_UNKNOWN_COM_ERROR = 1047 + ER_BAD_NULL_ERROR = 1048 + ER_BAD_DB_ERROR = 1049 + ER_TABLE_EXISTS_ERROR = 1050 + ER_BAD_TABLE_ERROR = 1051 + ER_NON_UNIQ_ERROR = 1052 + ER_SERVER_SHUTDOWN = 1053 + ER_BAD_FIELD_ERROR = 1054 + ER_WRONG_FIELD_WITH_GROUP = 1055 + ER_WRONG_GROUP_FIELD = 1056 + ER_WRONG_SUM_SELECT = 1057 + ER_WRONG_VALUE_COUNT = 1058 + ER_TOO_LONG_IDENT = 1059 + ER_DUP_FIELDNAME = 1060 + ER_DUP_KEYNAME = 1061 + ER_DUP_ENTRY = 1062 + ER_WRONG_FIELD_SPEC = 1063 + ER_PARSE_ERROR = 1064 + ER_EMPTY_QUERY = 1065 + ER_NONUNIQ_TABLE = 1066 + ER_INVALID_DEFAULT = 1067 + ER_MULTIPLE_PRI_KEY = 1068 + ER_TOO_MANY_KEYS = 1069 + ER_TOO_MANY_KEY_PARTS = 1070 + ER_TOO_LONG_KEY = 1071 + ER_KEY_COLUMN_DOES_NOT_EXITS = 1072 + ER_BLOB_USED_AS_KEY = 1073 + ER_TOO_BIG_FIELDLENGTH = 1074 + ER_WRONG_AUTO_KEY = 1075 + ER_READY = 1076 + ER_NORMAL_SHUTDOWN = 1077 + ER_GOT_SIGNAL = 1078 + ER_SHUTDOWN_COMPLETE = 1079 + ER_FORCING_CLOSE = 1080 + ER_IPSOCK_ERROR = 1081 + ER_NO_SUCH_INDEX = 1082 + ER_WRONG_FIELD_TERMINATORS = 1083 + ER_BLOBS_AND_NO_TERMINATED = 1084 + ER_TEXTFILE_NOT_READABLE = 1085 + ER_FILE_EXISTS_ERROR = 1086 + ER_LOAD_INFO = 1087 + ER_ALTER_INFO = 1088 + ER_WRONG_SUB_KEY = 1089 + ER_CANT_REMOVE_ALL_FIELDS = 1090 + ER_CANT_DROP_FIELD_OR_KEY = 1091 + ER_INSERT_INFO = 1092 + ER_UPDATE_TABLE_USED = 1093 + ER_NO_SUCH_THREAD = 1094 + ER_KILL_DENIED_ERROR = 1095 + ER_NO_TABLES_USED = 1096 + ER_TOO_BIG_SET = 1097 + ER_NO_UNIQUE_LOGFILE = 1098 + ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099 + ER_TABLE_NOT_LOCKED = 1100 + ER_BLOB_CANT_HAVE_DEFAULT = 1101 + ER_WRONG_DB_NAME = 1102 + ER_WRONG_TABLE_NAME = 1103 + ER_TOO_BIG_SELECT = 1104 + ER_UNKNOWN_ERROR = 1105 + ER_UNKNOWN_PROCEDURE = 1106 + ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 + ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108 + ER_UNKNOWN_TABLE = 1109 + ER_FIELD_SPECIFIED_TWICE = 1110 + ER_INVALID_GROUP_FUNC_USE = 1111 + ER_UNSUPPORTED_EXTENSION = 1112 + ER_TABLE_MUST_HAVE_COLUMNS = 1113 + ER_RECORD_FILE_FULL = 1114 + ER_UNKNOWN_CHARACTER_SET = 1115 + ER_TOO_MANY_TABLES = 1116 + ER_TOO_MANY_FIELDS = 1117 + ER_TOO_BIG_ROWSIZE = 1118 + ER_STACK_OVERRUN = 1119 + ER_WRONG_OUTER_JOIN = 1120 + ER_NULL_COLUMN_IN_INDEX = 1121 + ER_CANT_FIND_UDF = 1122 + ER_CANT_INITIALIZE_UDF = 1123 + ER_UDF_NO_PATHS = 1124 + ER_UDF_EXISTS = 1125 + ER_CANT_OPEN_LIBRARY = 1126 + ER_CANT_FIND_DL_ENTRY = 1127 + ER_FUNCTION_NOT_DEFINED = 1128 + ER_HOST_IS_BLOCKED = 1129 + ER_HOST_NOT_PRIVILEGED = 1130 + ER_PASSWORD_ANONYMOUS_USER = 1131 + ER_PASSWORD_NOT_ALLOWED = 1132 + ER_PASSWORD_NO_MATCH = 1133 + ER_UPDATE_INFO = 1134 + ER_CANT_CREATE_THREAD = 1135 + ER_WRONG_VALUE_COUNT_ON_ROW = 1136 + ER_CANT_REOPEN_TABLE = 1137 + ER_INVALID_USE_OF_NULL = 1138 + ER_REGEXP_ERROR = 1139 + ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 + ER_NONEXISTING_GRANT = 1141 + ER_TABLEACCESS_DENIED_ERROR = 1142 + ER_COLUMNACCESS_DENIED_ERROR = 1143 + ER_ILLEGAL_GRANT_FOR_TABLE = 1144 + ER_GRANT_WRONG_HOST_OR_USER = 1145 + ER_NO_SUCH_TABLE = 1146 + ER_NONEXISTING_TABLE_GRANT = 1147 + ER_NOT_ALLOWED_COMMAND = 1148 + ER_SYNTAX_ERROR = 1149 + ER_DELAYED_CANT_CHANGE_LOCK = 1150 + ER_TOO_MANY_DELAYED_THREADS = 1151 + ER_ABORTING_CONNECTION = 1152 + ER_NET_PACKET_TOO_LARGE = 1153 + ER_NET_READ_ERROR_FROM_PIPE = 1154 + ER_NET_FCNTL_ERROR = 1155 + ER_NET_PACKETS_OUT_OF_ORDER = 1156 + ER_NET_UNCOMPRESS_ERROR = 1157 + ER_NET_READ_ERROR = 1158 + ER_NET_READ_INTERRUPTED = 1159 + ER_NET_ERROR_ON_WRITE = 1160 + ER_NET_WRITE_INTERRUPTED = 1161 + ER_TOO_LONG_STRING = 1162 + ER_TABLE_CANT_HANDLE_BLOB = 1163 + ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 + ER_DELAYED_INSERT_TABLE_LOCKED = 1165 + ER_WRONG_COLUMN_NAME = 1166 + ER_WRONG_KEY_COLUMN = 1167 + ER_WRONG_MRG_TABLE = 1168 + ER_DUP_UNIQUE = 1169 + ER_BLOB_KEY_WITHOUT_LENGTH = 1170 + ER_PRIMARY_CANT_HAVE_NULL = 1171 + ER_TOO_MANY_ROWS = 1172 + ER_REQUIRES_PRIMARY_KEY = 1173 + ER_NO_RAID_COMPILED = 1174 + ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 + ER_KEY_DOES_NOT_EXITS = 1176 + ER_CHECK_NO_SUCH_TABLE = 1177 + ER_CHECK_NOT_IMPLEMENTED = 1178 + ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 + ER_ERROR_DURING_COMMIT = 1180 + ER_ERROR_DURING_ROLLBACK = 1181 + ER_ERROR_DURING_FLUSH_LOGS = 1182 + ER_ERROR_DURING_CHECKPOINT = 1183 + ER_NEW_ABORTING_CONNECTION = 1184 + ER_DUMP_NOT_IMPLEMENTED = 1185 + ER_FLUSH_MASTER_BINLOG_CLOSED = 1186 + ER_INDEX_REBUILD = 1187 + ER_MASTER = 1188 + ER_MASTER_NET_READ = 1189 + ER_MASTER_NET_WRITE = 1190 + ER_FT_MATCHING_KEY_NOT_FOUND = 1191 + ER_LOCK_OR_ACTIVE_TRANSACTION = 1192 + ER_UNKNOWN_SYSTEM_VARIABLE = 1193 + ER_CRASHED_ON_USAGE = 1194 + ER_CRASHED_ON_REPAIR = 1195 + ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196 + ER_TRANS_CACHE_FULL = 1197 + ER_SLAVE_MUST_STOP = 1198 + ER_SLAVE_NOT_RUNNING = 1199 + ER_BAD_SLAVE = 1200 + ER_MASTER_INFO = 1201 + ER_SLAVE_THREAD = 1202 + ER_TOO_MANY_USER_CONNECTIONS = 1203 + ER_SET_CONSTANTS_ONLY = 1204 + ER_LOCK_WAIT_TIMEOUT = 1205 + ER_LOCK_TABLE_FULL = 1206 + ER_READ_ONLY_TRANSACTION = 1207 + ER_DROP_DB_WITH_READ_LOCK = 1208 + ER_CREATE_DB_WITH_READ_LOCK = 1209 + ER_WRONG_ARGUMENTS = 1210 + ER_NO_PERMISSION_TO_CREATE_USER = 1211 + ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212 + ER_LOCK_DEADLOCK = 1213 + ER_TABLE_CANT_HANDLE_FT = 1214 + ER_CANNOT_ADD_FOREIGN = 1215 + ER_NO_REFERENCED_ROW = 1216 + ER_ROW_IS_REFERENCED = 1217 + ER_CONNECT_TO_MASTER = 1218 + ER_QUERY_ON_MASTER = 1219 + ER_ERROR_WHEN_EXECUTING_COMMAND = 1220 + ER_WRONG_USAGE = 1221 + ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222 + ER_CANT_UPDATE_WITH_READLOCK = 1223 + ER_MIXING_NOT_ALLOWED = 1224 + ER_DUP_ARGUMENT = 1225 + ER_USER_LIMIT_REACHED = 1226 + ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227 + ER_LOCAL_VARIABLE = 1228 + ER_GLOBAL_VARIABLE = 1229 + ER_NO_DEFAULT = 1230 + ER_WRONG_VALUE_FOR_VAR = 1231 + ER_WRONG_TYPE_FOR_VAR = 1232 + ER_VAR_CANT_BE_READ = 1233 + ER_CANT_USE_OPTION_HERE = 1234 + ER_NOT_SUPPORTED_YET = 1235 + ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236 + ER_SLAVE_IGNORED_TABLE = 1237 + ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238 + ER_WRONG_FK_DEF = 1239 + ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240 + ER_OPERAND_COLUMNS = 1241 + ER_SUBQUERY_NO_1_ROW = 1242 + ER_UNKNOWN_STMT_HANDLER = 1243 + ER_CORRUPT_HELP_DB = 1244 + ER_CYCLIC_REFERENCE = 1245 + ER_AUTO_CONVERT = 1246 + ER_ILLEGAL_REFERENCE = 1247 + ER_DERIVED_MUST_HAVE_ALIAS = 1248 + ER_SELECT_REDUCED = 1249 + ER_TABLENAME_NOT_ALLOWED_HERE = 1250 + ER_NOT_SUPPORTED_AUTH_MODE = 1251 + ER_SPATIAL_CANT_HAVE_NULL = 1252 + ER_COLLATION_CHARSET_MISMATCH = 1253 + ER_SLAVE_WAS_RUNNING = 1254 + ER_SLAVE_WAS_NOT_RUNNING = 1255 + ER_TOO_BIG_FOR_UNCOMPRESS = 1256 + ER_ZLIB_Z_MEM_ERROR = 1257 + ER_ZLIB_Z_BUF_ERROR = 1258 + ER_ZLIB_Z_DATA_ERROR = 1259 + ER_CUT_VALUE_GROUP_CONCAT = 1260 + ER_WARN_TOO_FEW_RECORDS = 1261 + ER_WARN_TOO_MANY_RECORDS = 1262 + ER_WARN_NULL_TO_NOTNULL = 1263 + ER_WARN_DATA_OUT_OF_RANGE = 1264 + WARN_DATA_TRUNCATED = 1265 + ER_WARN_USING_OTHER_HANDLER = 1266 + ER_CANT_AGGREGATE_2COLLATIONS = 1267 + ER_DROP_USER = 1268 + ER_REVOKE_GRANTS = 1269 + ER_CANT_AGGREGATE_3COLLATIONS = 1270 + ER_CANT_AGGREGATE_NCOLLATIONS = 1271 + ER_VARIABLE_IS_NOT_STRUCT = 1272 + ER_UNKNOWN_COLLATION = 1273 + ER_SLAVE_IGNORED_SSL_PARAMS = 1274 + ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275 + ER_WARN_FIELD_RESOLVED = 1276 + ER_BAD_SLAVE_UNTIL_COND = 1277 + ER_MISSING_SKIP_SLAVE = 1278 + ER_UNTIL_COND_IGNORED = 1279 + ER_WRONG_NAME_FOR_INDEX = 1280 + ER_WRONG_NAME_FOR_CATALOG = 1281 + ER_WARN_QC_RESIZE = 1282 + ER_BAD_FT_COLUMN = 1283 + ER_UNKNOWN_KEY_CACHE = 1284 + ER_WARN_HOSTNAME_WONT_WORK = 1285 + ER_UNKNOWN_STORAGE_ENGINE = 1286 + ER_WARN_DEPRECATED_SYNTAX = 1287 + ER_NON_UPDATABLE_TABLE = 1288 + ER_FEATURE_DISABLED = 1289 + ER_OPTION_PREVENTS_STATEMENT = 1290 + ER_DUPLICATED_VALUE_IN_TYPE = 1291 + ER_TRUNCATED_WRONG_VALUE = 1292 + ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293 + ER_INVALID_ON_UPDATE = 1294 + ER_UNSUPPORTED_PS = 1295 + ER_GET_ERRMSG = 1296 + ER_GET_TEMPORARY_ERRMSG = 1297 + ER_UNKNOWN_TIME_ZONE = 1298 + ER_WARN_INVALID_TIMESTAMP = 1299 + ER_INVALID_CHARACTER_STRING = 1300 + ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301 + ER_CONFLICTING_DECLARATIONS = 1302 + ER_SP_NO_RECURSIVE_CREATE = 1303 + ER_SP_ALREADY_EXISTS = 1304 + ER_SP_DOES_NOT_EXIST = 1305 + ER_SP_DROP_FAILED = 1306 + ER_SP_STORE_FAILED = 1307 + ER_SP_LILABEL_MISMATCH = 1308 + ER_SP_LABEL_REDEFINE = 1309 + ER_SP_LABEL_MISMATCH = 1310 + ER_SP_UNINIT_VAR = 1311 + ER_SP_BADSELECT = 1312 + ER_SP_BADRETURN = 1313 + ER_SP_BADSTATEMENT = 1314 + ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315 + ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316 + ER_QUERY_INTERRUPTED = 1317 + ER_SP_WRONG_NO_OF_ARGS = 1318 + ER_SP_COND_MISMATCH = 1319 + ER_SP_NORETURN = 1320 + ER_SP_NORETURNEND = 1321 + ER_SP_BAD_CURSOR_QUERY = 1322 + ER_SP_BAD_CURSOR_SELECT = 1323 + ER_SP_CURSOR_MISMATCH = 1324 + ER_SP_CURSOR_ALREADY_OPEN = 1325 + ER_SP_CURSOR_NOT_OPEN = 1326 + ER_SP_UNDECLARED_VAR = 1327 + ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328 + ER_SP_FETCH_NO_DATA = 1329 + ER_SP_DUP_PARAM = 1330 + ER_SP_DUP_VAR = 1331 + ER_SP_DUP_COND = 1332 + ER_SP_DUP_CURS = 1333 + ER_SP_CANT_ALTER = 1334 + ER_SP_SUBSELECT_NYI = 1335 + ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336 + ER_SP_VARCOND_AFTER_CURSHNDLR = 1337 + ER_SP_CURSOR_AFTER_HANDLER = 1338 + ER_SP_CASE_NOT_FOUND = 1339 + ER_FPARSER_TOO_BIG_FILE = 1340 + ER_FPARSER_BAD_HEADER = 1341 + ER_FPARSER_EOF_IN_COMMENT = 1342 + ER_FPARSER_ERROR_IN_PARAMETER = 1343 + ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344 + ER_VIEW_NO_EXPLAIN = 1345 + ER_FRM_UNKNOWN_TYPE = 1346 + ER_WRONG_OBJECT = 1347 + ER_NONUPDATEABLE_COLUMN = 1348 + ER_VIEW_SELECT_DERIVED = 1349 + ER_VIEW_SELECT_CLAUSE = 1350 + ER_VIEW_SELECT_VARIABLE = 1351 + ER_VIEW_SELECT_TMPTABLE = 1352 + ER_VIEW_WRONG_LIST = 1353 + ER_WARN_VIEW_MERGE = 1354 + ER_WARN_VIEW_WITHOUT_KEY = 1355 + ER_VIEW_INVALID = 1356 + ER_SP_NO_DROP_SP = 1357 + ER_SP_GOTO_IN_HNDLR = 1358 + ER_TRG_ALREADY_EXISTS = 1359 + ER_TRG_DOES_NOT_EXIST = 1360 + ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361 + ER_TRG_CANT_CHANGE_ROW = 1362 + ER_TRG_NO_SUCH_ROW_IN_TRG = 1363 + ER_NO_DEFAULT_FOR_FIELD = 1364 + ER_DIVISION_BY_ZERO = 1365 + ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366 + ER_ILLEGAL_VALUE_FOR_TYPE = 1367 + ER_VIEW_NONUPD_CHECK = 1368 + ER_VIEW_CHECK_FAILED = 1369 + ER_PROCACCESS_DENIED_ERROR = 1370 + ER_RELAY_LOG_FAIL = 1371 + ER_PASSWD_LENGTH = 1372 + ER_UNKNOWN_TARGET_BINLOG = 1373 + ER_IO_ERR_LOG_INDEX_READ = 1374 + ER_BINLOG_PURGE_PROHIBITED = 1375 + ER_FSEEK_FAIL = 1376 + ER_BINLOG_PURGE_FATAL_ERR = 1377 + ER_LOG_IN_USE = 1378 + ER_LOG_PURGE_UNKNOWN_ERR = 1379 + ER_RELAY_LOG_INIT = 1380 + ER_NO_BINARY_LOGGING = 1381 + ER_RESERVED_SYNTAX = 1382 + ER_WSAS_FAILED = 1383 + ER_DIFF_GROUPS_PROC = 1384 + ER_NO_GROUP_FOR_PROC = 1385 + ER_ORDER_WITH_PROC = 1386 + ER_LOGGING_PROHIBIT_CHANGING_OF = 1387 + ER_NO_FILE_MAPPING = 1388 + ER_WRONG_MAGIC = 1389 + ER_PS_MANY_PARAM = 1390 + ER_KEY_PART_0 = 1391 + ER_VIEW_CHECKSUM = 1392 + ER_VIEW_MULTIUPDATE = 1393 + ER_VIEW_NO_INSERT_FIELD_LIST = 1394 + ER_VIEW_DELETE_MERGE_VIEW = 1395 + ER_CANNOT_USER = 1396 + ER_XAER_NOTA = 1397 + ER_XAER_INVAL = 1398 + ER_XAER_RMFAIL = 1399 + ER_XAER_OUTSIDE = 1400 + ER_XAER_RMERR = 1401 + ER_XA_RBROLLBACK = 1402 + ER_NONEXISTING_PROC_GRANT = 1403 + ER_PROC_AUTO_GRANT_FAIL = 1404 + ER_PROC_AUTO_REVOKE_FAIL = 1405 + ER_DATA_TOO_LONG = 1406 + ER_SP_BAD_SQLSTATE = 1407 + ER_STARTUP = 1408 + ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409 + ER_CANT_CREATE_USER_WITH_GRANT = 1410 + ER_WRONG_VALUE_FOR_TYPE = 1411 + ER_TABLE_DEF_CHANGED = 1412 + ER_SP_DUP_HANDLER = 1413 + ER_SP_NOT_VAR_ARG = 1414 + ER_SP_NO_RETSET = 1415 + ER_CANT_CREATE_GEOMETRY_OBJECT = 1416 + ER_FAILED_ROUTINE_BREAK_BINLOG = 1417 + ER_BINLOG_UNSAFE_ROUTINE = 1418 + ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419 + ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420 + ER_STMT_HAS_NO_OPEN_CURSOR = 1421 + ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422 + ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423 + ER_SP_NO_RECURSION = 1424 + ER_TOO_BIG_SCALE = 1425 + ER_TOO_BIG_PRECISION = 1426 + ER_M_BIGGER_THAN_D = 1427 + ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428 + ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429 + ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430 + ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431 + ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432 + ER_FOREIGN_DATA_STRING_INVALID = 1433 + ER_CANT_CREATE_FEDERATED_TABLE = 1434 + ER_TRG_IN_WRONG_SCHEMA = 1435 + ER_STACK_OVERRUN_NEED_MORE = 1436 + ER_TOO_LONG_BODY = 1437 + ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438 + ER_TOO_BIG_DISPLAYWIDTH = 1439 + ER_XAER_DUPID = 1440 + ER_DATETIME_FUNCTION_OVERFLOW = 1441 + ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442 + ER_VIEW_PREVENT_UPDATE = 1443 + ER_PS_NO_RECURSION = 1444 + ER_SP_CANT_SET_AUTOCOMMIT = 1445 + ER_MALFORMED_DEFINER = 1446 + ER_VIEW_FRM_NO_USER = 1447 + ER_VIEW_OTHER_USER = 1448 + ER_NO_SUCH_USER = 1449 + ER_FORBID_SCHEMA_CHANGE = 1450 + ER_ROW_IS_REFERENCED_2 = 1451 + ER_NO_REFERENCED_ROW_2 = 1452 + ER_SP_BAD_VAR_SHADOW = 1453 + ER_TRG_NO_DEFINER = 1454 + ER_OLD_FILE_FORMAT = 1455 + ER_SP_RECURSION_LIMIT = 1456 + ER_SP_PROC_TABLE_CORRUPT = 1457 + ER_SP_WRONG_NAME = 1458 + ER_TABLE_NEEDS_UPGRADE = 1459 + ER_SP_NO_AGGREGATE = 1460 + ER_MAX_PREPARED_STMT_COUNT_REACHED = 1461 + ER_VIEW_RECURSIVE = 1462 + ER_NON_GROUPING_FIELD_USED = 1463 + ER_TABLE_CANT_HANDLE_SPKEYS = 1464 + ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465 + ER_REMOVED_SPACES = 1466 + ER_AUTOINC_READ_FAILED = 1467 + ER_USERNAME = 1468 + ER_HOSTNAME = 1469 + ER_WRONG_STRING_LENGTH = 1470 + ER_NON_INSERTABLE_TABLE = 1471 + ER_ADMIN_WRONG_MRG_TABLE = 1472 + ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473 + ER_NAME_BECOMES_EMPTY = 1474 + ER_AMBIGUOUS_FIELD_TERM = 1475 + ER_FOREIGN_SERVER_EXISTS = 1476 + ER_FOREIGN_SERVER_DOESNT_EXIST = 1477 + ER_ILLEGAL_HA_CREATE_OPTION = 1478 + ER_PARTITION_REQUIRES_VALUES_ERROR = 1479 + ER_PARTITION_WRONG_VALUES_ERROR = 1480 + ER_PARTITION_MAXVALUE_ERROR = 1481 + ER_PARTITION_SUBPARTITION_ERROR = 1482 + ER_PARTITION_SUBPART_MIX_ERROR = 1483 + ER_PARTITION_WRONG_NO_PART_ERROR = 1484 + ER_PARTITION_WRONG_NO_SUBPART_ERROR = 1485 + ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486 + ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR = 1487 + ER_FIELD_NOT_FOUND_PART_ERROR = 1488 + ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR = 1489 + ER_INCONSISTENT_PARTITION_INFO_ERROR = 1490 + ER_PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491 + ER_PARTITIONS_MUST_BE_DEFINED_ERROR = 1492 + ER_RANGE_NOT_INCREASING_ERROR = 1493 + ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494 + ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495 + ER_PARTITION_ENTRY_ERROR = 1496 + ER_MIX_HANDLER_ERROR = 1497 + ER_PARTITION_NOT_DEFINED_ERROR = 1498 + ER_TOO_MANY_PARTITIONS_ERROR = 1499 + ER_SUBPARTITION_ERROR = 1500 + ER_CANT_CREATE_HANDLER_FILE = 1501 + ER_BLOB_FIELD_IN_PART_FUNC_ERROR = 1502 + ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503 + ER_NO_PARTS_ERROR = 1504 + ER_PARTITION_MGMT_ON_NONPARTITIONED = 1505 + ER_FOREIGN_KEY_ON_PARTITIONED = 1506 + ER_DROP_PARTITION_NON_EXISTENT = 1507 + ER_DROP_LAST_PARTITION = 1508 + ER_COALESCE_ONLY_ON_HASH_PARTITION = 1509 + ER_REORG_HASH_ONLY_ON_SAME_NO = 1510 + ER_REORG_NO_PARAM_ERROR = 1511 + ER_ONLY_ON_RANGE_LIST_PARTITION = 1512 + ER_ADD_PARTITION_SUBPART_ERROR = 1513 + ER_ADD_PARTITION_NO_NEW_PARTITION = 1514 + ER_COALESCE_PARTITION_NO_PARTITION = 1515 + ER_REORG_PARTITION_NOT_EXIST = 1516 + ER_SAME_NAME_PARTITION = 1517 + ER_NO_BINLOG_ERROR = 1518 + ER_CONSECUTIVE_REORG_PARTITIONS = 1519 + ER_REORG_OUTSIDE_RANGE = 1520 + ER_PARTITION_FUNCTION_FAILURE = 1521 + ER_PART_STATE_ERROR = 1522 + ER_LIMITED_PART_RANGE = 1523 + ER_PLUGIN_IS_NOT_LOADED = 1524 + ER_WRONG_VALUE = 1525 + ER_NO_PARTITION_FOR_GIVEN_VALUE = 1526 + ER_FILEGROUP_OPTION_ONLY_ONCE = 1527 + ER_CREATE_FILEGROUP_FAILED = 1528 + ER_DROP_FILEGROUP_FAILED = 1529 + ER_TABLESPACE_AUTO_EXTEND_ERROR = 1530 + ER_WRONG_SIZE_NUMBER = 1531 + ER_SIZE_OVERFLOW_ERROR = 1532 + ER_ALTER_FILEGROUP_FAILED = 1533 + ER_BINLOG_ROW_LOGGING_FAILED = 1534 + ER_BINLOG_ROW_WRONG_TABLE_DEF = 1535 + ER_BINLOG_ROW_RBR_TO_SBR = 1536 + ER_EVENT_ALREADY_EXISTS = 1537 + ER_EVENT_STORE_FAILED = 1538 + ER_EVENT_DOES_NOT_EXIST = 1539 + ER_EVENT_CANT_ALTER = 1540 + ER_EVENT_DROP_FAILED = 1541 + ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542 + ER_EVENT_ENDS_BEFORE_STARTS = 1543 + ER_EVENT_EXEC_TIME_IN_THE_PAST = 1544 + ER_EVENT_OPEN_TABLE_FAILED = 1545 + ER_EVENT_NEITHER_M_EXPR_NOR_M_AT = 1546 + ER_OBSOLETE_COL_COUNT_DOESNT_MATCH_CORRUPTED = 1547 + ER_OBSOLETE_CANNOT_LOAD_FROM_TABLE = 1548 + ER_EVENT_CANNOT_DELETE = 1549 + ER_EVENT_COMPILE_ERROR = 1550 + ER_EVENT_SAME_NAME = 1551 + ER_EVENT_DATA_TOO_LONG = 1552 + ER_DROP_INDEX_FK = 1553 + ER_WARN_DEPRECATED_SYNTAX_WITH_VER = 1554 + ER_CANT_WRITE_LOCK_LOG_TABLE = 1555 + ER_CANT_LOCK_LOG_TABLE = 1556 + ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED = 1557 + ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558 + ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559 + ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560 + ER_NDB_CANT_SWITCH_BINLOG_FORMAT = 1561 + ER_PARTITION_NO_TEMPORARY = 1562 + ER_PARTITION_CONST_DOMAIN_ERROR = 1563 + ER_PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564 + ER_DDL_LOG_ERROR = 1565 + ER_NULL_IN_VALUES_LESS_THAN = 1566 + ER_WRONG_PARTITION_NAME = 1567 + ER_CANT_CHANGE_TX_CHARACTERISTICS = 1568 + ER_DUP_ENTRY_AUTOINCREMENT_CASE = 1569 + ER_EVENT_MODIFY_QUEUE_ERROR = 1570 + ER_EVENT_SET_VAR_ERROR = 1571 + ER_PARTITION_MERGE_ERROR = 1572 + ER_CANT_ACTIVATE_LOG = 1573 + ER_RBR_NOT_AVAILABLE = 1574 + ER_BASE64_DECODE_ERROR = 1575 + ER_EVENT_RECURSION_FORBIDDEN = 1576 + ER_EVENTS_DB_ERROR = 1577 + ER_ONLY_INTEGERS_ALLOWED = 1578 + ER_UNSUPORTED_LOG_ENGINE = 1579 + ER_BAD_LOG_STATEMENT = 1580 + ER_CANT_RENAME_LOG_TABLE = 1581 + ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582 + ER_WRONG_PARAMETERS_TO_NATIVE_FCT = 1583 + ER_WRONG_PARAMETERS_TO_STORED_FCT = 1584 + ER_NATIVE_FCT_NAME_COLLISION = 1585 + ER_DUP_ENTRY_WITH_KEY_NAME = 1586 + ER_BINLOG_PURGE_EMFILE = 1587 + ER_EVENT_CANNOT_CREATE_IN_THE_PAST = 1588 + ER_EVENT_CANNOT_ALTER_IN_THE_PAST = 1589 + ER_SLAVE_INCIDENT = 1590 + ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591 + ER_BINLOG_UNSAFE_STATEMENT = 1592 + ER_SLAVE_FATAL_ERROR = 1593 + ER_SLAVE_RELAY_LOG_READ_FAILURE = 1594 + ER_SLAVE_RELAY_LOG_WRITE_FAILURE = 1595 + ER_SLAVE_CREATE_EVENT_FAILURE = 1596 + ER_SLAVE_MASTER_COM_FAILURE = 1597 + ER_BINLOG_LOGGING_IMPOSSIBLE = 1598 + ER_VIEW_NO_CREATION_CTX = 1599 + ER_VIEW_INVALID_CREATION_CTX = 1600 + ER_SR_INVALID_CREATION_CTX = 1601 + ER_TRG_CORRUPTED_FILE = 1602 + ER_TRG_NO_CREATION_CTX = 1603 + ER_TRG_INVALID_CREATION_CTX = 1604 + ER_EVENT_INVALID_CREATION_CTX = 1605 + ER_TRG_CANT_OPEN_TABLE = 1606 + ER_CANT_CREATE_SROUTINE = 1607 + ER_NEVER_USED = 1608 + ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609 + ER_SLAVE_CORRUPT_EVENT = 1610 + ER_LOAD_DATA_INVALID_COLUMN = 1611 + ER_LOG_PURGE_NO_FILE = 1612 + ER_XA_RBTIMEOUT = 1613 + ER_XA_RBDEADLOCK = 1614 + ER_NEED_REPREPARE = 1615 + ER_DELAYED_NOT_SUPPORTED = 1616 + WARN_NO_MASTER_INFO = 1617 + WARN_OPTION_IGNORED = 1618 + WARN_PLUGIN_DELETE_BUILTIN = 1619 + WARN_PLUGIN_BUSY = 1620 + ER_VARIABLE_IS_READONLY = 1621 + ER_WARN_ENGINE_TRANSACTION_ROLLBACK = 1622 + ER_SLAVE_HEARTBEAT_FAILURE = 1623 + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624 + ER_NDB_REPLICATION_SCHEMA_ERROR = 1625 + ER_CONFLICT_FN_PARSE_ERROR = 1626 + ER_EXCEPTIONS_WRITE_ERROR = 1627 + ER_TOO_LONG_TABLE_COMMENT = 1628 + ER_TOO_LONG_FIELD_COMMENT = 1629 + ER_FUNC_INEXISTENT_NAME_COLLISION = 1630 + ER_DATABASE_NAME = 1631 + ER_TABLE_NAME = 1632 + ER_PARTITION_NAME = 1633 + ER_SUBPARTITION_NAME = 1634 + ER_TEMPORARY_NAME = 1635 + ER_RENAMED_NAME = 1636 + ER_TOO_MANY_CONCURRENT_TRXS = 1637 + WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638 + ER_DEBUG_SYNC_TIMEOUT = 1639 + ER_DEBUG_SYNC_HIT_LIMIT = 1640 + ER_DUP_SIGNAL_SET = 1641 + ER_SIGNAL_WARN = 1642 + ER_SIGNAL_NOT_FOUND = 1643 + ER_SIGNAL_EXCEPTION = 1644 + ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645 + ER_SIGNAL_BAD_CONDITION_TYPE = 1646 + WARN_COND_ITEM_TRUNCATED = 1647 + ER_COND_ITEM_TOO_LONG = 1648 + ER_UNKNOWN_LOCALE = 1649 + ER_SLAVE_IGNORE_SERVER_IDS = 1650 + ER_QUERY_CACHE_DISABLED = 1651 + ER_SAME_NAME_PARTITION_FIELD = 1652 + ER_PARTITION_COLUMN_LIST_ERROR = 1653 + ER_WRONG_TYPE_COLUMN_VALUE_ERROR = 1654 + ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655 + ER_MAXVALUE_IN_VALUES_IN = 1656 + ER_TOO_MANY_VALUES_ERROR = 1657 + ER_ROW_SINGLE_PARTITION_FIELD_ERROR = 1658 + ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659 + ER_PARTITION_FIELDS_TOO_LONG = 1660 + ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661 + ER_BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662 + ER_BINLOG_UNSAFE_AND_STMT_ENGINE = 1663 + ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664 + ER_BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665 + ER_BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666 + ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667 + ER_BINLOG_UNSAFE_LIMIT = 1668 + ER_BINLOG_UNSAFE_INSERT_DELAYED = 1669 + ER_BINLOG_UNSAFE_SYSTEM_TABLE = 1670 + ER_BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671 + ER_BINLOG_UNSAFE_UDF = 1672 + ER_BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673 + ER_BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674 + ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675 + ER_MESSAGE_AND_STATEMENT = 1676 + ER_SLAVE_CONVERSION_FAILED = 1677 + ER_SLAVE_CANT_CREATE_CONVERSION = 1678 + ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679 + ER_PATH_LENGTH = 1680 + ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681 + ER_WRONG_NATIVE_TABLE_STRUCTURE = 1682 + ER_WRONG_PERFSCHEMA_USAGE = 1683 + ER_WARN_I_S_SKIPPED_TABLE = 1684 + ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685 + ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686 + ER_SPATIAL_MUST_HAVE_GEOM_COL = 1687 + ER_TOO_LONG_INDEX_COMMENT = 1688 + ER_LOCK_ABORTED = 1689 + ER_DATA_OUT_OF_RANGE = 1690 + ER_WRONG_SPVAR_TYPE_IN_LIMIT = 1691 + ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692 + ER_BINLOG_UNSAFE_MIXED_STATEMENT = 1693 + ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694 + ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695 + ER_FAILED_READ_FROM_PAR_FILE = 1696 + ER_VALUES_IS_NOT_INT_TYPE_ERROR = 1697 + ER_ACCESS_DENIED_NO_PASSWORD_ERROR = 1698 + ER_SET_PASSWORD_AUTH_PLUGIN = 1699 + ER_GRANT_PLUGIN_USER_EXISTS = 1700 + ER_TRUNCATE_ILLEGAL_FK = 1701 + ER_PLUGIN_IS_PERMANENT = 1702 + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703 + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704 + ER_STMT_CACHE_FULL = 1705 + ER_MULTI_UPDATE_KEY_CONFLICT = 1706 + ER_TABLE_NEEDS_REBUILD = 1707 + WARN_OPTION_BELOW_LIMIT = 1708 + ER_INDEX_COLUMN_TOO_LONG = 1709 + ER_ERROR_IN_TRIGGER_BODY = 1710 + ER_ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711 + ER_INDEX_CORRUPT = 1712 + ER_UNDO_RECORD_TOO_BIG = 1713 + ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714 + ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715 + ER_BINLOG_UNSAFE_REPLACE_SELECT = 1716 + ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717 + ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718 + ER_BINLOG_UNSAFE_UPDATE_IGNORE = 1719 + ER_PLUGIN_NO_UNINSTALL = 1720 + ER_PLUGIN_NO_INSTALL = 1721 + ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722 + ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723 + ER_BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724 + ER_TABLE_IN_FK_CHECK = 1725 + ER_UNSUPPORTED_ENGINE = 1726 + ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727 + ER_CANNOT_LOAD_FROM_TABLE_V2 = 1728 + ER_MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729 + ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730 + ER_PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731 + ER_PARTITION_EXCHANGE_PART_TABLE = 1732 + ER_PARTITION_EXCHANGE_TEMP_TABLE = 1733 + ER_PARTITION_INSTEAD_OF_SUBPARTITION = 1734 + ER_UNKNOWN_PARTITION = 1735 + ER_TABLES_DIFFERENT_METADATA = 1736 + ER_ROW_DOES_NOT_MATCH_PARTITION = 1737 + ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738 + ER_WARN_INDEX_NOT_APPLICABLE = 1739 + ER_PARTITION_EXCHANGE_FOREIGN_KEY = 1740 + ER_NO_SUCH_KEY_VALUE = 1741 + ER_RPL_INFO_DATA_TOO_LONG = 1742 + ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE = 1743 + ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE = 1744 + ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745 + ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746 + ER_PARTITION_CLAUSE_ON_NONPARTITIONED = 1747 + ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748 + ER_NO_SUCH_PARTITION__UNUSED = 1749 + ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750 + ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751 + ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752 + ER_MTS_FEATURE_IS_NOT_SUPPORTED = 1753 + ER_MTS_UPDATED_DBS_GREATER_MAX = 1754 + ER_MTS_CANT_PARALLEL = 1755 + ER_MTS_INCONSISTENT_DATA = 1756 + ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757 + ER_DA_INVALID_CONDITION_NUMBER = 1758 + ER_INSECURE_PLAIN_TEXT = 1759 + ER_INSECURE_CHANGE_MASTER = 1760 + ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761 + ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762 + ER_SQLTHREAD_WITH_SECURE_SLAVE = 1763 + ER_TABLE_HAS_NO_FT = 1764 + ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765 + ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766 + ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST = 1767 + ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION_WHEN_GTID_NEXT_LIST_IS_NULL = 1768 + ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769 + ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770 + ER_SKIPPING_LOGGED_TRANSACTION = 1771 + ER_MALFORMED_GTID_SET_SPECIFICATION = 1772 + ER_MALFORMED_GTID_SET_ENCODING = 1773 + ER_MALFORMED_GTID_SPECIFICATION = 1774 + ER_GNO_EXHAUSTED = 1775 + ER_BAD_SLAVE_AUTO_POSITION = 1776 + ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON = 1777 + ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778 + ER_GTID_MODE_2_OR_3_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779 + ER_GTID_MODE_REQUIRES_BINLOG = 1780 + ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781 + ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782 + ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783 + ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF = 1784 + ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785 + ER_GTID_UNSAFE_CREATE_SELECT = 1786 + ER_GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION = 1787 + ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788 + ER_MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789 + ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790 + ER_UNKNOWN_EXPLAIN_FORMAT = 1791 + ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792 + ER_TOO_LONG_TABLE_PARTITION_COMMENT = 1793 + ER_SLAVE_CONFIGURATION = 1794 + ER_INNODB_FT_LIMIT = 1795 + ER_INNODB_NO_FT_TEMP_TABLE = 1796 + ER_INNODB_FT_WRONG_DOCID_COLUMN = 1797 + ER_INNODB_FT_WRONG_DOCID_INDEX = 1798 + ER_INNODB_ONLINE_LOG_TOO_BIG = 1799 + ER_UNKNOWN_ALTER_ALGORITHM = 1800 + ER_UNKNOWN_ALTER_LOCK = 1801 + ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802 + ER_MTS_RECOVERY_FAILURE = 1803 + ER_MTS_RESET_WORKERS = 1804 + ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805 + ER_SLAVE_SILENT_RETRY_TRANSACTION = 1806 + ER_DISCARD_FK_CHECKS_RUNNING = 1807 + ER_TABLE_SCHEMA_MISMATCH = 1808 + ER_TABLE_IN_SYSTEM_TABLESPACE = 1809 + ER_IO_READ_ERROR = 1810 + ER_IO_WRITE_ERROR = 1811 + ER_TABLESPACE_MISSING = 1812 + ER_TABLESPACE_EXISTS = 1813 + ER_TABLESPACE_DISCARDED = 1814 + ER_INTERNAL_ERROR = 1815 + ER_INNODB_IMPORT_ERROR = 1816 + ER_INNODB_INDEX_CORRUPT = 1817 + ER_INVALID_YEAR_COLUMN_LENGTH = 1818 + ER_NOT_VALID_PASSWORD = 1819 + ER_MUST_CHANGE_PASSWORD = 1820 + ER_FK_NO_INDEX_CHILD = 1821 + ER_FK_NO_INDEX_PARENT = 1822 + ER_FK_FAIL_ADD_SYSTEM = 1823 + ER_FK_CANNOT_OPEN_PARENT = 1824 + ER_FK_INCORRECT_OPTION = 1825 + ER_FK_DUP_NAME = 1826 + ER_PASSWORD_FORMAT = 1827 + ER_FK_COLUMN_CANNOT_DROP = 1828 + ER_FK_COLUMN_CANNOT_DROP_CHILD = 1829 + ER_FK_COLUMN_NOT_NULL = 1830 + ER_DUP_INDEX = 1831 + ER_FK_COLUMN_CANNOT_CHANGE = 1832 + ER_FK_COLUMN_CANNOT_CHANGE_CHILD = 1833 + ER_FK_CANNOT_DELETE_PARENT = 1834 + ER_MALFORMED_PACKET = 1835 + ER_READ_ONLY_MODE = 1836 + ER_GTID_NEXT_TYPE_UNDEFINED_GROUP = 1837 + ER_VARIABLE_NOT_SETTABLE_IN_SP = 1838 + ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF = 1839 + ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840 + ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841 + ER_GTID_PURGED_WAS_CHANGED = 1842 + ER_GTID_EXECUTED_WAS_CHANGED = 1843 + ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844 + ER_ALTER_OPERATION_NOT_SUPPORTED = 1845 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_IGNORE = 1852 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857 + ER_SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858 + ER_DUP_UNKNOWN_IN_INDEX = 1859 + ER_IDENT_CAUSES_TOO_LONG_PATH = 1860 + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861 + ER_MUST_CHANGE_PASSWORD_LOGIN = 1862 + ER_ROW_IN_WRONG_PARTITION = 1863 + ER_ERROR_LAST = 1863 +) diff --git a/vendor/github.com/siddontang/go-mysql/mysql/errname.go b/vendor/github.com/siddontang/go-mysql/mysql/errname.go new file mode 100644 index 0000000..7f8575a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/errname.go @@ -0,0 +1,868 @@ +package mysql + +var MySQLErrName = map[uint16]string{ + ER_HASHCHK: "hashchk", + ER_NISAMCHK: "isamchk", + ER_NO: "NO", + ER_YES: "YES", + ER_CANT_CREATE_FILE: "Can't create file '%-.200s' (errno: %d - %s)", + ER_CANT_CREATE_TABLE: "Can't create table '%-.200s' (errno: %d)", + ER_CANT_CREATE_DB: "Can't create database '%-.192s' (errno: %d)", + ER_DB_CREATE_EXISTS: "Can't create database '%-.192s'; database exists", + ER_DB_DROP_EXISTS: "Can't drop database '%-.192s'; database doesn't exist", + ER_DB_DROP_DELETE: "Error dropping database (can't delete '%-.192s', errno: %d)", + ER_DB_DROP_RMDIR: "Error dropping database (can't rmdir '%-.192s', errno: %d)", + ER_CANT_DELETE_FILE: "Error on delete of '%-.192s' (errno: %d - %s)", + ER_CANT_FIND_SYSTEM_REC: "Can't read record in system table", + ER_CANT_GET_STAT: "Can't get status of '%-.200s' (errno: %d - %s)", + ER_CANT_GET_WD: "Can't get working directory (errno: %d - %s)", + ER_CANT_LOCK: "Can't lock file (errno: %d - %s)", + ER_CANT_OPEN_FILE: "Can't open file: '%-.200s' (errno: %d - %s)", + ER_FILE_NOT_FOUND: "Can't find file: '%-.200s' (errno: %d - %s)", + ER_CANT_READ_DIR: "Can't read dir of '%-.192s' (errno: %d - %s)", + ER_CANT_SET_WD: "Can't change dir to '%-.192s' (errno: %d - %s)", + ER_CHECKREAD: "Record has changed since last read in table '%-.192s'", + ER_DISK_FULL: "Disk full (%s); waiting for someone to free some space... (errno: %d - %s)", + ER_DUP_KEY: "Can't write; duplicate key in table '%-.192s'", + ER_ERROR_ON_CLOSE: "Error on close of '%-.192s' (errno: %d - %s)", + ER_ERROR_ON_READ: "Error reading file '%-.200s' (errno: %d - %s)", + ER_ERROR_ON_RENAME: "Error on rename of '%-.210s' to '%-.210s' (errno: %d - %s)", + ER_ERROR_ON_WRITE: "Error writing file '%-.200s' (errno: %d - %s)", + ER_FILE_USED: "'%-.192s' is locked against change", + ER_FILSORT_ABORT: "Sort aborted", + ER_FORM_NOT_FOUND: "View '%-.192s' doesn't exist for '%-.192s'", + ER_GET_ERRNO: "Got error %d from storage engine", + ER_ILLEGAL_HA: "Table storage engine for '%-.192s' doesn't have this option", + ER_KEY_NOT_FOUND: "Can't find record in '%-.192s'", + ER_NOT_FORM_FILE: "Incorrect information in file: '%-.200s'", + ER_NOT_KEYFILE: "Incorrect key file for table '%-.200s'; try to repair it", + ER_OLD_KEYFILE: "Old key file for table '%-.192s'; repair it!", + ER_OPEN_AS_READONLY: "Table '%-.192s' is read only", + ER_OUTOFMEMORY: "Out of memory; restart server and try again (needed %d bytes)", + ER_OUT_OF_SORTMEMORY: "Out of sort memory, consider increasing server sort buffer size", + ER_UNEXPECTED_EOF: "Unexpected EOF found when reading file '%-.192s' (errno: %d - %s)", + ER_CON_COUNT_ERROR: "Too many connections", + ER_OUT_OF_RESOURCES: "Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space", + ER_BAD_HOST_ERROR: "Can't get hostname for your address", + ER_HANDSHAKE_ERROR: "Bad handshake", + ER_DBACCESS_DENIED_ERROR: "Access denied for user '%-.48s'@'%-.64s' to database '%-.192s'", + ER_ACCESS_DENIED_ERROR: "Access denied for user '%-.48s'@'%-.64s' (using password: %s)", + ER_NO_DB_ERROR: "No database selected", + ER_UNKNOWN_COM_ERROR: "Unknown command", + ER_BAD_NULL_ERROR: "Column '%-.192s' cannot be null", + ER_BAD_DB_ERROR: "Unknown database '%-.192s'", + ER_TABLE_EXISTS_ERROR: "Table '%-.192s' already exists", + ER_BAD_TABLE_ERROR: "Unknown table '%-.100s'", + ER_NON_UNIQ_ERROR: "Column '%-.192s' in %-.192s is ambiguous", + ER_SERVER_SHUTDOWN: "Server shutdown in progress", + ER_BAD_FIELD_ERROR: "Unknown column '%-.192s' in '%-.192s'", + ER_WRONG_FIELD_WITH_GROUP: "'%-.192s' isn't in GROUP BY", + ER_WRONG_GROUP_FIELD: "Can't group on '%-.192s'", + ER_WRONG_SUM_SELECT: "Statement has sum functions and columns in same statement", + ER_WRONG_VALUE_COUNT: "Column count doesn't match value count", + ER_TOO_LONG_IDENT: "Identifier name '%-.100s' is too long", + ER_DUP_FIELDNAME: "Duplicate column name '%-.192s'", + ER_DUP_KEYNAME: "Duplicate key name '%-.192s'", + ER_DUP_ENTRY: "Duplicate entry '%-.192s' for key %d", + ER_WRONG_FIELD_SPEC: "Incorrect column specifier for column '%-.192s'", + ER_PARSE_ERROR: "%s near '%-.80s' at line %d", + ER_EMPTY_QUERY: "Query was empty", + ER_NONUNIQ_TABLE: "Not unique table/alias: '%-.192s'", + ER_INVALID_DEFAULT: "Invalid default value for '%-.192s'", + ER_MULTIPLE_PRI_KEY: "Multiple primary key defined", + ER_TOO_MANY_KEYS: "Too many keys specified; max %d keys allowed", + ER_TOO_MANY_KEY_PARTS: "Too many key parts specified; max %d parts allowed", + ER_TOO_LONG_KEY: "Specified key was too long; max key length is %d bytes", + ER_KEY_COLUMN_DOES_NOT_EXITS: "Key column '%-.192s' doesn't exist in table", + ER_BLOB_USED_AS_KEY: "BLOB column '%-.192s' can't be used in key specification with the used table type", + ER_TOO_BIG_FIELDLENGTH: "Column length too big for column '%-.192s' (max = %lu); use BLOB or TEXT instead", + ER_WRONG_AUTO_KEY: "Incorrect table definition; there can be only one auto column and it must be defined as a key", + ER_READY: "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d", + ER_NORMAL_SHUTDOWN: "%s: Normal shutdown\n", + ER_GOT_SIGNAL: "%s: Got signal %d. Aborting!\n", + ER_SHUTDOWN_COMPLETE: "%s: Shutdown complete\n", + ER_FORCING_CLOSE: "%s: Forcing close of thread %ld user: '%-.48s'\n", + ER_IPSOCK_ERROR: "Can't create IP socket", + ER_NO_SUCH_INDEX: "Table '%-.192s' has no index like the one used in CREATE INDEX; recreate the table", + ER_WRONG_FIELD_TERMINATORS: "Field separator argument is not what is expected; check the manual", + ER_BLOBS_AND_NO_TERMINATED: "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'", + ER_TEXTFILE_NOT_READABLE: "The file '%-.128s' must be in the database directory or be readable by all", + ER_FILE_EXISTS_ERROR: "File '%-.200s' already exists", + ER_LOAD_INFO: "Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld", + ER_ALTER_INFO: "Records: %ld Duplicates: %ld", + ER_WRONG_SUB_KEY: "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys", + ER_CANT_REMOVE_ALL_FIELDS: "You can't delete all columns with ALTER TABLE; use DROP TABLE instead", + ER_CANT_DROP_FIELD_OR_KEY: "Can't DROP '%-.192s'; check that column/key exists", + ER_INSERT_INFO: "Records: %ld Duplicates: %ld Warnings: %ld", + ER_UPDATE_TABLE_USED: "You can't specify target table '%-.192s' for update in FROM clause", + ER_NO_SUCH_THREAD: "Unknown thread id: %lu", + ER_KILL_DENIED_ERROR: "You are not owner of thread %lu", + ER_NO_TABLES_USED: "No tables used", + ER_TOO_BIG_SET: "Too many strings for column %-.192s and SET", + ER_NO_UNIQUE_LOGFILE: "Can't generate a unique log-filename %-.200s.(1-999)\n", + ER_TABLE_NOT_LOCKED_FOR_WRITE: "Table '%-.192s' was locked with a READ lock and can't be updated", + ER_TABLE_NOT_LOCKED: "Table '%-.192s' was not locked with LOCK TABLES", + ER_BLOB_CANT_HAVE_DEFAULT: "BLOB/TEXT column '%-.192s' can't have a default value", + ER_WRONG_DB_NAME: "Incorrect database name '%-.100s'", + ER_WRONG_TABLE_NAME: "Incorrect table name '%-.100s'", + ER_TOO_BIG_SELECT: "The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay", + ER_UNKNOWN_ERROR: "Unknown error", + ER_UNKNOWN_PROCEDURE: "Unknown procedure '%-.192s'", + ER_WRONG_PARAMCOUNT_TO_PROCEDURE: "Incorrect parameter count to procedure '%-.192s'", + ER_WRONG_PARAMETERS_TO_PROCEDURE: "Incorrect parameters to procedure '%-.192s'", + ER_UNKNOWN_TABLE: "Unknown table '%-.192s' in %-.32s", + ER_FIELD_SPECIFIED_TWICE: "Column '%-.192s' specified twice", + ER_INVALID_GROUP_FUNC_USE: "Invalid use of group function", + ER_UNSUPPORTED_EXTENSION: "Table '%-.192s' uses an extension that doesn't exist in this MySQL version", + ER_TABLE_MUST_HAVE_COLUMNS: "A table must have at least 1 column", + ER_RECORD_FILE_FULL: "The table '%-.192s' is full", + ER_UNKNOWN_CHARACTER_SET: "Unknown character set: '%-.64s'", + ER_TOO_MANY_TABLES: "Too many tables; MySQL can only use %d tables in a join", + ER_TOO_MANY_FIELDS: "Too many columns", + ER_TOO_BIG_ROWSIZE: "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs", + ER_STACK_OVERRUN: "Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld --thread_stack=#' to specify a bigger stack if needed", + ER_WRONG_OUTER_JOIN: "Cross dependency found in OUTER JOIN; examine your ON conditions", + ER_NULL_COLUMN_IN_INDEX: "Table handler doesn't support NULL in given index. Please change column '%-.192s' to be NOT NULL or use another handler", + ER_CANT_FIND_UDF: "Can't load function '%-.192s'", + ER_CANT_INITIALIZE_UDF: "Can't initialize function '%-.192s'; %-.80s", + ER_UDF_NO_PATHS: "No paths allowed for shared library", + ER_UDF_EXISTS: "Function '%-.192s' already exists", + ER_CANT_OPEN_LIBRARY: "Can't open shared library '%-.192s' (errno: %d %-.128s)", + ER_CANT_FIND_DL_ENTRY: "Can't find symbol '%-.128s' in library", + ER_FUNCTION_NOT_DEFINED: "Function '%-.192s' is not defined", + ER_HOST_IS_BLOCKED: "Host '%-.64s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'", + ER_HOST_NOT_PRIVILEGED: "Host '%-.64s' is not allowed to connect to this MySQL server", + ER_PASSWORD_ANONYMOUS_USER: "You are using MySQL as an anonymous user and anonymous users are not allowed to change passwords", + ER_PASSWORD_NOT_ALLOWED: "You must have privileges to update tables in the mysql database to be able to change passwords for others", + ER_PASSWORD_NO_MATCH: "Can't find any matching row in the user table", + ER_UPDATE_INFO: "Rows matched: %ld Changed: %ld Warnings: %ld", + ER_CANT_CREATE_THREAD: "Can't create a new thread (errno %d); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug", + ER_WRONG_VALUE_COUNT_ON_ROW: "Column count doesn't match value count at row %ld", + ER_CANT_REOPEN_TABLE: "Can't reopen table: '%-.192s'", + ER_INVALID_USE_OF_NULL: "Invalid use of NULL value", + ER_REGEXP_ERROR: "Got error '%-.64s' from regexp", + ER_MIX_OF_GROUP_FUNC_AND_FIELDS: "Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause", + ER_NONEXISTING_GRANT: "There is no such grant defined for user '%-.48s' on host '%-.64s'", + ER_TABLEACCESS_DENIED_ERROR: "%-.128s command denied to user '%-.48s'@'%-.64s' for table '%-.64s'", + ER_COLUMNACCESS_DENIED_ERROR: "%-.16s command denied to user '%-.48s'@'%-.64s' for column '%-.192s' in table '%-.192s'", + ER_ILLEGAL_GRANT_FOR_TABLE: "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used", + ER_GRANT_WRONG_HOST_OR_USER: "The host or user argument to GRANT is too long", + ER_NO_SUCH_TABLE: "Table '%-.192s.%-.192s' doesn't exist", + ER_NONEXISTING_TABLE_GRANT: "There is no such grant defined for user '%-.48s' on host '%-.64s' on table '%-.192s'", + ER_NOT_ALLOWED_COMMAND: "The used command is not allowed with this MySQL version", + ER_SYNTAX_ERROR: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use", + ER_DELAYED_CANT_CHANGE_LOCK: "Delayed insert thread couldn't get requested lock for table %-.192s", + ER_TOO_MANY_DELAYED_THREADS: "Too many delayed threads in use", + ER_ABORTING_CONNECTION: "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)", + ER_NET_PACKET_TOO_LARGE: "Got a packet bigger than 'max_allowed_packet' bytes", + ER_NET_READ_ERROR_FROM_PIPE: "Got a read error from the connection pipe", + ER_NET_FCNTL_ERROR: "Got an error from fcntl()", + ER_NET_PACKETS_OUT_OF_ORDER: "Got packets out of order", + ER_NET_UNCOMPRESS_ERROR: "Couldn't uncompress communication packet", + ER_NET_READ_ERROR: "Got an error reading communication packets", + ER_NET_READ_INTERRUPTED: "Got timeout reading communication packets", + ER_NET_ERROR_ON_WRITE: "Got an error writing communication packets", + ER_NET_WRITE_INTERRUPTED: "Got timeout writing communication packets", + ER_TOO_LONG_STRING: "Result string is longer than 'max_allowed_packet' bytes", + ER_TABLE_CANT_HANDLE_BLOB: "The used table type doesn't support BLOB/TEXT columns", + ER_TABLE_CANT_HANDLE_AUTO_INCREMENT: "The used table type doesn't support AUTO_INCREMENT columns", + ER_DELAYED_INSERT_TABLE_LOCKED: "INSERT DELAYED can't be used with table '%-.192s' because it is locked with LOCK TABLES", + ER_WRONG_COLUMN_NAME: "Incorrect column name '%-.100s'", + ER_WRONG_KEY_COLUMN: "The used storage engine can't index column '%-.192s'", + ER_WRONG_MRG_TABLE: "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist", + ER_DUP_UNIQUE: "Can't write, because of unique constraint, to table '%-.192s'", + ER_BLOB_KEY_WITHOUT_LENGTH: "BLOB/TEXT column '%-.192s' used in key specification without a key length", + ER_PRIMARY_CANT_HAVE_NULL: "All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead", + ER_TOO_MANY_ROWS: "Result consisted of more than one row", + ER_REQUIRES_PRIMARY_KEY: "This table type requires a primary key", + ER_NO_RAID_COMPILED: "This version of MySQL is not compiled with RAID support", + ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE: "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", + ER_KEY_DOES_NOT_EXITS: "Key '%-.192s' doesn't exist in table '%-.192s'", + ER_CHECK_NO_SUCH_TABLE: "Can't open table", + ER_CHECK_NOT_IMPLEMENTED: "The storage engine for the table doesn't support %s", + ER_CANT_DO_THIS_DURING_AN_TRANSACTION: "You are not allowed to execute this command in a transaction", + ER_ERROR_DURING_COMMIT: "Got error %d during COMMIT", + ER_ERROR_DURING_ROLLBACK: "Got error %d during ROLLBACK", + ER_ERROR_DURING_FLUSH_LOGS: "Got error %d during FLUSH_LOGS", + ER_ERROR_DURING_CHECKPOINT: "Got error %d during CHECKPOINT", + ER_NEW_ABORTING_CONNECTION: "Aborted connection %ld to db: '%-.192s' user: '%-.48s' host: '%-.64s' (%-.64s)", + ER_DUMP_NOT_IMPLEMENTED: "The storage engine for the table does not support binary table dump", + ER_FLUSH_MASTER_BINLOG_CLOSED: "Binlog closed, cannot RESET MASTER", + ER_INDEX_REBUILD: "Failed rebuilding the index of dumped table '%-.192s'", + ER_MASTER: "Error from master: '%-.64s'", + ER_MASTER_NET_READ: "Net error reading from master", + ER_MASTER_NET_WRITE: "Net error writing to master", + ER_FT_MATCHING_KEY_NOT_FOUND: "Can't find FULLTEXT index matching the column list", + ER_LOCK_OR_ACTIVE_TRANSACTION: "Can't execute the given command because you have active locked tables or an active transaction", + ER_UNKNOWN_SYSTEM_VARIABLE: "Unknown system variable '%-.64s'", + ER_CRASHED_ON_USAGE: "Table '%-.192s' is marked as crashed and should be repaired", + ER_CRASHED_ON_REPAIR: "Table '%-.192s' is marked as crashed and last (automatic?) repair failed", + ER_WARNING_NOT_COMPLETE_ROLLBACK: "Some non-transactional changed tables couldn't be rolled back", + ER_TRANS_CACHE_FULL: "Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again", + ER_SLAVE_MUST_STOP: "This operation cannot be performed with a running slave; run STOP SLAVE first", + ER_SLAVE_NOT_RUNNING: "This operation requires a running slave; configure slave and do START SLAVE", + ER_BAD_SLAVE: "The server is not configured as slave; fix in config file or with CHANGE MASTER TO", + ER_MASTER_INFO: "Could not initialize master info structure; more error messages can be found in the MySQL error log", + ER_SLAVE_THREAD: "Could not create slave thread; check system resources", + ER_TOO_MANY_USER_CONNECTIONS: "User %-.64s already has more than 'max_user_connections' active connections", + ER_SET_CONSTANTS_ONLY: "You may only use constant expressions with SET", + ER_LOCK_WAIT_TIMEOUT: "Lock wait timeout exceeded; try restarting transaction", + ER_LOCK_TABLE_FULL: "The total number of locks exceeds the lock table size", + ER_READ_ONLY_TRANSACTION: "Update locks cannot be acquired during a READ UNCOMMITTED transaction", + ER_DROP_DB_WITH_READ_LOCK: "DROP DATABASE not allowed while thread is holding global read lock", + ER_CREATE_DB_WITH_READ_LOCK: "CREATE DATABASE not allowed while thread is holding global read lock", + ER_WRONG_ARGUMENTS: "Incorrect arguments to %s", + ER_NO_PERMISSION_TO_CREATE_USER: "'%-.48s'@'%-.64s' is not allowed to create new users", + ER_UNION_TABLES_IN_DIFFERENT_DIR: "Incorrect table definition; all MERGE tables must be in the same database", + ER_LOCK_DEADLOCK: "Deadlock found when trying to get lock; try restarting transaction", + ER_TABLE_CANT_HANDLE_FT: "The used table type doesn't support FULLTEXT indexes", + ER_CANNOT_ADD_FOREIGN: "Cannot add foreign key constraint", + ER_NO_REFERENCED_ROW: "Cannot add or update a child row: a foreign key constraint fails", + ER_ROW_IS_REFERENCED: "Cannot delete or update a parent row: a foreign key constraint fails", + ER_CONNECT_TO_MASTER: "Error connecting to master: %-.128s", + ER_QUERY_ON_MASTER: "Error running query on master: %-.128s", + ER_ERROR_WHEN_EXECUTING_COMMAND: "Error when executing command %s: %-.128s", + ER_WRONG_USAGE: "Incorrect usage of %s and %s", + ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT: "The used SELECT statements have a different number of columns", + ER_CANT_UPDATE_WITH_READLOCK: "Can't execute the query because you have a conflicting read lock", + ER_MIXING_NOT_ALLOWED: "Mixing of transactional and non-transactional tables is disabled", + ER_DUP_ARGUMENT: "Option '%s' used twice in statement", + ER_USER_LIMIT_REACHED: "User '%-.64s' has exceeded the '%s' resource (current value: %ld)", + ER_SPECIFIC_ACCESS_DENIED_ERROR: "Access denied; you need (at least one of) the %-.128s privilege(s) for this operation", + ER_LOCAL_VARIABLE: "Variable '%-.64s' is a SESSION variable and can't be used with SET GLOBAL", + ER_GLOBAL_VARIABLE: "Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", + ER_NO_DEFAULT: "Variable '%-.64s' doesn't have a default value", + ER_WRONG_VALUE_FOR_VAR: "Variable '%-.64s' can't be set to the value of '%-.200s'", + ER_WRONG_TYPE_FOR_VAR: "Incorrect argument type to variable '%-.64s'", + ER_VAR_CANT_BE_READ: "Variable '%-.64s' can only be set, not read", + ER_CANT_USE_OPTION_HERE: "Incorrect usage/placement of '%s'", + ER_NOT_SUPPORTED_YET: "This version of MySQL doesn't yet support '%s'", + ER_MASTER_FATAL_ERROR_READING_BINLOG: "Got fatal error %d from master when reading data from binary log: '%-.320s'", + ER_SLAVE_IGNORED_TABLE: "Slave SQL thread ignored the query because of replicate-*-table rules", + ER_INCORRECT_GLOBAL_LOCAL_VAR: "Variable '%-.192s' is a %s variable", + ER_WRONG_FK_DEF: "Incorrect foreign key definition for '%-.192s': %s", + ER_KEY_REF_DO_NOT_MATCH_TABLE_REF: "Key reference and table reference don't match", + ER_OPERAND_COLUMNS: "Operand should contain %d column(s)", + ER_SUBQUERY_NO_1_ROW: "Subquery returns more than 1 row", + ER_UNKNOWN_STMT_HANDLER: "Unknown prepared statement handler (%.*s) given to %s", + ER_CORRUPT_HELP_DB: "Help database is corrupt or does not exist", + ER_CYCLIC_REFERENCE: "Cyclic reference on subqueries", + ER_AUTO_CONVERT: "Converting column '%s' from %s to %s", + ER_ILLEGAL_REFERENCE: "Reference '%-.64s' not supported (%s)", + ER_DERIVED_MUST_HAVE_ALIAS: "Every derived table must have its own alias", + ER_SELECT_REDUCED: "Select %u was reduced during optimization", + ER_TABLENAME_NOT_ALLOWED_HERE: "Table '%-.192s' from one of the SELECTs cannot be used in %-.32s", + ER_NOT_SUPPORTED_AUTH_MODE: "Client does not support authentication protocol requested by server; consider upgrading MySQL client", + ER_SPATIAL_CANT_HAVE_NULL: "All parts of a SPATIAL index must be NOT NULL", + ER_COLLATION_CHARSET_MISMATCH: "COLLATION '%s' is not valid for CHARACTER SET '%s'", + ER_SLAVE_WAS_RUNNING: "Slave is already running", + ER_SLAVE_WAS_NOT_RUNNING: "Slave already has been stopped", + ER_TOO_BIG_FOR_UNCOMPRESS: "Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)", + ER_ZLIB_Z_MEM_ERROR: "ZLIB: Not enough memory", + ER_ZLIB_Z_BUF_ERROR: "ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)", + ER_ZLIB_Z_DATA_ERROR: "ZLIB: Input data corrupted", + ER_CUT_VALUE_GROUP_CONCAT: "Row %u was cut by GROUP_CONCAT()", + ER_WARN_TOO_FEW_RECORDS: "Row %ld doesn't contain data for all columns", + ER_WARN_TOO_MANY_RECORDS: "Row %ld was truncated; it contained more data than there were input columns", + ER_WARN_NULL_TO_NOTNULL: "Column set to default value; NULL supplied to NOT NULL column '%s' at row %ld", + ER_WARN_DATA_OUT_OF_RANGE: "Out of range value for column '%s' at row %ld", + WARN_DATA_TRUNCATED: "Data truncated for column '%s' at row %ld", + ER_WARN_USING_OTHER_HANDLER: "Using storage engine %s for table '%s'", + ER_CANT_AGGREGATE_2COLLATIONS: "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'", + ER_DROP_USER: "Cannot drop one or more of the requested users", + ER_REVOKE_GRANTS: "Can't revoke all privileges for one or more of the requested users", + ER_CANT_AGGREGATE_3COLLATIONS: "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", + ER_CANT_AGGREGATE_NCOLLATIONS: "Illegal mix of collations for operation '%s'", + ER_VARIABLE_IS_NOT_STRUCT: "Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)", + ER_UNKNOWN_COLLATION: "Unknown collation: '%-.64s'", + ER_SLAVE_IGNORED_SSL_PARAMS: "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started", + ER_SERVER_IS_IN_SECURE_AUTH_MODE: "Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format", + ER_WARN_FIELD_RESOLVED: "Field or reference '%-.192s%s%-.192s%s%-.192s' of SELECT #%d was resolved in SELECT #%d", + ER_BAD_SLAVE_UNTIL_COND: "Incorrect parameter or combination of parameters for START SLAVE UNTIL", + ER_MISSING_SKIP_SLAVE: "It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart", + ER_UNTIL_COND_IGNORED: "SQL thread is not to be started so UNTIL options are ignored", + ER_WRONG_NAME_FOR_INDEX: "Incorrect index name '%-.100s'", + ER_WRONG_NAME_FOR_CATALOG: "Incorrect catalog name '%-.100s'", + ER_WARN_QC_RESIZE: "Query cache failed to set size %lu; new query cache size is %lu", + ER_BAD_FT_COLUMN: "Column '%-.192s' cannot be part of FULLTEXT index", + ER_UNKNOWN_KEY_CACHE: "Unknown key cache '%-.100s'", + ER_WARN_HOSTNAME_WONT_WORK: "MySQL is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work", + ER_UNKNOWN_STORAGE_ENGINE: "Unknown storage engine '%s'", + ER_WARN_DEPRECATED_SYNTAX: "'%s' is deprecated and will be removed in a future release. Please use %s instead", + ER_NON_UPDATABLE_TABLE: "The target table %-.100s of the %s is not updatable", + ER_FEATURE_DISABLED: "The '%s' feature is disabled; you need MySQL built with '%s' to have it working", + ER_OPTION_PREVENTS_STATEMENT: "The MySQL server is running with the %s option so it cannot execute this statement", + ER_DUPLICATED_VALUE_IN_TYPE: "Column '%-.100s' has duplicated value '%-.64s' in %s", + ER_TRUNCATED_WRONG_VALUE: "Truncated incorrect %-.32s value: '%-.128s'", + ER_TOO_MUCH_AUTO_TIMESTAMP_COLS: "Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause", + ER_INVALID_ON_UPDATE: "Invalid ON UPDATE clause for '%-.192s' column", + ER_UNSUPPORTED_PS: "This command is not supported in the prepared statement protocol yet", + ER_GET_ERRMSG: "Got error %d '%-.100s' from %s", + ER_GET_TEMPORARY_ERRMSG: "Got temporary error %d '%-.100s' from %s", + ER_UNKNOWN_TIME_ZONE: "Unknown or incorrect time zone: '%-.64s'", + ER_WARN_INVALID_TIMESTAMP: "Invalid TIMESTAMP value in column '%s' at row %ld", + ER_INVALID_CHARACTER_STRING: "Invalid %s character string: '%.64s'", + ER_WARN_ALLOWED_PACKET_OVERFLOWED: "Result of %s() was larger than max_allowed_packet (%ld) - truncated", + ER_CONFLICTING_DECLARATIONS: "Conflicting declarations: '%s%s' and '%s%s'", + ER_SP_NO_RECURSIVE_CREATE: "Can't create a %s from within another stored routine", + ER_SP_ALREADY_EXISTS: "%s %s already exists", + ER_SP_DOES_NOT_EXIST: "%s %s does not exist", + ER_SP_DROP_FAILED: "Failed to DROP %s %s", + ER_SP_STORE_FAILED: "Failed to CREATE %s %s", + ER_SP_LILABEL_MISMATCH: "%s with no matching label: %s", + ER_SP_LABEL_REDEFINE: "Redefining label %s", + ER_SP_LABEL_MISMATCH: "End-label %s without match", + ER_SP_UNINIT_VAR: "Referring to uninitialized variable %s", + ER_SP_BADSELECT: "PROCEDURE %s can't return a result set in the given context", + ER_SP_BADRETURN: "RETURN is only allowed in a FUNCTION", + ER_SP_BADSTATEMENT: "%s is not allowed in stored procedures", + ER_UPDATE_LOG_DEPRECATED_IGNORED: "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored.", + ER_UPDATE_LOG_DEPRECATED_TRANSLATED: "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN.", + ER_QUERY_INTERRUPTED: "Query execution was interrupted", + ER_SP_WRONG_NO_OF_ARGS: "Incorrect number of arguments for %s %s; expected %u, got %u", + ER_SP_COND_MISMATCH: "Undefined CONDITION: %s", + ER_SP_NORETURN: "No RETURN found in FUNCTION %s", + ER_SP_NORETURNEND: "FUNCTION %s ended without RETURN", + ER_SP_BAD_CURSOR_QUERY: "Cursor statement must be a SELECT", + ER_SP_BAD_CURSOR_SELECT: "Cursor SELECT must not have INTO", + ER_SP_CURSOR_MISMATCH: "Undefined CURSOR: %s", + ER_SP_CURSOR_ALREADY_OPEN: "Cursor is already open", + ER_SP_CURSOR_NOT_OPEN: "Cursor is not open", + ER_SP_UNDECLARED_VAR: "Undeclared variable: %s", + ER_SP_WRONG_NO_OF_FETCH_ARGS: "Incorrect number of FETCH variables", + ER_SP_FETCH_NO_DATA: "No data - zero rows fetched, selected, or processed", + ER_SP_DUP_PARAM: "Duplicate parameter: %s", + ER_SP_DUP_VAR: "Duplicate variable: %s", + ER_SP_DUP_COND: "Duplicate condition: %s", + ER_SP_DUP_CURS: "Duplicate cursor: %s", + ER_SP_CANT_ALTER: "Failed to ALTER %s %s", + ER_SP_SUBSELECT_NYI: "Subquery value not supported", + ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG: "%s is not allowed in stored function or trigger", + ER_SP_VARCOND_AFTER_CURSHNDLR: "Variable or condition declaration after cursor or handler declaration", + ER_SP_CURSOR_AFTER_HANDLER: "Cursor declaration after handler declaration", + ER_SP_CASE_NOT_FOUND: "Case not found for CASE statement", + ER_FPARSER_TOO_BIG_FILE: "Configuration file '%-.192s' is too big", + ER_FPARSER_BAD_HEADER: "Malformed file type header in file '%-.192s'", + ER_FPARSER_EOF_IN_COMMENT: "Unexpected end of file while parsing comment '%-.200s'", + ER_FPARSER_ERROR_IN_PARAMETER: "Error while parsing parameter '%-.192s' (line: '%-.192s')", + ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER: "Unexpected end of file while skipping unknown parameter '%-.192s'", + ER_VIEW_NO_EXPLAIN: "EXPLAIN/SHOW can not be issued; lacking privileges for underlying table", + ER_FRM_UNKNOWN_TYPE: "File '%-.192s' has unknown type '%-.64s' in its header", + ER_WRONG_OBJECT: "'%-.192s.%-.192s' is not %s", + ER_NONUPDATEABLE_COLUMN: "Column '%-.192s' is not updatable", + ER_VIEW_SELECT_DERIVED: "View's SELECT contains a subquery in the FROM clause", + ER_VIEW_SELECT_CLAUSE: "View's SELECT contains a '%s' clause", + ER_VIEW_SELECT_VARIABLE: "View's SELECT contains a variable or parameter", + ER_VIEW_SELECT_TMPTABLE: "View's SELECT refers to a temporary table '%-.192s'", + ER_VIEW_WRONG_LIST: "View's SELECT and view's field list have different column counts", + ER_WARN_VIEW_MERGE: "View merge algorithm can't be used here for now (assumed undefined algorithm)", + ER_WARN_VIEW_WITHOUT_KEY: "View being updated does not have complete key of underlying table in it", + ER_VIEW_INVALID: "View '%-.192s.%-.192s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them", + ER_SP_NO_DROP_SP: "Can't drop or alter a %s from within another stored routine", + ER_SP_GOTO_IN_HNDLR: "GOTO is not allowed in a stored procedure handler", + ER_TRG_ALREADY_EXISTS: "Trigger already exists", + ER_TRG_DOES_NOT_EXIST: "Trigger does not exist", + ER_TRG_ON_VIEW_OR_TEMP_TABLE: "Trigger's '%-.192s' is view or temporary table", + ER_TRG_CANT_CHANGE_ROW: "Updating of %s row is not allowed in %strigger", + ER_TRG_NO_SUCH_ROW_IN_TRG: "There is no %s row in %s trigger", + ER_NO_DEFAULT_FOR_FIELD: "Field '%-.192s' doesn't have a default value", + ER_DIVISION_BY_ZERO: "Division by 0", + ER_TRUNCATED_WRONG_VALUE_FOR_FIELD: "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %ld", + ER_ILLEGAL_VALUE_FOR_TYPE: "Illegal %s '%-.192s' value found during parsing", + ER_VIEW_NONUPD_CHECK: "CHECK OPTION on non-updatable view '%-.192s.%-.192s'", + ER_VIEW_CHECK_FAILED: "CHECK OPTION failed '%-.192s.%-.192s'", + ER_PROCACCESS_DENIED_ERROR: "%-.16s command denied to user '%-.48s'@'%-.64s' for routine '%-.192s'", + ER_RELAY_LOG_FAIL: "Failed purging old relay logs: %s", + ER_PASSWD_LENGTH: "Password hash should be a %d-digit hexadecimal number", + ER_UNKNOWN_TARGET_BINLOG: "Target log not found in binlog index", + ER_IO_ERR_LOG_INDEX_READ: "I/O error reading log index file", + ER_BINLOG_PURGE_PROHIBITED: "Server configuration does not permit binlog purge", + ER_FSEEK_FAIL: "Failed on fseek()", + ER_BINLOG_PURGE_FATAL_ERR: "Fatal error during log purge", + ER_LOG_IN_USE: "A purgeable log is in use, will not purge", + ER_LOG_PURGE_UNKNOWN_ERR: "Unknown error during log purge", + ER_RELAY_LOG_INIT: "Failed initializing relay log position: %s", + ER_NO_BINARY_LOGGING: "You are not using binary logging", + ER_RESERVED_SYNTAX: "The '%-.64s' syntax is reserved for purposes internal to the MySQL server", + ER_WSAS_FAILED: "WSAStartup Failed", + ER_DIFF_GROUPS_PROC: "Can't handle procedures with different groups yet", + ER_NO_GROUP_FOR_PROC: "Select must have a group with this procedure", + ER_ORDER_WITH_PROC: "Can't use ORDER clause with this procedure", + ER_LOGGING_PROHIBIT_CHANGING_OF: "Binary logging and replication forbid changing the global server %s", + ER_NO_FILE_MAPPING: "Can't map file: %-.200s, errno: %d", + ER_WRONG_MAGIC: "Wrong magic in %-.64s", + ER_PS_MANY_PARAM: "Prepared statement contains too many placeholders", + ER_KEY_PART_0: "Key part '%-.192s' length cannot be 0", + ER_VIEW_CHECKSUM: "View text checksum failed", + ER_VIEW_MULTIUPDATE: "Can not modify more than one base table through a join view '%-.192s.%-.192s'", + ER_VIEW_NO_INSERT_FIELD_LIST: "Can not insert into join view '%-.192s.%-.192s' without fields list", + ER_VIEW_DELETE_MERGE_VIEW: "Can not delete from join view '%-.192s.%-.192s'", + ER_CANNOT_USER: "Operation %s failed for %.256s", + ER_XAER_NOTA: "XAER_NOTA: Unknown XID", + ER_XAER_INVAL: "XAER_INVAL: Invalid arguments (or unsupported command)", + ER_XAER_RMFAIL: "XAER_RMFAIL: The command cannot be executed when global transaction is in the %.64s state", + ER_XAER_OUTSIDE: "XAER_OUTSIDE: Some work is done outside global transaction", + ER_XAER_RMERR: "XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency", + ER_XA_RBROLLBACK: "XA_RBROLLBACK: Transaction branch was rolled back", + ER_NONEXISTING_PROC_GRANT: "There is no such grant defined for user '%-.48s' on host '%-.64s' on routine '%-.192s'", + ER_PROC_AUTO_GRANT_FAIL: "Failed to grant EXECUTE and ALTER ROUTINE privileges", + ER_PROC_AUTO_REVOKE_FAIL: "Failed to revoke all privileges to dropped routine", + ER_DATA_TOO_LONG: "Data too long for column '%s' at row %ld", + ER_SP_BAD_SQLSTATE: "Bad SQLSTATE: '%s'", + ER_STARTUP: "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d %s", + ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR: "Can't load value from file with fixed size rows to variable", + ER_CANT_CREATE_USER_WITH_GRANT: "You are not allowed to create a user with GRANT", + ER_WRONG_VALUE_FOR_TYPE: "Incorrect %-.32s value: '%-.128s' for function %-.32s", + ER_TABLE_DEF_CHANGED: "Table definition has changed, please retry transaction", + ER_SP_DUP_HANDLER: "Duplicate handler declared in the same block", + ER_SP_NOT_VAR_ARG: "OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger", + ER_SP_NO_RETSET: "Not allowed to return a result set from a %s", + ER_CANT_CREATE_GEOMETRY_OBJECT: "Cannot get geometry object from data you send to the GEOMETRY field", + ER_FAILED_ROUTINE_BREAK_BINLOG: "A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes", + ER_BINLOG_UNSAFE_ROUTINE: "This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)", + ER_BINLOG_CREATE_ROUTINE_NEED_SUPER: "You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)", + ER_EXEC_STMT_WITH_OPEN_CURSOR: "You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it.", + ER_STMT_HAS_NO_OPEN_CURSOR: "The statement (%lu) has no open cursor.", + ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG: "Explicit or implicit commit is not allowed in stored function or trigger.", + ER_NO_DEFAULT_FOR_VIEW_FIELD: "Field of view '%-.192s.%-.192s' underlying table doesn't have a default value", + ER_SP_NO_RECURSION: "Recursive stored functions and triggers are not allowed.", + ER_TOO_BIG_SCALE: "Too big scale %d specified for column '%-.192s'. Maximum is %lu.", + ER_TOO_BIG_PRECISION: "Too big precision %d specified for column '%-.192s'. Maximum is %lu.", + ER_M_BIGGER_THAN_D: "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.192s').", + ER_WRONG_LOCK_OF_SYSTEM_TABLE: "You can't combine write-locking of system tables with other tables or lock types", + ER_CONNECT_TO_FOREIGN_DATA_SOURCE: "Unable to connect to foreign data source: %.64s", + ER_QUERY_ON_FOREIGN_DATA_SOURCE: "There was a problem processing the query on the foreign data source. Data source error: %-.64s", + ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST: "The foreign data source you are trying to reference does not exist. Data source error: %-.64s", + ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE: "Can't create federated table. The data source connection string '%-.64s' is not in the correct format", + ER_FOREIGN_DATA_STRING_INVALID: "The data source connection string '%-.64s' is not in the correct format", + ER_CANT_CREATE_FEDERATED_TABLE: "Can't create federated table. Foreign data src error: %-.64s", + ER_TRG_IN_WRONG_SCHEMA: "Trigger in wrong schema", + ER_STACK_OVERRUN_NEED_MORE: "Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld --thread_stack=#' to specify a bigger stack.", + ER_TOO_LONG_BODY: "Routine body for '%-.100s' is too long", + ER_WARN_CANT_DROP_DEFAULT_KEYCACHE: "Cannot drop default keycache", + ER_TOO_BIG_DISPLAYWIDTH: "Display width out of range for column '%-.192s' (max = %lu)", + ER_XAER_DUPID: "XAER_DUPID: The XID already exists", + ER_DATETIME_FUNCTION_OVERFLOW: "Datetime function: %-.32s field overflow", + ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG: "Can't update table '%-.192s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.", + ER_VIEW_PREVENT_UPDATE: "The definition of table '%-.192s' prevents operation %.192s on table '%-.192s'.", + ER_PS_NO_RECURSION: "The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner", + ER_SP_CANT_SET_AUTOCOMMIT: "Not allowed to set autocommit from a stored function or trigger", + ER_MALFORMED_DEFINER: "Definer is not fully qualified", + ER_VIEW_FRM_NO_USER: "View '%-.192s'.'%-.192s' has no definer information (old table format). Current user is used as definer. Please recreate the view!", + ER_VIEW_OTHER_USER: "You need the SUPER privilege for creation view with '%-.192s'@'%-.192s' definer", + ER_NO_SUCH_USER: "The user specified as a definer ('%-.64s'@'%-.64s') does not exist", + ER_FORBID_SCHEMA_CHANGE: "Changing schema from '%-.192s' to '%-.192s' is not allowed.", + ER_ROW_IS_REFERENCED_2: "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)", + ER_NO_REFERENCED_ROW_2: "Cannot add or update a child row: a foreign key constraint fails (%.192s)", + ER_SP_BAD_VAR_SHADOW: "Variable '%-.64s' must be quoted with `...`, or renamed", + ER_TRG_NO_DEFINER: "No definer attribute for trigger '%-.192s'.'%-.192s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger.", + ER_OLD_FILE_FORMAT: "'%-.192s' has an old format, you should re-create the '%s' object(s)", + ER_SP_RECURSION_LIMIT: "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.192s", + ER_SP_PROC_TABLE_CORRUPT: "Failed to load routine %-.192s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)", + ER_SP_WRONG_NAME: "Incorrect routine name '%-.192s'", + ER_TABLE_NEEDS_UPGRADE: "Table upgrade required. Please do \"REPAIR TABLE `%-.32s`\" or dump/reload to fix it!", + ER_SP_NO_AGGREGATE: "AGGREGATE is not supported for stored functions", + ER_MAX_PREPARED_STMT_COUNT_REACHED: "Can't create more than max_prepared_stmt_count statements (current value: %lu)", + ER_VIEW_RECURSIVE: "`%-.192s`.`%-.192s` contains view recursion", + ER_NON_GROUPING_FIELD_USED: "Non-grouping field '%-.192s' is used in %-.64s clause", + ER_TABLE_CANT_HANDLE_SPKEYS: "The used table type doesn't support SPATIAL indexes", + ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA: "Triggers can not be created on system tables", + ER_REMOVED_SPACES: "Leading spaces are removed from name '%s'", + ER_AUTOINC_READ_FAILED: "Failed to read auto-increment value from storage engine", + ER_USERNAME: "user name", + ER_HOSTNAME: "host name", + ER_WRONG_STRING_LENGTH: "String '%-.70s' is too long for %s (should be no longer than %d)", + ER_NON_INSERTABLE_TABLE: "The target table %-.100s of the %s is not insertable-into", + ER_ADMIN_WRONG_MRG_TABLE: "Table '%-.64s' is differently defined or of non-MyISAM type or doesn't exist", + ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT: "Too high level of nesting for select", + ER_NAME_BECOMES_EMPTY: "Name '%-.64s' has become ''", + ER_AMBIGUOUS_FIELD_TERM: "First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY", + ER_FOREIGN_SERVER_EXISTS: "The foreign server, %s, you are trying to create already exists.", + ER_FOREIGN_SERVER_DOESNT_EXIST: "The foreign server name you are trying to reference does not exist. Data source error: %-.64s", + ER_ILLEGAL_HA_CREATE_OPTION: "Table storage engine '%-.64s' does not support the create option '%.64s'", + ER_PARTITION_REQUIRES_VALUES_ERROR: "Syntax error: %-.64s PARTITIONING requires definition of VALUES %-.64s for each partition", + ER_PARTITION_WRONG_VALUES_ERROR: "Only %-.64s PARTITIONING can use VALUES %-.64s in partition definition", + ER_PARTITION_MAXVALUE_ERROR: "MAXVALUE can only be used in last partition definition", + ER_PARTITION_SUBPARTITION_ERROR: "Subpartitions can only be hash partitions and by key", + ER_PARTITION_SUBPART_MIX_ERROR: "Must define subpartitions on all partitions if on one partition", + ER_PARTITION_WRONG_NO_PART_ERROR: "Wrong number of partitions defined, mismatch with previous setting", + ER_PARTITION_WRONG_NO_SUBPART_ERROR: "Wrong number of subpartitions defined, mismatch with previous setting", + ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR: "Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed", + ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR: "Expression in RANGE/LIST VALUES must be constant", + ER_FIELD_NOT_FOUND_PART_ERROR: "Field in list of fields for partition function not found in table", + ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR: "List of fields is only allowed in KEY partitions", + ER_INCONSISTENT_PARTITION_INFO_ERROR: "The partition info in the frm file is not consistent with what can be written into the frm file", + ER_PARTITION_FUNC_NOT_ALLOWED_ERROR: "The %-.192s function returns the wrong type", + ER_PARTITIONS_MUST_BE_DEFINED_ERROR: "For %-.64s partitions each partition must be defined", + ER_RANGE_NOT_INCREASING_ERROR: "VALUES LESS THAN value must be strictly increasing for each partition", + ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR: "VALUES value must be of same type as partition function", + ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR: "Multiple definition of same constant in list partitioning", + ER_PARTITION_ENTRY_ERROR: "Partitioning can not be used stand-alone in query", + ER_MIX_HANDLER_ERROR: "The mix of handlers in the partitions is not allowed in this version of MySQL", + ER_PARTITION_NOT_DEFINED_ERROR: "For the partitioned engine it is necessary to define all %-.64s", + ER_TOO_MANY_PARTITIONS_ERROR: "Too many partitions (including subpartitions) were defined", + ER_SUBPARTITION_ERROR: "It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning", + ER_CANT_CREATE_HANDLER_FILE: "Failed to create specific handler file", + ER_BLOB_FIELD_IN_PART_FUNC_ERROR: "A BLOB field is not allowed in partition function", + ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF: "A %-.192s must include all columns in the table's partitioning function", + ER_NO_PARTS_ERROR: "Number of %-.64s = 0 is not an allowed value", + ER_PARTITION_MGMT_ON_NONPARTITIONED: "Partition management on a not partitioned table is not possible", + ER_FOREIGN_KEY_ON_PARTITIONED: "Foreign key clause is not yet supported in conjunction with partitioning", + ER_DROP_PARTITION_NON_EXISTENT: "Error in list of partitions to %-.64s", + ER_DROP_LAST_PARTITION: "Cannot remove all partitions, use DROP TABLE instead", + ER_COALESCE_ONLY_ON_HASH_PARTITION: "COALESCE PARTITION can only be used on HASH/KEY partitions", + ER_REORG_HASH_ONLY_ON_SAME_NO: "REORGANIZE PARTITION can only be used to reorganize partitions not to change their numbers", + ER_REORG_NO_PARAM_ERROR: "REORGANIZE PARTITION without parameters can only be used on auto-partitioned tables using HASH PARTITIONs", + ER_ONLY_ON_RANGE_LIST_PARTITION: "%-.64s PARTITION can only be used on RANGE/LIST partitions", + ER_ADD_PARTITION_SUBPART_ERROR: "Trying to Add partition(s) with wrong number of subpartitions", + ER_ADD_PARTITION_NO_NEW_PARTITION: "At least one partition must be added", + ER_COALESCE_PARTITION_NO_PARTITION: "At least one partition must be coalesced", + ER_REORG_PARTITION_NOT_EXIST: "More partitions to reorganize than there are partitions", + ER_SAME_NAME_PARTITION: "Duplicate partition name %-.192s", + ER_NO_BINLOG_ERROR: "It is not allowed to shut off binlog on this command", + ER_CONSECUTIVE_REORG_PARTITIONS: "When reorganizing a set of partitions they must be in consecutive order", + ER_REORG_OUTSIDE_RANGE: "Reorganize of range partitions cannot change total ranges except for last partition where it can extend the range", + ER_PARTITION_FUNCTION_FAILURE: "Partition function not supported in this version for this handler", + ER_PART_STATE_ERROR: "Partition state cannot be defined from CREATE/ALTER TABLE", + ER_LIMITED_PART_RANGE: "The %-.64s handler only supports 32 bit integers in VALUES", + ER_PLUGIN_IS_NOT_LOADED: "Plugin '%-.192s' is not loaded", + ER_WRONG_VALUE: "Incorrect %-.32s value: '%-.128s'", + ER_NO_PARTITION_FOR_GIVEN_VALUE: "Table has no partition for value %-.64s", + ER_FILEGROUP_OPTION_ONLY_ONCE: "It is not allowed to specify %s more than once", + ER_CREATE_FILEGROUP_FAILED: "Failed to create %s", + ER_DROP_FILEGROUP_FAILED: "Failed to drop %s", + ER_TABLESPACE_AUTO_EXTEND_ERROR: "The handler doesn't support autoextend of tablespaces", + ER_WRONG_SIZE_NUMBER: "A size parameter was incorrectly specified, either number or on the form 10M", + ER_SIZE_OVERFLOW_ERROR: "The size number was correct but we don't allow the digit part to be more than 2 billion", + ER_ALTER_FILEGROUP_FAILED: "Failed to alter: %s", + ER_BINLOG_ROW_LOGGING_FAILED: "Writing one row to the row-based binary log failed", + ER_BINLOG_ROW_WRONG_TABLE_DEF: "Table definition on master and slave does not match: %s", + ER_BINLOG_ROW_RBR_TO_SBR: "Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events", + ER_EVENT_ALREADY_EXISTS: "Event '%-.192s' already exists", + ER_EVENT_STORE_FAILED: "Failed to store event %s. Error code %d from storage engine.", + ER_EVENT_DOES_NOT_EXIST: "Unknown event '%-.192s'", + ER_EVENT_CANT_ALTER: "Failed to alter event '%-.192s'", + ER_EVENT_DROP_FAILED: "Failed to drop %s", + ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG: "INTERVAL is either not positive or too big", + ER_EVENT_ENDS_BEFORE_STARTS: "ENDS is either invalid or before STARTS", + ER_EVENT_EXEC_TIME_IN_THE_PAST: "Event execution time is in the past. Event has been disabled", + ER_EVENT_OPEN_TABLE_FAILED: "Failed to open mysql.event", + ER_EVENT_NEITHER_M_EXPR_NOR_M_AT: "No datetime expression provided", + ER_OBSOLETE_COL_COUNT_DOESNT_MATCH_CORRUPTED: "Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted", + ER_OBSOLETE_CANNOT_LOAD_FROM_TABLE: "Cannot load from mysql.%s. The table is probably corrupted", + ER_EVENT_CANNOT_DELETE: "Failed to delete the event from mysql.event", + ER_EVENT_COMPILE_ERROR: "Error during compilation of event's body", + ER_EVENT_SAME_NAME: "Same old and new event name", + ER_EVENT_DATA_TOO_LONG: "Data for column '%s' too long", + ER_DROP_INDEX_FK: "Cannot drop index '%-.192s': needed in a foreign key constraint", + ER_WARN_DEPRECATED_SYNTAX_WITH_VER: "The syntax '%s' is deprecated and will be removed in MySQL %s. Please use %s instead", + ER_CANT_WRITE_LOCK_LOG_TABLE: "You can't write-lock a log table. Only read access is possible", + ER_CANT_LOCK_LOG_TABLE: "You can't use locks with log tables.", + ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED: "Upholding foreign key constraints for table '%.192s', entry '%-.192s', key %d would lead to a duplicate entry", + ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE: "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error.", + ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR: "Cannot switch out of the row-based binary log format when the session has open temporary tables", + ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT: "Cannot change the binary logging format inside a stored function or trigger", + ER_NDB_CANT_SWITCH_BINLOG_FORMAT: "The NDB cluster engine does not support changing the binlog format on the fly yet", + ER_PARTITION_NO_TEMPORARY: "Cannot create temporary table with partitions", + ER_PARTITION_CONST_DOMAIN_ERROR: "Partition constant is out of partition function domain", + ER_PARTITION_FUNCTION_IS_NOT_ALLOWED: "This partition function is not allowed", + ER_DDL_LOG_ERROR: "Error in DDL log", + ER_NULL_IN_VALUES_LESS_THAN: "Not allowed to use NULL value in VALUES LESS THAN", + ER_WRONG_PARTITION_NAME: "Incorrect partition name", + ER_CANT_CHANGE_TX_CHARACTERISTICS: "Transaction characteristics can't be changed while a transaction is in progress", + ER_DUP_ENTRY_AUTOINCREMENT_CASE: "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.192s' for key '%-.192s'", + ER_EVENT_MODIFY_QUEUE_ERROR: "Internal scheduler error %d", + ER_EVENT_SET_VAR_ERROR: "Error during starting/stopping of the scheduler. Error code %u", + ER_PARTITION_MERGE_ERROR: "Engine cannot be used in partitioned tables", + ER_CANT_ACTIVATE_LOG: "Cannot activate '%-.64s' log", + ER_RBR_NOT_AVAILABLE: "The server was not built with row-based replication", + ER_BASE64_DECODE_ERROR: "Decoding of base64 string failed", + ER_EVENT_RECURSION_FORBIDDEN: "Recursion of EVENT DDL statements is forbidden when body is present", + ER_EVENTS_DB_ERROR: "Cannot proceed because system tables used by Event Scheduler were found damaged at server start", + ER_ONLY_INTEGERS_ALLOWED: "Only integers allowed as number here", + ER_UNSUPORTED_LOG_ENGINE: "This storage engine cannot be used for log tables\"", + ER_BAD_LOG_STATEMENT: "You cannot '%s' a log table if logging is enabled", + ER_CANT_RENAME_LOG_TABLE: "Cannot rename '%s'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to '%s'", + ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT: "Incorrect parameter count in the call to native function '%-.192s'", + ER_WRONG_PARAMETERS_TO_NATIVE_FCT: "Incorrect parameters in the call to native function '%-.192s'", + ER_WRONG_PARAMETERS_TO_STORED_FCT: "Incorrect parameters in the call to stored function '%-.192s'", + ER_NATIVE_FCT_NAME_COLLISION: "This function '%-.192s' has the same name as a native function", + ER_DUP_ENTRY_WITH_KEY_NAME: "Duplicate entry '%-.64s' for key '%-.192s'", + 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_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_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_SLAVE_FATAL_ERROR: "Fatal error: %s", + ER_SLAVE_RELAY_LOG_READ_FAILURE: "Relay log read failure: %s", + ER_SLAVE_RELAY_LOG_WRITE_FAILURE: "Relay log write failure: %s", + ER_SLAVE_CREATE_EVENT_FAILURE: "Failed to create %s", + ER_SLAVE_MASTER_COM_FAILURE: "Master command %s failed: %s", + ER_BINLOG_LOGGING_IMPOSSIBLE: "Binary logging not possible. Message: %s", + ER_VIEW_NO_CREATION_CTX: "View `%-.64s`.`%-.64s` has no creation context", + ER_VIEW_INVALID_CREATION_CTX: "Creation context of view `%-.64s`.`%-.64s' is invalid", + ER_SR_INVALID_CREATION_CTX: "Creation context of stored routine `%-.64s`.`%-.64s` is invalid", + ER_TRG_CORRUPTED_FILE: "Corrupted TRG file for table `%-.64s`.`%-.64s`", + ER_TRG_NO_CREATION_CTX: "Triggers for table `%-.64s`.`%-.64s` have no creation context", + ER_TRG_INVALID_CREATION_CTX: "Trigger creation context of table `%-.64s`.`%-.64s` is invalid", + ER_EVENT_INVALID_CREATION_CTX: "Creation context of event `%-.64s`.`%-.64s` is invalid", + ER_TRG_CANT_OPEN_TABLE: "Cannot open table for trigger `%-.64s`.`%-.64s`", + ER_CANT_CREATE_SROUTINE: "Cannot create stored routine `%-.64s`. Check warnings", + ER_NEVER_USED: "Ambiguous slave modes combination. %s", + ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT: "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement.", + ER_SLAVE_CORRUPT_EVENT: "Corrupted replication event was detected", + ER_LOAD_DATA_INVALID_COLUMN: "Invalid column reference (%-.64s) in LOAD DATA", + ER_LOG_PURGE_NO_FILE: "Being purged log %s was not found", + ER_XA_RBTIMEOUT: "XA_RBTIMEOUT: Transaction branch was rolled back: took too long", + ER_XA_RBDEADLOCK: "XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected", + ER_NEED_REPREPARE: "Prepared statement needs to be re-prepared", + ER_DELAYED_NOT_SUPPORTED: "DELAYED option not supported for table '%-.192s'", + WARN_NO_MASTER_INFO: "The master info structure does not exist", + WARN_OPTION_IGNORED: "<%-.64s> option ignored", + WARN_PLUGIN_DELETE_BUILTIN: "Built-in plugins cannot be deleted", + WARN_PLUGIN_BUSY: "Plugin is busy and will be uninstalled on shutdown", + ER_VARIABLE_IS_READONLY: "%s variable '%s' is read-only. Use SET %s to assign the value", + ER_WARN_ENGINE_TRANSACTION_ROLLBACK: "Storage engine %s does not support rollback for this statement. Transaction rolled back and must be restarted", + ER_SLAVE_HEARTBEAT_FAILURE: "Unexpected master's heartbeat data: %s", + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE: "The requested value for the heartbeat period is either negative or exceeds the maximum allowed (%s seconds).", + ER_NDB_REPLICATION_SCHEMA_ERROR: "Bad schema for mysql.ndb_replication table. Message: %-.64s", + ER_CONFLICT_FN_PARSE_ERROR: "Error in parsing conflict function. Message: %-.64s", + ER_EXCEPTIONS_WRITE_ERROR: "Write to exceptions table failed. Message: %-.128s\"", + ER_TOO_LONG_TABLE_COMMENT: "Comment for table '%-.64s' is too long (max = %lu)", + ER_TOO_LONG_FIELD_COMMENT: "Comment for field '%-.64s' is too long (max = %lu)", + ER_FUNC_INEXISTENT_NAME_COLLISION: "FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual", + ER_DATABASE_NAME: "Database", + ER_TABLE_NAME: "Table", + ER_PARTITION_NAME: "Partition", + ER_SUBPARTITION_NAME: "Subpartition", + ER_TEMPORARY_NAME: "Temporary", + ER_RENAMED_NAME: "Renamed", + ER_TOO_MANY_CONCURRENT_TRXS: "Too many active concurrent transactions", + WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED: "Non-ASCII separator arguments are not fully supported", + ER_DEBUG_SYNC_TIMEOUT: "debug sync point wait timed out", + ER_DEBUG_SYNC_HIT_LIMIT: "debug sync point hit limit reached", + ER_DUP_SIGNAL_SET: "Duplicate condition information item '%s'", + ER_SIGNAL_WARN: "Unhandled user-defined warning condition", + ER_SIGNAL_NOT_FOUND: "Unhandled user-defined not found condition", + ER_SIGNAL_EXCEPTION: "Unhandled user-defined exception condition", + ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER: "RESIGNAL when handler not active", + ER_SIGNAL_BAD_CONDITION_TYPE: "SIGNAL/RESIGNAL can only use a CONDITION defined with SQLSTATE", + WARN_COND_ITEM_TRUNCATED: "Data truncated for condition item '%s'", + ER_COND_ITEM_TOO_LONG: "Data too long for condition item '%s'", + ER_UNKNOWN_LOCALE: "Unknown locale: '%-.64s'", + ER_SLAVE_IGNORE_SERVER_IDS: "The requested server id %d clashes with the slave startup option --replicate-same-server-id", + ER_QUERY_CACHE_DISABLED: "Query cache is disabled; restart the server with query_cache_type=1 to enable it", + ER_SAME_NAME_PARTITION_FIELD: "Duplicate partition field name '%-.192s'", + ER_PARTITION_COLUMN_LIST_ERROR: "Inconsistency in usage of column lists for partitioning", + ER_WRONG_TYPE_COLUMN_VALUE_ERROR: "Partition column values of incorrect type", + ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR: "Too many fields in '%-.192s'", + ER_MAXVALUE_IN_VALUES_IN: "Cannot use MAXVALUE as value in VALUES IN", + ER_TOO_MANY_VALUES_ERROR: "Cannot have more than one value for this type of %-.64s partitioning", + ER_ROW_SINGLE_PARTITION_FIELD_ERROR: "Row expressions in VALUES IN only allowed for multi-field column partitioning", + ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD: "Field '%-.192s' is of a not allowed type for this type of partitioning", + ER_PARTITION_FIELDS_TOO_LONG: "The total length of the partitioning fields is too large", + ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE: "Cannot execute statement: impossible to write to binary log since both row-incapable engines and statement-incapable engines are involved.", + ER_BINLOG_ROW_MODE_AND_STMT_ENGINE: "Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = ROW and at least one table uses a storage engine limited to statement-based logging.", + ER_BINLOG_UNSAFE_AND_STMT_ENGINE: "Cannot execute statement: impossible to write to binary log since statement is unsafe, storage engine is limited to statement-based logging, and BINLOG_FORMAT = MIXED. %s", + ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE: "Cannot execute statement: impossible to write to binary log since statement is in row format and at least one table uses a storage engine limited to statement-based logging.", + ER_BINLOG_STMT_MODE_AND_ROW_ENGINE: "Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging.%s", + ER_BINLOG_ROW_INJECTION_AND_STMT_MODE: "Cannot execute statement: impossible to write to binary log since statement is in row format and BINLOG_FORMAT = STATEMENT.", + ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE: "Cannot execute statement: impossible to write to binary log since more than one engine is involved and at least one engine is self-logging.", + ER_BINLOG_UNSAFE_LIMIT: "The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.", + ER_BINLOG_UNSAFE_INSERT_DELAYED: "The statement is unsafe because it uses INSERT DELAYED. This is unsafe because the times when rows are inserted cannot be predicted.", + ER_BINLOG_UNSAFE_SYSTEM_TABLE: "The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves.", + ER_BINLOG_UNSAFE_AUTOINC_COLUMNS: "Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly.", + ER_BINLOG_UNSAFE_UDF: "Statement is unsafe because it uses a UDF which may not return the same value on the slave.", + ER_BINLOG_UNSAFE_SYSTEM_VARIABLE: "Statement is unsafe because it uses a system variable that may have a different value on the slave.", + ER_BINLOG_UNSAFE_SYSTEM_FUNCTION: "Statement is unsafe because it uses a system function that may return a different value on the slave.", + ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS: "Statement is unsafe because it accesses a non-transactional table after accessing a transactional table within the same transaction.", + ER_MESSAGE_AND_STATEMENT: "%s Statement: %s", + ER_SLAVE_CONVERSION_FAILED: "Column %d of table '%-.192s.%-.192s' cannot be converted from type '%-.32s' to type '%-.32s'", + ER_SLAVE_CANT_CREATE_CONVERSION: "Can't create conversion table for table '%-.192s.%-.192s'", + ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT: "Cannot modify @@session.binlog_format inside a transaction", + ER_PATH_LENGTH: "The path specified for %.64s is too long.", + ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT: "'%s' is deprecated and will be removed in a future release.", + ER_WRONG_NATIVE_TABLE_STRUCTURE: "Native table '%-.64s'.'%-.64s' has the wrong structure", + ER_WRONG_PERFSCHEMA_USAGE: "Invalid performance_schema usage.", + ER_WARN_I_S_SKIPPED_TABLE: "Table '%s'.'%s' was skipped since its definition is being modified by concurrent DDL statement", + ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT: "Cannot modify @@session.binlog_direct_non_transactional_updates inside a transaction", + ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT: "Cannot change the binlog direct flag inside a stored function or trigger", + ER_SPATIAL_MUST_HAVE_GEOM_COL: "A SPATIAL index may only contain a geometrical type column", + ER_TOO_LONG_INDEX_COMMENT: "Comment for index '%-.64s' is too long (max = %lu)", + ER_LOCK_ABORTED: "Wait on a lock was aborted due to a pending exclusive lock", + ER_DATA_OUT_OF_RANGE: "%s value is out of range in '%s'", + ER_WRONG_SPVAR_TYPE_IN_LIMIT: "A variable of a non-integer based type in LIMIT clause", + ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE: "Mixing self-logging and non-self-logging engines in a statement is unsafe.", + ER_BINLOG_UNSAFE_MIXED_STATEMENT: "Statement accesses nontransactional table as well as transactional or temporary table, and writes to any of them.", + ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN: "Cannot modify @@session.sql_log_bin inside a transaction", + ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN: "Cannot change the sql_log_bin inside a stored function or trigger", + ER_FAILED_READ_FROM_PAR_FILE: "Failed to read from the .par file", + ER_VALUES_IS_NOT_INT_TYPE_ERROR: "VALUES value for partition '%-.64s' must have type INT", + ER_ACCESS_DENIED_NO_PASSWORD_ERROR: "Access denied for user '%-.48s'@'%-.64s'", + ER_SET_PASSWORD_AUTH_PLUGIN: "SET PASSWORD has no significance for users authenticating via plugins", + ER_GRANT_PLUGIN_USER_EXISTS: "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists", + ER_TRUNCATE_ILLEGAL_FK: "Cannot truncate a table referenced in a foreign key constraint (%.192s)", + ER_PLUGIN_IS_PERMANENT: "Plugin '%s' is force_plus_permanent and can not be unloaded", + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN: "The requested value for the heartbeat period is less than 1 millisecond. The value is reset to 0, meaning that heartbeating will effectively be disabled.", + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX: "The requested value for the heartbeat period exceeds the value of `slave_net_timeout' seconds. A sensible value for the period should be less than the timeout.", + ER_STMT_CACHE_FULL: "Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again", + ER_MULTI_UPDATE_KEY_CONFLICT: "Primary key/partition key update is not allowed since the table is updated both as '%-.192s' and '%-.192s'.", + ER_TABLE_NEEDS_REBUILD: "Table rebuild required. Please do \"ALTER TABLE `%-.32s` FORCE\" or dump/reload to fix it!", + WARN_OPTION_BELOW_LIMIT: "The value of '%s' should be no less than the value of '%s'", + ER_INDEX_COLUMN_TOO_LONG: "Index column size too large. The maximum column size is %lu bytes.", + ER_ERROR_IN_TRIGGER_BODY: "Trigger '%-.64s' has an error in its body: '%-.256s'", + ER_ERROR_IN_UNKNOWN_TRIGGER_BODY: "Unknown trigger has an error in its body: '%-.256s'", + ER_INDEX_CORRUPT: "Index %s is corrupted", + ER_UNDO_RECORD_TOO_BIG: "Undo log record is too big.", + ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT: "INSERT IGNORE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave.", + ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE: "INSERT... SELECT... ON DUPLICATE KEY UPDATE is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are updated. This order cannot be predicted and may differ on master and the slave.", + ER_BINLOG_UNSAFE_REPLACE_SELECT: "REPLACE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave.", + ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT: "CREATE... IGNORE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave.", + ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT: "CREATE... REPLACE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave.", + ER_BINLOG_UNSAFE_UPDATE_IGNORE: "UPDATE IGNORE is unsafe because the order in which rows are updated determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave.", + ER_PLUGIN_NO_UNINSTALL: "Plugin '%s' is marked as not dynamically uninstallable. You have to stop the server to uninstall it.", + ER_PLUGIN_NO_INSTALL: "Plugin '%s' is marked as not dynamically installable. You have to stop the server to install it.", + ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT: "Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave.", + ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC: "CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave.", + ER_BINLOG_UNSAFE_INSERT_TWO_KEYS: "INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe", + ER_TABLE_IN_FK_CHECK: "Table is being used in foreign key check.", + ER_UNSUPPORTED_ENGINE: "Storage engine '%s' does not support system tables. [%s.%s]", + ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST: "INSERT into autoincrement field which is not the first part in the composed primary key is unsafe.", + ER_CANNOT_LOAD_FROM_TABLE_V2: "Cannot load from %s.%s. The table is probably corrupted", + ER_MASTER_DELAY_VALUE_OUT_OF_RANGE: "The requested value %u for the master delay exceeds the maximum %u", + ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT: "Only Format_description_log_event and row events are allowed in BINLOG statements (but %s was provided)", + ER_PARTITION_EXCHANGE_DIFFERENT_OPTION: "Non matching attribute '%-.64s' between partition and table", + ER_PARTITION_EXCHANGE_PART_TABLE: "Table to exchange with partition is partitioned: '%-.64s'", + ER_PARTITION_EXCHANGE_TEMP_TABLE: "Table to exchange with partition is temporary: '%-.64s'", + ER_PARTITION_INSTEAD_OF_SUBPARTITION: "Subpartitioned table, use subpartition instead of partition", + ER_UNKNOWN_PARTITION: "Unknown partition '%-.64s' in table '%-.64s'", + ER_TABLES_DIFFERENT_METADATA: "Tables have different definitions", + ER_ROW_DOES_NOT_MATCH_PARTITION: "Found a row that does not match the partition", + ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX: "Option binlog_cache_size (%lu) is greater than max_binlog_cache_size (%lu); setting binlog_cache_size equal to max_binlog_cache_size.", + ER_WARN_INDEX_NOT_APPLICABLE: "Cannot use %-.64s access on index '%-.64s' due to type or collation conversion on field '%-.64s'", + ER_PARTITION_EXCHANGE_FOREIGN_KEY: "Table to exchange with partition has foreign key references: '%-.64s'", + ER_NO_SUCH_KEY_VALUE: "Key value '%-.192s' was not found in table '%-.192s.%-.192s'", + ER_RPL_INFO_DATA_TOO_LONG: "Data for column '%s' too long", + ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE: "Replication event checksum verification failed while reading from network.", + ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE: "Replication event checksum verification failed while reading from a log file.", + ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX: "Option binlog_stmt_cache_size (%lu) is greater than max_binlog_stmt_cache_size (%lu); setting binlog_stmt_cache_size equal to max_binlog_stmt_cache_size.", + ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT: "Can't update table '%-.192s' while '%-.192s' is being created.", + ER_PARTITION_CLAUSE_ON_NONPARTITIONED: "PARTITION () clause on non partitioned table", + ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET: "Found a row not matching the given partition set", + ER_NO_SUCH_PARTITION__UNUSED: "partition '%-.64s' doesn't exist", + ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE: "Failure while changing the type of replication repository: %s.", + ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE: "The creation of some temporary tables could not be rolled back.", + ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE: "Some temporary tables were dropped, but these operations could not be rolled back.", + ER_MTS_FEATURE_IS_NOT_SUPPORTED: "%s is not supported in multi-threaded slave mode. %s", + ER_MTS_UPDATED_DBS_GREATER_MAX: "The number of modified databases exceeds the maximum %d; the database names will not be included in the replication event metadata.", + ER_MTS_CANT_PARALLEL: "Cannot execute the current event group in the parallel mode. Encountered event %s, relay-log name %s, position %s which prevents execution of this event group in parallel mode. Reason: %s.", + ER_MTS_INCONSISTENT_DATA: "%s", + ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING: "FULLTEXT index is not supported for partitioned tables.", + ER_DA_INVALID_CONDITION_NUMBER: "Invalid condition number", + ER_INSECURE_PLAIN_TEXT: "Sending passwords in plain text without SSL/TLS is extremely insecure.", + ER_INSECURE_CHANGE_MASTER: "Storing MySQL user name or password information in the master.info repository is not secure and is therefore not recommended. Please see the MySQL Manual for more about this issue and possible alternatives.", + ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO: "Foreign key constraint for table '%.192s', record '%-.192s' would lead to a duplicate entry in table '%.192s', key '%.192s'", + ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO: "Foreign key constraint for table '%.192s', record '%-.192s' would lead to a duplicate entry in a child table", + ER_SQLTHREAD_WITH_SECURE_SLAVE: "Setting authentication options is not possible when only the Slave SQL Thread is being started.", + ER_TABLE_HAS_NO_FT: "The table does not have FULLTEXT index to support this query", + ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER: "The system variable %.200s cannot be set in stored functions or triggers.", + ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION: "The system variable %.200s cannot be set when there is an ongoing transaction.", + ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST: "The system variable @@SESSION.GTID_NEXT has the value %.200s, which is not listed in @@SESSION.GTID_NEXT_LIST.", + ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION_WHEN_GTID_NEXT_LIST_IS_NULL: "When @@SESSION.GTID_NEXT_LIST == NULL, the system variable @@SESSION.GTID_NEXT cannot change inside a transaction.", + ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION: "The statement 'SET %.200s' cannot invoke a stored function.", + ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL: "The system variable @@SESSION.GTID_NEXT cannot be 'AUTOMATIC' when @@SESSION.GTID_NEXT_LIST is non-NULL.", + ER_SKIPPING_LOGGED_TRANSACTION: "Skipping transaction %.200s because it has already been executed and logged.", + ER_MALFORMED_GTID_SET_SPECIFICATION: "Malformed GTID set specification '%.200s'.", + ER_MALFORMED_GTID_SET_ENCODING: "Malformed GTID set encoding.", + ER_MALFORMED_GTID_SPECIFICATION: "Malformed GTID specification '%.200s'.", + ER_GNO_EXHAUSTED: "Impossible to generate Global Transaction Identifier: the integer component reached the maximal value. Restart the server with a new server_uuid.", + ER_BAD_SLAVE_AUTO_POSITION: "Parameters MASTER_LOG_FILE, MASTER_LOG_POS, RELAY_LOG_FILE and RELAY_LOG_POS cannot be set when MASTER_AUTO_POSITION is active.", + ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON: "CHANGE MASTER TO MASTER_AUTO_POSITION = 1 can only be executed when @@GLOBAL.GTID_MODE = ON.", + ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET: "Cannot execute statements with implicit commit inside a transaction when @@SESSION.GTID_NEXT != AUTOMATIC or @@SESSION.GTID_NEXT_LIST != NULL.", + ER_GTID_MODE_2_OR_3_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON: "@@GLOBAL.GTID_MODE = ON or UPGRADE_STEP_2 requires @@GLOBAL.ENFORCE_GTID_CONSISTENCY = 1.", + ER_GTID_MODE_REQUIRES_BINLOG: "@@GLOBAL.GTID_MODE = ON or UPGRADE_STEP_1 or UPGRADE_STEP_2 requires --log-bin and --log-slave-updates.", + ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF: "@@SESSION.GTID_NEXT cannot be set to UUID:NUMBER when @@GLOBAL.GTID_MODE = OFF.", + ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON: "@@SESSION.GTID_NEXT cannot be set to ANONYMOUS when @@GLOBAL.GTID_MODE = ON.", + ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF: "@@SESSION.GTID_NEXT_LIST cannot be set to a non-NULL value when @@GLOBAL.GTID_MODE = OFF.", + ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF: "Found a Gtid_log_event or Previous_gtids_log_event when @@GLOBAL.GTID_MODE = OFF.", + ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE: "When @@GLOBAL.ENFORCE_GTID_CONSISTENCY = 1, updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.", + ER_GTID_UNSAFE_CREATE_SELECT: "CREATE TABLE ... SELECT is forbidden when @@GLOBAL.ENFORCE_GTID_CONSISTENCY = 1.", + ER_GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION: "When @@GLOBAL.ENFORCE_GTID_CONSISTENCY = 1, the statements CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can be executed in a non-transactional context only, and require that AUTOCOMMIT = 1.", + ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME: "The value of @@GLOBAL.GTID_MODE can only change one step at a time: OFF <-> UPGRADE_STEP_1 <-> UPGRADE_STEP_2 <-> ON. Also note that this value must be stepped up or down simultaneously on all servers; see the Manual for instructions.", + ER_MASTER_HAS_PURGED_REQUIRED_GTIDS: "The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.", + ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID: "@@SESSION.GTID_NEXT cannot be changed by a client that owns a GTID. The client owns %s. Ownership is released on COMMIT or ROLLBACK.", + ER_UNKNOWN_EXPLAIN_FORMAT: "Unknown EXPLAIN format name: '%s'", + ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION: "Cannot execute statement in a READ ONLY transaction.", + ER_TOO_LONG_TABLE_PARTITION_COMMENT: "Comment for table partition '%-.64s' is too long (max = %lu)", + ER_SLAVE_CONFIGURATION: "Slave is not configured or failed to initialize properly. You must at least set --server-id to enable either a master or a slave. Additional error messages can be found in the MySQL error log.", + ER_INNODB_FT_LIMIT: "InnoDB presently supports one FULLTEXT index creation at a time", + ER_INNODB_NO_FT_TEMP_TABLE: "Cannot create FULLTEXT index on temporary InnoDB table", + ER_INNODB_FT_WRONG_DOCID_COLUMN: "Column '%-.192s' is of wrong type for an InnoDB FULLTEXT index", + ER_INNODB_FT_WRONG_DOCID_INDEX: "Index '%-.192s' is of wrong type for an InnoDB FULLTEXT index", + ER_INNODB_ONLINE_LOG_TOO_BIG: "Creating index '%-.192s' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again.", + ER_UNKNOWN_ALTER_ALGORITHM: "Unknown ALGORITHM '%s'", + ER_UNKNOWN_ALTER_LOCK: "Unknown LOCK type '%s'", + ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS: "CHANGE MASTER cannot be executed when the slave was stopped with an error or killed in MTS mode. Consider using RESET SLAVE or START SLAVE UNTIL.", + ER_MTS_RECOVERY_FAILURE: "Cannot recover after SLAVE errored out in parallel execution mode. Additional error messages can be found in the MySQL error log.", + ER_MTS_RESET_WORKERS: "Cannot clean up worker info tables. Additional error messages can be found in the MySQL error log.", + ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2: "Column count of %s.%s is wrong. Expected %d, found %d. The table is probably corrupted", + ER_SLAVE_SILENT_RETRY_TRANSACTION: "Slave must silently retry current transaction", + ER_DISCARD_FK_CHECKS_RUNNING: "There is a foreign key check running on table '%-.192s'. Cannot discard the table.", + ER_TABLE_SCHEMA_MISMATCH: "Schema mismatch (%s)", + ER_TABLE_IN_SYSTEM_TABLESPACE: "Table '%-.192s' in system tablespace", + ER_IO_READ_ERROR: "IO Read error: (%lu, %s) %s", + ER_IO_WRITE_ERROR: "IO Write error: (%lu, %s) %s", + ER_TABLESPACE_MISSING: "Tablespace is missing for table '%-.192s'", + ER_TABLESPACE_EXISTS: "Tablespace for table '%-.192s' exists. Please DISCARD the tablespace before IMPORT.", + ER_TABLESPACE_DISCARDED: "Tablespace has been discarded for table '%-.192s'", + ER_INTERNAL_ERROR: "Internal error: %s", + ER_INNODB_IMPORT_ERROR: "ALTER TABLE '%-.192s' IMPORT TABLESPACE failed with error %lu : '%s'", + ER_INNODB_INDEX_CORRUPT: "Index corrupt: %s", + ER_INVALID_YEAR_COLUMN_LENGTH: "YEAR(%lu) column type is deprecated. Creating YEAR(4) column instead.", + ER_NOT_VALID_PASSWORD: "Your password does not satisfy the current policy requirements", + ER_MUST_CHANGE_PASSWORD: "You must SET PASSWORD before executing this statement", + ER_FK_NO_INDEX_CHILD: "Failed to add the foreign key constaint. Missing index for constraint '%s' in the foreign table '%s'", + ER_FK_NO_INDEX_PARENT: "Failed to add the foreign key constaint. Missing index for constraint '%s' in the referenced table '%s'", + ER_FK_FAIL_ADD_SYSTEM: "Failed to add the foreign key constraint '%s' to system tables", + ER_FK_CANNOT_OPEN_PARENT: "Failed to open the referenced table '%s'", + ER_FK_INCORRECT_OPTION: "Failed to add the foreign key constraint on table '%s'. Incorrect options in FOREIGN KEY constraint '%s'", + ER_FK_DUP_NAME: "Duplicate foreign key constraint name '%s'", + ER_PASSWORD_FORMAT: "The password hash doesn't have the expected format. Check if the correct password algorithm is being used with the PASSWORD() function.", + ER_FK_COLUMN_CANNOT_DROP: "Cannot drop column '%-.192s': needed in a foreign key constraint '%-.192s'", + ER_FK_COLUMN_CANNOT_DROP_CHILD: "Cannot drop column '%-.192s': needed in a foreign key constraint '%-.192s' of table '%-.192s'", + ER_FK_COLUMN_NOT_NULL: "Column '%-.192s' cannot be NOT NULL: needed in a foreign key constraint '%-.192s' SET NULL", + ER_DUP_INDEX: "Duplicate index '%-.64s' defined on the table '%-.64s.%-.64s'. This is deprecated and will be disallowed in a future release.", + ER_FK_COLUMN_CANNOT_CHANGE: "Cannot change column '%-.192s': used in a foreign key constraint '%-.192s'", + ER_FK_COLUMN_CANNOT_CHANGE_CHILD: "Cannot change column '%-.192s': used in a foreign key constraint '%-.192s' of table '%-.192s'", + ER_FK_CANNOT_DELETE_PARENT: "Cannot delete rows from table which is parent in a foreign key constraint '%-.192s' of table '%-.192s'", + ER_MALFORMED_PACKET: "Malformed communication packet.", + ER_READ_ONLY_MODE: "Running in read-only mode", + ER_GTID_NEXT_TYPE_UNDEFINED_GROUP: "When @@SESSION.GTID_NEXT is set to a GTID, you must explicitly set it again after a COMMIT or ROLLBACK. If you see this error message in the slave SQL thread, it means that a table in the current transaction is transactional on the master and non-transactional on the slave. In a client connection, it means that you executed SET @@SESSION.GTID_NEXT before a transaction and forgot to set @@SESSION.GTID_NEXT to a different identifier or to 'AUTOMATIC' after COMMIT or ROLLBACK. Current @@SESSION.GTID_NEXT is '%s'.", + ER_VARIABLE_NOT_SETTABLE_IN_SP: "The system variable %.200s cannot be set in stored procedures.", + ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF: "@@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_MODE = ON.", + ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY: "@@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.", + ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY: "@@GLOBAL.GTID_PURGED can only be set when there are no ongoing transactions (not even in other clients).", + ER_GTID_PURGED_WAS_CHANGED: "@@GLOBAL.GTID_PURGED was changed from '%s' to '%s'.", + ER_GTID_EXECUTED_WAS_CHANGED: "@@GLOBAL.GTID_EXECUTED was changed from '%s' to '%s'.", + ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES: "Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT, and both replicated and non replicated tables are written to.", + ER_ALTER_OPERATION_NOT_SUPPORTED: "%s is not supported for this operation. Try %s.", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON: "%s is not supported. Reason: %s. Try %s.", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY: "COPY algorithm requires a lock", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION: "Partition specific operations do not yet support LOCK/ALGORITHM", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME: "Columns participating in a foreign key are renamed", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE: "Cannot change column type INPLACE", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK: "Adding foreign keys needs foreign_key_checks=OFF", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_IGNORE: "Creating unique indexes with IGNORE requires COPY algorithm to remove duplicate rows", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK: "Dropping a primary key is not allowed without also adding a new primary key", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC: "Adding an auto-increment column requires a lock", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS: "Cannot replace hidden FTS_DOC_ID with a user-visible one", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS: "Cannot drop or rename FTS_DOC_ID", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS: "Fulltext index creation requires a lock", + ER_SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE: "sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction", + ER_DUP_UNKNOWN_IN_INDEX: "Duplicate entry for key '%-.192s'", + ER_IDENT_CAUSES_TOO_LONG_PATH: "Long database name and identifier for object resulted in path length exceeding %d characters. Path: '%s'.", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL: "cannot silently convert NULL values, as required in this SQL_MODE", + ER_MUST_CHANGE_PASSWORD_LOGIN: "Your password has expired. To log in you must change it using a client that supports expired passwords.", + ER_ROW_IN_WRONG_PARTITION: "Found a row in wrong partition %s", +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/error.go b/vendor/github.com/siddontang/go-mysql/mysql/error.go new file mode 100644 index 0000000..227e70e --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/error.go @@ -0,0 +1,59 @@ +package mysql + +import ( + "fmt" + + "github.com/juju/errors" +) + +var ( + ErrBadConn = errors.New("connection was bad") + ErrMalformPacket = errors.New("Malform packet error") + + ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") +) + +type MyError struct { + Code uint16 + Message string + State string +} + +func (e *MyError) Error() string { + return fmt.Sprintf("ERROR %d (%s): %s", e.Code, e.State, e.Message) +} + +//default mysql error, must adapt errname message format +func NewDefaultError(errCode uint16, args ...interface{}) *MyError { + e := new(MyError) + e.Code = errCode + + if s, ok := MySQLState[errCode]; ok { + e.State = s + } else { + e.State = DEFAULT_MYSQL_STATE + } + + if format, ok := MySQLErrName[errCode]; ok { + e.Message = fmt.Sprintf(format, args...) + } else { + e.Message = fmt.Sprint(args...) + } + + return e +} + +func NewError(errCode uint16, message string) *MyError { + e := new(MyError) + e.Code = errCode + + if s, ok := MySQLState[errCode]; ok { + e.State = s + } else { + e.State = DEFAULT_MYSQL_STATE + } + + e.Message = message + + return e +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/field.go b/vendor/github.com/siddontang/go-mysql/mysql/field.go new file mode 100644 index 0000000..c26f6a2 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/field.go @@ -0,0 +1,157 @@ +package mysql + +import ( + "encoding/binary" +) + +type FieldData []byte + +type Field struct { + Data FieldData + Schema []byte + Table []byte + OrgTable []byte + Name []byte + OrgName []byte + Charset uint16 + ColumnLength uint32 + Type uint8 + Flag uint16 + Decimal uint8 + + DefaultValueLength uint64 + DefaultValue []byte +} + +func (p FieldData) Parse() (f *Field, err error) { + f = new(Field) + + f.Data = p + + var n int + pos := 0 + //skip catelog, always def + n, err = SkipLengthEnodedString(p) + if err != nil { + return + } + pos += n + + //schema + f.Schema, _, n, err = LengthEnodedString(p[pos:]) + if err != nil { + return + } + pos += n + + //table + f.Table, _, n, err = LengthEnodedString(p[pos:]) + if err != nil { + return + } + pos += n + + //org_table + f.OrgTable, _, n, err = LengthEnodedString(p[pos:]) + if err != nil { + return + } + pos += n + + //name + f.Name, _, n, err = LengthEnodedString(p[pos:]) + if err != nil { + return + } + pos += n + + //org_name + f.OrgName, _, n, err = LengthEnodedString(p[pos:]) + if err != nil { + return + } + pos += n + + //skip oc + pos += 1 + + //charset + f.Charset = binary.LittleEndian.Uint16(p[pos:]) + pos += 2 + + //column length + f.ColumnLength = binary.LittleEndian.Uint32(p[pos:]) + pos += 4 + + //type + f.Type = p[pos] + pos++ + + //flag + f.Flag = binary.LittleEndian.Uint16(p[pos:]) + pos += 2 + + //decimals 1 + f.Decimal = p[pos] + pos++ + + //filter [0x00][0x00] + pos += 2 + + f.DefaultValue = nil + //if more data, command was field list + if len(p) > pos { + //length of default value lenenc-int + f.DefaultValueLength, _, n = LengthEncodedInt(p[pos:]) + pos += n + + if pos+int(f.DefaultValueLength) > len(p) { + err = ErrMalformPacket + return + } + + //default value string[$len] + f.DefaultValue = p[pos:(pos + int(f.DefaultValueLength))] + } + + return +} + +func (f *Field) Dump() []byte { + if f == nil { + f = &Field{} + } + if f.Data != nil { + return []byte(f.Data) + } + + l := len(f.Schema) + len(f.Table) + len(f.OrgTable) + len(f.Name) + len(f.OrgName) + len(f.DefaultValue) + 48 + + data := make([]byte, 0, l) + + data = append(data, PutLengthEncodedString([]byte("def"))...) + + data = append(data, PutLengthEncodedString(f.Schema)...) + + data = append(data, PutLengthEncodedString(f.Table)...) + data = append(data, PutLengthEncodedString(f.OrgTable)...) + + data = append(data, PutLengthEncodedString(f.Name)...) + data = append(data, PutLengthEncodedString(f.OrgName)...) + + data = append(data, 0x0c) + + data = append(data, Uint16ToBytes(f.Charset)...) + data = append(data, Uint32ToBytes(f.ColumnLength)...) + data = append(data, f.Type) + data = append(data, Uint16ToBytes(f.Flag)...) + data = append(data, f.Decimal) + data = append(data, 0, 0) + + if f.DefaultValue != nil { + data = append(data, Uint64ToBytes(f.DefaultValueLength)...) + data = append(data, f.DefaultValue...) + } + + return data +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/gtid.go b/vendor/github.com/siddontang/go-mysql/mysql/gtid.go new file mode 100644 index 0000000..db0d638 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/gtid.go @@ -0,0 +1,25 @@ +package mysql + +import "github.com/juju/errors" + +type GTIDSet interface { + String() string + + // Encode GTID set into binary format used in binlog dump commands + Encode() []byte + + Equal(o GTIDSet) bool + + Contain(o GTIDSet) bool +} + +func ParseGTIDSet(flavor string, s string) (GTIDSet, error) { + switch flavor { + case MySQLFlavor: + return ParseMysqlGTIDSet(s) + case MariaDBFlavor: + return ParseMariadbGTIDSet(s) + default: + return nil, errors.Errorf("invalid flavor %s", flavor) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/mariadb_gtid.go b/vendor/github.com/siddontang/go-mysql/mysql/mariadb_gtid.go new file mode 100644 index 0000000..ea89458 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/mariadb_gtid.go @@ -0,0 +1,80 @@ +package mysql + +import ( + "fmt" + "strconv" + "strings" + + "github.com/juju/errors" +) + +type MariadbGTID struct { + DomainID uint32 + ServerID uint32 + SequenceNumber uint64 +} + +// We don't support multi source replication, so the mariadb gtid set may have only domain-server-sequence +func ParseMariadbGTIDSet(str string) (GTIDSet, error) { + if len(str) == 0 { + return MariadbGTID{0, 0, 0}, nil + } + + seps := strings.Split(str, "-") + + var gtid MariadbGTID + + if len(seps) != 3 { + return gtid, errors.Errorf("invalid Mariadb GTID %v, must domain-server-sequence", str) + } + + domainID, err := strconv.ParseUint(seps[0], 10, 32) + if err != nil { + return gtid, errors.Errorf("invalid MariaDB GTID Domain ID (%v): %v", seps[0], err) + } + + serverID, err := strconv.ParseUint(seps[1], 10, 32) + if err != nil { + return gtid, errors.Errorf("invalid MariaDB GTID Server ID (%v): %v", seps[1], err) + } + + sequenceID, err := strconv.ParseUint(seps[2], 10, 64) + if err != nil { + return gtid, errors.Errorf("invalid MariaDB GTID Sequence number (%v): %v", seps[2], err) + } + + return MariadbGTID{ + DomainID: uint32(domainID), + ServerID: uint32(serverID), + SequenceNumber: sequenceID}, nil +} + +func (gtid MariadbGTID) String() string { + if gtid.DomainID == 0 && gtid.ServerID == 0 && gtid.SequenceNumber == 0 { + return "" + } + + return fmt.Sprintf("%d-%d-%d", gtid.DomainID, gtid.ServerID, gtid.SequenceNumber) +} + +func (gtid MariadbGTID) Encode() []byte { + return []byte(gtid.String()) +} + +func (gtid MariadbGTID) Equal(o GTIDSet) bool { + other, ok := o.(MariadbGTID) + if !ok { + return false + } + + return gtid == other +} + +func (gtid MariadbGTID) Contain(o GTIDSet) bool { + other, ok := o.(MariadbGTID) + if !ok { + return false + } + + return gtid.DomainID == other.DomainID && gtid.SequenceNumber >= other.SequenceNumber +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/mysql_gtid.go b/vendor/github.com/siddontang/go-mysql/mysql/mysql_gtid.go new file mode 100644 index 0000000..c54ded0 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/mysql_gtid.go @@ -0,0 +1,409 @@ +package mysql + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "sort" + "strconv" + "strings" + + "github.com/juju/errors" + "github.com/satori/go.uuid" + "github.com/siddontang/go/hack" +) + +// Like MySQL GTID Interval struct, [start, stop), left closed and right open +// See MySQL rpl_gtid.h +type Interval struct { + // The first GID of this interval. + Start int64 + // The first GID after this interval. + Stop int64 +} + +// Interval is [start, stop), but the GTID string's format is [n] or [n1-n2], closed interval +func parseInterval(str string) (i Interval, err error) { + p := strings.Split(str, "-") + switch len(p) { + case 1: + i.Start, err = strconv.ParseInt(p[0], 10, 64) + i.Stop = i.Start + 1 + case 2: + i.Start, err = strconv.ParseInt(p[0], 10, 64) + i.Stop, err = strconv.ParseInt(p[1], 10, 64) + i.Stop = i.Stop + 1 + default: + err = errors.Errorf("invalid interval format, must n[-n]") + } + + if err != nil { + return + } + + if i.Stop <= i.Start { + err = errors.Errorf("invalid interval format, must n[-n] and the end must >= start") + } + + return +} + +func (i Interval) String() string { + if i.Stop == i.Start+1 { + return fmt.Sprintf("%d", i.Start) + } else { + return fmt.Sprintf("%d-%d", i.Start, i.Stop-1) + } +} + +type IntervalSlice []Interval + +func (s IntervalSlice) Len() int { + return len(s) +} + +func (s IntervalSlice) Less(i, j int) bool { + if s[i].Start < s[j].Start { + return true + } else if s[i].Start > s[j].Start { + return false + } else { + return s[i].Stop < s[j].Stop + } +} + +func (s IntervalSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s IntervalSlice) Sort() { + sort.Sort(s) +} + +func (s IntervalSlice) Normalize() IntervalSlice { + var n IntervalSlice + if len(s) == 0 { + return n + } + + s.Sort() + + n = append(n, s[0]) + + for i := 1; i < len(s); i++ { + last := n[len(n)-1] + if s[i].Start > last.Stop { + n = append(n, s[i]) + continue + } else { + n[len(n)-1] = Interval{last.Start, s[i].Stop} + } + } + + return n +} + +// Return true if sub in s +func (s IntervalSlice) Contain(sub IntervalSlice) bool { + j := 0 + for i := 0; i < len(sub); i++ { + for ; j < len(s); j++ { + if sub[i].Start > s[j].Stop { + continue + } else { + break + } + } + if j == len(s) { + return false + } + + if sub[i].Start < s[j].Start || sub[i].Stop > s[j].Stop { + return false + } + } + + return true +} + +func (s IntervalSlice) Equal(o IntervalSlice) bool { + if len(s) != len(o) { + return false + } + + for i := 0; i < len(s); i++ { + if s[i].Start != o[i].Start || s[i].Stop != o[i].Stop { + return false + } + } + + return true +} + +func (s IntervalSlice) Compare(o IntervalSlice) int { + if s.Equal(o) { + return 0 + } else if s.Contain(o) { + return 1 + } else { + return -1 + } +} + +// Refer http://dev.mysql.com/doc/refman/5.6/en/replication-gtids-concepts.html +type UUIDSet struct { + SID uuid.UUID + + Intervals IntervalSlice +} + +func ParseUUIDSet(str string) (*UUIDSet, error) { + str = strings.TrimSpace(str) + sep := strings.Split(str, ":") + if len(sep) < 2 { + return nil, errors.Errorf("invalid GTID format, must UUID:interval[:interval]") + } + + var err error + s := new(UUIDSet) + if s.SID, err = uuid.FromString(sep[0]); err != nil { + return nil, errors.Trace(err) + } + + // Handle interval + for i := 1; i < len(sep); i++ { + if in, err := parseInterval(sep[i]); err != nil { + return nil, errors.Trace(err) + } else { + s.Intervals = append(s.Intervals, in) + } + } + + s.Intervals = s.Intervals.Normalize() + + return s, nil +} + +func NewUUIDSet(sid uuid.UUID, in ...Interval) *UUIDSet { + s := new(UUIDSet) + s.SID = sid + + s.Intervals = in + s.Intervals = s.Intervals.Normalize() + + return s +} + +func (s *UUIDSet) Contain(sub *UUIDSet) bool { + if !bytes.Equal(s.SID.Bytes(), sub.SID.Bytes()) { + return false + } + + return s.Intervals.Contain(sub.Intervals) +} + +func (s *UUIDSet) Bytes() []byte { + var buf bytes.Buffer + + buf.WriteString(s.SID.String()) + + for _, i := range s.Intervals { + buf.WriteString(":") + buf.WriteString(i.String()) + } + + return buf.Bytes() +} + +func (s *UUIDSet) AddInterval(in IntervalSlice) { + s.Intervals = append(s.Intervals, in...) + s.Intervals = s.Intervals.Normalize() +} + +func (s *UUIDSet) String() string { + return hack.String(s.Bytes()) +} + +func (s *UUIDSet) encode(w io.Writer) { + w.Write(s.SID.Bytes()) + n := int64(len(s.Intervals)) + + binary.Write(w, binary.LittleEndian, n) + + for _, i := range s.Intervals { + binary.Write(w, binary.LittleEndian, i.Start) + binary.Write(w, binary.LittleEndian, i.Stop) + } +} + +func (s *UUIDSet) Encode() []byte { + var buf bytes.Buffer + + s.encode(&buf) + + return buf.Bytes() +} + +func (s *UUIDSet) decode(data []byte) (int, error) { + if len(data) < 24 { + return 0, errors.Errorf("invalid uuid set buffer, less 24") + } + + pos := 0 + var err error + if s.SID, err = uuid.FromBytes(data[0:16]); err != nil { + return 0, err + } + pos += 16 + + n := int64(binary.LittleEndian.Uint64(data[pos : pos+8])) + pos += 8 + if len(data) < int(16*n)+pos { + return 0, errors.Errorf("invalid uuid set buffer, must %d, but %d", pos+int(16*n), len(data)) + } + + s.Intervals = make([]Interval, 0, n) + + var in Interval + for i := int64(0); i < n; i++ { + in.Start = int64(binary.LittleEndian.Uint64(data[pos : pos+8])) + pos += 8 + in.Stop = int64(binary.LittleEndian.Uint64(data[pos : pos+8])) + pos += 8 + s.Intervals = append(s.Intervals, in) + } + + return pos, nil +} + +func (s *UUIDSet) Decode(data []byte) error { + n, err := s.decode(data) + if n != len(data) { + return errors.Errorf("invalid uuid set buffer, must %d, but %d", n, len(data)) + } + return err +} + +type MysqlGTIDSet struct { + Sets map[string]*UUIDSet +} + +func ParseMysqlGTIDSet(str string) (GTIDSet, error) { + s := new(MysqlGTIDSet) + + sp := strings.Split(str, ",") + + s.Sets = make(map[string]*UUIDSet, len(sp)) + + //todo, handle redundant same uuid + for i := 0; i < len(sp); i++ { + if set, err := ParseUUIDSet(sp[i]); err != nil { + return nil, errors.Trace(err) + } else { + s.AddSet(set) + } + + } + return s, nil +} + +func DecodeMysqlGTIDSet(data []byte) (*MysqlGTIDSet, error) { + s := new(MysqlGTIDSet) + + if len(data) < 8 { + return nil, errors.Errorf("invalid gtid set buffer, less 4") + } + + n := int(binary.LittleEndian.Uint64(data)) + s.Sets = make(map[string]*UUIDSet, n) + + pos := 8 + + for i := 0; i < n; i++ { + set := new(UUIDSet) + if n, err := set.decode(data[pos:]); err != nil { + return nil, errors.Trace(err) + } else { + pos += n + + s.AddSet(set) + } + } + return s, nil +} + +func (s *MysqlGTIDSet) AddSet(set *UUIDSet) { + sid := set.SID.String() + o, ok := s.Sets[sid] + if ok { + o.AddInterval(set.Intervals) + } else { + s.Sets[sid] = set + } +} + +func (s *MysqlGTIDSet) Contain(o GTIDSet) bool { + sub, ok := o.(*MysqlGTIDSet) + if !ok { + return false + } + + for key, set := range sub.Sets { + o, ok := s.Sets[key] + if !ok { + return false + } + + if !o.Contain(set) { + return false + } + } + + return true +} + +func (s *MysqlGTIDSet) Equal(o GTIDSet) bool { + sub, ok := o.(*MysqlGTIDSet) + if !ok { + return false + } + + for key, set := range sub.Sets { + o, ok := s.Sets[key] + if !ok { + return false + } + + if !o.Intervals.Equal(set.Intervals) { + return false + } + } + + return true + +} + +func (s *MysqlGTIDSet) String() string { + var buf bytes.Buffer + sep := "" + for _, set := range s.Sets { + buf.WriteString(sep) + buf.WriteString(set.String()) + sep = "," + } + + return hack.String(buf.Bytes()) +} + +func (s *MysqlGTIDSet) Encode() []byte { + var buf bytes.Buffer + + binary.Write(&buf, binary.LittleEndian, uint64(len(s.Sets))) + + for i, _ := range s.Sets { + s.Sets[i].encode(&buf) + } + + return buf.Bytes() +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/mysql_test.go b/vendor/github.com/siddontang/go-mysql/mysql/mysql_test.go new file mode 100644 index 0000000..ce251ee --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/mysql_test.go @@ -0,0 +1,153 @@ +package mysql + +import ( + "testing" + + "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + check.TestingT(t) +} + +type mysqlTestSuite struct { +} + +var _ = check.Suite(&mysqlTestSuite{}) + +func (s *mysqlTestSuite) SetUpSuite(c *check.C) { + +} + +func (s *mysqlTestSuite) TearDownSuite(c *check.C) { + +} + +func (t *mysqlTestSuite) TestMysqlGTIDInterval(c *check.C) { + i, err := parseInterval("1-2") + c.Assert(err, check.IsNil) + c.Assert(i, check.DeepEquals, Interval{1, 3}) + + i, err = parseInterval("1") + c.Assert(err, check.IsNil) + c.Assert(i, check.DeepEquals, Interval{1, 2}) + + i, err = parseInterval("1-1") + c.Assert(err, check.IsNil) + c.Assert(i, check.DeepEquals, Interval{1, 2}) + + i, err = parseInterval("1-2") + c.Assert(err, check.IsNil) +} + +func (t *mysqlTestSuite) TestMysqlGTIDIntervalSlice(c *check.C) { + i := IntervalSlice{Interval{1, 2}, Interval{2, 4}, Interval{2, 3}} + i.Sort() + c.Assert(i, check.DeepEquals, IntervalSlice{Interval{1, 2}, Interval{2, 3}, Interval{2, 4}}) + n := i.Normalize() + c.Assert(n, check.DeepEquals, IntervalSlice{Interval{1, 4}}) + + i = IntervalSlice{Interval{1, 2}, Interval{3, 5}, Interval{1, 3}} + i.Sort() + c.Assert(i, check.DeepEquals, IntervalSlice{Interval{1, 2}, Interval{1, 3}, Interval{3, 5}}) + n = i.Normalize() + c.Assert(n, check.DeepEquals, IntervalSlice{Interval{1, 5}}) + + i = IntervalSlice{Interval{1, 2}, Interval{4, 5}, Interval{1, 3}} + i.Sort() + c.Assert(i, check.DeepEquals, IntervalSlice{Interval{1, 2}, Interval{1, 3}, Interval{4, 5}}) + n = i.Normalize() + c.Assert(n, check.DeepEquals, IntervalSlice{Interval{1, 3}, Interval{4, 5}}) + + n1 := IntervalSlice{Interval{1, 3}, Interval{4, 5}} + n2 := IntervalSlice{Interval{1, 2}} + + c.Assert(n1.Contain(n2), check.Equals, true) + c.Assert(n2.Contain(n1), check.Equals, false) + + n1 = IntervalSlice{Interval{1, 3}, Interval{4, 5}} + n2 = IntervalSlice{Interval{1, 6}} + + c.Assert(n1.Contain(n2), check.Equals, false) + c.Assert(n2.Contain(n1), check.Equals, true) +} + +func (t *mysqlTestSuite) TestMysqlGTIDCodec(c *check.C) { + us, err := ParseUUIDSet("de278ad0-2106-11e4-9f8e-6edd0ca20947:1-2") + c.Assert(err, check.IsNil) + + c.Assert(us.String(), check.Equals, "de278ad0-2106-11e4-9f8e-6edd0ca20947:1-2") + + buf := us.Encode() + err = us.Decode(buf) + c.Assert(err, check.IsNil) + + gs, err := ParseMysqlGTIDSet("de278ad0-2106-11e4-9f8e-6edd0ca20947:1-2,de278ad0-2106-11e4-9f8e-6edd0ca20948:1-2") + c.Assert(err, check.IsNil) + + buf = gs.Encode() + o, err := DecodeMysqlGTIDSet(buf) + c.Assert(err, check.IsNil) + c.Assert(gs, check.DeepEquals, o) +} + +func (t *mysqlTestSuite) TestMysqlGTIDContain(c *check.C) { + g1, err := ParseMysqlGTIDSet("3E11FA47-71CA-11E1-9E33-C80AA9429562:23") + c.Assert(err, check.IsNil) + + g2, err := ParseMysqlGTIDSet("3E11FA47-71CA-11E1-9E33-C80AA9429562:21-57") + c.Assert(err, check.IsNil) + + c.Assert(g2.Contain(g1), check.Equals, true) + c.Assert(g1.Contain(g2), check.Equals, false) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryInt8(c *check.C) { + i8 := ParseBinaryInt8([]byte{128}) + c.Assert(i8, check.Equals, int8(-128)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryUint8(c *check.C) { + u8 := ParseBinaryUint8([]byte{128}) + c.Assert(u8, check.Equals, uint8(128)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryInt16(c *check.C) { + i16 := ParseBinaryInt16([]byte{1, 128}) + c.Assert(i16, check.Equals, int16(-128*256 + 1)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryUint16(c *check.C) { + u16 := ParseBinaryUint16([]byte{1, 128}) + c.Assert(u16, check.Equals, uint16(128*256 + 1)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryInt24(c *check.C) { + i32 := ParseBinaryInt24([]byte{1, 2, 128}) + c.Assert(i32, check.Equals, int32(-128*65536 + 2*256 + 1)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryUint24(c *check.C) { + u32 := ParseBinaryUint24([]byte{1, 2, 128}) + c.Assert(u32, check.Equals, uint32(128*65536 + 2*256 + 1)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryInt32(c *check.C) { + i32 := ParseBinaryInt32([]byte{1, 2, 3, 128}) + c.Assert(i32, check.Equals, int32(-128*16777216 + 3*65536 + 2*256 + 1)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryUint32(c *check.C) { + u32 := ParseBinaryUint32([]byte{1, 2, 3, 128}) + c.Assert(u32, check.Equals, uint32(128*16777216 + 3*65536 + 2*256 + 1)) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryInt64(c *check.C) { + i64 := ParseBinaryInt64([]byte{1, 2, 3, 4, 5, 6, 7, 128}) + c.Assert(i64, check.Equals, -128*int64(72057594037927936) + 7*int64(281474976710656) + 6*int64(1099511627776) + 5*int64(4294967296) + 4*16777216 + 3*65536 + 2*256 + 1) +} + +func (t *mysqlTestSuite) TestMysqlParseBinaryUint64(c *check.C) { + u64 := ParseBinaryUint64([]byte{1, 2, 3, 4, 5, 6, 7, 128}) + c.Assert(u64, check.Equals, 128*uint64(72057594037927936) + 7*uint64(281474976710656) + 6*uint64(1099511627776) + 5*uint64(4294967296) + 4*16777216 + 3*65536 + 2*256 + 1) +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/parse_binary.go b/vendor/github.com/siddontang/go-mysql/mysql/parse_binary.go new file mode 100644 index 0000000..b9b8179 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/parse_binary.go @@ -0,0 +1,53 @@ +package mysql + +import ( + "encoding/binary" + "math" +) + +func ParseBinaryInt8(data []byte) int8 { + return int8(data[0]) +} +func ParseBinaryUint8(data []byte) uint8 { + return data[0] +} + +func ParseBinaryInt16(data []byte) int16 { + return int16(binary.LittleEndian.Uint16(data)) +} +func ParseBinaryUint16(data []byte) uint16 { + return binary.LittleEndian.Uint16(data) +} + +func ParseBinaryInt24(data []byte) int32 { + u32 := uint32(ParseBinaryUint24(data)) + if u32&0x00800000 != 0 { + u32 |= 0xFF000000 + } + return int32(u32) +} +func ParseBinaryUint24(data []byte) uint32 { + return uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16 +} + +func ParseBinaryInt32(data []byte) int32 { + return int32(binary.LittleEndian.Uint32(data)) +} +func ParseBinaryUint32(data []byte) uint32 { + return binary.LittleEndian.Uint32(data) +} + +func ParseBinaryInt64(data []byte) int64 { + return int64(binary.LittleEndian.Uint64(data)) +} +func ParseBinaryUint64(data []byte) uint64 { + return binary.LittleEndian.Uint64(data) +} + +func ParseBinaryFloat32(data []byte) float32 { + return math.Float32frombits(binary.LittleEndian.Uint32(data)) +} + +func ParseBinaryFloat64(data []byte) float64 { + return math.Float64frombits(binary.LittleEndian.Uint64(data)) +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/position.go b/vendor/github.com/siddontang/go-mysql/mysql/position.go new file mode 100644 index 0000000..bee5485 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/position.go @@ -0,0 +1,33 @@ +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) +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/result.go b/vendor/github.com/siddontang/go-mysql/mysql/result.go new file mode 100644 index 0000000..d6c80e4 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/result.go @@ -0,0 +1,14 @@ +package mysql + +type Result struct { + Status uint16 + + InsertId uint64 + AffectedRows uint64 + + *Resultset +} + +type Executer interface { + Execute(query string, args ...interface{}) (*Result, error) +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/resultset.go b/vendor/github.com/siddontang/go-mysql/mysql/resultset.go new file mode 100644 index 0000000..1e2ade5 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/resultset.go @@ -0,0 +1,400 @@ +package mysql + +import ( + "strconv" + + "github.com/juju/errors" + "github.com/siddontang/go/hack" +) + +type RowData []byte + +func (p RowData) Parse(f []*Field, binary bool) ([]interface{}, error) { + if binary { + return p.ParseBinary(f) + } else { + return p.ParseText(f) + } +} + +func (p RowData) ParseText(f []*Field) ([]interface{}, error) { + data := make([]interface{}, len(f)) + + var err error + var v []byte + var isNull bool + var pos int = 0 + var n int = 0 + + for i := range f { + v, isNull, n, err = LengthEnodedString(p[pos:]) + if err != nil { + return nil, errors.Trace(err) + } + + pos += n + + if isNull { + data[i] = nil + } else { + isUnsigned := f[i].Flag&UNSIGNED_FLAG != 0 + + switch f[i].Type { + case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24, + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_YEAR: + if isUnsigned { + data[i], err = strconv.ParseUint(string(v), 10, 64) + } else { + data[i], err = strconv.ParseInt(string(v), 10, 64) + } + case MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE: + data[i], err = strconv.ParseFloat(string(v), 64) + default: + data[i] = v + } + + if err != nil { + return nil, errors.Trace(err) + } + } + } + + return data, nil +} + +func (p RowData) ParseBinary(f []*Field) ([]interface{}, error) { + data := make([]interface{}, len(f)) + + if p[0] != OK_HEADER { + return nil, ErrMalformPacket + } + + pos := 1 + ((len(f) + 7 + 2) >> 3) + + nullBitmap := p[1:pos] + + var isNull bool + var n int + var err error + var v []byte + for i := range data { + if nullBitmap[(i+2)/8]&(1<<(uint(i+2)%8)) > 0 { + data[i] = nil + continue + } + + isUnsigned := f[i].Flag&UNSIGNED_FLAG != 0 + + switch f[i].Type { + case MYSQL_TYPE_NULL: + data[i] = nil + continue + + case MYSQL_TYPE_TINY: + if isUnsigned { + data[i] = ParseBinaryUint8(p[pos : pos+1]) + } else { + data[i] = ParseBinaryInt8(p[pos : pos+1]) + } + pos++ + continue + + case MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR: + if isUnsigned { + data[i] = ParseBinaryUint16(p[pos : pos+2]) + } else { + data[i] = ParseBinaryInt16(p[pos : pos+2]) + } + pos += 2 + continue + + case MYSQL_TYPE_INT24: + if isUnsigned { + data[i] = ParseBinaryUint24(p[pos : pos+3]) + } else { + data[i] = ParseBinaryInt24(p[pos : pos+3]) + } + pos += 4 + continue + + case MYSQL_TYPE_LONG: + if isUnsigned { + data[i] = ParseBinaryUint32(p[pos : pos+4]) + } else { + data[i] = ParseBinaryInt32(p[pos : pos+4]) + } + pos += 4 + continue + + case MYSQL_TYPE_LONGLONG: + if isUnsigned { + data[i] = ParseBinaryUint64(p[pos : pos+8]) + } else { + data[i] = ParseBinaryInt64(p[pos : pos+8]) + } + pos += 8 + continue + + case MYSQL_TYPE_FLOAT: + data[i] = ParseBinaryFloat32(p[pos : pos+4]) + pos += 4 + continue + + case MYSQL_TYPE_DOUBLE: + data[i] = ParseBinaryFloat64(p[pos : pos+4]) + pos += 8 + continue + + case MYSQL_TYPE_DECIMAL, MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, + MYSQL_TYPE_BIT, MYSQL_TYPE_ENUM, MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB, + MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB, + MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY: + v, isNull, n, err = LengthEnodedString(p[pos:]) + pos += n + if err != nil { + return nil, errors.Trace(err) + } + + if !isNull { + data[i] = v + continue + } else { + data[i] = nil + continue + } + case MYSQL_TYPE_DATE, MYSQL_TYPE_NEWDATE: + var num uint64 + num, isNull, n = LengthEncodedInt(p[pos:]) + + pos += n + + if isNull { + data[i] = nil + continue + } + + data[i], err = FormatBinaryDate(int(num), p[pos:]) + pos += int(num) + + if err != nil { + return nil, errors.Trace(err) + } + + case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME: + var num uint64 + num, isNull, n = LengthEncodedInt(p[pos:]) + + pos += n + + if isNull { + data[i] = nil + continue + } + + data[i], err = FormatBinaryDateTime(int(num), p[pos:]) + pos += int(num) + + if err != nil { + return nil, errors.Trace(err) + } + + case MYSQL_TYPE_TIME: + var num uint64 + num, isNull, n = LengthEncodedInt(p[pos:]) + + pos += n + + if isNull { + data[i] = nil + continue + } + + data[i], err = FormatBinaryTime(int(num), p[pos:]) + pos += int(num) + + if err != nil { + return nil, errors.Trace(err) + } + + default: + return nil, errors.Errorf("Stmt Unknown FieldType %d %s", f[i].Type, f[i].Name) + } + } + + return data, nil +} + +type Resultset struct { + Fields []*Field + FieldNames map[string]int + Values [][]interface{} + + RowDatas []RowData +} + +func (r *Resultset) RowNumber() int { + return len(r.Values) +} + +func (r *Resultset) ColumnNumber() int { + return len(r.Fields) +} + +func (r *Resultset) GetValue(row, column int) (interface{}, error) { + if row >= len(r.Values) || row < 0 { + return nil, errors.Errorf("invalid row index %d", row) + } + + if column >= len(r.Fields) || column < 0 { + return nil, errors.Errorf("invalid column index %d", column) + } + + return r.Values[row][column], nil +} + +func (r *Resultset) NameIndex(name string) (int, error) { + if column, ok := r.FieldNames[name]; ok { + return column, nil + } else { + return 0, errors.Errorf("invalid field name %s", name) + } +} + +func (r *Resultset) GetValueByName(row int, name string) (interface{}, error) { + if column, err := r.NameIndex(name); err != nil { + return nil, errors.Trace(err) + } else { + return r.GetValue(row, column) + } +} + +func (r *Resultset) IsNull(row, column int) (bool, error) { + d, err := r.GetValue(row, column) + if err != nil { + return false, err + } + + return d == nil, nil +} + +func (r *Resultset) IsNullByName(row int, name string) (bool, error) { + if column, err := r.NameIndex(name); err != nil { + return false, err + } else { + return r.IsNull(row, column) + } +} + +func (r *Resultset) GetUint(row, column int) (uint64, error) { + d, err := r.GetValue(row, column) + if err != nil { + return 0, err + } + + switch v := d.(type) { + case uint64: + return v, nil + case int64: + return uint64(v), nil + case float64: + return uint64(v), nil + case string: + return strconv.ParseUint(v, 10, 64) + case []byte: + return strconv.ParseUint(string(v), 10, 64) + case nil: + return 0, nil + default: + return 0, errors.Errorf("data type is %T", v) + } +} + +func (r *Resultset) GetUintByName(row int, name string) (uint64, error) { + if column, err := r.NameIndex(name); err != nil { + return 0, err + } else { + return r.GetUint(row, column) + } +} + +func (r *Resultset) GetInt(row, column int) (int64, error) { + v, err := r.GetUint(row, column) + if err != nil { + return 0, err + } + + return int64(v), nil +} + +func (r *Resultset) GetIntByName(row int, name string) (int64, error) { + v, err := r.GetUintByName(row, name) + if err != nil { + return 0, err + } + + return int64(v), nil +} + +func (r *Resultset) GetFloat(row, column int) (float64, error) { + d, err := r.GetValue(row, column) + if err != nil { + return 0, err + } + + switch v := d.(type) { + case float64: + return v, nil + case uint64: + return float64(v), nil + case int64: + return float64(v), nil + case string: + return strconv.ParseFloat(v, 64) + case []byte: + return strconv.ParseFloat(string(v), 64) + case nil: + return 0, nil + default: + return 0, errors.Errorf("data type is %T", v) + } +} + +func (r *Resultset) GetFloatByName(row int, name string) (float64, error) { + if column, err := r.NameIndex(name); err != nil { + return 0, err + } else { + return r.GetFloat(row, column) + } +} + +func (r *Resultset) GetString(row, column int) (string, error) { + d, err := r.GetValue(row, column) + if err != nil { + return "", err + } + + switch v := d.(type) { + case string: + return v, nil + case []byte: + return hack.String(v), nil + case int64: + return strconv.FormatInt(v, 10), nil + case uint64: + return strconv.FormatUint(v, 10), nil + case float64: + return strconv.FormatFloat(v, 'f', -1, 64), nil + case nil: + return "", nil + default: + return "", errors.Errorf("data type is %T", v) + } +} + +func (r *Resultset) GetStringByName(row int, name string) (string, error) { + if column, err := r.NameIndex(name); err != nil { + return "", err + } else { + return r.GetString(row, column) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/resultset_helper.go b/vendor/github.com/siddontang/go-mysql/mysql/resultset_helper.go new file mode 100644 index 0000000..488d253 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/resultset_helper.go @@ -0,0 +1,205 @@ +package mysql + +import ( + "math" + "strconv" + + "github.com/juju/errors" + "github.com/siddontang/go/hack" +) + +func formatTextValue(value interface{}) ([]byte, error) { + switch v := value.(type) { + case int8: + return strconv.AppendInt(nil, int64(v), 10), nil + case int16: + return strconv.AppendInt(nil, int64(v), 10), nil + case int32: + return strconv.AppendInt(nil, int64(v), 10), nil + case int64: + return strconv.AppendInt(nil, int64(v), 10), nil + case int: + return strconv.AppendInt(nil, int64(v), 10), nil + case uint8: + return strconv.AppendUint(nil, uint64(v), 10), nil + case uint16: + return strconv.AppendUint(nil, uint64(v), 10), nil + case uint32: + return strconv.AppendUint(nil, uint64(v), 10), nil + case uint64: + return strconv.AppendUint(nil, uint64(v), 10), nil + case uint: + return strconv.AppendUint(nil, uint64(v), 10), nil + case float32: + return strconv.AppendFloat(nil, float64(v), 'f', -1, 64), nil + case float64: + return strconv.AppendFloat(nil, float64(v), 'f', -1, 64), nil + case []byte: + return v, nil + case string: + return hack.Slice(v), nil + default: + return nil, errors.Errorf("invalid type %T", value) + } +} + +func formatBinaryValue(value interface{}) ([]byte, error) { + switch v := value.(type) { + case int8: + return Uint64ToBytes(uint64(v)), nil + case int16: + return Uint64ToBytes(uint64(v)), nil + case int32: + return Uint64ToBytes(uint64(v)), nil + case int64: + return Uint64ToBytes(uint64(v)), nil + case int: + return Uint64ToBytes(uint64(v)), nil + case uint8: + return Uint64ToBytes(uint64(v)), nil + case uint16: + return Uint64ToBytes(uint64(v)), nil + case uint32: + return Uint64ToBytes(uint64(v)), nil + case uint64: + return Uint64ToBytes(uint64(v)), nil + case uint: + return Uint64ToBytes(uint64(v)), nil + case float32: + return Uint64ToBytes(math.Float64bits(float64(v))), nil + case float64: + return Uint64ToBytes(math.Float64bits(v)), nil + case []byte: + return v, nil + case string: + return hack.Slice(v), nil + default: + return nil, errors.Errorf("invalid type %T", value) + } +} +func formatField(field *Field, value interface{}) error { + switch value.(type) { + case int8, int16, int32, int64, int: + field.Charset = 63 + field.Type = MYSQL_TYPE_LONGLONG + field.Flag = BINARY_FLAG | NOT_NULL_FLAG + case uint8, uint16, uint32, uint64, uint: + field.Charset = 63 + field.Type = MYSQL_TYPE_LONGLONG + field.Flag = BINARY_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG + case float32, float64: + field.Charset = 63 + field.Type = MYSQL_TYPE_DOUBLE + field.Flag = BINARY_FLAG | NOT_NULL_FLAG + case string, []byte: + field.Charset = 33 + field.Type = MYSQL_TYPE_VAR_STRING + default: + return errors.Errorf("unsupport type %T for resultset", value) + } + return nil +} + +func BuildSimpleTextResultset(names []string, values [][]interface{}) (*Resultset, error) { + r := new(Resultset) + + r.Fields = make([]*Field, len(names)) + + var b []byte + var err error + + for i, vs := range values { + if len(vs) != len(r.Fields) { + return nil, errors.Errorf("row %d has %d column not equal %d", i, len(vs), len(r.Fields)) + } + + var row []byte + for j, value := range vs { + if i == 0 { + field := &Field{} + r.Fields[j] = field + field.Name = hack.Slice(names[j]) + + if err = formatField(field, value); err != nil { + return nil, errors.Trace(err) + } + } + b, err = formatTextValue(value) + + if err != nil { + return nil, errors.Trace(err) + } + + row = append(row, PutLengthEncodedString(b)...) + } + + r.RowDatas = append(r.RowDatas, row) + } + + return r, nil +} + +func BuildSimpleBinaryResultset(names []string, values [][]interface{}) (*Resultset, error) { + r := new(Resultset) + + r.Fields = make([]*Field, len(names)) + + var b []byte + var err error + + bitmapLen := ((len(names) + 7 + 2) >> 3) + + for i, vs := range values { + if len(vs) != len(r.Fields) { + return nil, errors.Errorf("row %d has %d column not equal %d", i, len(vs), len(r.Fields)) + } + + var row []byte + nullBitmap := make([]byte, bitmapLen) + + row = append(row, 0) + row = append(row, nullBitmap...) + + for j, value := range vs { + if i == 0 { + field := &Field{} + r.Fields[j] = field + field.Name = hack.Slice(names[j]) + + if err = formatField(field, value); err != nil { + return nil, errors.Trace(err) + } + } + if value == nil { + nullBitmap[(i+2)/8] |= (1 << (uint(i+2) % 8)) + continue + } + + b, err = formatBinaryValue(value) + + if err != nil { + return nil, errors.Trace(err) + } + + if r.Fields[j].Type == MYSQL_TYPE_VAR_STRING { + row = append(row, PutLengthEncodedString(b)...) + } else { + row = append(row, b...) + } + } + + copy(row[1:], nullBitmap) + + r.RowDatas = append(r.RowDatas, row) + } + + return r, nil +} + +func BuildSimpleResultset(names []string, values [][]interface{}, binary bool) (*Resultset, error) { + if binary { + return BuildSimpleBinaryResultset(names, values) + } else { + return BuildSimpleTextResultset(names, values) + } +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/state.go b/vendor/github.com/siddontang/go-mysql/mysql/state.go new file mode 100644 index 0000000..568d84b --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/state.go @@ -0,0 +1,233 @@ +package mysql + +const ( + DEFAULT_MYSQL_STATE = "HY000" +) + +var MySQLState = map[uint16]string{ + ER_DUP_KEY: "23000", + ER_OUTOFMEMORY: "HY001", + ER_OUT_OF_SORTMEMORY: "HY001", + ER_CON_COUNT_ERROR: "08004", + ER_BAD_HOST_ERROR: "08S01", + ER_HANDSHAKE_ERROR: "08S01", + ER_DBACCESS_DENIED_ERROR: "42000", + ER_ACCESS_DENIED_ERROR: "28000", + ER_NO_DB_ERROR: "3D000", + ER_UNKNOWN_COM_ERROR: "08S01", + ER_BAD_NULL_ERROR: "23000", + ER_BAD_DB_ERROR: "42000", + ER_TABLE_EXISTS_ERROR: "42S01", + ER_BAD_TABLE_ERROR: "42S02", + ER_NON_UNIQ_ERROR: "23000", + ER_SERVER_SHUTDOWN: "08S01", + ER_BAD_FIELD_ERROR: "42S22", + ER_WRONG_FIELD_WITH_GROUP: "42000", + ER_WRONG_SUM_SELECT: "42000", + ER_WRONG_GROUP_FIELD: "42000", + ER_WRONG_VALUE_COUNT: "21S01", + ER_TOO_LONG_IDENT: "42000", + ER_DUP_FIELDNAME: "42S21", + ER_DUP_KEYNAME: "42000", + ER_DUP_ENTRY: "23000", + ER_WRONG_FIELD_SPEC: "42000", + ER_PARSE_ERROR: "42000", + ER_EMPTY_QUERY: "42000", + ER_NONUNIQ_TABLE: "42000", + ER_INVALID_DEFAULT: "42000", + ER_MULTIPLE_PRI_KEY: "42000", + ER_TOO_MANY_KEYS: "42000", + ER_TOO_MANY_KEY_PARTS: "42000", + ER_TOO_LONG_KEY: "42000", + ER_KEY_COLUMN_DOES_NOT_EXITS: "42000", + ER_BLOB_USED_AS_KEY: "42000", + ER_TOO_BIG_FIELDLENGTH: "42000", + ER_WRONG_AUTO_KEY: "42000", + ER_FORCING_CLOSE: "08S01", + ER_IPSOCK_ERROR: "08S01", + ER_NO_SUCH_INDEX: "42S12", + ER_WRONG_FIELD_TERMINATORS: "42000", + ER_BLOBS_AND_NO_TERMINATED: "42000", + ER_CANT_REMOVE_ALL_FIELDS: "42000", + ER_CANT_DROP_FIELD_OR_KEY: "42000", + ER_BLOB_CANT_HAVE_DEFAULT: "42000", + ER_WRONG_DB_NAME: "42000", + ER_WRONG_TABLE_NAME: "42000", + ER_TOO_BIG_SELECT: "42000", + ER_UNKNOWN_PROCEDURE: "42000", + ER_WRONG_PARAMCOUNT_TO_PROCEDURE: "42000", + ER_UNKNOWN_TABLE: "42S02", + ER_FIELD_SPECIFIED_TWICE: "42000", + ER_UNSUPPORTED_EXTENSION: "42000", + ER_TABLE_MUST_HAVE_COLUMNS: "42000", + ER_UNKNOWN_CHARACTER_SET: "42000", + ER_TOO_BIG_ROWSIZE: "42000", + ER_WRONG_OUTER_JOIN: "42000", + ER_NULL_COLUMN_IN_INDEX: "42000", + ER_PASSWORD_ANONYMOUS_USER: "42000", + ER_PASSWORD_NOT_ALLOWED: "42000", + ER_PASSWORD_NO_MATCH: "42000", + ER_WRONG_VALUE_COUNT_ON_ROW: "21S01", + ER_INVALID_USE_OF_NULL: "22004", + ER_REGEXP_ERROR: "42000", + ER_MIX_OF_GROUP_FUNC_AND_FIELDS: "42000", + ER_NONEXISTING_GRANT: "42000", + ER_TABLEACCESS_DENIED_ERROR: "42000", + ER_COLUMNACCESS_DENIED_ERROR: "42000", + ER_ILLEGAL_GRANT_FOR_TABLE: "42000", + ER_GRANT_WRONG_HOST_OR_USER: "42000", + ER_NO_SUCH_TABLE: "42S02", + ER_NONEXISTING_TABLE_GRANT: "42000", + ER_NOT_ALLOWED_COMMAND: "42000", + ER_SYNTAX_ERROR: "42000", + ER_ABORTING_CONNECTION: "08S01", + ER_NET_PACKET_TOO_LARGE: "08S01", + ER_NET_READ_ERROR_FROM_PIPE: "08S01", + ER_NET_FCNTL_ERROR: "08S01", + ER_NET_PACKETS_OUT_OF_ORDER: "08S01", + ER_NET_UNCOMPRESS_ERROR: "08S01", + ER_NET_READ_ERROR: "08S01", + ER_NET_READ_INTERRUPTED: "08S01", + ER_NET_ERROR_ON_WRITE: "08S01", + ER_NET_WRITE_INTERRUPTED: "08S01", + ER_TOO_LONG_STRING: "42000", + ER_TABLE_CANT_HANDLE_BLOB: "42000", + ER_TABLE_CANT_HANDLE_AUTO_INCREMENT: "42000", + ER_WRONG_COLUMN_NAME: "42000", + ER_WRONG_KEY_COLUMN: "42000", + ER_DUP_UNIQUE: "23000", + ER_BLOB_KEY_WITHOUT_LENGTH: "42000", + ER_PRIMARY_CANT_HAVE_NULL: "42000", + ER_TOO_MANY_ROWS: "42000", + ER_REQUIRES_PRIMARY_KEY: "42000", + ER_KEY_DOES_NOT_EXITS: "42000", + ER_CHECK_NO_SUCH_TABLE: "42000", + ER_CHECK_NOT_IMPLEMENTED: "42000", + ER_CANT_DO_THIS_DURING_AN_TRANSACTION: "25000", + ER_NEW_ABORTING_CONNECTION: "08S01", + ER_MASTER_NET_READ: "08S01", + ER_MASTER_NET_WRITE: "08S01", + ER_TOO_MANY_USER_CONNECTIONS: "42000", + ER_READ_ONLY_TRANSACTION: "25000", + ER_NO_PERMISSION_TO_CREATE_USER: "42000", + ER_LOCK_DEADLOCK: "40001", + ER_NO_REFERENCED_ROW: "23000", + ER_ROW_IS_REFERENCED: "23000", + ER_CONNECT_TO_MASTER: "08S01", + ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT: "21000", + ER_USER_LIMIT_REACHED: "42000", + ER_SPECIFIC_ACCESS_DENIED_ERROR: "42000", + ER_NO_DEFAULT: "42000", + ER_WRONG_VALUE_FOR_VAR: "42000", + ER_WRONG_TYPE_FOR_VAR: "42000", + ER_CANT_USE_OPTION_HERE: "42000", + ER_NOT_SUPPORTED_YET: "42000", + ER_WRONG_FK_DEF: "42000", + ER_OPERAND_COLUMNS: "21000", + ER_SUBQUERY_NO_1_ROW: "21000", + ER_ILLEGAL_REFERENCE: "42S22", + ER_DERIVED_MUST_HAVE_ALIAS: "42000", + ER_SELECT_REDUCED: "01000", + ER_TABLENAME_NOT_ALLOWED_HERE: "42000", + ER_NOT_SUPPORTED_AUTH_MODE: "08004", + ER_SPATIAL_CANT_HAVE_NULL: "42000", + ER_COLLATION_CHARSET_MISMATCH: "42000", + ER_WARN_TOO_FEW_RECORDS: "01000", + ER_WARN_TOO_MANY_RECORDS: "01000", + ER_WARN_NULL_TO_NOTNULL: "22004", + ER_WARN_DATA_OUT_OF_RANGE: "22003", + WARN_DATA_TRUNCATED: "01000", + ER_WRONG_NAME_FOR_INDEX: "42000", + ER_WRONG_NAME_FOR_CATALOG: "42000", + ER_UNKNOWN_STORAGE_ENGINE: "42000", + ER_TRUNCATED_WRONG_VALUE: "22007", + ER_SP_NO_RECURSIVE_CREATE: "2F003", + ER_SP_ALREADY_EXISTS: "42000", + ER_SP_DOES_NOT_EXIST: "42000", + ER_SP_LILABEL_MISMATCH: "42000", + ER_SP_LABEL_REDEFINE: "42000", + ER_SP_LABEL_MISMATCH: "42000", + ER_SP_UNINIT_VAR: "01000", + ER_SP_BADSELECT: "0A000", + ER_SP_BADRETURN: "42000", + ER_SP_BADSTATEMENT: "0A000", + ER_UPDATE_LOG_DEPRECATED_IGNORED: "42000", + ER_UPDATE_LOG_DEPRECATED_TRANSLATED: "42000", + ER_QUERY_INTERRUPTED: "70100", + ER_SP_WRONG_NO_OF_ARGS: "42000", + ER_SP_COND_MISMATCH: "42000", + ER_SP_NORETURN: "42000", + ER_SP_NORETURNEND: "2F005", + ER_SP_BAD_CURSOR_QUERY: "42000", + ER_SP_BAD_CURSOR_SELECT: "42000", + ER_SP_CURSOR_MISMATCH: "42000", + ER_SP_CURSOR_ALREADY_OPEN: "24000", + ER_SP_CURSOR_NOT_OPEN: "24000", + ER_SP_UNDECLARED_VAR: "42000", + ER_SP_FETCH_NO_DATA: "02000", + ER_SP_DUP_PARAM: "42000", + ER_SP_DUP_VAR: "42000", + ER_SP_DUP_COND: "42000", + ER_SP_DUP_CURS: "42000", + ER_SP_SUBSELECT_NYI: "0A000", + ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG: "0A000", + ER_SP_VARCOND_AFTER_CURSHNDLR: "42000", + ER_SP_CURSOR_AFTER_HANDLER: "42000", + ER_SP_CASE_NOT_FOUND: "20000", + ER_DIVISION_BY_ZERO: "22012", + ER_ILLEGAL_VALUE_FOR_TYPE: "22007", + ER_PROCACCESS_DENIED_ERROR: "42000", + ER_XAER_NOTA: "XAE04", + ER_XAER_INVAL: "XAE05", + ER_XAER_RMFAIL: "XAE07", + ER_XAER_OUTSIDE: "XAE09", + ER_XAER_RMERR: "XAE03", + ER_XA_RBROLLBACK: "XA100", + ER_NONEXISTING_PROC_GRANT: "42000", + ER_DATA_TOO_LONG: "22001", + ER_SP_BAD_SQLSTATE: "42000", + ER_CANT_CREATE_USER_WITH_GRANT: "42000", + ER_SP_DUP_HANDLER: "42000", + ER_SP_NOT_VAR_ARG: "42000", + ER_SP_NO_RETSET: "0A000", + ER_CANT_CREATE_GEOMETRY_OBJECT: "22003", + ER_TOO_BIG_SCALE: "42000", + ER_TOO_BIG_PRECISION: "42000", + ER_M_BIGGER_THAN_D: "42000", + ER_TOO_LONG_BODY: "42000", + ER_TOO_BIG_DISPLAYWIDTH: "42000", + ER_XAER_DUPID: "XAE08", + ER_DATETIME_FUNCTION_OVERFLOW: "22008", + ER_ROW_IS_REFERENCED_2: "23000", + ER_NO_REFERENCED_ROW_2: "23000", + ER_SP_BAD_VAR_SHADOW: "42000", + ER_SP_WRONG_NAME: "42000", + ER_SP_NO_AGGREGATE: "42000", + ER_MAX_PREPARED_STMT_COUNT_REACHED: "42000", + ER_NON_GROUPING_FIELD_USED: "42000", + ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED: "23000", + ER_CANT_CHANGE_TX_CHARACTERISTICS: "25001", + ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT: "42000", + ER_WRONG_PARAMETERS_TO_NATIVE_FCT: "42000", + ER_WRONG_PARAMETERS_TO_STORED_FCT: "42000", + ER_DUP_ENTRY_WITH_KEY_NAME: "23000", + ER_XA_RBTIMEOUT: "XA106", + ER_XA_RBDEADLOCK: "XA102", + ER_FUNC_INEXISTENT_NAME_COLLISION: "42000", + ER_DUP_SIGNAL_SET: "42000", + ER_SIGNAL_WARN: "01000", + ER_SIGNAL_NOT_FOUND: "02000", + ER_SIGNAL_EXCEPTION: "HY000", + ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER: "0K000", + ER_SPATIAL_MUST_HAVE_GEOM_COL: "42000", + ER_DATA_OUT_OF_RANGE: "22003", + ER_ACCESS_DENIED_NO_PASSWORD_ERROR: "28000", + ER_TRUNCATE_ILLEGAL_FK: "42000", + ER_DA_INVALID_CONDITION_NUMBER: "35000", + ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO: "23000", + ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO: "23000", + ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION: "25006", + ER_ALTER_OPERATION_NOT_SUPPORTED: "0A000", + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON: "0A000", + ER_DUP_UNKNOWN_IN_INDEX: "23000", +} diff --git a/vendor/github.com/siddontang/go-mysql/mysql/util.go b/vendor/github.com/siddontang/go-mysql/mysql/util.go new file mode 100644 index 0000000..fa874de --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/mysql/util.go @@ -0,0 +1,338 @@ +package mysql + +import ( + "crypto/rand" + "crypto/sha1" + "encoding/binary" + "fmt" + "io" + "runtime" + "strings" + + "github.com/juju/errors" + "github.com/siddontang/go/hack" +) + +func Pstack() string { + buf := make([]byte, 1024) + n := runtime.Stack(buf, false) + return string(buf[0:n]) +} + +func CalcPassword(scramble, password []byte) []byte { + if len(password) == 0 { + return nil + } + + // stage1Hash = SHA1(password) + crypt := sha1.New() + crypt.Write(password) + stage1 := crypt.Sum(nil) + + // scrambleHash = SHA1(scramble + SHA1(stage1Hash)) + // inner Hash + crypt.Reset() + crypt.Write(stage1) + hash := crypt.Sum(nil) + + // outer Hash + crypt.Reset() + crypt.Write(scramble) + crypt.Write(hash) + scramble = crypt.Sum(nil) + + // token = scrambleHash XOR stage1Hash + for i := range scramble { + scramble[i] ^= stage1[i] + } + return scramble +} + +func RandomBuf(size int) ([]byte, error) { + buf := make([]byte, size) + + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + return nil, errors.Trace(err) + } + + // avoid to generate '\0' + for i, b := range buf { + if uint8(b) == 0 { + buf[i] = '0' + } + } + + return buf, nil +} + +// little endian +func FixedLengthInt(buf []byte) uint64 { + var num uint64 = 0 + for i, b := range buf { + num |= uint64(b) << (uint(i) * 8) + } + return num +} + +// big endian +func BFixedLengthInt(buf []byte) uint64 { + var num uint64 = 0 + for i, b := range buf { + num |= uint64(b) << (uint(len(buf)-i-1) * 8) + } + return num +} + +func LengthEncodedInt(b []byte) (num uint64, isNull bool, n int) { + switch b[0] { + + // 251: NULL + case 0xfb: + n = 1 + isNull = true + return + + // 252: value of following 2 + case 0xfc: + num = uint64(b[1]) | uint64(b[2])<<8 + n = 3 + return + + // 253: value of following 3 + case 0xfd: + num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 + n = 4 + return + + // 254: value of following 8 + case 0xfe: + num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 | + uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 | + uint64(b[7])<<48 | uint64(b[8])<<56 + n = 9 + return + } + + // 0-250: value of first byte + num = uint64(b[0]) + n = 1 + return +} + +func PutLengthEncodedInt(n uint64) []byte { + switch { + case n <= 250: + return []byte{byte(n)} + + case n <= 0xffff: + return []byte{0xfc, byte(n), byte(n >> 8)} + + case n <= 0xffffff: + return []byte{0xfd, byte(n), byte(n >> 8), byte(n >> 16)} + + case n <= 0xffffffffffffffff: + return []byte{0xfe, byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24), + byte(n >> 32), byte(n >> 40), byte(n >> 48), byte(n >> 56)} + } + return nil +} + +func LengthEnodedString(b []byte) ([]byte, bool, int, error) { + // Get length + num, isNull, n := LengthEncodedInt(b) + if num < 1 { + return nil, isNull, n, nil + } + + n += int(num) + + // Check data length + if len(b) >= n { + return b[n-int(num) : n], false, n, nil + } + return nil, false, n, io.EOF +} + +func SkipLengthEnodedString(b []byte) (int, error) { + // Get length + num, _, n := LengthEncodedInt(b) + if num < 1 { + return n, nil + } + + n += int(num) + + // Check data length + if len(b) >= n { + return n, nil + } + return n, io.EOF +} + +func PutLengthEncodedString(b []byte) []byte { + data := make([]byte, 0, len(b)+9) + data = append(data, PutLengthEncodedInt(uint64(len(b)))...) + data = append(data, b...) + return data +} + +func Uint16ToBytes(n uint16) []byte { + return []byte{ + byte(n), + byte(n >> 8), + } +} + +func Uint32ToBytes(n uint32) []byte { + return []byte{ + byte(n), + byte(n >> 8), + byte(n >> 16), + byte(n >> 24), + } +} + +func Uint64ToBytes(n uint64) []byte { + return []byte{ + byte(n), + byte(n >> 8), + byte(n >> 16), + byte(n >> 24), + byte(n >> 32), + byte(n >> 40), + byte(n >> 48), + byte(n >> 56), + } +} + +func FormatBinaryDate(n int, data []byte) ([]byte, error) { + switch n { + case 0: + return []byte("0000-00-00"), nil + case 4: + return []byte(fmt.Sprintf("%04d-%02d-%02d", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3])), nil + default: + return nil, errors.Errorf("invalid date packet length %d", n) + } +} + +func FormatBinaryDateTime(n int, data []byte) ([]byte, error) { + switch n { + case 0: + return []byte("0000-00-00 00:00:00"), nil + case 4: + return []byte(fmt.Sprintf("%04d-%02d-%02d 00:00:00", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3])), nil + case 7: + return []byte(fmt.Sprintf( + "%04d-%02d-%02d %02d:%02d:%02d", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3], + data[4], + data[5], + data[6])), nil + case 11: + return []byte(fmt.Sprintf( + "%04d-%02d-%02d %02d:%02d:%02d.%06d", + binary.LittleEndian.Uint16(data[:2]), + data[2], + data[3], + data[4], + data[5], + data[6], + binary.LittleEndian.Uint32(data[7:11]))), nil + default: + return nil, errors.Errorf("invalid datetime packet length %d", n) + } +} + +func FormatBinaryTime(n int, data []byte) ([]byte, error) { + if n == 0 { + return []byte("0000-00-00"), nil + } + + var sign byte + if data[0] == 1 { + sign = byte('-') + } + + switch n { + case 8: + return []byte(fmt.Sprintf( + "%c%02d:%02d:%02d", + sign, + uint16(data[1])*24+uint16(data[5]), + data[6], + data[7], + )), nil + case 12: + return []byte(fmt.Sprintf( + "%c%02d:%02d:%02d.%06d", + sign, + uint16(data[1])*24+uint16(data[5]), + data[6], + data[7], + binary.LittleEndian.Uint32(data[8:12]), + )), nil + default: + return nil, errors.Errorf("invalid time packet length %d", n) + } +} + +var ( + DONTESCAPE = byte(255) + + EncodeMap [256]byte +) + +// only support utf-8 +func Escape(sql string) string { + dest := make([]byte, 0, 2*len(sql)) + + for _, w := range hack.Slice(sql) { + if c := EncodeMap[w]; c == DONTESCAPE { + dest = append(dest, w) + } else { + dest = append(dest, '\\', c) + } + } + + return string(dest) +} + +func GetNetProto(addr string) string { + if strings.Contains(addr, "/") { + return "unix" + } else { + return "tcp" + } +} + +var encodeRef = map[byte]byte{ + '\x00': '0', + '\'': '\'', + '"': '"', + '\b': 'b', + '\n': 'n', + '\r': 'r', + '\t': 't', + 26: 'Z', // ctl-Z + '\\': '\\', +} + +func init() { + for i := range EncodeMap { + EncodeMap[i] = DONTESCAPE + } + for i := range EncodeMap { + if to, ok := encodeRef[byte(i)]; ok { + EncodeMap[byte(i)] = to + } + } +} diff --git a/vendor/github.com/siddontang/go-mysql/packet/conn.go b/vendor/github.com/siddontang/go-mysql/packet/conn.go new file mode 100644 index 0000000..3772e1a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/packet/conn.go @@ -0,0 +1,163 @@ +package packet + +import ( + "bufio" + "bytes" + "io" + "net" + + "github.com/juju/errors" + . "github.com/siddontang/go-mysql/mysql" +) + +/* + Conn is the base class to handle MySQL protocol. +*/ +type Conn struct { + net.Conn + br *bufio.Reader + + Sequence uint8 +} + +func NewConn(conn net.Conn) *Conn { + c := new(Conn) + + c.br = bufio.NewReaderSize(conn, 4096) + c.Conn = conn + + return c +} + +func (c *Conn) ReadPacket() ([]byte, error) { + var buf bytes.Buffer + + if err := c.ReadPacketTo(&buf); err != nil { + return nil, errors.Trace(err) + } else { + return buf.Bytes(), nil + } + + // header := []byte{0, 0, 0, 0} + + // if _, err := io.ReadFull(c.br, header); err != nil { + // return nil, ErrBadConn + // } + + // length := int(uint32(header[0]) | uint32(header[1])<<8 | uint32(header[2])<<16) + // if length < 1 { + // return nil, fmt.Errorf("invalid payload length %d", length) + // } + + // sequence := uint8(header[3]) + + // if sequence != c.Sequence { + // return nil, fmt.Errorf("invalid sequence %d != %d", sequence, c.Sequence) + // } + + // c.Sequence++ + + // data := make([]byte, length) + // if _, err := io.ReadFull(c.br, data); err != nil { + // return nil, ErrBadConn + // } else { + // if length < MaxPayloadLen { + // return data, nil + // } + + // var buf []byte + // buf, err = c.ReadPacket() + // if err != nil { + // return nil, ErrBadConn + // } else { + // return append(data, buf...), nil + // } + // } +} + +func (c *Conn) ReadPacketTo(w io.Writer) error { + header := []byte{0, 0, 0, 0} + + if _, err := io.ReadFull(c.br, header); err != nil { + return ErrBadConn + } + + length := int(uint32(header[0]) | uint32(header[1])<<8 | uint32(header[2])<<16) + if length < 1 { + return errors.Errorf("invalid payload length %d", length) + } + + sequence := uint8(header[3]) + + if sequence != c.Sequence { + return errors.Errorf("invalid sequence %d != %d", sequence, c.Sequence) + } + + c.Sequence++ + + if n, err := io.CopyN(w, c.br, int64(length)); err != nil { + return ErrBadConn + } else if n != int64(length) { + return ErrBadConn + } else { + if length < MaxPayloadLen { + return nil + } + + if err := c.ReadPacketTo(w); err != nil { + return err + } + } + + return nil +} + +// data already has 4 bytes header +// will modify data inplace +func (c *Conn) WritePacket(data []byte) error { + length := len(data) - 4 + + for length >= MaxPayloadLen { + data[0] = 0xff + data[1] = 0xff + data[2] = 0xff + + data[3] = c.Sequence + + if n, err := c.Write(data[:4+MaxPayloadLen]); err != nil { + return ErrBadConn + } else if n != (4 + MaxPayloadLen) { + return ErrBadConn + } else { + c.Sequence++ + length -= MaxPayloadLen + data = data[MaxPayloadLen:] + } + } + + data[0] = byte(length) + data[1] = byte(length >> 8) + data[2] = byte(length >> 16) + data[3] = c.Sequence + + if n, err := c.Write(data); err != nil { + return ErrBadConn + } else if n != len(data) { + return ErrBadConn + } else { + c.Sequence++ + return nil + } +} + +func (c *Conn) ResetSequence() { + c.Sequence = 0 +} + +func (c *Conn) Close() error { + c.Sequence = 0 + if c.Conn != nil { + return c.Conn.Close() + } + return nil +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/backup.go b/vendor/github.com/siddontang/go-mysql/replication/backup.go new file mode 100644 index 0000000..f1fda21 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/backup.go @@ -0,0 +1,90 @@ +package replication + +import ( + "io" + "os" + "path" + "time" + + "github.com/juju/errors" + . "github.com/siddontang/go-mysql/mysql" +) + +// Like mysqlbinlog remote raw backup +// Backup remote binlog from position (filename, offset) and write in backupDir +func (b *BinlogSyncer) StartBackup(backupDir string, p Position, timeout time.Duration) error { + if timeout == 0 { + // a very long timeout here + timeout = 30 * 3600 * 24 * time.Second + } + + b.SetRawMode(true) + + os.MkdirAll(backupDir, 0755) + + s, err := b.StartSync(p) + if err != nil { + return errors.Trace(err) + } + + var filename string + var offset uint32 + + var f *os.File + defer func() { + if f != nil { + f.Close() + } + }() + + for { + e, err := s.GetEventTimeout(timeout) + if errors.Cause(err) == ErrGetEventTimeout { + // for backward compatibility + return ErrGetEventTimeout + } else if err != nil { + return errors.Trace(err) + } + + offset = e.Header.LogPos + + if e.Header.EventType == ROTATE_EVENT { + rotateEvent := e.Event.(*RotateEvent) + filename = string(rotateEvent.NextLogName) + + if e.Header.Timestamp == 0 || offset == 0 { + // fake rotate event + continue + } + } else if e.Header.EventType == FORMAT_DESCRIPTION_EVENT { + // FormateDescriptionEvent is the first event in binlog, we will close old one and create a new + + if f != nil { + f.Close() + } + + if len(filename) == 0 { + return errors.Errorf("empty binlog filename for FormateDescriptionEvent") + } + + f, err = os.OpenFile(path.Join(backupDir, filename), os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return errors.Trace(err) + } + + // write binlog header fe'bin' + if _, err = f.Write(BinLogFileHeader); err != nil { + return errors.Trace(err) + } + + } + + if n, err := f.Write(e.RawData); err != nil { + return errors.Trace(err) + } else if n != len(e.RawData) { + return errors.Trace(io.ErrShortWrite) + } + } + + return nil +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/binlogstreamer.go b/vendor/github.com/siddontang/go-mysql/replication/binlogstreamer.go new file mode 100644 index 0000000..706fe08 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/binlogstreamer.go @@ -0,0 +1,72 @@ +package replication + +import ( + "time" + + "github.com/juju/errors" +) + +var ( + ErrGetEventTimeout = errors.New("Get event timeout, try get later") + ErrNeedSyncAgain = errors.New("Last sync error or closed, try sync and get event again") + ErrSyncClosed = errors.New("Sync was closed") +) + +type BinlogStreamer struct { + ch chan *BinlogEvent + ech chan error + err error +} + +func (s *BinlogStreamer) GetEvent() (*BinlogEvent, error) { + if s.err != nil { + return nil, ErrNeedSyncAgain + } + + select { + case c := <-s.ch: + return c, nil + case s.err = <-s.ech: + return nil, s.err + } +} + +// if timeout, ErrGetEventTimeout will returns +// timeout value won't be set too large, otherwise it may waste lots of memory +func (s *BinlogStreamer) GetEventTimeout(d time.Duration) (*BinlogEvent, error) { + if s.err != nil { + return nil, ErrNeedSyncAgain + } + + select { + case c := <-s.ch: + return c, nil + case s.err = <-s.ech: + return nil, s.err + case <-time.After(d): + return nil, ErrGetEventTimeout + } +} + +func (s *BinlogStreamer) close() { + s.closeWithError(ErrSyncClosed) +} + +func (s *BinlogStreamer) closeWithError(err error) { + if err == nil { + err = ErrSyncClosed + } + select { + case s.ech <- err: + default: + } +} + +func newBinlogStreamer() *BinlogStreamer { + s := new(BinlogStreamer) + + s.ch = make(chan *BinlogEvent, 1024) + s.ech = make(chan error, 4) + + return s +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/binlogsyncer.go b/vendor/github.com/siddontang/go-mysql/replication/binlogsyncer.go new file mode 100644 index 0000000..bcea08a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/binlogsyncer.go @@ -0,0 +1,559 @@ +package replication + +import ( + "encoding/binary" + "fmt" + "os" + "sync" + "time" + + "github.com/juju/errors" + "github.com/satori/go.uuid" + "github.com/siddontang/go-mysql/client" + . "github.com/siddontang/go-mysql/mysql" +) + +var ( + errSyncRunning = errors.New("Sync is running, must Close first") + errNotRegistered = errors.New("Syncer is not registered as a slave") +) + +type BinlogSyncer struct { + m sync.Mutex + + flavor string + + c *client.Conn + serverID uint32 + + localhost string + host string + port uint16 + user string + password string + + masterID uint32 + + wg sync.WaitGroup + + parser *BinlogParser + + nextPos Position + + running bool + semiSyncEnabled bool + + stopCh chan struct{} +} + +func NewBinlogSyncer(serverID uint32, flavor string) *BinlogSyncer { + b := new(BinlogSyncer) + b.flavor = flavor + + b.serverID = serverID + + b.masterID = 0 + + b.parser = NewBinlogParser() + + b.running = false + b.semiSyncEnabled = false + + b.stopCh = make(chan struct{}, 1) + + return b +} + +// LocalHostname returns the hostname that register slave would register as. +func (b *BinlogSyncer) LocalHostname() string { + + if b.localhost == "" { + h, _ := os.Hostname() + return h + } + return b.localhost +} + +// SetLocalHostname set's the hostname that register salve would register as. +func (b *BinlogSyncer) SetLocalHostname(name string) { + b.localhost = name +} + +func (b *BinlogSyncer) Close() { + b.m.Lock() + defer b.m.Unlock() + + b.close() +} + +func (b *BinlogSyncer) close() { + if b.c != nil { + b.c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + } + + select { + case b.stopCh <- struct{}{}: + default: + } + + b.wg.Wait() + + if b.c != nil { + b.c.Close() + } + + b.running = false + b.c = nil +} + +func (b *BinlogSyncer) checkExec() error { + if b.running { + return errSyncRunning + } else if b.c == nil { + return errNotRegistered + } + + return nil +} + +func (b *BinlogSyncer) GetMasterUUID() (uuid.UUID, error) { + b.m.Lock() + defer b.m.Unlock() + + if err := b.checkExec(); err != nil { + return uuid.UUID{}, err + } + + if r, err := b.c.Execute("SHOW GLOBAL VARIABLES LIKE 'SERVER_UUID'"); err != nil { + return uuid.UUID{}, err + } else { + s, _ := r.GetString(0, 1) + if s == "" || s == "NONE" { + return uuid.UUID{}, nil + } else { + return uuid.FromString(s) + } + } +} + +// You must register slave at first before you do other operations +// This function will close old replication sync if exists +func (b *BinlogSyncer) RegisterSlave(host string, port uint16, user string, password string) error { + b.m.Lock() + defer b.m.Unlock() + + // first, close old replication sync + b.close() + + b.host = host + b.port = port + b.user = user + b.password = password + + err := b.registerSlave() + if err != nil { + b.close() + } + return errors.Trace(err) +} + +// If you close sync before and want to restart again, you can call this before other operations +// This function will close old replication sync if exists +func (b *BinlogSyncer) ReRegisterSlave() error { + b.m.Lock() + defer b.m.Unlock() + + if len(b.host) == 0 || len(b.user) == 0 { + return errors.Errorf("empty host and user, you must register slave before") + } + + b.close() + + err := b.registerSlave() + if err != nil { + b.close() + } + + return errors.Trace(err) +} + +func (b *BinlogSyncer) registerSlave() error { + var err error + b.c, err = client.Connect(fmt.Sprintf("%s:%d", b.host, b.port), b.user, b.password, "") + if err != nil { + return errors.Trace(err) + } + + //for mysql 5.6+, binlog has a crc32 checksum + //before mysql 5.6, this will not work, don't matter.:-) + if r, err := b.c.Execute("SHOW GLOBAL VARIABLES LIKE 'BINLOG_CHECKSUM'"); err != nil { + return errors.Trace(err) + } else { + s, _ := r.GetString(0, 1) + if s != "" { + // maybe CRC32 or NONE + + // mysqlbinlog.cc use NONE, see its below comments: + // Make a notice to the server that this client + // is checksum-aware. It does not need the first fake Rotate + // necessary checksummed. + // That preference is specified below. + + if _, err = b.c.Execute(`SET @master_binlog_checksum='NONE'`); err != nil { + return errors.Trace(err) + } + + // if _, err = b.c.Execute(`SET @master_binlog_checksum=@@global.binlog_checksum`); err != nil { + // return errors.Trace(err) + // } + + } + } + + if err = b.writeRegisterSlaveCommand(); err != nil { + return errors.Trace(err) + } + + if _, err = b.c.ReadOKPacket(); err != nil { + return errors.Trace(err) + } + + return nil +} + +func (b *BinlogSyncer) EnableSemiSync() error { + b.m.Lock() + defer b.m.Unlock() + + if err := b.checkExec(); err != nil { + return errors.Trace(err) + } + + if r, err := b.c.Execute("SHOW VARIABLES LIKE 'rpl_semi_sync_master_enabled';"); err != nil { + return errors.Trace(err) + } else { + s, _ := r.GetString(0, 1) + if s != "ON" { + return errors.Errorf("master does not support semi synchronous replication") + } + } + + _, err := b.c.Execute(`SET @rpl_semi_sync_slave = 1;`) + if err != nil { + b.semiSyncEnabled = true + } + return errors.Trace(err) +} + +func (b *BinlogSyncer) startDumpStream() *BinlogStreamer { + b.running = true + b.stopCh = make(chan struct{}, 1) + + s := newBinlogStreamer() + + b.wg.Add(1) + go b.onStream(s) + return s +} + +func (b *BinlogSyncer) StartSync(pos Position) (*BinlogStreamer, error) { + b.m.Lock() + defer b.m.Unlock() + + if err := b.checkExec(); err != nil { + return nil, err + } + + //always start from position 4 + if pos.Pos < 4 { + pos.Pos = 4 + } + + err := b.writeBinglogDumpCommand(pos) + if err != nil { + return nil, err + } + + return b.startDumpStream(), nil +} + +func (b *BinlogSyncer) SetRawMode(mode bool) error { + b.m.Lock() + defer b.m.Unlock() + + if err := b.checkExec(); err != nil { + return errors.Trace(err) + } + + b.parser.SetRawMode(mode) + return nil +} + +func (b *BinlogSyncer) ExecuteSql(query string, args ...interface{}) (*Result, error) { + b.m.Lock() + defer b.m.Unlock() + + if err := b.checkExec(); err != nil { + return nil, err + } + + return b.c.Execute(query, args...) +} + +func (b *BinlogSyncer) StartSyncGTID(gset GTIDSet) (*BinlogStreamer, error) { + b.m.Lock() + defer b.m.Unlock() + + if err := b.checkExec(); err != nil { + return nil, err + } + + var err error + switch b.flavor { + case MySQLFlavor: + err = b.writeBinlogDumpMysqlGTIDCommand(gset) + case MariaDBFlavor: + err = b.writeBinlogDumpMariadbGTIDCommand(gset) + default: + err = fmt.Errorf("invalid flavor %s", b.flavor) + } + + if err != nil { + return nil, err + } + + return b.startDumpStream(), nil +} + +func (b *BinlogSyncer) writeBinglogDumpCommand(p Position) error { + b.c.ResetSequence() + + data := make([]byte, 4+1+4+2+4+len(p.Name)) + + pos := 4 + data[pos] = COM_BINLOG_DUMP + pos++ + + binary.LittleEndian.PutUint32(data[pos:], p.Pos) + pos += 4 + + binary.LittleEndian.PutUint16(data[pos:], BINLOG_DUMP_NEVER_STOP) + pos += 2 + + binary.LittleEndian.PutUint32(data[pos:], b.serverID) + pos += 4 + + copy(data[pos:], p.Name) + + return b.c.WritePacket(data) +} + +func (b *BinlogSyncer) writeBinlogDumpMysqlGTIDCommand(gset GTIDSet) error { + p := Position{"", 4} + gtidData := gset.Encode() + + b.c.ResetSequence() + + data := make([]byte, 4+1+2+4+4+len(p.Name)+8+4+len(gtidData)) + pos := 4 + data[pos] = COM_BINLOG_DUMP_GTID + pos++ + + binary.LittleEndian.PutUint16(data[pos:], 0) + pos += 2 + + binary.LittleEndian.PutUint32(data[pos:], b.serverID) + pos += 4 + + binary.LittleEndian.PutUint32(data[pos:], uint32(len(p.Name))) + pos += 4 + + n := copy(data[pos:], p.Name) + pos += n + + binary.LittleEndian.PutUint64(data[pos:], uint64(p.Pos)) + pos += 8 + + binary.LittleEndian.PutUint32(data[pos:], uint32(len(gtidData))) + pos += 4 + n = copy(data[pos:], gtidData) + pos += n + + data = data[0:pos] + + return b.c.WritePacket(data) +} + +func (b *BinlogSyncer) writeBinlogDumpMariadbGTIDCommand(gset GTIDSet) error { + // Copy from vitess + + startPos := gset.String() + + // Tell the server that we understand GTIDs by setting our slave capability + // to MARIA_SLAVE_CAPABILITY_GTID = 4 (MariaDB >= 10.0.1). + if _, err := b.c.Execute("SET @mariadb_slave_capability=4"); err != nil { + return errors.Errorf("failed to set @mariadb_slave_capability=4: %v", err) + } + + // Set the slave_connect_state variable before issuing COM_BINLOG_DUMP to + // provide the start position in GTID form. + query := fmt.Sprintf("SET @slave_connect_state='%s'", startPos) + + if _, err := b.c.Execute(query); err != nil { + return errors.Errorf("failed to set @slave_connect_state='%s': %v", startPos, err) + } + + // Real slaves set this upon connecting if their gtid_strict_mode option was + // enabled. We always use gtid_strict_mode because we need it to make our + // internal GTID comparisons safe. + if _, err := b.c.Execute("SET @slave_gtid_strict_mode=1"); err != nil { + return errors.Errorf("failed to set @slave_gtid_strict_mode=1: %v", err) + } + + // Since we use @slave_connect_state, the file and position here are ignored. + return b.writeBinglogDumpCommand(Position{"", 0}) +} + +func (b *BinlogSyncer) writeRegisterSlaveCommand() error { + b.c.ResetSequence() + + hostname := b.LocalHostname() + + // This should be the name of slave host not the host we are connecting to. + data := make([]byte, 4+1+4+1+len(hostname)+1+len(b.user)+1+len(b.password)+2+4+4) + pos := 4 + + data[pos] = COM_REGISTER_SLAVE + pos++ + + binary.LittleEndian.PutUint32(data[pos:], b.serverID) + pos += 4 + + // This should be the name of slave hostname not the host we are connecting to. + data[pos] = uint8(len(hostname)) + pos++ + n := copy(data[pos:], hostname) + pos += n + + data[pos] = uint8(len(b.user)) + pos++ + n = copy(data[pos:], b.user) + pos += n + + data[pos] = uint8(len(b.password)) + pos++ + n = copy(data[pos:], b.password) + pos += n + + binary.LittleEndian.PutUint16(data[pos:], b.port) + pos += 2 + + //replication rank, not used + binary.LittleEndian.PutUint32(data[pos:], 0) + pos += 4 + + binary.LittleEndian.PutUint32(data[pos:], b.masterID) + + return b.c.WritePacket(data) +} + +func (b *BinlogSyncer) replySemiSyncACK(p Position) error { + b.c.ResetSequence() + + data := make([]byte, 4+1+8+len(p.Name)) + pos := 4 + // semi sync indicator + data[pos] = SemiSyncIndicator + pos++ + + binary.LittleEndian.PutUint64(data[pos:], uint64(p.Pos)) + pos += 8 + + copy(data[pos:], p.Name) + + err := b.c.WritePacket(data) + if err != nil { + return errors.Trace(err) + } + + _, err = b.c.ReadOKPacket() + if err != nil { + } + return errors.Trace(err) +} + +func (b *BinlogSyncer) onStream(s *BinlogStreamer) { + defer func() { + if e := recover(); e != nil { + s.closeWithError(fmt.Errorf("Err: %v\n Stack: %s", e, Pstack())) + } + b.wg.Done() + }() + + for { + data, err := b.c.ReadPacket() + if err != nil { + s.closeWithError(err) + return + } + + switch data[0] { + case OK_HEADER: + if err = b.parseEvent(s, data); err != nil { + s.closeWithError(err) + return + } + case ERR_HEADER: + err = b.c.HandleErrorPacket(data) + s.closeWithError(err) + return + default: + s.closeWithError(fmt.Errorf("invalid stream header %c", data[0])) + return + } + } +} + +func (b *BinlogSyncer) parseEvent(s *BinlogStreamer, data []byte) error { + //skip OK byte, 0x00 + data = data[1:] + + needACK := false + if b.semiSyncEnabled && (data[0] == SemiSyncIndicator) { + needACK = (data[1] == 0x01) + //skip semi sync header + data = data[2:] + } + + e, err := b.parser.parse(data) + if err != nil { + return errors.Trace(err) + } + + b.nextPos.Pos = e.Header.LogPos + + if re, ok := e.Event.(*RotateEvent); ok { + b.nextPos.Name = string(re.NextLogName) + b.nextPos.Pos = uint32(re.Position) + } + + needStop := false + select { + case s.ch <- e: + case <-b.stopCh: + needStop = true + } + + if needACK { + err := b.replySemiSyncACK(b.nextPos) + if err != nil { + return errors.Trace(err) + } + } + + if needStop { + return errors.New("sync is been closing...") + } + + return nil +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/bitmap.go b/vendor/github.com/siddontang/go-mysql/replication/bitmap.go new file mode 100644 index 0000000..5de9938 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/bitmap.go @@ -0,0 +1,38 @@ +package replication + +var bitCountInByte = [256]uint8{ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +} + +func bitCount(bitmap []byte) int { + var n uint32 = 0 + + for _, b := range bitmap { + n += uint32(bitCountInByte[b]) + } + + return int(n) +} + +func bitGet(bitmap []byte, i int) byte { + return bitmap[i/8] & (1 << (uint(i) & 7)) +} + +func isNullSet(nullBitmap []byte, i int) bool { + return nullBitmap[i/8]&(1<<(uint(i)%8)) > 0 +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/const.go b/vendor/github.com/siddontang/go-mysql/replication/const.go new file mode 100644 index 0000000..ef82b6c --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/const.go @@ -0,0 +1,185 @@ +package replication + +const ( + //we only support MySQL 5.0.0+ binlog format, maybe??? + MinBinlogVersion = 4 +) + +var ( + //binlog header [ fe `bin` ] + BinLogFileHeader []byte = []byte{0xfe, 0x62, 0x69, 0x6e} + + SemiSyncIndicator byte = 0xef +) + +const ( + LOG_EVENT_BINLOG_IN_USE_F uint16 = 0x0001 + LOG_EVENT_FORCED_ROTATE_F uint16 = 0x0002 + LOG_EVENT_THREAD_SPECIFIC_F uint16 = 0x0004 + LOG_EVENT_SUPPRESS_USE_F uint16 = 0x0008 + LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F uint16 = 0x0010 + LOG_EVENT_ARTIFICIAL_F uint16 = 0x0020 + LOG_EVENT_RELAY_LOG_F uint16 = 0x0040 + LOG_EVENT_IGNORABLE_F uint16 = 0x0080 + LOG_EVENT_NO_FILTER_F uint16 = 0x0100 + LOG_EVENT_MTS_ISOLATE_F uint16 = 0x0200 +) + +const ( + BINLOG_DUMP_NEVER_STOP uint16 = 0x00 + BINLOG_DUMP_NON_BLOCK uint16 = 0x01 + BINLOG_THROUGH_POSITION uint16 = 0x02 + BINLOG_THROUGH_GTID uint16 = 0x04 +) + +const ( + BINLOG_ROW_IMAGE_FULL = "FULL" + BINLOG_ROW_IAMGE_MINIMAL = "MINIMAL" + BINLOG_ROW_IMAGE_NOBLOB = "NOBLOB" +) + +type EventType byte + +const ( + UNKNOWN_EVENT EventType = iota + START_EVENT_V3 + QUERY_EVENT + STOP_EVENT + ROTATE_EVENT + INTVAR_EVENT + LOAD_EVENT + SLAVE_EVENT + CREATE_FILE_EVENT + APPEND_BLOCK_EVENT + EXEC_LOAD_EVENT + DELETE_FILE_EVENT + NEW_LOAD_EVENT + RAND_EVENT + USER_VAR_EVENT + FORMAT_DESCRIPTION_EVENT + XID_EVENT + BEGIN_LOAD_QUERY_EVENT + EXECUTE_LOAD_QUERY_EVENT + TABLE_MAP_EVENT + WRITE_ROWS_EVENTv0 + UPDATE_ROWS_EVENTv0 + DELETE_ROWS_EVENTv0 + WRITE_ROWS_EVENTv1 + UPDATE_ROWS_EVENTv1 + DELETE_ROWS_EVENTv1 + INCIDENT_EVENT + HEARTBEAT_EVENT + IGNORABLE_EVENT + ROWS_QUERY_EVENT + WRITE_ROWS_EVENTv2 + UPDATE_ROWS_EVENTv2 + DELETE_ROWS_EVENTv2 + GTID_EVENT + ANONYMOUS_GTID_EVENT + PREVIOUS_GTIDS_EVENT +) + +const ( + // MariaDB event starts from 160 + MARIADB_ANNOTATE_ROWS_EVENT EventType = 160 + iota + MARIADB_BINLOG_CHECKPOINT_EVENT + MARIADB_GTID_EVENT + MARIADB_GTID_LIST_EVENT +) + +func (e EventType) String() string { + switch e { + case UNKNOWN_EVENT: + return "UnknownEvent" + case START_EVENT_V3: + return "StartEventV3" + case QUERY_EVENT: + return "QueryEvent" + case STOP_EVENT: + return "StopEvent" + case ROTATE_EVENT: + return "RotateEvent" + case INTVAR_EVENT: + return "IntVarEvent" + case LOAD_EVENT: + return "LoadEvent" + case SLAVE_EVENT: + return "SlaveEvent" + case CREATE_FILE_EVENT: + return "CreateFileEvent" + case APPEND_BLOCK_EVENT: + return "AppendBlockEvent" + case EXEC_LOAD_EVENT: + return "ExecLoadEvent" + case DELETE_FILE_EVENT: + return "DeleteFileEvent" + case NEW_LOAD_EVENT: + return "NewLoadEvent" + case RAND_EVENT: + return "RandEvent" + case USER_VAR_EVENT: + return "UserVarEvent" + case FORMAT_DESCRIPTION_EVENT: + return "FormatDescriptionEvent" + case XID_EVENT: + return "XIDEvent" + case BEGIN_LOAD_QUERY_EVENT: + return "BeginLoadQueryEvent" + case EXECUTE_LOAD_QUERY_EVENT: + return "ExectueLoadQueryEvent" + case TABLE_MAP_EVENT: + return "TableMapEvent" + case WRITE_ROWS_EVENTv0: + return "WriteRowsEventV0" + case UPDATE_ROWS_EVENTv0: + return "UpdateRowsEventV0" + case DELETE_ROWS_EVENTv0: + return "DeleteRowsEventV0" + case WRITE_ROWS_EVENTv1: + return "WriteRowsEventV1" + case UPDATE_ROWS_EVENTv1: + return "UpdateRowsEventV1" + case DELETE_ROWS_EVENTv1: + return "DeleteRowsEventV1" + case INCIDENT_EVENT: + return "IncidentEvent" + case HEARTBEAT_EVENT: + return "HeartbeatEvent" + case IGNORABLE_EVENT: + return "IgnorableEvent" + case ROWS_QUERY_EVENT: + return "RowsQueryEvent" + case WRITE_ROWS_EVENTv2: + return "WriteRowsEventV2" + case UPDATE_ROWS_EVENTv2: + return "UpdateRowsEventV2" + case DELETE_ROWS_EVENTv2: + return "DeleteRowsEventV2" + case GTID_EVENT: + return "GTIDEvent" + case ANONYMOUS_GTID_EVENT: + return "AnonymousGTIDEvent" + case PREVIOUS_GTIDS_EVENT: + return "PreviousGTIDsEvent" + case MARIADB_ANNOTATE_ROWS_EVENT: + return "MariadbAnnotateRowsEvent" + case MARIADB_BINLOG_CHECKPOINT_EVENT: + return "MariadbBinLogCheckPointEvent" + case MARIADB_GTID_EVENT: + return "MariadbGTIDEvent" + case MARIADB_GTID_LIST_EVENT: + return "MariadbGTIDListEvent" + + default: + return "UnknownEvent" + } +} + +const ( + BINLOG_CHECKSUM_ALG_OFF byte = 0 // Events are without checksum though its generator + // is checksum-capable New Master (NM). + BINLOG_CHECKSUM_ALG_CRC32 byte = 1 // CRC32 of zlib algorithm. + // BINLOG_CHECKSUM_ALG_ENUM_END, // the cut line: valid alg range is [1, 0x7f]. + BINLOG_CHECKSUM_ALG_UNDEF byte = 255 // special value to tag undetermined yet checksum + // or events from checksum-unaware servers +) diff --git a/vendor/github.com/siddontang/go-mysql/replication/doc.go b/vendor/github.com/siddontang/go-mysql/replication/doc.go new file mode 100644 index 0000000..790eb45 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/doc.go @@ -0,0 +1,8 @@ +/* +Replication package is to handle MySQL replication protocol. + +Todo: + ++ Get table information when handing rows event. +*/ +package replication diff --git a/vendor/github.com/siddontang/go-mysql/replication/event.go b/vendor/github.com/siddontang/go-mysql/replication/event.go new file mode 100644 index 0000000..1a0d2c6 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/event.go @@ -0,0 +1,458 @@ +package replication + +import ( + "encoding/binary" + //"encoding/hex" + "fmt" + "io" + "strconv" + "strings" + "time" + "unicode" + + "github.com/juju/errors" + "github.com/satori/go.uuid" + . "github.com/siddontang/go-mysql/mysql" +) + +const ( + EventHeaderSize = 19 +) + +type BinlogEvent struct { + // raw binlog data, including crc32 checksum if exists + RawData []byte + + Header *EventHeader + Event Event +} + +func (e *BinlogEvent) Dump(w io.Writer) { + e.Header.Dump(w) + e.Event.Dump(w) +} + +type Event interface { + //Dump Event, format like python-mysql-replication + Dump(w io.Writer) + + Decode(data []byte) error +} + +type EventError struct { + Header *EventHeader + + //Error message + Err string + + //Event data + Data []byte +} + +func (e *EventError) Error() string { + return e.Err +} + +type EventHeader struct { + Timestamp uint32 + EventType EventType + ServerID uint32 + EventSize uint32 + LogPos uint32 + Flags uint16 +} + +func (h *EventHeader) Decode(data []byte) error { + if len(data) < EventHeaderSize { + return errors.Errorf("header size too short %d, must 19", len(data)) + } + + pos := 0 + + h.Timestamp = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + h.EventType = EventType(data[pos]) + pos++ + + h.ServerID = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + h.EventSize = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + h.LogPos = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + h.Flags = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + if h.EventSize < uint32(EventHeaderSize) { + return errors.Errorf("invalid event size %d, must >= 19", h.EventSize) + } + + return nil +} + +func (h *EventHeader) Dump(w io.Writer) { + fmt.Fprintf(w, "=== %s ===\n", EventType(h.EventType)) + 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, "Event size: %d\n", h.EventSize) +} + +var ( + checksumVersionSplitMysql []int = []int{5, 6, 1} + checksumVersionProductMysql int = (checksumVersionSplitMysql[0]*256+checksumVersionSplitMysql[1])*256 + checksumVersionSplitMysql[2] + + checksumVersionSplitMariaDB []int = []int{5, 3, 0} + checksumVersionProductMariaDB int = (checksumVersionSplitMariaDB[0]*256+checksumVersionSplitMariaDB[1])*256 + checksumVersionSplitMariaDB[2] +) + +// server version format X.Y.Zabc, a is not . or number +func splitServerVersion(server string) []int { + seps := strings.Split(server, ".") + if len(seps) < 3 { + return []int{0, 0, 0} + } + + x, _ := strconv.Atoi(seps[0]) + y, _ := strconv.Atoi(seps[1]) + + index := 0 + for i, c := range seps[2] { + if !unicode.IsNumber(c) { + index = i + break + } + } + + z, _ := strconv.Atoi(seps[2][0:index]) + + return []int{x, y, z} +} + +func calcVersionProduct(server string) int { + versionSplit := splitServerVersion(server) + + return ((versionSplit[0]*256+versionSplit[1])*256 + versionSplit[2]) +} + +type FormatDescriptionEvent struct { + Version uint16 + //len = 50 + ServerVersion []byte + CreateTimestamp uint32 + EventHeaderLength uint8 + EventTypeHeaderLengths []byte + + // 0 is off, 1 is for CRC32, 255 is undefined + ChecksumAlgorithm byte +} + +func (e *FormatDescriptionEvent) Decode(data []byte) error { + pos := 0 + e.Version = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + e.ServerVersion = make([]byte, 50) + copy(e.ServerVersion, data[pos:]) + pos += 50 + + e.CreateTimestamp = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.EventHeaderLength = data[pos] + pos++ + + if e.EventHeaderLength != byte(EventHeaderSize) { + return errors.Errorf("invalid event header length %d, must 19", e.EventHeaderLength) + } + + server := string(e.ServerVersion) + checksumProduct := checksumVersionProductMysql + if strings.Contains(strings.ToLower(server), "mariadb") { + checksumProduct = checksumVersionProductMariaDB + } + + if calcVersionProduct(string(e.ServerVersion)) >= checksumProduct { + // here, the last 5 bytes is 1 byte check sum alg type and 4 byte checksum if exists + e.ChecksumAlgorithm = data[len(data)-5] + e.EventTypeHeaderLengths = data[pos : len(data)-5] + } else { + e.ChecksumAlgorithm = BINLOG_CHECKSUM_ALG_UNDEF + e.EventTypeHeaderLengths = data[pos:] + } + + return nil +} + +func (e *FormatDescriptionEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Version: %d\n", e.Version) + fmt.Fprintf(w, "Server version: %s\n", e.ServerVersion) + //fmt.Fprintf(w, "Create date: %s\n", time.Unix(int64(e.CreateTimestamp), 0).Format(TimeFormat)) + fmt.Fprintf(w, "Checksum algorithm: %d\n", e.ChecksumAlgorithm) + //fmt.Fprintf(w, "Event header lengths: \n%s", hex.Dump(e.EventTypeHeaderLengths)) + fmt.Fprintln(w) +} + +type RotateEvent struct { + Position uint64 + NextLogName []byte +} + +func (e *RotateEvent) Decode(data []byte) error { + e.Position = binary.LittleEndian.Uint64(data[0:]) + e.NextLogName = data[8:] + + return nil +} + +func (e *RotateEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Position: %d\n", e.Position) + fmt.Fprintf(w, "Next log name: %s\n", e.NextLogName) + fmt.Fprintln(w) +} + +type XIDEvent struct { + XID uint64 +} + +func (e *XIDEvent) Decode(data []byte) error { + e.XID = binary.LittleEndian.Uint64(data) + return nil +} + +func (e *XIDEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "XID: %d\n", e.XID) + fmt.Fprintln(w) +} + +type QueryEvent struct { + SlaveProxyID uint32 + ExecutionTime uint32 + ErrorCode uint16 + StatusVars []byte + Schema []byte + Query []byte +} + +func (e *QueryEvent) Decode(data []byte) error { + pos := 0 + + e.SlaveProxyID = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.ExecutionTime = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + schemaLength := uint8(data[pos]) + pos++ + + e.ErrorCode = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + statusVarsLength := binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + e.StatusVars = data[pos : pos+int(statusVarsLength)] + pos += int(statusVarsLength) + + e.Schema = data[pos : pos+int(schemaLength)] + pos += int(schemaLength) + + //skip 0x00 + pos++ + + e.Query = data[pos:] + return nil +} + +func (e *QueryEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Slave proxy ID: %d\n", e.SlaveProxyID) + fmt.Fprintf(w, "Execution time: %d\n", e.ExecutionTime) + fmt.Fprintf(w, "Error code: %d\n", e.ErrorCode) + //fmt.Fprintf(w, "Status vars: \n%s", hex.Dump(e.StatusVars)) + fmt.Fprintf(w, "Schema: %s\n", e.Schema) + fmt.Fprintf(w, "Query: %s\n", e.Query) + fmt.Fprintln(w) +} + +type GTIDEvent struct { + CommitFlag uint8 + SID []byte + GNO int64 +} + +func (e *GTIDEvent) Decode(data []byte) error { + e.CommitFlag = uint8(data[0]) + + e.SID = data[1:17] + + e.GNO = int64(binary.LittleEndian.Uint64(data[17:])) + return nil +} + +func (e *GTIDEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Commit flag: %d\n", e.CommitFlag) + u, _ := uuid.FromBytes(e.SID) + fmt.Fprintf(w, "GTID_NEXT: %s:%d\n", u.String(), e.GNO) + fmt.Fprintln(w) +} + +type BeginLoadQueryEvent struct { + FileID uint32 + BlockData []byte +} + +func (e *BeginLoadQueryEvent) Decode(data []byte) error { + pos := 0 + + e.FileID = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.BlockData = data[pos:] + + return nil +} + +func (e *BeginLoadQueryEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "File ID: %d\n", e.FileID) + fmt.Fprintf(w, "Block data: %s\n", e.BlockData) + fmt.Fprintln(w) +} + +type ExecuteLoadQueryEvent struct { + SlaveProxyID uint32 + ExecutionTime uint32 + SchemaLength uint8 + ErrorCode uint16 + StatusVars uint16 + FileID uint32 + StartPos uint32 + EndPos uint32 + DupHandlingFlags uint8 +} + +func (e *ExecuteLoadQueryEvent) Decode(data []byte) error { + pos := 0 + + e.SlaveProxyID = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.ExecutionTime = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.SchemaLength = uint8(data[pos]) + pos++ + + e.ErrorCode = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + e.StatusVars = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + e.FileID = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.StartPos = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.EndPos = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + e.DupHandlingFlags = uint8(data[pos]) + + return nil +} + +func (e *ExecuteLoadQueryEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Slave proxy ID: %d\n", e.SlaveProxyID) + fmt.Fprintf(w, "Execution time: %d\n", e.ExecutionTime) + fmt.Fprintf(w, "Schame length: %d\n", e.SchemaLength) + fmt.Fprintf(w, "Error code: %d\n", e.ErrorCode) + fmt.Fprintf(w, "Status vars length: %d\n", e.StatusVars) + fmt.Fprintf(w, "File ID: %d\n", e.FileID) + fmt.Fprintf(w, "Start pos: %d\n", e.StartPos) + fmt.Fprintf(w, "End pos: %d\n", e.EndPos) + fmt.Fprintf(w, "Dup handling flags: %d\n", e.DupHandlingFlags) + fmt.Fprintln(w) +} + +// case MARIADB_ANNOTATE_ROWS_EVENT: +// return "MariadbAnnotateRowsEvent" + +type MariadbAnnotaeRowsEvent struct { + Query []byte +} + +func (e *MariadbAnnotaeRowsEvent) Decode(data []byte) error { + e.Query = data + return nil +} + +func (e *MariadbAnnotaeRowsEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Query: %s\n", e.Query) + fmt.Fprintln(w) +} + +type MariadbBinlogCheckPointEvent struct { + Info []byte +} + +func (e *MariadbBinlogCheckPointEvent) Decode(data []byte) error { + e.Info = data + return nil +} + +func (e *MariadbBinlogCheckPointEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Info: %s\n", e.Info) + fmt.Fprintln(w) +} + +type MariadbGTIDEvent struct { + GTID MariadbGTID +} + +func (e *MariadbGTIDEvent) Decode(data []byte) error { + e.GTID.SequenceNumber = binary.LittleEndian.Uint64(data) + e.GTID.DomainID = binary.LittleEndian.Uint32(data[8:]) + + // we don't care commit id now, maybe later + + return nil +} + +func (e *MariadbGTIDEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "GTID: %s\n", e.GTID) + fmt.Fprintln(w) +} + +type MariadbGTIDListEvent struct { + GTIDs []MariadbGTID +} + +func (e *MariadbGTIDListEvent) Decode(data []byte) error { + pos := 0 + v := binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + + count := v & uint32((1<<28)-1) + + e.GTIDs = make([]MariadbGTID, count) + + for i := uint32(0); i < count; i++ { + e.GTIDs[i].DomainID = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + e.GTIDs[i].ServerID = binary.LittleEndian.Uint32(data[pos:]) + pos += 4 + e.GTIDs[i].SequenceNumber = binary.LittleEndian.Uint64(data[pos:]) + } + + return nil +} + +func (e *MariadbGTIDListEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Lists: %v\n", e.GTIDs) + fmt.Fprintln(w) +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/generic_event.go b/vendor/github.com/siddontang/go-mysql/replication/generic_event.go new file mode 100644 index 0000000..b0fa83a --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/generic_event.go @@ -0,0 +1,171 @@ +package replication + +import ( + "encoding/hex" + "fmt" + "io" +) + +// we don't parse all event, so some we will use GenericEvent instead +type GenericEvent struct { + Data []byte +} + +func (e *GenericEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Event data: \n%s", hex.Dump(e.Data)) + fmt.Fprintln(w) +} + +func (e *GenericEvent) Decode(data []byte) error { + e.Data = data + + return nil +} + +//below events are generic events, maybe later I will consider handle some. + +// type StartEventV3 struct { +// Version uint16 +// ServerVersion [50]byte +// CreateTimestamp uint32 +// } + +// type StopEvent struct{} + +// type LoadEvent struct { +// SlaveProxyID uint32 +// ExecTime uint32 +// SkipLines uint32 +// TableNameLen uint8 +// SchemaLen uint8 +// NumFileds uint32 +// FieldTerm uint8 +// EnclosedBy uint8 +// LineTerm uint8 +// LineStart uint8 +// EscapedBy uint8 +// OptFlags uint8 +// EmptyFlags uint8 + +// //len = 1 * NumFields +// FieldNameLengths []byte + +// //len = sum(FieldNameLengths) + NumFields +// //array of nul-terminated strings +// FieldNames []byte + +// //len = TableNameLen + 1, nul-terminated string +// TableName []byte + +// //len = SchemaLen + 1, nul-terminated string +// SchemaName []byte + +// //string.NUL +// FileName []byte +// } + +// type NewLoadEvent struct { +// SlaveProxyID uint32 +// ExecTime uint32 +// SkipLines uint32 +// TableNameLen uint8 +// SchemaLen uint8 +// NumFields uint32 +// FieldTermLen uint8 +// FieldTerm []byte +// EnclosedByLen uint8 +// EnclosedBy []byte +// LineTermLen uint8 +// LineTerm []byte +// LineStartLen uint8 +// LineStart []byte +// EscapedByLen uint8 +// EscapedBy []byte +// OptFlags uint8 + +// //len = 1 * NumFields +// FieldNameLengths []byte + +// //len = sum(FieldNameLengths) + NumFields +// //array of nul-terminated strings +// FieldNames []byte + +// //len = TableNameLen, nul-terminated string +// TableName []byte + +// //len = SchemaLen, nul-terminated string +// SchemaName []byte + +// //string.EOF +// FileName []byte +// } + +// type CreateFileEvent struct { +// FileID uint32 +// BlockData []byte +// } + +// type AppendBlockEvent struct { +// FileID uint32 +// BlockData []byte +// } + +// type ExecLoadEvent struct { +// FileID uint32 +// } + +// type BeginLoadQueryEvent struct { +// FileID uint32 +// BlockData []byte +// } + +// type ExecuteLoadQueryEvent struct { +// SlaveProxyID uint32 +// ExecutionTime uint32 +// SchemaLength uint8 +// ErrorCode uint16 +// StatusVarsLength uint16 + +// FileID uint32 +// StartPos uint32 +// EndPos uint32 +// DupHandlingFlags uint8 +// } + +// type DeleteFileEvent struct { +// FileID uint32 +// } + +// type RandEvent struct { +// Seed1 uint64 +// Seed2 uint64 +// } + +// type IntVarEvent struct { +// Type uint8 +// Value uint64 +// } + +// type UserVarEvent struct { +// NameLength uint32 +// Name []byte +// IsNull uint8 + +// //if not is null +// Type uint8 +// Charset uint32 +// ValueLength uint32 +// Value []byte + +// //if more data +// Flags uint8 +// } + +// type IncidentEvent struct { +// Type uint16 +// MessageLength uint8 +// Message []byte +// } + +// type HeartbeatEvent struct { +// } diff --git a/vendor/github.com/siddontang/go-mysql/replication/parser.go b/vendor/github.com/siddontang/go-mysql/replication/parser.go new file mode 100644 index 0000000..4610ab9 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/parser.go @@ -0,0 +1,266 @@ +package replication + +import ( + "bytes" + "fmt" + "io" + "os" + + "github.com/juju/errors" +) + +type BinlogParser struct { + format *FormatDescriptionEvent + + tables map[uint64]*TableMapEvent + + // for rawMode, we only parse FormatDescriptionEvent and RotateEvent + rawMode bool +} + +func NewBinlogParser() *BinlogParser { + p := new(BinlogParser) + + p.tables = make(map[uint64]*TableMapEvent) + + return p +} + +type OnEventFunc func(*BinlogEvent) error + +func (p *BinlogParser) ParseFile(name string, offset int64, onEvent OnEventFunc) error { + f, err := os.Open(name) + if err != nil { + return errors.Trace(err) + } + defer f.Close() + + b := make([]byte, 4) + if _, err = f.Read(b); err != nil { + return errors.Trace(err) + } else if !bytes.Equal(b, BinLogFileHeader) { + return errors.Errorf("%s is not a valid binlog file, head 4 bytes must fe'bin' ", name) + } + + if offset < 4 { + offset = 4 + } + + if _, err = f.Seek(offset, os.SEEK_SET); err != nil { + return errors.Errorf("seek %s to %d error %v", name, offset, err) + } + + return p.ParseReader(f, onEvent) +} + +func (p *BinlogParser) ParseReader(r io.Reader, onEvent OnEventFunc) error { + p.tables = make(map[uint64]*TableMapEvent) + p.format = nil + + var err error + var n int64 + + for { + headBuf := make([]byte, EventHeaderSize) + + if _, err = io.ReadFull(r, headBuf); err == io.EOF { + return nil + } else if err != nil { + return errors.Trace(err) + } + + var h *EventHeader + h, err = p.parseHeader(headBuf) + if err != nil { + return errors.Trace(err) + } + + if h.EventSize <= uint32(EventHeaderSize) { + return errors.Errorf("invalid event header, event size is %d, too small", h.EventSize) + + } + + var buf bytes.Buffer + if n, err = io.CopyN(&buf, r, int64(h.EventSize)-int64(EventHeaderSize)); err != nil { + return errors.Errorf("get event body err %v, need %d - %d, but got %d", err, h.EventSize, EventHeaderSize, n) + } + + data := buf.Bytes() + rawData := data + + eventLen := int(h.EventSize) - EventHeaderSize + + if len(data) != eventLen { + return errors.Errorf("invalid data size %d in event %s, less event length %d", len(data), h.EventType, eventLen) + } + + var e Event + e, err = p.parseEvent(h, data) + if err != nil { + break + } + + if err = onEvent(&BinlogEvent{rawData, h, e}); err != nil { + return errors.Trace(err) + } + } + + return nil +} + +func (p *BinlogParser) SetRawMode(mode bool) { + p.rawMode = mode +} + +func (p *BinlogParser) parseHeader(data []byte) (*EventHeader, error) { + h := new(EventHeader) + err := h.Decode(data) + if err != nil { + return nil, err + } + + return h, nil +} + +func (p *BinlogParser) parseEvent(h *EventHeader, data []byte) (Event, error) { + var e Event + + if h.EventType == FORMAT_DESCRIPTION_EVENT { + p.format = &FormatDescriptionEvent{} + e = p.format + } else { + if p.format != nil && p.format.ChecksumAlgorithm == BINLOG_CHECKSUM_ALG_CRC32 { + data = data[0 : len(data)-4] + } + + if h.EventType == ROTATE_EVENT { + e = &RotateEvent{} + } else if !p.rawMode { + switch h.EventType { + case QUERY_EVENT: + e = &QueryEvent{} + case XID_EVENT: + e = &XIDEvent{} + case TABLE_MAP_EVENT: + te := &TableMapEvent{} + if p.format.EventTypeHeaderLengths[TABLE_MAP_EVENT-1] == 6 { + te.tableIDSize = 4 + } else { + te.tableIDSize = 6 + } + e = te + case WRITE_ROWS_EVENTv0, + UPDATE_ROWS_EVENTv0, + DELETE_ROWS_EVENTv0, + WRITE_ROWS_EVENTv1, + DELETE_ROWS_EVENTv1, + UPDATE_ROWS_EVENTv1, + WRITE_ROWS_EVENTv2, + UPDATE_ROWS_EVENTv2, + DELETE_ROWS_EVENTv2: + e = p.newRowsEvent(h) + case ROWS_QUERY_EVENT: + e = &RowsQueryEvent{} + case GTID_EVENT: + e = >IDEvent{} + case BEGIN_LOAD_QUERY_EVENT: + e = &BeginLoadQueryEvent{} + case EXECUTE_LOAD_QUERY_EVENT: + e = &ExecuteLoadQueryEvent{} + case MARIADB_ANNOTATE_ROWS_EVENT: + e = &MariadbAnnotaeRowsEvent{} + case MARIADB_BINLOG_CHECKPOINT_EVENT: + e = &MariadbBinlogCheckPointEvent{} + case MARIADB_GTID_LIST_EVENT: + e = &MariadbGTIDListEvent{} + case MARIADB_GTID_EVENT: + ee := &MariadbGTIDEvent{} + ee.GTID.ServerID = h.ServerID + e = ee + default: + e = &GenericEvent{} + } + } else { + e = &GenericEvent{} + } + } + + if err := e.Decode(data); err != nil { + return nil, &EventError{h, err.Error(), data} + } + + if te, ok := e.(*TableMapEvent); ok { + p.tables[te.TableID] = te + } + + //If MySQL restart, it may use the same table id for different tables. + //We must clear the table map before parsing new events. + //We have no better way to known whether the event is before or after restart, + //So we have to clear the table map on every rotate event. + if _, ok := e.(*RotateEvent); ok { + p.tables = make(map[uint64]*TableMapEvent) + } + + return e, nil +} + +func (p *BinlogParser) parse(data []byte) (*BinlogEvent, error) { + rawData := data + + h, err := p.parseHeader(data) + + if err != nil { + return nil, err + } + + data = data[EventHeaderSize:] + eventLen := int(h.EventSize) - EventHeaderSize + + if len(data) != eventLen { + return nil, fmt.Errorf("invalid data size %d in event %s, less event length %d", len(data), h.EventType, eventLen) + } + + e, err := p.parseEvent(h, data) + if err != nil { + return nil, err + } + + return &BinlogEvent{rawData, h, e}, nil +} + +func (p *BinlogParser) newRowsEvent(h *EventHeader) *RowsEvent { + e := &RowsEvent{} + if p.format.EventTypeHeaderLengths[h.EventType-1] == 6 { + e.tableIDSize = 4 + } else { + e.tableIDSize = 6 + } + + e.needBitmap2 = false + e.tables = p.tables + + switch h.EventType { + case WRITE_ROWS_EVENTv0: + e.Version = 0 + case UPDATE_ROWS_EVENTv0: + e.Version = 0 + case DELETE_ROWS_EVENTv0: + e.Version = 0 + case WRITE_ROWS_EVENTv1: + e.Version = 1 + case DELETE_ROWS_EVENTv1: + e.Version = 1 + case UPDATE_ROWS_EVENTv1: + e.Version = 1 + e.needBitmap2 = true + case WRITE_ROWS_EVENTv2: + e.Version = 2 + case UPDATE_ROWS_EVENTv2: + e.Version = 2 + e.needBitmap2 = true + case DELETE_ROWS_EVENTv2: + e.Version = 2 + } + + return e +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/parser_test.go b/vendor/github.com/siddontang/go-mysql/replication/parser_test.go new file mode 100644 index 0000000..ba1c96e --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/parser_test.go @@ -0,0 +1,42 @@ +package replication + +import ( + . "gopkg.in/check.v1" +) + +func (t *testSyncerSuite) TestIndexOutOfRange(c *C) { + parser := NewBinlogParser() + + parser.format = &FormatDescriptionEvent{ + Version: 0x4, + ServerVersion: []uint8{0x35, 0x2e, 0x36, 0x2e, 0x32, 0x30, 0x2d, 0x6c, 0x6f, 0x67, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + CreateTimestamp: 0x0, + EventHeaderLength: 0x13, + EventTypeHeaderLengths: []uint8{0x38, 0xd, 0x0, 0x8, 0x0, 0x12, 0x0, 0x4, 0x4, 0x4, 0x4, 0x12, 0x0, 0x0, 0x5c, 0x0, 0x4, 0x1a, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x2, 0x0, 0x0, 0x0, 0xa, 0xa, 0xa, 0x19, 0x19, 0x0}, + ChecksumAlgorithm: 0x1, + } + + parser.tables = map[uint64]*TableMapEvent{ + 0x3043b: &TableMapEvent{tableIDSize: 6, TableID: 0x3043b, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x61, 0x70, 0x70, 0x5f, 0x63, 0x72, 0x6f, 0x6e}, ColumnCount: 0x15, ColumnType: []uint8{0x3, 0xf, 0xc, 0xc, 0xf, 0x3, 0xc, 0x3, 0xfc, 0xf, 0x1, 0xfe, 0x2, 0xc, 0xf, 0xf, 0xc, 0xf, 0xf, 0x3, 0xf}, ColumnMeta: []uint16{0x0, 0x180, 0x0, 0x0, 0x2fd, 0x0, 0x0, 0x0, 0x2, 0x180, 0x0, 0xfe78, 0x0, 0x0, 0x180, 0x180, 0x0, 0x180, 0x180, 0x0, 0x2fd}, NullBitmap: []uint8{0xf8, 0xfb, 0x17}}, + 0x30453: &TableMapEvent{tableIDSize: 6, TableID: 0x30453, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x73, 0x74, 0x67, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70}, ColumnCount: 0x36, ColumnType: []uint8{0x3, 0x3, 0x3, 0x3, 0x3, 0xf, 0xf, 0x8, 0x3, 0x3, 0x3, 0xf, 0xf, 0x1, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xfe, 0x12, 0xf, 0xf, 0xf, 0xf6, 0x1, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xfe, 0xf6, 0x12, 0x3, 0xf, 0xf, 0x1, 0x1, 0x12, 0xf, 0xf, 0xf, 0xf, 0x3, 0xf, 0x3}, ColumnMeta: []uint16{0x0, 0x0, 0x0, 0x0, 0x0, 0x2fd, 0x12c, 0x0, 0x0, 0x0, 0x0, 0x180, 0x180, 0x0, 0x30, 0x180, 0x180, 0x180, 0x30, 0xc0, 0xfe03, 0x0, 0x180, 0x180, 0x180, 0xc02, 0x0, 0x5a, 0x5a, 0x5a, 0x5a, 0x2fd, 0x2fd, 0x2fd, 0xc0, 0x12c, 0x30, 0xc, 0xfe06, 0xb02, 0x0, 0x0, 0x180, 0x180, 0x0, 0x0, 0x0, 0x180, 0x180, 0x2d, 0x2fd, 0x0, 0x2fd, 0x0}, NullBitmap: []uint8{0xee, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x17}}, + 0x30504: &TableMapEvent{tableIDSize: 6, TableID: 0x30504, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x67, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70}, ColumnCount: 0x13, ColumnType: []uint8{0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0xf, 0xc, 0xc, 0xc, 0xf, 0xf, 0x3, 0xf}, ColumnMeta: []uint16{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x180, 0x0, 0x0, 0x0, 0x180, 0x180, 0x0, 0x2fd}, NullBitmap: []uint8{0x6, 0xfb, 0x5}}, + 0x30450: &TableMapEvent{tableIDSize: 6, TableID: 0x30450, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74}, ColumnCount: 0x16, ColumnType: []uint8{0x3, 0xfc, 0xc, 0x3, 0xc, 0xf, 0x3, 0xf, 0xc, 0xf, 0xf, 0xf, 0xf, 0x3, 0xc, 0xf, 0xf, 0xf, 0xf, 0x3, 0x3, 0xf}, ColumnMeta: []uint16{0x0, 0x2, 0x0, 0x0, 0x0, 0x2d, 0x0, 0x180, 0x0, 0x180, 0x180, 0x2fd, 0x2d, 0x0, 0x0, 0x180, 0x180, 0x2fd, 0x2d, 0x0, 0x0, 0x2fd}, NullBitmap: []uint8{0xfe, 0xff, 0x2f}}, + 0x305bb: &TableMapEvent{tableIDSize: 6, TableID: 0x305bb, Flags: 0x1, Schema: []uint8{0x79, 0x6d, 0x63, 0x61, 0x63, 0x68, 0x67, 0x6f}, Table: []uint8{0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x6c, 0x6f, 0x67}, ColumnCount: 0x11, ColumnType: []uint8{0x3, 0x3, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xc, 0xf, 0xf, 0xc, 0xf, 0xf, 0x3, 0xf}, ColumnMeta: []uint16{0x0, 0x0, 0x2fd, 0x12c, 0x2fd, 0x2fd, 0x2d, 0x12c, 0x2fd, 0x0, 0x180, 0x180, 0x0, 0x180, 0x180, 0x0, 0x2fd}, NullBitmap: []uint8{0xfe, 0x7f, 0x1}}, + 0x16c36b: &TableMapEvent{tableIDSize: 6, TableID: 0x16c36b, Flags: 0x1, Schema: []uint8{0x61, 0x63, 0x70}, Table: []uint8{0x73, 0x74, 0x67, 0x5f, 0x6d, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x32}, ColumnCount: 0xe, ColumnType: []uint8{0x8, 0x8, 0x3, 0x3, 0x2, 0x2, 0xf, 0x12, 0xf, 0xf, 0x12, 0xf, 0xf, 0xf}, ColumnMeta: []uint16{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x0, 0x180, 0x180, 0x0, 0x180, 0x180, 0x2fd}, NullBitmap: []uint8{0xba, 0x3f}}, + 0x16c368: &TableMapEvent{tableIDSize: 6, TableID: 0x16c368, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x73, 0x74, 0x67, 0x5f, 0x6d, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x32}, ColumnCount: 0xe, ColumnType: []uint8{0x8, 0x8, 0x3, 0x3, 0x2, 0x2, 0xf, 0x12, 0xf, 0xf, 0x12, 0xf, 0xf, 0xf}, ColumnMeta: []uint16{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x0, 0x180, 0x180, 0x0, 0x180, 0x180, 0x2fd}, NullBitmap: []uint8{0xba, 0x3f}}, + 0x3045a: &TableMapEvent{tableIDSize: 6, TableID: 0x3045a, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x63, 0x6f, 0x6e, 0x73}, ColumnCount: 0x1e, ColumnType: []uint8{0x3, 0x3, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xfe, 0x12, 0xf, 0xf, 0xf, 0xf6, 0xf, 0xf, 0xf, 0xf, 0x1, 0x1, 0x1, 0x12, 0xf, 0xf, 0x12, 0xf, 0xf, 0x3, 0xf, 0x1}, ColumnMeta: []uint16{0x0, 0x0, 0x30, 0x180, 0x180, 0x180, 0x30, 0xc0, 0xfe03, 0x0, 0x180, 0x180, 0x180, 0xc02, 0x180, 0x180, 0x180, 0x180, 0x0, 0x0, 0x0, 0x0, 0x180, 0x180, 0x0, 0x180, 0x180, 0x0, 0x2fd, 0x0}, NullBitmap: []uint8{0xfc, 0xff, 0xe3, 0x37}}, + 0x3045f: &TableMapEvent{tableIDSize: 6, TableID: 0x3045f, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x63, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72}, ColumnCount: 0x19, ColumnType: []uint8{0x3, 0x3, 0x3, 0x1, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xfe, 0x3, 0xc, 0x1, 0xc, 0xf, 0xf, 0xc, 0xf, 0xf, 0x3, 0xf, 0x4, 0x4}, ColumnMeta: []uint16{0x0, 0x0, 0x0, 0x0, 0x2fd, 0x2fd, 0x2fd, 0xc0, 0x12c, 0x30, 0xc, 0xfe06, 0x0, 0x0, 0x0, 0x0, 0x180, 0x180, 0x0, 0x180, 0x180, 0x0, 0x2fd, 0x4, 0x4}, NullBitmap: []uint8{0xf0, 0xef, 0x5f, 0x0}}, + 0x3065f: &TableMapEvent{tableIDSize: 6, TableID: 0x3065f, Flags: 0x1, Schema: []uint8{0x73, 0x65, 0x69, 0x75, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72}, Table: []uint8{0x63, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x70, 0x65, 0x61, 0x6b, 0x6f, 0x75, 0x74, 0x5f, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72}, ColumnCount: 0xd, ColumnType: []uint8{0x3, 0x3, 0x3, 0x3, 0x1, 0x12, 0xf, 0xf, 0x12, 0xf, 0xf, 0x3, 0xf}, ColumnMeta: []uint16{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x180, 0x180, 0x0, 0x180, 0x180, 0x0, 0x2fd}, NullBitmap: []uint8{0xe0, 0x17}}, + } + + _, err := parser.parse([]byte{ + /* 0x00, */ 0xc1, 0x86, 0x8e, 0x55, 0x1e, 0xa5, 0x14, 0x80, 0xa, 0x55, 0x0, 0x0, 0x0, 0x7, 0xc, + 0xbf, 0xe, 0x0, 0x0, 0x5f, 0x6, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0xd, 0xff, + 0x0, 0x0, 0x19, 0x63, 0x7, 0x0, 0xca, 0x61, 0x5, 0x0, 0x5e, 0xf7, 0xc, 0x0, 0xf5, 0x7, + 0x0, 0x0, 0x1, 0x99, 0x96, 0x76, 0x74, 0xdd, 0x10, 0x0, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70, + 0x5f, 0x64, 0x62, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6, 0x0, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0xb1, 0x3c, 0x38, 0xcb, + }) + + c.Assert(err, IsNil) +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/replication_test.go b/vendor/github.com/siddontang/go-mysql/replication/replication_test.go new file mode 100644 index 0000000..4061089 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/replication_test.go @@ -0,0 +1,283 @@ +package replication + +import ( + "flag" + "fmt" + "os" + "sync" + "testing" + "time" + + "github.com/siddontang/go-mysql/client" + "github.com/siddontang/go-mysql/mysql" + . "gopkg.in/check.v1" +) + +// Use docker mysql to test, mysql is 3306, mariadb is 3316 +var testHost = flag.String("host", "127.0.0.1", "MySQL master host") + +var testOutputLogs = flag.Bool("out", true, "output binlog event") + +func TestBinLogSyncer(t *testing.T) { + TestingT(t) +} + +type testSyncerSuite struct { + b *BinlogSyncer + c *client.Conn + + wg sync.WaitGroup + + flavor string +} + +var _ = Suite(&testSyncerSuite{}) + +func (t *testSyncerSuite) SetUpSuite(c *C) { + +} + +func (t *testSyncerSuite) TearDownSuite(c *C) { +} + +func (t *testSyncerSuite) SetUpTest(c *C) { +} + +func (t *testSyncerSuite) TearDownTest(c *C) { + if t.b != nil { + t.b.Close() + t.b = nil + } + + if t.c != nil { + t.c.Close() + t.c = nil + } +} + +func (t *testSyncerSuite) testExecute(c *C, query string) { + _, err := t.c.Execute(query) + c.Assert(err, IsNil) +} + +func (t *testSyncerSuite) testSync(c *C, s *BinlogStreamer) { + t.wg.Add(1) + go func() { + defer t.wg.Done() + + if s == nil { + return + } + + for { + e, err := s.GetEventTimeout(2 * time.Second) + if err != nil { + if err != ErrGetEventTimeout { + c.Fatal(err) + } + return + } + + if *testOutputLogs { + e.Dump(os.Stdout) + os.Stdout.Sync() + } + } + }() + + //use mixed format + t.testExecute(c, "SET SESSION binlog_format = 'MIXED'") + + str := `DROP TABLE IF EXISTS test_replication` + t.testExecute(c, str) + + str = `CREATE TABLE IF NOT EXISTS test_replication ( + id BIGINT(64) UNSIGNED NOT NULL AUTO_INCREMENT, + str VARCHAR(256), + f FLOAT, + d DOUBLE, + de DECIMAL(10,2), + i INT, + bi BIGINT, + e enum ("e1", "e2"), + b BIT(8), + y YEAR, + da DATE, + ts TIMESTAMP, + dt DATETIME, + tm TIME, + t TEXT, + bb BLOB, + se SET('a', 'b', 'c'), + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8` + + t.testExecute(c, str) + + //use row format + t.testExecute(c, "SET SESSION binlog_format = 'ROW'") + + t.testExecute(c, `INSERT INTO test_replication (str, f, i, e, b, y, da, ts, dt, tm, de, t, bb, se) + VALUES ("3", -3.14, 10, "e1", 0b0011, 1985, + "2012-05-07", "2012-05-07 14:01:01", "2012-05-07 14:01:01", + "14:01:01", -45363.64, "abc", "12345", "a,b")`) + + id := 100 + + if t.flavor == mysql.MySQLFlavor { + t.testExecute(c, "SET SESSION binlog_row_image = 'MINIMAL'") + + t.testExecute(c, fmt.Sprintf(`INSERT INTO test_replication (id, str, f, i, bb, de) VALUES (%d, "4", -3.14, 100, "abc", -45635.64)`, id)) + t.testExecute(c, fmt.Sprintf(`UPDATE test_replication SET f = -12.14, de = 555.34 WHERE id = %d`, id)) + t.testExecute(c, fmt.Sprintf(`DELETE FROM test_replication WHERE id = %d`, id)) + } + + t.wg.Wait() +} + +func (t *testSyncerSuite) setupTest(c *C, flavor string) { + var port uint16 = 3306 + switch flavor { + case mysql.MariaDBFlavor: + port = 3316 + } + + t.flavor = flavor + + var err error + if t.c != nil { + t.c.Close() + } + + t.c, err = client.Connect(fmt.Sprintf("%s:%d", *testHost, port), "root", "", "") + if err != nil { + c.Skip(err.Error()) + } + + // _, err = t.c.Execute("CREATE DATABASE IF NOT EXISTS test") + // c.Assert(err, IsNil) + + _, err = t.c.Execute("USE test") + c.Assert(err, IsNil) + + if t.b != nil { + t.b.Close() + } + + t.b = NewBinlogSyncer(100, flavor) + + err = t.b.RegisterSlave(*testHost, port, "root", "") + c.Assert(err, IsNil) +} + +func (t *testSyncerSuite) testPositionSync(c *C) { + //get current master binlog file and position + r, err := t.c.Execute("SHOW MASTER STATUS") + c.Assert(err, IsNil) + binFile, _ := r.GetString(0, 0) + binPos, _ := r.GetInt(0, 1) + + s, err := t.b.StartSync(mysql.Position{binFile, uint32(binPos)}) + c.Assert(err, IsNil) + + t.testSync(c, s) +} + +func (t *testSyncerSuite) TestMysqlPositionSync(c *C) { + t.setupTest(c, mysql.MySQLFlavor) + t.testPositionSync(c) +} + +func (t *testSyncerSuite) TestMysqlGTIDSync(c *C) { + t.setupTest(c, mysql.MySQLFlavor) + + r, err := t.c.Execute("SELECT @@gtid_mode") + c.Assert(err, IsNil) + modeOn, _ := r.GetString(0, 0) + if modeOn != "ON" { + c.Skip("GTID mode is not ON") + } + + masterUuid, err := t.b.GetMasterUUID() + c.Assert(err, IsNil) + + set, _ := mysql.ParseMysqlGTIDSet(fmt.Sprintf("%s:%d-%d", masterUuid.String(), 1, 2)) + + s, err := t.b.StartSyncGTID(set) + c.Assert(err, IsNil) + + t.testSync(c, s) +} + +func (t *testSyncerSuite) TestMariadbPositionSync(c *C) { + t.setupTest(c, mysql.MariaDBFlavor) + + t.testPositionSync(c) +} + +func (t *testSyncerSuite) TestMariadbGTIDSync(c *C) { + t.setupTest(c, mysql.MariaDBFlavor) + + // get current master gtid binlog pos + r, err := t.c.Execute("SELECT @@gtid_binlog_pos") + c.Assert(err, IsNil) + + str, _ := r.GetString(0, 0) + set, _ := mysql.ParseMariadbGTIDSet(str) + + s, err := t.b.StartSyncGTID(set) + c.Assert(err, IsNil) + + t.testSync(c, s) +} + +func (t *testSyncerSuite) TestMysqlSemiPositionSync(c *C) { + t.setupTest(c, mysql.MySQLFlavor) + + err := t.b.EnableSemiSync() + if err != nil { + c.Skip(fmt.Sprintf("mysql doest not support semi synchronous replication %v", err)) + } + + t.testPositionSync(c) +} + +func (t *testSyncerSuite) TestMysqlBinlogCodec(c *C) { + t.setupTest(c, mysql.MySQLFlavor) + + t.testExecute(c, "RESET MASTER") + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + + t.testSync(c, nil) + + t.testExecute(c, "FLUSH LOGS") + + t.testSync(c, nil) + }() + + os.RemoveAll("./var") + + err := t.b.StartBackup("./var", mysql.Position{"", uint32(0)}, 2*time.Second) + c.Check(err, Equals, ErrGetEventTimeout) + + p := NewBinlogParser() + + f := func(e *BinlogEvent) error { + if *testOutputLogs { + e.Dump(os.Stdout) + os.Stdout.Sync() + } + return nil + } + + err = p.ParseFile("./var/mysql.000001", 0, f) + c.Assert(err, IsNil) + + err = p.ParseFile("./var/mysql.000002", 0, f) + c.Assert(err, IsNil) +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/row_event.go b/vendor/github.com/siddontang/go-mysql/replication/row_event.go new file mode 100644 index 0000000..3fe8598 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/row_event.go @@ -0,0 +1,778 @@ +package replication + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "strconv" + "time" + + "github.com/juju/errors" + . "github.com/siddontang/go-mysql/mysql" + "github.com/siddontang/go/hack" +) + +type TableMapEvent struct { + tableIDSize int + + TableID uint64 + + Flags uint16 + + Schema []byte + Table []byte + + ColumnCount uint64 + ColumnType []byte + ColumnMeta []uint16 + + //len = (ColumnCount + 7) / 8 + NullBitmap []byte +} + +func (e *TableMapEvent) Decode(data []byte) error { + pos := 0 + e.TableID = FixedLengthInt(data[0:e.tableIDSize]) + pos += e.tableIDSize + + e.Flags = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + schemaLength := data[pos] + pos++ + + e.Schema = data[pos : pos+int(schemaLength)] + pos += int(schemaLength) + + //skip 0x00 + pos++ + + tableLength := data[pos] + pos++ + + e.Table = data[pos : pos+int(tableLength)] + pos += int(tableLength) + + //skip 0x00 + pos++ + + var n int + e.ColumnCount, _, n = LengthEncodedInt(data[pos:]) + pos += n + + e.ColumnType = data[pos : pos+int(e.ColumnCount)] + pos += int(e.ColumnCount) + + var err error + var metaData []byte + if metaData, _, n, err = LengthEnodedString(data[pos:]); err != nil { + return errors.Trace(err) + } + + if err = e.decodeMeta(metaData); err != nil { + return errors.Trace(err) + } + + pos += n + + if len(data[pos:]) != bitmapByteSize(int(e.ColumnCount)) { + return io.EOF + } + + e.NullBitmap = data[pos:] + + return nil +} + +func bitmapByteSize(columnCount int) int { + return int(columnCount+7) / 8 +} + +// see mysql sql/log_event.h +/* + 0 byte + MYSQL_TYPE_DECIMAL + MYSQL_TYPE_TINY + MYSQL_TYPE_SHORT + MYSQL_TYPE_LONG + MYSQL_TYPE_NULL + MYSQL_TYPE_TIMESTAMP + MYSQL_TYPE_LONGLONG + MYSQL_TYPE_INT24 + MYSQL_TYPE_DATE + MYSQL_TYPE_TIME + MYSQL_TYPE_DATETIME + MYSQL_TYPE_YEAR + + 1 byte + MYSQL_TYPE_FLOAT + MYSQL_TYPE_DOUBLE + MYSQL_TYPE_BLOB + MYSQL_TYPE_GEOMETRY + + //maybe + MYSQL_TYPE_TIME2 + MYSQL_TYPE_DATETIME2 + MYSQL_TYPE_TIMESTAMP2 + + 2 byte + MYSQL_TYPE_VARCHAR + MYSQL_TYPE_BIT + MYSQL_TYPE_NEWDECIMAL + MYSQL_TYPE_VAR_STRING + MYSQL_TYPE_STRING + + This enumeration value is only used internally and cannot exist in a binlog. + MYSQL_TYPE_NEWDATE + MYSQL_TYPE_ENUM + MYSQL_TYPE_SET + MYSQL_TYPE_TINY_BLOB + MYSQL_TYPE_MEDIUM_BLOB + MYSQL_TYPE_LONG_BLOB +*/ +func (e *TableMapEvent) decodeMeta(data []byte) error { + pos := 0 + e.ColumnMeta = make([]uint16, e.ColumnCount) + for i, t := range e.ColumnType { + switch t { + case MYSQL_TYPE_STRING: + var x uint16 = uint16(data[pos]) << 8 //real type + x += uint16(data[pos+1]) //pack or field length + e.ColumnMeta[i] = x + pos += 2 + case MYSQL_TYPE_NEWDECIMAL: + var x uint16 = uint16(data[pos]) << 8 //precision + x += uint16(data[pos+1]) //decimals + e.ColumnMeta[i] = x + pos += 2 + case MYSQL_TYPE_VAR_STRING, + MYSQL_TYPE_VARCHAR, + MYSQL_TYPE_BIT: + e.ColumnMeta[i] = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + case MYSQL_TYPE_BLOB, + MYSQL_TYPE_DOUBLE, + MYSQL_TYPE_FLOAT, + MYSQL_TYPE_GEOMETRY: + e.ColumnMeta[i] = uint16(data[pos]) + pos++ + case MYSQL_TYPE_TIME2, + MYSQL_TYPE_DATETIME2, + MYSQL_TYPE_TIMESTAMP2: + e.ColumnMeta[i] = uint16(data[pos]) + pos++ + case MYSQL_TYPE_NEWDATE, + MYSQL_TYPE_ENUM, + MYSQL_TYPE_SET, + MYSQL_TYPE_TINY_BLOB, + MYSQL_TYPE_MEDIUM_BLOB, + MYSQL_TYPE_LONG_BLOB: + return errors.Errorf("unsupport type in binlog %d", t) + default: + e.ColumnMeta[i] = 0 + } + } + + return nil +} + +func (e *TableMapEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "TableID: %d\n", e.TableID) + fmt.Fprintf(w, "Flags: %d\n", e.Flags) + fmt.Fprintf(w, "Schema: %s\n", e.Schema) + fmt.Fprintf(w, "Table: %s\n", e.Table) + fmt.Fprintf(w, "Column count: %d\n", e.ColumnCount) + fmt.Fprintf(w, "Column type: \n%s", hex.Dump(e.ColumnType)) + fmt.Fprintf(w, "NULL bitmap: \n%s", hex.Dump(e.NullBitmap)) + fmt.Fprintln(w) +} + +type RowsEvent struct { + //0, 1, 2 + Version int + + tableIDSize int + tables map[uint64]*TableMapEvent + needBitmap2 bool + + Table *TableMapEvent + + TableID uint64 + + Flags uint16 + + //if version == 2 + ExtraData []byte + + //lenenc_int + ColumnCount uint64 + //len = (ColumnCount + 7) / 8 + ColumnBitmap1 []byte + + //if UPDATE_ROWS_EVENTv1 or v2 + //len = (ColumnCount + 7) / 8 + ColumnBitmap2 []byte + + //rows: invalid: int64, float64, bool, []byte, string + Rows [][]interface{} +} + +func (e *RowsEvent) Decode(data []byte) error { + pos := 0 + e.TableID = FixedLengthInt(data[0:e.tableIDSize]) + pos += e.tableIDSize + + e.Flags = binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + if e.Version == 2 { + dataLen := binary.LittleEndian.Uint16(data[pos:]) + pos += 2 + + e.ExtraData = data[pos : pos+int(dataLen-2)] + pos += int(dataLen - 2) + } + + var n int + e.ColumnCount, _, n = LengthEncodedInt(data[pos:]) + pos += n + + bitCount := bitmapByteSize(int(e.ColumnCount)) + e.ColumnBitmap1 = data[pos : pos+bitCount] + pos += bitCount + + if e.needBitmap2 { + e.ColumnBitmap2 = data[pos : pos+bitCount] + pos += bitCount + } + + var ok bool + e.Table, ok = e.tables[e.TableID] + if !ok { + return errors.Errorf("invalid table id %d, no correspond table map event", e.TableID) + } + + var err error + + // ... repeat rows until event-end + // Why using pos < len(data) - 1 instead of origin pos < len(data) to check? + // See mysql + // https://github.com/mysql/mysql-server/blob/5.7/sql/log_event.cc#L9006 + // https://github.com/mysql/mysql-server/blob/5.7/sql/log_event.cc#L2492 + // A user panics here but he can't give me more information, and using len(data) - 1 + // fixes this panic, so I will try to construct a test case for this later. + for pos < len(data)-1 { + if n, err = e.decodeRows(data[pos:], e.Table, e.ColumnBitmap1); err != nil { + return errors.Trace(err) + } + pos += n + + if e.needBitmap2 { + if n, err = e.decodeRows(data[pos:], e.Table, e.ColumnBitmap2); err != nil { + return errors.Trace(err) + } + pos += n + } + } + + return nil +} + +func (e *RowsEvent) decodeRows(data []byte, table *TableMapEvent, bitmap []byte) (int, error) { + row := make([]interface{}, e.ColumnCount) + + pos := 0 + + count := (bitCount(bitmap) + 7) / 8 + + nullBitmap := data[pos : pos+count] + pos += count + + nullbitIndex := 0 + + var n int + var err error + for i := 0; i < int(e.ColumnCount); i++ { + if bitGet(bitmap, i) == 0 { + continue + } + + isNull := (uint32(nullBitmap[nullbitIndex/8]) >> uint32(nullbitIndex%8)) & 0x01 + + if isNull > 0 { + row[i] = nil + nullbitIndex++ + continue + } + + row[i], n, err = e.decodeValue(data[pos:], table.ColumnType[i], table.ColumnMeta[i]) + + if err != nil { + return 0, nil + } + pos += n + + nullbitIndex++ + } + + e.Rows = append(e.Rows, row) + return pos, nil +} + +// see mysql sql/log_event.cc log_event_print_value +func (e *RowsEvent) decodeValue(data []byte, tp byte, meta uint16) (v interface{}, n int, err error) { + var length int = 0 + + if tp == MYSQL_TYPE_STRING { + if meta >= 256 { + b0 := uint8(meta >> 8) + b1 := uint8(meta & 0xFF) + + if b0&0x30 != 0x30 { + length = int(uint16(b1) | (uint16((b0&0x30)^0x30) << 4)) + tp = byte(b0 | 0x30) + } else { + length = int(meta & 0xFF) + tp = b0 + } + } else { + length = int(meta) + } + } + + switch tp { + case MYSQL_TYPE_NULL: + return nil, 0, nil + case MYSQL_TYPE_LONG: + n = 4 + v = ParseBinaryInt32(data) + case MYSQL_TYPE_TINY: + n = 1 + v = ParseBinaryInt8(data) + case MYSQL_TYPE_SHORT: + n = 2 + v = ParseBinaryInt16(data) + case MYSQL_TYPE_INT24: + n = 3 + v = ParseBinaryInt24(data) + case MYSQL_TYPE_LONGLONG: + n = 8 + v = ParseBinaryInt64(data) + case MYSQL_TYPE_NEWDECIMAL: + prec := uint8(meta >> 8) + scale := uint8(meta & 0xFF) + v, n, err = decodeDecimal(data, int(prec), int(scale)) + case MYSQL_TYPE_FLOAT: + n = 4 + v = ParseBinaryFloat32(data) + case MYSQL_TYPE_DOUBLE: + n = 8 + v = ParseBinaryFloat64(data) + case MYSQL_TYPE_BIT: + nbits := ((meta >> 8) * 8) + (meta & 0xFF) + n = int(nbits+7) / 8 + + //use int64 for bit + v, err = decodeBit(data, int(nbits), int(n)) + case MYSQL_TYPE_TIMESTAMP: + n = 4 + t := binary.LittleEndian.Uint32(data) + v = time.Unix(int64(t), 0) + case MYSQL_TYPE_TIMESTAMP2: + v, n, err = decodeTimestamp2(data, meta) + case MYSQL_TYPE_DATETIME: + n = 8 + i64 := binary.LittleEndian.Uint64(data) + d := i64 / 1000000 + t := i64 % 1000000 + v = time.Date(int(d/10000), + time.Month((d%10000)/100), + int(d%100), + int(t/10000), + int((t%10000)/100), + int(t%100), + 0, + time.UTC).Format(TimeFormat) + case MYSQL_TYPE_DATETIME2: + v, n, err = decodeDatetime2(data, meta) + case MYSQL_TYPE_TIME: + n = 3 + i32 := uint32(FixedLengthInt(data[0:3])) + if i32 == 0 { + v = "00:00:00" + } else { + sign := "" + if i32 < 0 { + sign = "-" + } + v = fmt.Sprintf("%s%02d:%02d:%02d", sign, i32/10000, (i32%10000)/100, i32%100) + } + case MYSQL_TYPE_TIME2: + v, n, err = decodeTime2(data, meta) + case MYSQL_TYPE_DATE: + n = 3 + i32 := uint32(FixedLengthInt(data[0:3])) + if i32 == 0 { + v = "0000-00-00" + } else { + v = fmt.Sprintf("%04d-%02d-%02d", i32/(16*32), i32/32%16, i32%32) + } + + case MYSQL_TYPE_YEAR: + n = 1 + v = int(data[0]) + 1900 + case MYSQL_TYPE_ENUM: + l := meta & 0xFF + switch l { + case 1: + v = int64(data[0]) + n = 1 + case 2: + v = int64(binary.BigEndian.Uint16(data)) + n = 2 + default: + err = fmt.Errorf("Unknown ENUM packlen=%d", l) + } + case MYSQL_TYPE_SET: + nbits := meta & 0xFF + n = int(nbits+7) / 8 + + v, err = decodeBit(data, int(nbits), n) + case MYSQL_TYPE_BLOB: + switch meta { + case 1: + length = int(data[0]) + v = data[1 : 1+length] + n = length + 1 + case 2: + length = int(binary.LittleEndian.Uint16(data)) + v = data[2 : 2+length] + n = length + 2 + case 3: + length = int(FixedLengthInt(data[0:3])) + v = data[3 : 3+length] + n = length + 3 + case 4: + length = int(binary.LittleEndian.Uint32(data)) + v = data[4 : 4+length] + n = length + 4 + default: + err = fmt.Errorf("invalid blob packlen = %d", meta) + } + case MYSQL_TYPE_VARCHAR, + MYSQL_TYPE_VAR_STRING: + length = int(meta) + v, n = decodeString(data, length) + case MYSQL_TYPE_STRING: + v, n = decodeString(data, length) + default: + err = fmt.Errorf("unsupport type %d in binlog and don't know how to handle", tp) + } + return +} + +func decodeString(data []byte, length int) (v string, n int) { + if length < 256 { + length = int(data[0]) + + n = int(length) + 1 + v = hack.String(data[1:n]) + } else { + length = int(binary.LittleEndian.Uint16(data[0:])) + n = length + 2 + v = hack.String(data[2:n]) + } + + return +} + +const digitsPerInteger int = 9 + +var compressedBytes = []int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4} + +func decodeDecimalDecompressValue(compIndx int, data []byte, mask uint8) (size int, value uint32) { + size = compressedBytes[compIndx] + databuff := make([]byte, size) + for i := 0; i < size; i++ { + databuff[i] = data[i] ^ mask + } + value = uint32(BFixedLengthInt(databuff)) + return +} + +func decodeDecimal(data []byte, precision int, decimals int) (float64, int, error) { + //see python mysql replication and https://github.com/jeremycole/mysql_binlog + integral := (precision - decimals) + uncompIntegral := int(integral / digitsPerInteger) + uncompFractional := int(decimals / digitsPerInteger) + compIntegral := integral - (uncompIntegral * digitsPerInteger) + compFractional := decimals - (uncompFractional * digitsPerInteger) + + binSize := uncompIntegral*4 + compressedBytes[compIntegral] + + uncompFractional*4 + compressedBytes[compFractional] + + buf := make([]byte, binSize) + copy(buf, data[:binSize]) + + //must copy the data for later change + data = buf + + // Support negative + // The sign is encoded in the high bit of the the byte + // But this bit can also be used in the value + value := uint32(data[0]) + var res bytes.Buffer + var mask uint32 = 0 + if value&0x80 == 0 { + mask = uint32((1 << 32) - 1) + res.WriteString("-") + } + + //clear sign + data[0] ^= 0x80 + + pos, value := decodeDecimalDecompressValue(compIntegral, data, uint8(mask)) + res.WriteString(fmt.Sprintf("%d", value)) + + for i := 0; i < uncompIntegral; i++ { + value = binary.BigEndian.Uint32(data[pos:]) ^ mask + pos += 4 + res.WriteString(fmt.Sprintf("%09d", value)) + } + + res.WriteString(".") + + for i := 0; i < uncompFractional; i++ { + value = binary.BigEndian.Uint32(data[pos:]) ^ mask + pos += 4 + res.WriteString(fmt.Sprintf("%09d", value)) + } + + if size, value := decodeDecimalDecompressValue(compFractional, data[pos:], uint8(mask)); size > 0 { + res.WriteString(fmt.Sprintf("%0*d", compFractional, value)) + pos += size + } + + f, err := strconv.ParseFloat(hack.String(res.Bytes()), 64) + return f, pos, err +} + +func decodeBit(data []byte, nbits int, length int) (value int64, err error) { + if nbits > 1 { + switch length { + case 1: + value = int64(data[0]) + case 2: + value = int64(binary.BigEndian.Uint16(data)) + case 3: + value = int64(BFixedLengthInt(data[0:3])) + case 4: + value = int64(binary.BigEndian.Uint32(data)) + case 5: + value = int64(BFixedLengthInt(data[0:5])) + case 6: + value = int64(BFixedLengthInt(data[0:6])) + case 7: + value = int64(BFixedLengthInt(data[0:7])) + case 8: + value = int64(binary.BigEndian.Uint64(data)) + default: + err = fmt.Errorf("invalid bit length %d", length) + } + } else { + if length != 1 { + err = fmt.Errorf("invalid bit length %d", length) + } else { + value = int64(data[0]) + } + } + return +} + +func decodeTimestamp2(data []byte, dec uint16) (string, int, error) { + //get timestamp binary length + n := int(4 + (dec+1)/2) + sec := int64(binary.BigEndian.Uint32(data[0:4])) + usec := int64(0) + switch dec { + case 1, 2: + usec = int64(data[4]) * 10000 + case 3, 4: + usec = int64(binary.BigEndian.Uint16(data[4:])) * 100 + case 5, 6: + usec = int64(BFixedLengthInt(data[4:7])) + } + + if sec == 0 { + return "0000-00-00 00:00:00", n, nil + } + + t := time.Unix(sec, usec*1000) + return t.Format(TimeFormat), n, nil +} + +const DATETIMEF_INT_OFS int64 = 0x8000000000 + +func decodeDatetime2(data []byte, dec uint16) (string, int, error) { + //get datetime binary length + n := int(5 + (dec+1)/2) + + intPart := int64(BFixedLengthInt(data[0:5])) - DATETIMEF_INT_OFS + var frac int64 = 0 + + switch dec { + case 1, 2: + frac = int64(data[5]) * 10000 + case 3, 4: + frac = int64(binary.BigEndian.Uint16(data[5:7])) * 100 + case 5, 6: + frac = int64(BFixedLengthInt(data[5:8])) + } + + if intPart == 0 { + return "0000-00-00 00:00:00", n, nil + } + + tmp := intPart<<24 + frac + //handle sign??? + if tmp < 0 { + tmp = -tmp + } + + //ingore second part, no precision now + //var secPart int64 = tmp % (1 << 24) + ymdhms := tmp >> 24 + + ymd := ymdhms >> 17 + ym := ymd >> 5 + hms := ymdhms % (1 << 17) + + day := int(ymd % (1 << 5)) + month := int(ym % 13) + year := int(ym / 13) + + second := int(hms % (1 << 6)) + minute := int((hms >> 6) % (1 << 6)) + hour := int((hms >> 12)) + + return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second), n, nil +} + +const TIMEF_OFS int64 = 0x800000000000 +const TIMEF_INT_OFS int64 = 0x800000 + +func decodeTime2(data []byte, dec uint16) (string, int, error) { + //time binary length + n := int(3 + (dec+1)/2) + + tmp := int64(0) + intPart := int64(0) + frac := int64(0) + switch dec { + case 1: + case 2: + intPart = int64(BFixedLengthInt(data[0:3])) - TIMEF_INT_OFS + frac = int64(data[3]) + if intPart < 0 && frac > 0 { + /* + Negative values are stored with reverse fractional part order, + for binary sort compatibility. + + Disk value intpart frac Time value Memory value + 800000.00 0 0 00:00:00.00 0000000000.000000 + 7FFFFF.FF -1 255 -00:00:00.01 FFFFFFFFFF.FFD8F0 + 7FFFFF.9D -1 99 -00:00:00.99 FFFFFFFFFF.F0E4D0 + 7FFFFF.00 -1 0 -00:00:01.00 FFFFFFFFFF.000000 + 7FFFFE.FF -1 255 -00:00:01.01 FFFFFFFFFE.FFD8F0 + 7FFFFE.F6 -2 246 -00:00:01.10 FFFFFFFFFE.FE7960 + + Formula to convert fractional part from disk format + (now stored in "frac" variable) to absolute value: "0x100 - frac". + To reconstruct in-memory value, we shift + to the next integer value and then substruct fractional part. + */ + intPart++ /* Shift to the next integer value */ + frac -= 0x100 /* -(0x100 - frac) */ + } + tmp = intPart<<24 + frac*10000 + case 3: + case 4: + intPart = int64(BFixedLengthInt(data[0:3])) - TIMEF_INT_OFS + frac = int64(binary.BigEndian.Uint16(data[3:5])) + if intPart < 0 && frac > 0 { + /* + Fix reverse fractional part order: "0x10000 - frac". + See comments for FSP=1 and FSP=2 above. + */ + intPart++ /* Shift to the next integer value */ + frac -= 0x10000 /* -(0x10000-frac) */ + } + tmp = intPart<<24 + frac*100 + + case 5: + case 6: + tmp = int64(BFixedLengthInt(data[0:6])) - TIMEF_OFS + default: + intPart = int64(BFixedLengthInt(data[0:3])) - TIMEF_INT_OFS + tmp = intPart << 24 + } + + if intPart == 0 { + return "00:00:00", n, nil + } + + hms := int64(0) + sign := "" + if tmp < 0 { + tmp = -tmp + sign = "-" + } + + //ingore second part, no precision now + //var secPart int64 = tmp % (1 << 24) + + hms = tmp >> 24 + + hour := (hms >> 12) % (1 << 10) /* 10 bits starting at 12th */ + minute := (hms >> 6) % (1 << 6) /* 6 bits starting at 6th */ + second := hms % (1 << 6) /* 6 bits starting at 0th */ + // secondPart := tmp % (1 << 24) + + return fmt.Sprintf("%s%02d:%02d:%02d", sign, hour, minute, second), n, nil +} + +func (e *RowsEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "TableID: %d\n", e.TableID) + fmt.Fprintf(w, "Flags: %d\n", e.Flags) + fmt.Fprintf(w, "Column count: %d\n", e.ColumnCount) + + fmt.Fprintf(w, "Values:\n") + for _, rows := range e.Rows { + fmt.Fprintf(w, "--\n") + for j, d := range rows { + if _, ok := d.([]byte); ok { + fmt.Fprintf(w, "%d:%q\n", j, d) + } else { + fmt.Fprintf(w, "%d:%#v\n", j, d) + } + } + } + fmt.Fprintln(w) +} + +type RowsQueryEvent struct { + Query []byte +} + +func (e *RowsQueryEvent) Decode(data []byte) error { + //ignore length byte 1 + e.Query = data[1:] + return nil +} + +func (e *RowsQueryEvent) Dump(w io.Writer) { + fmt.Fprintf(w, "Query: %s\n", e.Query) + fmt.Fprintln(w) +} diff --git a/vendor/github.com/siddontang/go-mysql/replication/row_event_test.go b/vendor/github.com/siddontang/go-mysql/replication/row_event_test.go new file mode 100644 index 0000000..539bab6 --- /dev/null +++ b/vendor/github.com/siddontang/go-mysql/replication/row_event_test.go @@ -0,0 +1,328 @@ +package replication + +import ( + "fmt" + + . "gopkg.in/check.v1" +) + +type testDecodeSuite struct{} + +var _ = Suite(&testDecodeSuite{}) + +type decodeDecimalChecker struct { + *CheckerInfo +} + +func (_ *decodeDecimalChecker) Check(params []interface{}, names []string) (bool, string) { + var test int + val := struct { + Value float64 + Pos int + Err error + EValue float64 + EPos int + EErr error + }{} + + for i, name := range names { + switch name { + case "obtainedValue": + val.Value, _ = params[i].(float64) + case "obtainedPos": + val.Pos, _ = params[i].(int) + case "obtainedErr": + val.Err, _ = params[i].(error) + case "expectedValue": + val.EValue, _ = params[i].(float64) + case "expectedPos": + val.EPos, _ = params[i].(int) + case "expectedErr": + val.EErr, _ = params[i].(error) + case "caseNumber": + test = params[i].(int) + } + } + errorMsgFmt := fmt.Sprintf("For Test %v: ", test) + "Did not get expected %v(%v), got %v instead." + if val.Err != val.EErr { + return false, fmt.Sprintf(errorMsgFmt, "error", val.EErr, val.Err) + } + if val.Pos != val.EPos { + return false, fmt.Sprintf(errorMsgFmt, "position", val.EPos, val.Pos) + } + if val.Value != val.EValue { + return false, fmt.Sprintf(errorMsgFmt, "value", val.EValue, val.Value) + } + return true, "" +} + +var DecodeDecimalsEquals = &decodeDecimalChecker{ + &CheckerInfo{Name: "Equals", Params: []string{"obtainedValue", "obtainedPos", "obtainedErr", "expectedValue", "expectedPos", "expectedErr", "caseNumber"}}, +} + +func (_ *testDecodeSuite) TestDecodeDecimal(c *C) { + // _PLACEHOLDER_ := 0 + testcases := []struct { + Data []byte + Precision int + Decimals int + Expected float64 + ExpectedPos int + ExpectedErr error + }{ + // These are cases from the mysql test cases + /* + -- Generated with gentestsql.go -- + DROP TABLE IF EXISTS decodedecimal; + CREATE TABLE decodedecimal ( + id int(11) not null auto_increment, + v4_2 decimal(4,2), + v5_0 decimal(5,0), + v7_3 decimal(7,3), + v10_2 decimal(10,2), + v10_3 decimal(10,3), + v13_2 decimal(13,2), + v15_14 decimal(15,14), + v20_10 decimal(20,10), + v30_5 decimal(30,5), + v30_20 decimal(30,20), + v30_25 decimal(30,25), + prec int(11), + scale int(11), + PRIMARY KEY(id) + ) engine=InnoDB; + INSERT INTO decodedecimal (v4_2,v5_0,v7_3,v10_2,v10_3,v13_2,v15_14,v20_10,v30_5,v30_20,v30_25,prec,scale) VALUES + ("-10.55","-10.55","-10.55","-10.55","-10.55","-10.55","-10.55","-10.55","-10.55","-10.55","-10.55",4,2), + ("0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345","0.0123456789012345678912345",30,25), + ("12345","12345","12345","12345","12345","12345","12345","12345","12345","12345","12345",5,0), + ("12345","12345","12345","12345","12345","12345","12345","12345","12345","12345","12345",10,3), + ("123.45","123.45","123.45","123.45","123.45","123.45","123.45","123.45","123.45","123.45","123.45",10,3), + ("-123.45","-123.45","-123.45","-123.45","-123.45","-123.45","-123.45","-123.45","-123.45","-123.45","-123.45",20,10), + (".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",15,14), + (".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",".00012345000098765",22,20), + (".12345000098765",".12345000098765",".12345000098765",".12345000098765",".12345000098765",".12345000098765",".12345000098765",".12345000098765",".12345000098765",".12345000098765",".12345000098765",30,20), + ("-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765","-.000000012345000098765",30,20), + ("1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5","1234500009876.5",30,5), + ("111111111.11","111111111.11","111111111.11","111111111.11","111111111.11","111111111.11","111111111.11","111111111.11","111111111.11","111111111.11","111111111.11",10,2), + ("000000000.01","000000000.01","000000000.01","000000000.01","000000000.01","000000000.01","000000000.01","000000000.01","000000000.01","000000000.01","000000000.01",7,3), + ("123.4","123.4","123.4","123.4","123.4","123.4","123.4","123.4","123.4","123.4","123.4",10,2), + ("-562.58","-562.58","-562.58","-562.58","-562.58","-562.58","-562.58","-562.58","-562.58","-562.58","-562.58",13,2), + ("-3699.01","-3699.01","-3699.01","-3699.01","-3699.01","-3699.01","-3699.01","-3699.01","-3699.01","-3699.01","-3699.01",13,2), + ("-1948.14","-1948.14","-1948.14","-1948.14","-1948.14","-1948.14","-1948.14","-1948.14","-1948.14","-1948.14","-1948.14",13,2) + ; + select * from decodedecimal; + +----+--------+-------+-----------+-------------+-------------+----------------+-------------------+-----------------------+---------------------+---------------------------------+---------------------------------+------+-------+ + | id | v4_2 | v5_0 | v7_3 | v10_2 | v10_3 | v13_2 | v15_14 | v20_10 | v30_5 | v30_20 | v30_25 | prec | scale | + +----+--------+-------+-----------+-------------+-------------+----------------+-------------------+-----------------------+---------------------+---------------------------------+---------------------------------+------+-------+ + | 1 | -10.55 | -11 | -10.550 | -10.55 | -10.550 | -10.55 | -9.99999999999999 | -10.5500000000 | -10.55000 | -10.55000000000000000000 | -10.5500000000000000000000000 | 4 | 2 | + | 2 | 0.01 | 0 | 0.012 | 0.01 | 0.012 | 0.01 | 0.01234567890123 | 0.0123456789 | 0.01235 | 0.01234567890123456789 | 0.0123456789012345678912345 | 30 | 25 | + | 3 | 99.99 | 12345 | 9999.999 | 12345.00 | 12345.000 | 12345.00 | 9.99999999999999 | 12345.0000000000 | 12345.00000 | 12345.00000000000000000000 | 12345.0000000000000000000000000 | 5 | 0 | + | 4 | 99.99 | 12345 | 9999.999 | 12345.00 | 12345.000 | 12345.00 | 9.99999999999999 | 12345.0000000000 | 12345.00000 | 12345.00000000000000000000 | 12345.0000000000000000000000000 | 10 | 3 | + | 5 | 99.99 | 123 | 123.450 | 123.45 | 123.450 | 123.45 | 9.99999999999999 | 123.4500000000 | 123.45000 | 123.45000000000000000000 | 123.4500000000000000000000000 | 10 | 3 | + | 6 | -99.99 | -123 | -123.450 | -123.45 | -123.450 | -123.45 | -9.99999999999999 | -123.4500000000 | -123.45000 | -123.45000000000000000000 | -123.4500000000000000000000000 | 20 | 10 | + | 7 | 0.00 | 0 | 0.000 | 0.00 | 0.000 | 0.00 | 0.00012345000099 | 0.0001234500 | 0.00012 | 0.00012345000098765000 | 0.0001234500009876500000000 | 15 | 14 | + | 8 | 0.00 | 0 | 0.000 | 0.00 | 0.000 | 0.00 | 0.00012345000099 | 0.0001234500 | 0.00012 | 0.00012345000098765000 | 0.0001234500009876500000000 | 22 | 20 | + | 9 | 0.12 | 0 | 0.123 | 0.12 | 0.123 | 0.12 | 0.12345000098765 | 0.1234500010 | 0.12345 | 0.12345000098765000000 | 0.1234500009876500000000000 | 30 | 20 | + | 10 | 0.00 | 0 | 0.000 | 0.00 | 0.000 | 0.00 | -0.00000001234500 | -0.0000000123 | 0.00000 | -0.00000001234500009877 | -0.0000000123450000987650000 | 30 | 20 | + | 11 | 99.99 | 99999 | 9999.999 | 99999999.99 | 9999999.999 | 99999999999.99 | 9.99999999999999 | 9999999999.9999999999 | 1234500009876.50000 | 9999999999.99999999999999999999 | 99999.9999999999999999999999999 | 30 | 5 | + | 12 | 99.99 | 99999 | 9999.999 | 99999999.99 | 9999999.999 | 111111111.11 | 9.99999999999999 | 111111111.1100000000 | 111111111.11000 | 111111111.11000000000000000000 | 99999.9999999999999999999999999 | 10 | 2 | + | 13 | 0.01 | 0 | 0.010 | 0.01 | 0.010 | 0.01 | 0.01000000000000 | 0.0100000000 | 0.01000 | 0.01000000000000000000 | 0.0100000000000000000000000 | 7 | 3 | + | 14 | 99.99 | 123 | 123.400 | 123.40 | 123.400 | 123.40 | 9.99999999999999 | 123.4000000000 | 123.40000 | 123.40000000000000000000 | 123.4000000000000000000000000 | 10 | 2 | + | 15 | -99.99 | -563 | -562.580 | -562.58 | -562.580 | -562.58 | -9.99999999999999 | -562.5800000000 | -562.58000 | -562.58000000000000000000 | -562.5800000000000000000000000 | 13 | 2 | + | 16 | -99.99 | -3699 | -3699.010 | -3699.01 | -3699.010 | -3699.01 | -9.99999999999999 | -3699.0100000000 | -3699.01000 | -3699.01000000000000000000 | -3699.0100000000000000000000000 | 13 | 2 | + | 17 | -99.99 | -1948 | -1948.140 | -1948.14 | -1948.140 | -1948.14 | -9.99999999999999 | -1948.1400000000 | -1948.14000 | -1948.14000000000000000000 | -1948.1400000000000000000000000 | 13 | 2 | + +----+--------+-------+-----------+-------------+-------------+----------------+-------------------+-----------------------+---------------------+---------------------------------+---------------------------------+------+-------+ + */ + {[]byte{117, 200, 127, 255}, 4, 2, float64(-10.55), 2, nil}, + {[]byte{127, 255, 244, 127, 245}, 5, 0, float64(-11), 3, nil}, + {[]byte{127, 245, 253, 217, 127, 255}, 7, 3, float64(-10.550), 4, nil}, + {[]byte{127, 255, 255, 245, 200, 127, 255}, 10, 2, float64(-10.55), 5, nil}, + {[]byte{127, 255, 255, 245, 253, 217, 127, 255}, 10, 3, float64(-10.550), 6, nil}, + {[]byte{127, 255, 255, 255, 245, 200, 118, 196}, 13, 2, float64(-10.55), 6, nil}, + {[]byte{118, 196, 101, 54, 0, 254, 121, 96, 127, 255}, 15, 14, float64(-9.99999999999999), 8, nil}, + {[]byte{127, 255, 255, 255, 245, 223, 55, 170, 127, 255, 127, 255}, 20, 10, float64(-10.5500000000), 10, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, 255, 41, 39, 127, 255}, 30, 5, float64(-10.55000), 15, nil}, + {[]byte{127, 255, 255, 255, 245, 223, 55, 170, 127, 255, 255, 255, 255, 255, 127, 255}, 30, 20, float64(-10.55000000000000000000), 14, nil}, + {[]byte{127, 255, 245, 223, 55, 170, 127, 255, 255, 255, 255, 255, 255, 255, 255, 4, 0}, 30, 25, float64(-10.5500000000000000000000000), 15, nil}, + {[]byte{128, 1, 128, 0}, 4, 2, float64(0.01), 2, nil}, + {[]byte{128, 0, 0, 128, 0}, 5, 0, float64(0), 3, nil}, + {[]byte{128, 0, 0, 12, 128, 0}, 7, 3, float64(0.012), 4, nil}, + {[]byte{128, 0, 0, 0, 1, 128, 0}, 10, 2, float64(0.01), 5, nil}, + {[]byte{128, 0, 0, 0, 0, 12, 128, 0}, 10, 3, float64(0.012), 6, nil}, + {[]byte{128, 0, 0, 0, 0, 1, 128, 0}, 13, 2, float64(0.01), 6, nil}, + {[]byte{128, 0, 188, 97, 78, 1, 96, 11, 128, 0}, 15, 14, float64(0.01234567890123), 8, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 188, 97, 78, 9, 128, 0}, 20, 10, float64(0.0123456789), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 211, 128, 0}, 30, 5, float64(0.01235), 15, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 188, 97, 78, 53, 183, 191, 135, 89, 128, 0}, 30, 20, float64(0.01234567890123456789), 14, nil}, + {[]byte{128, 0, 0, 0, 188, 97, 78, 53, 183, 191, 135, 0, 135, 253, 217, 30, 0}, 30, 25, float64(0.0123456789012345678912345), 15, nil}, + {[]byte{227, 99, 128, 48}, 4, 2, float64(99.99), 2, nil}, + {[]byte{128, 48, 57, 167, 15}, 5, 0, float64(12345), 3, nil}, + {[]byte{167, 15, 3, 231, 128, 0}, 7, 3, float64(9999.999), 4, nil}, + {[]byte{128, 0, 48, 57, 0, 128, 0}, 10, 2, float64(12345.00), 5, nil}, + {[]byte{128, 0, 48, 57, 0, 0, 128, 0}, 10, 3, float64(12345.000), 6, nil}, + {[]byte{128, 0, 0, 48, 57, 0, 137, 59}, 13, 2, float64(12345.00), 6, nil}, + {[]byte{137, 59, 154, 201, 255, 1, 134, 159, 128, 0}, 15, 14, float64(9.99999999999999), 8, nil}, + {[]byte{128, 0, 0, 48, 57, 0, 0, 0, 0, 0, 128, 0}, 20, 10, float64(12345.0000000000), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 57, 0, 0, 0, 128, 0}, 30, 5, float64(12345.00000), 15, nil}, + {[]byte{128, 0, 0, 48, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 48}, 30, 20, float64(12345.00000000000000000000), 14, nil}, + {[]byte{128, 48, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0}, 30, 25, float64(12345.0000000000000000000000000), 15, nil}, + {[]byte{227, 99, 128, 48}, 4, 2, float64(99.99), 2, nil}, + {[]byte{128, 48, 57, 167, 15}, 5, 0, float64(12345), 3, nil}, + {[]byte{167, 15, 3, 231, 128, 0}, 7, 3, float64(9999.999), 4, nil}, + {[]byte{128, 0, 48, 57, 0, 128, 0}, 10, 2, float64(12345.00), 5, nil}, + {[]byte{128, 0, 48, 57, 0, 0, 128, 0}, 10, 3, float64(12345.000), 6, nil}, + {[]byte{128, 0, 0, 48, 57, 0, 137, 59}, 13, 2, float64(12345.00), 6, nil}, + {[]byte{137, 59, 154, 201, 255, 1, 134, 159, 128, 0}, 15, 14, float64(9.99999999999999), 8, nil}, + {[]byte{128, 0, 0, 48, 57, 0, 0, 0, 0, 0, 128, 0}, 20, 10, float64(12345.0000000000), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 57, 0, 0, 0, 128, 0}, 30, 5, float64(12345.00000), 15, nil}, + {[]byte{128, 0, 0, 48, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 48}, 30, 20, float64(12345.00000000000000000000), 14, nil}, + {[]byte{128, 48, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0}, 30, 25, float64(12345.0000000000000000000000000), 15, nil}, + {[]byte{227, 99, 128, 0}, 4, 2, float64(99.99), 2, nil}, + {[]byte{128, 0, 123, 128, 123}, 5, 0, float64(123), 3, nil}, + {[]byte{128, 123, 1, 194, 128, 0}, 7, 3, float64(123.450), 4, nil}, + {[]byte{128, 0, 0, 123, 45, 128, 0}, 10, 2, float64(123.45), 5, nil}, + {[]byte{128, 0, 0, 123, 1, 194, 128, 0}, 10, 3, float64(123.450), 6, nil}, + {[]byte{128, 0, 0, 0, 123, 45, 137, 59}, 13, 2, float64(123.45), 6, nil}, + {[]byte{137, 59, 154, 201, 255, 1, 134, 159, 128, 0}, 15, 14, float64(9.99999999999999), 8, nil}, + {[]byte{128, 0, 0, 0, 123, 26, 210, 116, 128, 0, 128, 0}, 20, 10, float64(123.4500000000), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 0, 175, 200, 128, 0}, 30, 5, float64(123.45000), 15, nil}, + {[]byte{128, 0, 0, 0, 123, 26, 210, 116, 128, 0, 0, 0, 0, 0, 128, 0}, 30, 20, float64(123.45000000000000000000), 14, nil}, + {[]byte{128, 0, 123, 26, 210, 116, 128, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0}, 30, 25, float64(123.4500000000000000000000000), 15, nil}, + {[]byte{28, 156, 127, 255}, 4, 2, float64(-99.99), 2, nil}, + {[]byte{127, 255, 132, 127, 132}, 5, 0, float64(-123), 3, nil}, + {[]byte{127, 132, 254, 61, 127, 255}, 7, 3, float64(-123.450), 4, nil}, + {[]byte{127, 255, 255, 132, 210, 127, 255}, 10, 2, float64(-123.45), 5, nil}, + {[]byte{127, 255, 255, 132, 254, 61, 127, 255}, 10, 3, float64(-123.450), 6, nil}, + {[]byte{127, 255, 255, 255, 132, 210, 118, 196}, 13, 2, float64(-123.45), 6, nil}, + {[]byte{118, 196, 101, 54, 0, 254, 121, 96, 127, 255}, 15, 14, float64(-9.99999999999999), 8, nil}, + {[]byte{127, 255, 255, 255, 132, 229, 45, 139, 127, 255, 127, 255}, 20, 10, float64(-123.4500000000), 10, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 132, 255, 80, 55, 127, 255}, 30, 5, float64(-123.45000), 15, nil}, + {[]byte{127, 255, 255, 255, 132, 229, 45, 139, 127, 255, 255, 255, 255, 255, 127, 255}, 30, 20, float64(-123.45000000000000000000), 14, nil}, + {[]byte{127, 255, 132, 229, 45, 139, 127, 255, 255, 255, 255, 255, 255, 255, 255, 20, 0}, 30, 25, float64(-123.4500000000000000000000000), 15, nil}, + {[]byte{128, 0, 128, 0}, 4, 2, float64(0.00), 2, nil}, + {[]byte{128, 0, 0, 128, 0}, 5, 0, float64(0), 3, nil}, + {[]byte{128, 0, 0, 0, 128, 0}, 7, 3, float64(0.000), 4, nil}, + {[]byte{128, 0, 0, 0, 0, 128, 0}, 10, 2, float64(0.00), 5, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 128, 0}, 10, 3, float64(0.000), 6, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 128, 0}, 13, 2, float64(0.00), 6, nil}, + {[]byte{128, 0, 1, 226, 58, 0, 0, 99, 128, 0}, 15, 14, float64(0.00012345000099), 8, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 1, 226, 58, 0, 128, 0}, 20, 10, float64(0.0001234500), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 128, 0}, 30, 5, float64(0.00012), 15, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 1, 226, 58, 0, 15, 18, 2, 0, 128, 0}, 30, 20, float64(0.00012345000098765000), 14, nil}, + {[]byte{128, 0, 0, 0, 1, 226, 58, 0, 15, 18, 2, 0, 0, 0, 0, 15, 0}, 30, 25, float64(0.0001234500009876500000000), 15, nil}, + {[]byte{128, 0, 128, 0}, 4, 2, float64(0.00), 2, nil}, + {[]byte{128, 0, 0, 128, 0}, 5, 0, float64(0), 3, nil}, + {[]byte{128, 0, 0, 0, 128, 0}, 7, 3, float64(0.000), 4, nil}, + {[]byte{128, 0, 0, 0, 0, 128, 0}, 10, 2, float64(0.00), 5, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 128, 0}, 10, 3, float64(0.000), 6, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 128, 0}, 13, 2, float64(0.00), 6, nil}, + {[]byte{128, 0, 1, 226, 58, 0, 0, 99, 128, 0}, 15, 14, float64(0.00012345000099), 8, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 1, 226, 58, 0, 128, 0}, 20, 10, float64(0.0001234500), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 128, 0}, 30, 5, float64(0.00012), 15, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 1, 226, 58, 0, 15, 18, 2, 0, 128, 0}, 30, 20, float64(0.00012345000098765000), 14, nil}, + {[]byte{128, 0, 0, 0, 1, 226, 58, 0, 15, 18, 2, 0, 0, 0, 0, 22, 0}, 30, 25, float64(0.0001234500009876500000000), 15, nil}, + {[]byte{128, 12, 128, 0}, 4, 2, float64(0.12), 2, nil}, + {[]byte{128, 0, 0, 128, 0}, 5, 0, float64(0), 3, nil}, + {[]byte{128, 0, 0, 123, 128, 0}, 7, 3, float64(0.123), 4, nil}, + {[]byte{128, 0, 0, 0, 12, 128, 0}, 10, 2, float64(0.12), 5, nil}, + {[]byte{128, 0, 0, 0, 0, 123, 128, 0}, 10, 3, float64(0.123), 6, nil}, + {[]byte{128, 0, 0, 0, 0, 12, 128, 7}, 13, 2, float64(0.12), 6, nil}, + {[]byte{128, 7, 91, 178, 144, 1, 129, 205, 128, 0}, 15, 14, float64(0.12345000098765), 8, nil}, + {[]byte{128, 0, 0, 0, 0, 7, 91, 178, 145, 0, 128, 0}, 20, 10, float64(0.1234500010), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 57, 128, 0}, 30, 5, float64(0.12345), 15, nil}, + {[]byte{128, 0, 0, 0, 0, 7, 91, 178, 144, 58, 222, 87, 208, 0, 128, 0}, 30, 20, float64(0.12345000098765000000), 14, nil}, + {[]byte{128, 0, 0, 7, 91, 178, 144, 58, 222, 87, 208, 0, 0, 0, 0, 30, 0}, 30, 25, float64(0.1234500009876500000000000), 15, nil}, + {[]byte{128, 0, 128, 0}, 4, 2, float64(0.00), 2, nil}, + {[]byte{128, 0, 0, 128, 0}, 5, 0, float64(0), 3, nil}, + {[]byte{128, 0, 0, 0, 128, 0}, 7, 3, float64(0.000), 4, nil}, + {[]byte{128, 0, 0, 0, 0, 128, 0}, 10, 2, float64(0.00), 5, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 128, 0}, 10, 3, float64(0.000), 6, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 127, 255}, 13, 2, float64(0.00), 6, nil}, + {[]byte{127, 255, 255, 255, 243, 255, 121, 59, 127, 255}, 15, 14, float64(-0.00000001234500), 8, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 255, 255, 243, 252, 128, 0}, 20, 10, float64(-0.0000000123), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255}, 30, 5, float64(0.00000), 15, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 255, 255, 243, 235, 111, 183, 93, 178, 127, 255}, 30, 20, float64(-0.00000001234500009877), 14, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 243, 235, 111, 183, 93, 255, 139, 69, 47, 30, 0}, 30, 25, float64(-0.0000000123450000987650000), 15, nil}, + {[]byte{227, 99, 129, 134}, 4, 2, float64(99.99), 2, nil}, + {[]byte{129, 134, 159, 167, 15}, 5, 0, float64(99999), 3, nil}, + {[]byte{167, 15, 3, 231, 133, 245}, 7, 3, float64(9999.999), 4, nil}, + {[]byte{133, 245, 224, 255, 99, 128, 152}, 10, 2, float64(99999999.99), 5, nil}, + {[]byte{128, 152, 150, 127, 3, 231, 227, 59}, 10, 3, float64(9999999.999), 6, nil}, + {[]byte{227, 59, 154, 201, 255, 99, 137, 59}, 13, 2, float64(99999999999.99), 6, nil}, + {[]byte{137, 59, 154, 201, 255, 1, 134, 159, 137, 59}, 15, 14, float64(9.99999999999999), 8, nil}, + {[]byte{137, 59, 154, 201, 255, 59, 154, 201, 255, 9, 128, 0}, 20, 10, float64(9999999999.9999999999), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 4, 210, 29, 205, 139, 148, 0, 195, 80, 137, 59}, 30, 5, float64(1234500009876.50000), 15, nil}, + {[]byte{137, 59, 154, 201, 255, 59, 154, 201, 255, 59, 154, 201, 255, 99, 129, 134}, 30, 20, float64(9999999999.99999999999999999999), 14, nil}, + {[]byte{129, 134, 159, 59, 154, 201, 255, 59, 154, 201, 255, 0, 152, 150, 127, 30, 0}, 30, 25, float64(99999.9999999999999999999999999), 15, nil}, + {[]byte{227, 99, 129, 134}, 4, 2, float64(99.99), 2, nil}, + {[]byte{129, 134, 159, 167, 15}, 5, 0, float64(99999), 3, nil}, + {[]byte{167, 15, 3, 231, 133, 245}, 7, 3, float64(9999.999), 4, nil}, + {[]byte{133, 245, 224, 255, 99, 128, 152}, 10, 2, float64(99999999.99), 5, nil}, + {[]byte{128, 152, 150, 127, 3, 231, 128, 6}, 10, 3, float64(9999999.999), 6, nil}, + {[]byte{128, 6, 159, 107, 199, 11, 137, 59}, 13, 2, float64(111111111.11), 6, nil}, + {[]byte{137, 59, 154, 201, 255, 1, 134, 159, 128, 6}, 15, 14, float64(9.99999999999999), 8, nil}, + {[]byte{128, 6, 159, 107, 199, 6, 142, 119, 128, 0, 128, 0}, 20, 10, float64(111111111.1100000000), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 6, 159, 107, 199, 0, 42, 248, 128, 6}, 30, 5, float64(111111111.11000), 15, nil}, + {[]byte{128, 6, 159, 107, 199, 6, 142, 119, 128, 0, 0, 0, 0, 0, 129, 134}, 30, 20, float64(111111111.11000000000000000000), 14, nil}, + {[]byte{129, 134, 159, 59, 154, 201, 255, 59, 154, 201, 255, 0, 152, 150, 127, 10, 0}, 30, 25, float64(99999.9999999999999999999999999), 15, nil}, + {[]byte{128, 1, 128, 0}, 4, 2, float64(0.01), 2, nil}, + {[]byte{128, 0, 0, 128, 0}, 5, 0, float64(0), 3, nil}, + {[]byte{128, 0, 0, 10, 128, 0}, 7, 3, float64(0.010), 4, nil}, + {[]byte{128, 0, 0, 0, 1, 128, 0}, 10, 2, float64(0.01), 5, nil}, + {[]byte{128, 0, 0, 0, 0, 10, 128, 0}, 10, 3, float64(0.010), 6, nil}, + {[]byte{128, 0, 0, 0, 0, 1, 128, 0}, 13, 2, float64(0.01), 6, nil}, + {[]byte{128, 0, 152, 150, 128, 0, 0, 0, 128, 0}, 15, 14, float64(0.01000000000000), 8, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 152, 150, 128, 0, 128, 0}, 20, 10, float64(0.0100000000), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 232, 128, 0}, 30, 5, float64(0.01000), 15, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 152, 150, 128, 0, 0, 0, 0, 0, 128, 0}, 30, 20, float64(0.01000000000000000000), 14, nil}, + {[]byte{128, 0, 0, 0, 152, 150, 128, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0}, 30, 25, float64(0.0100000000000000000000000), 15, nil}, + {[]byte{227, 99, 128, 0}, 4, 2, float64(99.99), 2, nil}, + {[]byte{128, 0, 123, 128, 123}, 5, 0, float64(123), 3, nil}, + {[]byte{128, 123, 1, 144, 128, 0}, 7, 3, float64(123.400), 4, nil}, + {[]byte{128, 0, 0, 123, 40, 128, 0}, 10, 2, float64(123.40), 5, nil}, + {[]byte{128, 0, 0, 123, 1, 144, 128, 0}, 10, 3, float64(123.400), 6, nil}, + {[]byte{128, 0, 0, 0, 123, 40, 137, 59}, 13, 2, float64(123.40), 6, nil}, + {[]byte{137, 59, 154, 201, 255, 1, 134, 159, 128, 0}, 15, 14, float64(9.99999999999999), 8, nil}, + {[]byte{128, 0, 0, 0, 123, 23, 215, 132, 0, 0, 128, 0}, 20, 10, float64(123.4000000000), 10, nil}, + {[]byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 0, 156, 64, 128, 0}, 30, 5, float64(123.40000), 15, nil}, + {[]byte{128, 0, 0, 0, 123, 23, 215, 132, 0, 0, 0, 0, 0, 0, 128, 0}, 30, 20, float64(123.40000000000000000000), 14, nil}, + {[]byte{128, 0, 123, 23, 215, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0}, 30, 25, float64(123.4000000000000000000000000), 15, nil}, + {[]byte{28, 156, 127, 253}, 4, 2, float64(-99.99), 2, nil}, + {[]byte{127, 253, 204, 125, 205}, 5, 0, float64(-563), 3, nil}, + {[]byte{125, 205, 253, 187, 127, 255}, 7, 3, float64(-562.580), 4, nil}, + {[]byte{127, 255, 253, 205, 197, 127, 255}, 10, 2, float64(-562.58), 5, nil}, + {[]byte{127, 255, 253, 205, 253, 187, 127, 255}, 10, 3, float64(-562.580), 6, nil}, + {[]byte{127, 255, 255, 253, 205, 197, 118, 196}, 13, 2, float64(-562.58), 6, nil}, + {[]byte{118, 196, 101, 54, 0, 254, 121, 96, 127, 255}, 15, 14, float64(-9.99999999999999), 8, nil}, + {[]byte{127, 255, 255, 253, 205, 221, 109, 230, 255, 255, 127, 255}, 20, 10, float64(-562.5800000000), 10, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 205, 255, 29, 111, 127, 255}, 30, 5, float64(-562.58000), 15, nil}, + {[]byte{127, 255, 255, 253, 205, 221, 109, 230, 255, 255, 255, 255, 255, 255, 127, 253}, 30, 20, float64(-562.58000000000000000000), 14, nil}, + {[]byte{127, 253, 205, 221, 109, 230, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0}, 30, 25, float64(-562.5800000000000000000000000), 15, nil}, + {[]byte{28, 156, 127, 241}, 4, 2, float64(-99.99), 2, nil}, + {[]byte{127, 241, 140, 113, 140}, 5, 0, float64(-3699), 3, nil}, + {[]byte{113, 140, 255, 245, 127, 255}, 7, 3, float64(-3699.010), 4, nil}, + {[]byte{127, 255, 241, 140, 254, 127, 255}, 10, 2, float64(-3699.01), 5, nil}, + {[]byte{127, 255, 241, 140, 255, 245, 127, 255}, 10, 3, float64(-3699.010), 6, nil}, + {[]byte{127, 255, 255, 241, 140, 254, 118, 196}, 13, 2, float64(-3699.01), 6, nil}, + {[]byte{118, 196, 101, 54, 0, 254, 121, 96, 127, 255}, 15, 14, float64(-9.99999999999999), 8, nil}, + {[]byte{127, 255, 255, 241, 140, 255, 103, 105, 127, 255, 127, 255}, 20, 10, float64(-3699.0100000000), 10, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 140, 255, 252, 23, 127, 255}, 30, 5, float64(-3699.01000), 15, nil}, + {[]byte{127, 255, 255, 241, 140, 255, 103, 105, 127, 255, 255, 255, 255, 255, 127, 241}, 30, 20, float64(-3699.01000000000000000000), 14, nil}, + {[]byte{127, 241, 140, 255, 103, 105, 127, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0}, 30, 25, float64(-3699.0100000000000000000000000), 15, nil}, + {[]byte{28, 156, 127, 248}, 4, 2, float64(-99.99), 2, nil}, + {[]byte{127, 248, 99, 120, 99}, 5, 0, float64(-1948), 3, nil}, + {[]byte{120, 99, 255, 115, 127, 255}, 7, 3, float64(-1948.140), 4, nil}, + {[]byte{127, 255, 248, 99, 241, 127, 255}, 10, 2, float64(-1948.14), 5, nil}, + {[]byte{127, 255, 248, 99, 255, 115, 127, 255}, 10, 3, float64(-1948.140), 6, nil}, + {[]byte{127, 255, 255, 248, 99, 241, 118, 196}, 13, 2, float64(-1948.14), 6, nil}, + {[]byte{118, 196, 101, 54, 0, 254, 121, 96, 127, 255}, 15, 14, float64(-9.99999999999999), 8, nil}, + {[]byte{127, 255, 255, 248, 99, 247, 167, 196, 255, 255, 127, 255}, 20, 10, float64(-1948.1400000000), 10, nil}, + {[]byte{127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 99, 255, 201, 79, 127, 255}, 30, 5, float64(-1948.14000), 15, nil}, + {[]byte{127, 255, 255, 248, 99, 247, 167, 196, 255, 255, 255, 255, 255, 255, 127, 248}, 30, 20, float64(-1948.14000000000000000000), 14, nil}, + {[]byte{127, 248, 99, 247, 167, 196, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0}, 30, 25, float64(-1948.1400000000000000000000000), 15, nil}, + } + for i, tc := range testcases { + value, pos, err := decodeDecimal(tc.Data, tc.Precision, tc.Decimals) + c.Assert(value, DecodeDecimalsEquals, pos, err, tc.Expected, tc.ExpectedPos, tc.ExpectedErr, i) + } +} diff --git a/vendor/github.com/siddontang/go/hack/hack.go b/vendor/github.com/siddontang/go/hack/hack.go new file mode 100644 index 0000000..74ee83c --- /dev/null +++ b/vendor/github.com/siddontang/go/hack/hack.go @@ -0,0 +1,27 @@ +package hack + +import ( + "reflect" + "unsafe" +) + +// no copy to change slice to string +// use your own risk +func String(b []byte) (s string) { + pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) + pstring.Data = pbytes.Data + pstring.Len = pbytes.Len + return +} + +// no copy to change string to slice +// use your own risk +func Slice(s string) (b []byte) { + pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) + pbytes.Data = pstring.Data + pbytes.Len = pstring.Len + pbytes.Cap = pstring.Len + return +} diff --git a/vendor/github.com/siddontang/go/hack/hack_test.go b/vendor/github.com/siddontang/go/hack/hack_test.go new file mode 100644 index 0000000..7b11b0b --- /dev/null +++ b/vendor/github.com/siddontang/go/hack/hack_test.go @@ -0,0 +1,36 @@ +package hack + +import ( + "bytes" + "testing" +) + +func TestString(t *testing.T) { + b := []byte("hello world") + a := String(b) + + if a != "hello world" { + t.Fatal(a) + } + + b[0] = 'a' + + if a != "aello world" { + t.Fatal(a) + } + + b = append(b, "abc"...) + if a != "aello world" { + t.Fatal(a) + } +} + +func TestByte(t *testing.T) { + a := "hello world" + + b := Slice(a) + + if !bytes.Equal(b, []byte("hello world")) { + t.Fatal(string(b)) + } +}